diff --git a/README.md b/README.md index 7a78a08b..6da56a3c 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ $ npm install caver-js@X.X.X Getting Started ================= -If you want to run your own EN (Endpoint Node), see [EN Operation Guide](https://docs.klaytn.com/node/en) to set up. You can also use Klaytn Public EN like below: +If you want to run your own EN (Endpoint Node), see [EN Operation Guide](https://docs.klaytn.com/node/endpoint-node) to set up. You can also use Klaytn Public EN like below: ``` $ node > const Caver = require('caver-js') @@ -73,8 +73,8 @@ Klaytn/vX.X.X/linux-amd64/goX.X.X ## Using caver-js account/wallet You can easily manage your account by using the account / wallet packages provided by caver-js. -[caver.klay.accounts](https://docs.klaytn.com/sdk/caverjs/caver.klay.accounts) package provides functions related to accounts, such as create, signTransaction, and privateKeyToAccount. -[caver.klay.accounts.wallet](https://docs.klaytn.com/sdk/caverjs/caver.klay.accounts#wallet) provides the **in-memory wallet** for easy account management in caver-js. +[caver.klay.accounts](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.klay.accounts) package provides functions related to accounts, such as create, signTransaction, and privateKeyToAccount. +[caver.klay.accounts.wallet](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.klay.accounts#wallet) provides the **in-memory wallet** for easy account management in caver-js. **Note** Functions associated with wallet and account provided by caver-js have no effect on the actual Klaytn network. @@ -101,7 +101,7 @@ You can add to the wallet instance of caver-js using the account object created ``` caver-js supports two types of private key formats. -One is a raw private key format of a 32-byte string type and the other is the [KlaytnWalletKey](https://docs.klaytn.com/klaytn/design/account#klaytn-wallet-key-format). +One is a raw private key format of a 32-byte string type and the other is the [KlaytnWalletKey](https://docs.klaytn.com/klaytn/design/accounts#klaytn-wallet-key-format). You can also add your account using the KlaytnWalletKey format as shown below: ``` @@ -121,7 +121,7 @@ The private key that matches a specific account stored in the wallet instance ca ``` ## Submitting a Transaction -You can use caver-js to submit various types of transactions to a node. Please refer to the [caver.klay.sendTransaction](https://docs.klaytn.com/sdk/caverjs/caver.klay/transaction#sendtransaction) to see how to send a transaction of each type. +You can use caver-js to submit various types of transactions to a node. Please refer to the [caver.klay.sendTransaction](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.klay/transaction#sendtransaction) to see how to send a transaction of each type. You can submit the transaction as shown below, and the result can be confirmed by the returned receipt: ``` @@ -207,19 +207,19 @@ caver-js provides the caver.utils.toPeb function for unit conversion. Please ref Documentation ================= -Documentation can be found at [Klaytn Docs-caver-js](https://docs.klaytn.com/sdk/caverjs). +Documentation can be found at [Klaytn Docs-caver-js](https://docs.klaytn.com/bapp/sdk/caver-js). API Specification ================= The API lists of caver-js are described in folloinwg links: -* [caver.klay](https://docs.klaytn.com/sdk/caverjs/caver.klay) -* [caver.klay.accounts](https://docs.klaytn.com/sdk/caverjs/caver.klay.accounts) -* [caver.klay.contract](https://docs.klaytn.com/sdk/caverjs/caver.klay.contract) -* [caver.klay.net](https://docs.klaytn.com/sdk/caverjs/caver.klay.net) -* [caver.klay.abi](https://docs.klaytn.com/sdk/caverjs/caver.klay.abi) -* [caver.utils](https://docs.klaytn.com/sdk/caverjs/caver.utils) +* [caver.klay](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.klay) +* [caver.klay.accounts](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.klay.accounts) +* [caver.klay.contract](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.klay.contract) +* [caver.klay.net](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.klay.net) +* [caver.klay.abi](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.klay.abi) +* [caver.utils](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.utils) Web3.js Similarity @@ -297,8 +297,8 @@ Sample Projects The BApp (Blockchain Application) Development sample projects using caver-js are the following: -* [Count BApp](https://docs.klaytn.com/tutorials/countbapp) -* [Klaystagram](https://docs.klaytn.com/tutorials/klaystagram) +* [Count BApp](https://docs.klaytn.com/bapp/tutorials/count-bapp) +* [Klaystagram](https://docs.klaytn.com/bapp/tutorials/klaystagram) Github Repository ================= diff --git a/package.json b/package.json index d9593a06..692cde29 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,17 @@ { "name": "caver-js", - "version": "1.1.2", + "version": "1.2.0", "description": "caver-js is a JavaScript API library that allows developers to interact with a Klaytn node", "main": "index.js", "scripts": { - "test": "npm run build && mocha test/packages/caver.klay.utils.js && mocha test/packages/caver.klay.net.js && npm run serTest && npm run walletTest", + "test": "npm run build && mocha test/packages/caver.utils.js && mocha test/packages/caver.klay.net.js && npm run serTest && npm run walletTest", "build-all": "gulp all", "build": "gulp default", - "serTest": "mocha test/transactionType/serializationTest.js && mocha test/compressPublicKey.js && mocha test/encodeContractDeploy.js && mocha test/isCompressedPublicKey.js && mocha test/parseAccountKey.js", + "serTest": "mocha test/transactionType/serializationTest.js && mocha test/compressPublicKey.js && mocha test/encodeContractDeploy.js && mocha test/isCompressedPublicKey.js && mocha test/parseAccountKey.js && mocha test/decodeTransaction.js", "walletTest": "mocha test/accountLib.js && mocha test/accounts.privateKeyToPublicKey.js && mocha test/accounts.recover.js && mocha test/packages/caver.klay.accounts.js && mocha test/getKlaytnWalletKey.js && mocha test/isValidPrivateKey.js && mocha test/privateKeyToAccount.js", - "txTest": "mocha test/estimateComputationCost.js && mocha test/getTransactionReceipt.js && mocha test/getTransaction.js && mocha test/setContractOptions.js && mocha test/encodeContractDeploy.js && mocha test/accounts.signTransaction.js && mocha test/transactionType/legacyTransaction.js && mocha test/transactionType/valueTransfer* && mocha test/transactionType/accountUpdate.js && mocha test/transactionType/contract* && mocha test/transactionType/cancelTransaction.js && mocha test/transactionType/feeDelegated*", - "etcTest": "mocha test/confirmationListener.js && mocha test/hashMessage.js && mocha test/iban.* && mocha test/randomHex.js && mocha test/sha3.js && mocha test/toChecksumAddress.js && mocha test/unitMap.js && mocha test/default* && mocha test/getNodeInfo.js && mocha test/eventEmitter.js && mocha test/packages/caver.klay.net.js && mocha test/getNetworkType.js && mocha test/invalidResponse.js && mocha test/isContractDeployment.js && mocha test/personal.js && mocha test/multiProviderTest.js && mocha test/subscription.js && mocha test/supportsSubscriptions.js && mocha test/contract.once.js", + "txTest": "mocha test/sendSignedTransaction.js && mocha test/estimateComputationCost.js && mocha test/getTransactionReceipt.js && mocha test/getTransaction.js && mocha test/setContractOptions.js && mocha test/encodeContractDeploy.js && mocha test/accounts.signTransaction.js && mocha test/sendTransactionCallback.js && mocha test/signWithMultiSig.js && mocha test/transactionType/legacyTransaction.js && mocha test/transactionType/valueTransfer* && mocha test/transactionType/accountUpdate.js && mocha test/transactionType/contract* && mocha test/transactionType/cancelTransaction.js && mocha test/transactionType/feeDelegated*", + "etcTest": "mocha test/packages/caver.utils.js && mocha test/confirmationListener.js && mocha test/hashMessage.js && mocha test/iban.* && mocha test/randomHex.js && mocha test/sha3.js && mocha test/toChecksumAddress.js && mocha test/unitMap.js && mocha test/default* && mocha test/getNodeInfo.js && mocha test/eventEmitter.js && mocha test/packages/caver.klay.net.js && mocha test/getNetworkType.js && mocha test/invalidResponse.js && mocha test/isContractDeployment.js && mocha test/personal.js && mocha test/multiProviderTest.js && mocha test/subscription.js && mocha test/supportsSubscriptions.js && mocha test/contract.once.js && mocha test/setProvider.js", + "accountKeyTest": "mocha test/scenarioTest/accountKeyPublic.js && mocha test/scenarioTest/accountKeyMultiSig.js && mocha test/scenarioTest/accountKeyRoleBased.js", "intTxTest": "npm run intLEGACYTest && npm run intVTTest && npm run intVTMTest && npm run intACCUPTest && npm run intDEPLTest && npm run intEXETest && npm run intCANCELTest && npm run intFDTest && npm run intFDRTest", "intLEGACYTest": "mocha --grep INT-LEGACY/ test/intTest.js", "intVTTest": "mocha --grep INT-VT/ test/intTest.js", @@ -33,6 +34,14 @@ "klaytn sdk", "klaytn api" ], + "repository": { + "type": "git", + "url": "https://github.com/klaytn/caver-js.git" + }, + "bugs": { + "url": "https://github.com/klaytn/caver-js/issues" + }, + "homepage": "https://github.com/klaytn/caver-js", "author": "Klaytn Team", "license": "LGPL", "dependencies": { @@ -55,7 +64,7 @@ "oboe": "2.1.3", "request": "2.87.0", "requestretry": "^2.0.2", - "scrypt.js": "0.2.0", + "scryptsy": "2.1.0", "underscore": "^1.9.1", "utf8": "2.1.1", "uuid": "2.0.1", diff --git a/packages/caver-core-helpers/src/formatters.js b/packages/caver-core-helpers/src/formatters.js index d2c5e3a1..8ae81146 100644 --- a/packages/caver-core-helpers/src/formatters.js +++ b/packages/caver-core-helpers/src/formatters.js @@ -76,7 +76,9 @@ var _txInputFormatter = function (options){ if (options.to) { options.humanReadable = options.humanReadable !== undefined? options.humanReadable : false if (options.humanReadable) throw new Error('HumanReadableAddress is not supported yet.') - options.to = inputAddressFormatter(options.to) + if (!utils.isContractDeployment(options) || options.to !== '0x') { + options.to = inputAddressFormatter(options.to) + } } if (options.data && options.input) { @@ -140,12 +142,9 @@ var inputTransactionFormatter = function (options) { options = _txInputFormatter(options); - // If 'feePayer' or 'senderRawTransaction' exist in transaction, it means it doesn't need 'from' field. - if (options.feePayer || options.senderRawTransaction) { - if (options.senderRawTransaction === undefined) { - throw new Error('The "senderRawTransaction" field must be defined for signing with feePayer!'); - } - + // If senderRawTransaction' exist in transaction, it means object is fee payer transaction format like below + // { senderRawTransaction: '', feePayer: '' } + if (options.senderRawTransaction) { if (options.feePayer === undefined) { throw new Error('The "feePayer" field must be defined for signing with feePayer!'); } @@ -164,8 +163,8 @@ var inputTransactionFormatter = function (options) { options.from = inputAddressFormatter(options.from); } - if (options.data && options.data.slice(0, 2) !== '0x') { - options.data = '0x' + options.data + if (options.data) { + options.data = utils.addHexPrefix(options.data) } const err = validateParams(options) @@ -198,8 +197,8 @@ var inputPersonalTransactionFormatter = function (options) { options.from = inputAddressFormatter(options.from); } - if (options.data && options.data.slice(0, 2) !== '0x') { - options.data = '0x' + options.data + if (options.data) { + options.data = utils.addHexPrefix(options.data) } return options; @@ -474,7 +473,6 @@ var outputPostFormatter = function(post){ }; var inputAddressFormatter = function (address) { - var iban = new utils.Iban(address); if (iban.isValid() && iban.isDirect()) { return iban.toAddress().toLowerCase(); diff --git a/packages/caver-core-helpers/src/validateFunction.js b/packages/caver-core-helpers/src/validateFunction.js index 614b4b90..7bfe5588 100644 --- a/packages/caver-core-helpers/src/validateFunction.js +++ b/packages/caver-core-helpers/src/validateFunction.js @@ -24,29 +24,50 @@ * @return {Error} */ -const VALID_GAS_PRICE = require('./constants').VALID_GAS_PRICE var utils = require('../../caver-utils') function validateParams (tx) { + let error + + // validate for fee payer transaction format + if (tx.senderRawTransaction) { + if (!tx.feePayer || tx.feePayer === '0x') { + error = new Error(`Invalid fee payer: ${tx.feePayer}`) + } else if (!utils.isAddress(tx.feePayer)) { + error = new Error(`Invalid address of fee payer: ${tx.feePayer}`) + } + return error + } + const isValidateType = validateTxType(tx.type) if (!isValidateType) { return new Error('The transaction type [' + tx.type + '] is not supported') } - var error = validateTxObjectWithType(tx) + error = validateTxObjectWithType(tx) if (error !== undefined) { return error } if (!tx.from) { error = new Error('"from" is missing') + } else if (!utils.isAddress(tx.from)) { + error = new Error(`Invalid address of from: ${tx.from}`) } else if (tx.gas === undefined && tx.gasLimit === undefined) { error = new Error('"gas" is missing') } else if (tx.nonce < 0 || tx.gas < 0 || tx.gasPrice < 0 || tx.chainId < 0) { error = new Error('gas, gasPrice, nonce or chainId is lower than 0') - // } else if (tx.gasPrice !== undefined && tx.gasPrice != VALID_GAS_PRICE) { - // error = new Error(`GasPrice should be a 25Gpeb(${VALID_GAS_PRICE})`); } + + // If feePayerSignatures is set in transaction object, feePayer also should be defined together. + if (tx.feePayerSignatures && !utils.isEmptySig(tx.feePayerSignatures)) { + if (!tx.feePayer || tx.feePayer === '0x') { + error = new Error(`"feePayer" is missing: feePayer must be defined with feePayerSignatures.`) + } else if (!utils.isAddress(tx.feePayer)) { + error = new Error(`Invalid address of fee payer: ${tx.feePayer}`) + } + } + return error } @@ -103,6 +124,8 @@ function validateParams (tx) { switch (cf) { case 0: case 'EVM': + case '0x': + case '0x0': return true } return false @@ -159,6 +182,11 @@ function validateParams (tx) { if (transaction.to === undefined && transaction.data === undefined) { return new Error('contract creation without any data provided') } + + if (transaction.to && transaction.to !== '0x' && !utils.isAddress(transaction.to)) { + return new Error(`Invalid address of to: ${transaction.to}`) + } + if (transaction.codeFormat !== undefined) { return new Error('"codeFormat" cannot be used with LEGACY transaction') } @@ -177,6 +205,9 @@ function validateParams (tx) { if (transaction.feeRatio !== undefined) { return new Error('"feeRatio" cannot be used with '+type+' transaction') } + if (transaction.feePayerSignatures !== undefined) { + return new Error('"feePayerSignatures" cannot be used with '+type+' transaction') + } } function validateFeeDelegated(transaction) { @@ -193,6 +224,9 @@ function validateParams (tx) { function validateNotAccountTransaction(transaction) { const type = transaction.type? transaction.type : 'LEGACY' + if (transaction.key !== undefined) { + return new Error('"key" cannot be used with '+type+' transaction') + } if (transaction.legacyKey !== undefined) { return new Error('"legacyKey" cannot be used with '+type+' transaction') } @@ -219,6 +253,8 @@ function validateParams (tx) { function checkValueTransferEssential(transaction) { if (transaction.to === undefined) { return new Error('"to" is missing') + } else if (!utils.isAddress(transaction.to)) { + return new Error(`Invalid address of to: ${transaction.to}`) } if (transaction.value === undefined) { return new Error('"value" is missing') @@ -258,6 +294,8 @@ function validateParams (tx) { function checkValueTransferMemoEssential(transaction) { if (transaction.to === undefined) { return new Error('"to" is missing') + } else if (!utils.isAddress(transaction.to)) { + return new Error(`Invalid address of to: ${transaction.to}`) } if (transaction.value === undefined) { return new Error('"value" is missing') @@ -302,12 +340,16 @@ function validateParams (tx) { return new Error('"codeFormat" cannot be used with '+transaction.type+' transaction') } - if (transaction.legacyKey === undefined && !transaction.publicKey && !transaction.multisig && !transaction.roleTransactionKey && !transaction.roleAccountUpdateKey && !transaction.roleFeePayerKey && transaction.failKey === undefined) { + if (!transaction.key && transaction.legacyKey === undefined && !transaction.publicKey && !transaction.multisig && !transaction.roleTransactionKey && !transaction.roleAccountUpdateKey && !transaction.roleFeePayerKey && transaction.failKey === undefined) { return new Error('Missing key information with '+transaction.type+' transaction') } const duplicatedKeyInfo = 'The key parameter to be used for '+transaction.type+' is duplicated.' - if (transaction.legacyKey !== undefined) { + if (transaction.key) { + if (transaction.legacyKey !== undefined || transaction.publicKey || transaction.multisig || transaction.roleTransactionKey || transaction.roleAccountUpdateKey || transaction.roleFeePayerKey || transaction.failKey !== undefined) { + return new Error(duplicatedKeyInfo) + } + } else if (transaction.legacyKey !== undefined) { if (transaction.publicKey || transaction.multisig || transaction.roleTransactionKey || transaction.roleAccountUpdateKey || transaction.roleFeePayerKey || transaction.failKey !== undefined) { return new Error(duplicatedKeyInfo) } @@ -366,7 +408,7 @@ function validateParams (tx) { if (transaction.data === undefined) { return new Error('"data" is missing') } - if (transaction.to !== undefined) { + if (transaction.to !== undefined && transaction.to !== '0x') { return new Error('"to" cannot be used with '+transaction.type+' transaction') } if (transaction.codeFormat !== undefined && !validateCodeFormat(transaction.codeFormat)) { @@ -401,6 +443,8 @@ function validateParams (tx) { function checkExecutionEssential(transaction) { if (transaction.to === undefined) { return new Error('"to" is missing') + } else if (!utils.isAddress(transaction.to)) { + return new Error(`Invalid address of to: ${transaction.to}`) } if (transaction.data === undefined) { return new Error('"data" is missing') diff --git a/packages/caver-core-method/src/index.js b/packages/caver-core-method/src/index.js index adcde298..d4497e9b 100644 --- a/packages/caver-core-method/src/index.js +++ b/packages/caver-core-method/src/index.js @@ -246,25 +246,6 @@ function toPayload (args) { return (this.transformPayload && this.transformPayload(payload)) || payload } -var getWallet = function(from, accounts) { - let wallet = null - - // is index given - if (_.isNumber(from)) { - wallet = accounts.wallet[from] - - // is account given - } else if (_.isObject(from) && from.address && from.privateKey) { - wallet = from - - // search in wallet for address - } else { - wallet = accounts.wallet[from.toLowerCase()] - } - - return wallet -} - const buildSendTxCallbackFunc = (defer, method, payload, isSendTx) => (err, result) => { try { result = method.formatOutput(result) } catch (e) { @@ -289,7 +270,7 @@ const buildSendTxCallbackFunc = (defer, method, payload, isSendTx) => (err, resu // return PROMISE if (!isSendTx) { defer.resolve(result) - } else if (!_.isObject(result)) { + } else { defer.eventEmitter.emit('transactionHash', result) method._confirmTransaction(defer, result, payload) } @@ -305,49 +286,82 @@ const buildSendSignedTxFunc = (method, payload, sendTxCallback) => (sign) => { } const buildSendRequestFunc = (defer, sendSignedTx, sendTxCallback) => (payload, method) => { + // Logic for handling multiple cases of parameters in sendSignedTransaction. + // 1. Object containing rawTransaction + // : call 'klay_sendRawTransaction' with RLP encoded transaction(rawTransaction) in object + // 2. A transaction object containing signatures or feePayerSignatures + // : call 'getRawTransactionWithSignatures', then call 'klay_sendRawTransaction' with result of getRawTransactionWithSignatures + if (method && method.accounts && payload.method === 'klay_sendRawTransaction') { + var tx = payload.params[0] + if (typeof tx !== 'string' && _.isObject(tx)) { + if (tx.rawTransaction) { + return sendSignedTx(tx) + } else { + return method.accounts.getRawTransactionWithSignatures(tx).then(sendSignedTx).catch((e) => {sendTxCallback(e)}) + } + } + } + if (method && method.accounts && method.accounts.wallet && method.accounts.wallet.length) { + let error switch (payload.method) { case 'klay_sendTransaction': { var tx = payload.params[0] - - // TODO : Check signTransactionWithSignature function with this logic - // and if need, implement sendTransactionWithSignature function. - // if (tx.signature) { - // return method.accounts.sendTransactionWithSignature(tx).then(sendSignedTx) - // } - - if (tx.senderRawTransaction && tx.from && tx.feePayer){ - console.log('"from" is ignored for a fee-delegated transaction.') - delete tx.from + + let error + if (!_.isObject(tx)) { + error = new Error('The transaction must be defined as an object.') + sendTxCallback(error) + return Promise.reject(error) + } + + let addressToUse = tx.from + + if (tx.senderRawTransaction && tx.feePayer){ + addressToUse = tx.feePayer + if (tx.from) { + console.log('"from" is ignored for a fee-delegated transaction.') + delete tx.from + } } - const wallet = getWallet(_.isObject(tx) && tx.from || tx.feePayer || null, method.accounts) + let wallet + + try { + wallet = method.accounts.wallet.getAccount(addressToUse) + } catch (e) { + sendTxCallback(e) + return Promise.reject(e) + } if (wallet && wallet.privateKey) { + privateKey = method.accounts._getRoleKey(tx, wallet) // If wallet was found, sign tx, and send using sendRawTransaction - return method.accounts.signTransaction(tx, wallet.privateKey, sendTxCallback).then(sendSignedTx) + return method.accounts.signTransaction(tx, privateKey).then(sendSignedTx).catch((e) => { sendTxCallback(e) }) + } else if (tx.signatures) { + // If signatures is defined inside of the transaction object, + // get rawTransaction string from signed transaction object and send to network + return method.accounts.getRawTransactionWithSignatures(tx).then(sendSignedTx).catch((e) => { sendTxCallback(e) }) } // If wallet was not found in caver-js wallet, then it has to use wallet in Node. // Signing to transaction using wallet in Node supports only LEGACY transaction, so if transaction is not LEGACY, return error. if (tx.feePayer !== undefined || (tx.type !== undefined && tx.type !== 'LEGACY')) { - var error = new Error('Only Legacy transactions can be signed on a Klaytn node!') + error = new Error('Only Legacy transactions can be signed on a Klaytn node!') sendTxCallback(error) return Promise.reject(error) } - if (!tx.senderRawTransaction){ - var error = validateParams(tx) - if (error) { - sendTxCallback(error) - return Promise.reject(error) - } + error = validateParams(tx) + if (error) { + sendTxCallback(error) + return Promise.reject(error) } break } case 'klay_sign': { const data = payload.params[1] - const wallet = getWallet(payload.params[0], method.accounts) + const wallet = method.accounts.wallet.getAccount(payload.params[0]) if (wallet && wallet.privateKey) { // If wallet was found, sign tx, and send using sendRawTransaction diff --git a/packages/caver-core-requestmanager/caver-providers-ws/src/index.js b/packages/caver-core-requestmanager/caver-providers-ws/src/index.js index cb837850..b64063b5 100644 --- a/packages/caver-core-requestmanager/caver-providers-ws/src/index.js +++ b/packages/caver-core-requestmanager/caver-providers-ws/src/index.js @@ -76,8 +76,12 @@ var WebsocketProvider = function WebsocketProvider(url, options) { headers.authorization = 'Basic ' + _btoa(parsedURL.username + ':' + parsedURL.password); } - this.connection = new Ws(url, protocol, undefined, headers); - this.reconnect = () => new Ws(url, protocol, undefined, headers); + // Allow a custom client configuration + var clientConfig = options.clientConfig || undefined; + + this.connection = new Ws(url, protocol, undefined, headers, undefined, clientConfig); + this.reconnect = () => new Ws(url, protocol, undefined, headers, undefined, clientConfig); + this.addDefaultEvents(); diff --git a/packages/caver-core/src/index.js b/packages/caver-core/src/index.js index 56efd4c1..fea86560 100644 --- a/packages/caver-core/src/index.js +++ b/packages/caver-core/src/index.js @@ -63,6 +63,19 @@ module.exports = { packageInit: function (pkg, [provider, net]) { if (!pkg) throw new Error('You need to instantiate using the "new" keyword.') + // make property of pkg._provider, which can properly set providers + Object.defineProperty(pkg, 'currentProvider', { + get: function () { + return pkg._provider; + }, + set: function (value) { + return pkg.setProvider(value); + }, + enumerable: true, + configurable: true + }); + + if (provider && provider._requestManager) { pkg._requestManager = new Manager(provider.currentProvider) // set requestmanager on package @@ -73,8 +86,7 @@ module.exports = { pkg.providers = Manager.providers pkg._provider = pkg._requestManager.provider - pkg.currentProvider = pkg._provider - + // add SETPROVIDER function (don't overwrite if already existing) if (!pkg.setProvider) { pkg.setProvider = (provider, net) => pkg._provider = pkg._requestManager.setProvider(provider, net).provider diff --git a/packages/caver-klay/caver-klay-accounts/src/account/account.js b/packages/caver-klay/caver-klay-accounts/src/account/account.js new file mode 100644 index 00000000..60b7f7e4 --- /dev/null +++ b/packages/caver-klay/caver-klay-accounts/src/account/account.js @@ -0,0 +1,73 @@ +const AccountKeyPublic = require('../accountKey/accountKeyPublic') +const AccountKeyMultiSig = require('../accountKey/accountKeyMultiSig') +const AccountKeyRoleBased = require('../accountKey/accountKeyRoleBased') +const isAddress = require('../../../../caver-utils/src/utils').isAddress +const addHexPrefix = require('../../../../caver-utils').addHexPrefix + +class Account { + static fromObject(obj) { return new Account(obj.address, new AccountKeyPublic(obj.privateKey)) } + + static isAccountKey(accountKey) { + let isAccountKey = false + if (accountKey instanceof AccountKeyPublic) isAccountKey = true + if (accountKey instanceof AccountKeyMultiSig) isAccountKey = true + if (accountKey instanceof AccountKeyRoleBased) isAccountKey = true + + return isAccountKey + } + + constructor(address, accountKey) { + if (!address || !accountKey) throw new Error('Failed to create Account. address and accountKey are needed to create Account.') + + if (!isAddress(address)) throw new Error(`Invalid address : ${address}`) + if (!Account.isAccountKey(accountKey)) throw new Error(`Invalid accountKey.`) + + address = addHexPrefix(address) + + Object.defineProperty(this, 'address', { + get: function () { return address }, + set: function (addressInput) { + if (!isAddress(addressInput)) throw new Error(`Invalid address : ${addressInput}`) + address = addHexPrefix(addressInput) + }, + enumerable: true + }) + + Object.defineProperty(this, 'accountKey', { + get: function () { return accountKey }, + set: function (accountKeyInput) { + if (!Account.isAccountKey(accountKeyInput) && accountKeyInput !== null) throw new Error(`Invalid accountKey.`) + + if (accountKey === null || accountKeyInput === null) { + accountKey = accountKeyInput + } else if (accountKey.type !== accountKeyInput.type) { + accountKey = accountKeyInput + } else { + accountKey.update(accountKeyInput) + } + }, + enumerable: true, + configurable: true + }) + + Object.defineProperty(this, 'privateKey', { + get: function () { return this.accountKey.defaultKey }, + set: function (privateKeyInput) { + throw new Error('The privateKey cannot be modified. The privateKey is set to default key of accountKey, so update accountKey to modify the privateKey.') + }, + enumerable: true + }) + } + + get keys() { return this.accountKey.keys } + get accountKeyType() { return this.accountKey.type } + get transactionKey() { return this.accountKey.transactionKey } + get updateKey() { return this.accountKey.updateKey } + get feePayerKey() { return this.accountKey.feePayerKey } + + toPublicKey(toPublicKeyFunc) { + return this.accountKey.toPublicKey(toPublicKeyFunc) + } +} + +module.exports = Account \ No newline at end of file diff --git a/packages/caver-klay/caver-klay-accounts/src/account/accountForUpdate.js b/packages/caver-klay/caver-klay-accounts/src/account/accountForUpdate.js new file mode 100644 index 00000000..f2069107 --- /dev/null +++ b/packages/caver-klay/caver-klay-accounts/src/account/accountForUpdate.js @@ -0,0 +1,72 @@ +const isValidRole = require('../../../../caver-utils').isValidRole +const isValidPublicKey = require('../../../../caver-utils').isValidPublicKey + +class AccountForUpdate { + constructor(address, keyForUpdate, options) { + this.address = address + this.keyForUpdate = keyFormatter(keyForUpdate, options) + } + + fillUpdateObject(updateObject) { + delete updateObject.key + Object.assign(updateObject, this.keyForUpdate) + } +} + +function keyFormatter(keyForUpdate, options) { + const keyObject = {} + + if (typeof keyForUpdate === 'string') { + if (options) throw new Error(`Failed to keyFormatter for AccountForUpdate: AccountKeyPublic/legacyKey/failKey cannot have options`) + switch(keyForUpdate) { + case 'legacyKey': + keyObject.legacyKey = true + break + case 'failKey': + keyObject.failKey = true + break + default: + if (!isValidPublicKey(keyForUpdate)) throw new Error(`Invalid public key`) + keyObject.publicKey = keyForUpdate + break + } + } else if (Array.isArray(keyForUpdate)) { + if (!options || !options.threshold || !options.weight) throw new Error('For AccountKeyMultiSig, threshold and weight should be defined in options object.') + if (!Array.isArray(options.weight)) throw new Error('The weight should be defined as a array.') + if (options.weight.length !== keyForUpdate.length) throw new Error('The length of keys in AccountKeyMultiSig and the length of weight array do not match.') + + keyObject.multisig = { + threshold: options.threshold, + keys: [] + } + + let weightSum = 0 + + for (let i = 0; i < keyForUpdate.length; i ++) { + const key = keyForUpdate[i] + if (!isValidPublicKey(key)) throw new Error(`Invalid public key`) + keyObject.multisig.keys.push({weight: options.weight[i], publicKey: key}) + weightSum += options.weight[i] + } + + if (weightSum < options.threshold) throw new Error(`Invalid options for AccountKeyMultiSig: The sum of weights is less than the threshold.`) + } else { + for (let key in keyForUpdate) { + if (!isValidRole(key)) throw new Error(`Invalid role is defined: ${key}`) + options = options || {} + if (key === 'transactionKey') { + keyObject.roleTransactionKey = keyFormatter(keyForUpdate[key], options.transactionKey) + } + if (key === 'updateKey') { + keyObject.roleAccountUpdateKey = keyFormatter(keyForUpdate[key], options.updateKey) + } + if (key === 'feePayerKey') { + keyObject.roleFeePayerKey = keyFormatter(keyForUpdate[key], options.feePayerKey) + } + } + } + + return keyObject +} + +module.exports = AccountForUpdate \ No newline at end of file diff --git a/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyEnum.js b/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyEnum.js new file mode 100644 index 00000000..0640f207 --- /dev/null +++ b/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyEnum.js @@ -0,0 +1,7 @@ +module.exports = { + AccountKeyEnum: { + ACCOUNT_KEY_PUBLIC: 'AccountKeyPublic', + ACCOUNT_KEY_MULTISIG: 'AccountKeyMultiSig', + ACCOUNT_KEY_ROLEBASED: 'AccountKeyRoleBased' + }, +} \ No newline at end of file diff --git a/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyMultiSig.js b/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyMultiSig.js new file mode 100644 index 00000000..d3e5a340 --- /dev/null +++ b/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyMultiSig.js @@ -0,0 +1,50 @@ +const AccountKeyEnum = require('./accountKeyEnum').AccountKeyEnum + +const MAX_MULTISIG_KEY_LENGTH = 10 + +class AccountKeyMultiSig { + constructor(keys) { + if (keys instanceof AccountKeyMultiSig) keys = keys.keys + + if (!Array.isArray(keys)) throw new Error('To create AccountKeyMultiSig, an array of private key strings is required.') + if (keys.length === 0) throw new Error(`Empty array.`) + if (keys.length > MAX_MULTISIG_KEY_LENGTH) throw new Error(`The maximum number of keys is ${MAX_MULTISIG_KEY_LENGTH}.`) + if (isDuple(keys)) throw new Error('There is a duplicate key.') + + this._keys = keys + } + + get type() { return AccountKeyEnum.ACCOUNT_KEY_MULTISIG } + + get defaultKey() { return this._keys[0] } + get keys() { return this._keys } + get transactionKey() { return this._keys } + get updateKey() { return this._keys } + get feePayerKey() { return this._keys } + + toPublicKey(toPublicKeyFunc) { + const keys = [] + + for (let i = 0; i < this._keys.length; i ++) { + let key = this._keys[i] + keys.push(toPublicKeyFunc(key)) + } + + return keys + } + + update(keys) { + this._keys = keys.keys + } +} + +function isDuple(keys) { + const map = new Map() + for (const key of keys) { + if (map.get(key) !== undefined) return true + map.set(key, true) + } + return false +} + +module.exports = AccountKeyMultiSig diff --git a/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyPublic.js b/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyPublic.js new file mode 100644 index 00000000..6ee74cc7 --- /dev/null +++ b/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyPublic.js @@ -0,0 +1,28 @@ +const AccountKeyEnum = require('./accountKeyEnum').AccountKeyEnum + +class AccountKeyPublic { + constructor(key) { + if (key instanceof AccountKeyPublic) key = key.keys + + if (typeof key !== 'string') throw new Error('To create AccountKeyPublic, a private key strings is required.') + this._key = key + } + + get type() { return AccountKeyEnum.ACCOUNT_KEY_PUBLIC } + + get defaultKey() { return this._key } + get keys() { return this._key } + get transactionKey() { return this._key } + get updateKey() { return this._key } + get feePayerKey() { return this._key } + + toPublicKey(toPublicKeyFunc) { + return toPublicKeyFunc(this._key) + } + + update(key) { + this._key = key.keys + } +} + +module.exports = AccountKeyPublic \ No newline at end of file diff --git a/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyRoleBased.js b/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyRoleBased.js new file mode 100644 index 00000000..ab80eac9 --- /dev/null +++ b/packages/caver-klay/caver-klay-accounts/src/accountKey/accountKeyRoleBased.js @@ -0,0 +1,98 @@ +const AccountKeyEnum = require('./accountKeyEnum').AccountKeyEnum +const AccountKeyPublic = require('./accountKeyPublic') +const AccountKeyMultiSig = require('./accountKeyMultiSig') +const utils = require('../../../../caver-utils') + +class AccountKeyRoleBased { + constructor(keyObj = {}) { + if (keyObj instanceof AccountKeyRoleBased) keyObj = keyObj.keys + + if (typeof keyObj !== 'object') throw new Error('RoleBasedKey should be created with Object') + + validateKeyObject(keyObj) + + this._transactionKey = makeAccountKey(keyObj.transactionKey) + this._updateKey = makeAccountKey(keyObj.updateKey) + this._feePayerKey = makeAccountKey(keyObj.feePayerKey) + } + + get type() { return AccountKeyEnum.ACCOUNT_KEY_ROLEBASED } + + get defaultKey() { + let definedKey = this._transactionKey? this._transactionKey : this._updateKey? this._updateKey : this._feePayerKey? this._feePayerKey : undefined + + if (!definedKey) throw new Error(`There is no key defined in AccountKeyRoleBased.`) + + return definedKey.defaultKey + } + + get keys() { + const keys = {} + + if (this._transactionKey !== undefined) keys.transactionKey = this._transactionKey.keys + if (this._updateKey !== undefined) keys.updateKey = this._updateKey.keys + if (this._feePayerKey !== undefined) keys.feePayerKey = this._feePayerKey.keys + + return keys + } + + get transactionKey() { + if (!this._transactionKey) return undefined + return this._transactionKey.keys + } + get updateKey() { + if (!this._updateKey) return undefined + return this._updateKey.keys + } + get feePayerKey() { + if (!this._feePayerKey) return undefined + return this._feePayerKey.keys + } + + toPublicKey(toPublicKeyFunc) { + const returnObject = {} + + if (this._transactionKey !== undefined) returnObject.transactionKey = this._transactionKey.toPublicKey(toPublicKeyFunc) + if (this._updateKey !== undefined) returnObject.updateKey = this._updateKey.toPublicKey(toPublicKeyFunc) + if (this._feePayerKey !== undefined) returnObject.feePayerKey = this._feePayerKey.toPublicKey(toPublicKeyFunc) + + return returnObject + } + + update(keys) { + // In the case of AccountKeyRoleBased, the key that does not update is not defined. + // To handle this case, when updating, only update the key for the defined role. + if (keys._transactionKey) this._transactionKey = keys._transactionKey + if (keys._updateKey) this._updateKey = keys._updateKey + if (keys._feePayerKey) this._feePayerKey = keys._feePayerKey + } +} + +function makeAccountKey(key) { + if (key === undefined) return undefined + if (Array.isArray(key) || key instanceof AccountKeyMultiSig) return new AccountKeyMultiSig(key) + if (typeof key !== 'string') throw new Error('Invalid account key type') + + return new AccountKeyPublic(key) +} + +function validateKeyObject(keyObject) { + const key = Object.keys(keyObject) + if (key.length === 0) throw new Error(`Failed to create AccountKeyRoleBased: empty object`) + + key.map((role) => { + if (!utils.isValidRole(role)) throw new Error(`Failed to create AccountKeyRoleBased. Invalid role is defined : ${role}`) + + if (Array.isArray(keyObject[role])) { + for (let p of keyObject[role]) { + const parsed = utils.parsePrivateKey(p) + p = parsed.privateKey + if (!utils.isValidPrivateKey(p)) throw new Error(`Failed to create AccountKeyRoleBased. Invalid private key : ${p}`) + } + } else { + if (!utils.isValidPrivateKey(keyObject[role])) throw new Error(`Failed to create AccountKeyRoleBased. Invalid private key : ${keyObject[role]}`) + } + }) +} + +module.exports = AccountKeyRoleBased \ No newline at end of file diff --git a/packages/caver-klay/caver-klay-accounts/src/index.js b/packages/caver-klay/caver-klay-accounts/src/index.js index 060792a6..0eb14647 100644 --- a/packages/caver-klay/caver-klay-accounts/src/index.js +++ b/packages/caver-klay/caver-klay-accounts/src/index.js @@ -29,21 +29,29 @@ var core = require('../../../caver-core'); var Method = require('../../../caver-core-method'); var Promise = require('any-promise'); // account, hash, rlp, nat, bytes library will be used from 'eth-lib' temporarily. -var Account = require("eth-lib/lib/account"); +var AccountLib = require("eth-lib/lib/account"); var Hash = require("eth-lib/lib/hash"); var RLP = require("eth-lib/lib/rlp"); var Nat = require("eth-lib/lib/nat"); var Bytes = require("eth-lib/lib/bytes"); var cryp = (typeof global === 'undefined') ? require('crypto-browserify') : require('crypto'); -var scryptsy = require('scrypt.js'); +var scrypt = require('./scrypt'); var uuid = require('uuid'); var utils = require('../../../caver-utils'); var helpers = require('../../../caver-core-helpers'); -const { encodeRLPByTxType, makeRawTransaction, getSenderTxHash } = require('./makeRawTransaction') +const { encodeRLPByTxType, makeRawTransaction, getSenderTxHash, decodeFromRawTransaction, splitFeePayer, extractSignatures } = require('./makeRawTransaction') var elliptic = require('elliptic') var secp256k1 = new (elliptic.ec)('secp256k1') +const AccountKeyPublic = require('./accountKey/accountKeyPublic') +const AccountKeyMultiSig = require('./accountKey/accountKeyMultiSig') +const AccountKeyRoleBased = require('./accountKey/accountKeyRoleBased') +const AccountKeyEnum = require('./accountKey/accountKeyEnum').AccountKeyEnum + +const Account = require('./account/account') +const AccountForUpdate = require('./account/accountForUpdate') + const rpc = require('../../../caver-rtm').rpc var isNot = function(value) { @@ -52,12 +60,92 @@ var isNot = function(value) { function coverInitialTxValue(tx) { if (typeof tx !== 'object') throw ('Invalid transaction') - tx.to = tx.to || '0x' - tx.data = tx.data || '0x' + if (!tx.senderRawTransaction && (!tx.type || tx.type === 'LEGACY' || tx.type.includes('SMART_CONTRACT_DEPLOY'))) { + tx.to = tx.to || '0x' + tx.data = utils.addHexPrefix(tx.data || '0x') + } tx.chainId = utils.numberToHex(tx.chainId) return tx } +/** + * resolveArgsForSignTransaction parse arguments for signTransaction. + * + * @method resolveArgsForSignTransaction + * @param {Object} args Parameters of signTransaction. + * @return {Object} + */ +function resolveArgsForSignTransaction(args) { + if (args.length === 0 || args.length > 3) throw new Error('Invalid parameter: The number of parameters is invalid.') + + // privateKey and callback are optional parameter + // "args.length === 2" means that user sent parameter privateKey or callback + let tx = args[0], privateKey, callback + + if (!tx || (!_.isObject(tx) && !_.isString(tx))) { + throw new Error('Invalid parameter: The transaction must be defined as an object or RLP encoded string') + } + + if (args.length === 2) { + if (_.isFunction(args[1])) { + callback = args[1] + } else { + privateKey = args[1] + } + } else if (args.length === 3) { + if (args[1] && typeof args[1] !== 'string' && !_.isArray(args[1])){ + return handleError('Invalid parameter: The parameter for the private key is invalid') + } + privateKey = args[1] + callback = args[2] + } + + // For handling when callback is undefined. + callback = callback || function () {} + + return { tx, privateKey, callback } +} + +/** + * resolveArgsForFeePayerSignTransaction parse arguments for feePayerSignTransaction. + * + * @method resolveArgsForFeePayerSignTransaction + * @param {Object} args Parameters of feePayerSignTransaction. + * @return {Object} + */ +function resolveArgsForFeePayerSignTransaction(args) { + if (args.length === 0 || args.length > 4) throw new Error('Invalid parameter: The number of parameters is invalid.') + + // privateKey and callback are optional parameter + // "args.length === 3" means that user sent parameter privateKey or callback + let tx = args[0], feePayer = args[1], privateKey, callback + + if (!tx || (!_.isObject(tx) && !_.isString(tx))) { + throw new Error('Invalid parameter: The transaction must be defined as an object or RLP encoded string') + } + + if (!utils.isAddress(feePayer)) throw new Error(`Invalid fee payer address : ${feePayer}`) + + if (args.length === 3) { + if (_.isFunction(args[2])) { + callback = args[2] + } else { + privateKey = args[2] + } + } else if (args.length === 4) { + if (args[2] && typeof args[2] !== 'string' && !_.isArray(args[2])){ + return handleError('Invalid parameter: The parameter for the private key is invalid') + } + privateKey = args[2] + callback = args[3] + } + + // For handling when callback is undefined. + callback = callback || function () {} + + return { tx, privateKey, feePayer, callback } +} + var Accounts = function Accounts(...args) { var _this = this; @@ -94,7 +182,7 @@ Accounts.prototype._addAccountFunctions = function (account) { account.encrypt = function encrypt(password, options = {}) { options.address = account.address - return _this.encrypt(account.privateKey, password, options); + return _this.encrypt(account.keys, password, options); }; account.getKlaytnWalletKey = function getKlaytnWalletKey() { @@ -105,75 +193,455 @@ Accounts.prototype._addAccountFunctions = function (account) { return account; }; -Accounts.prototype.create = function create(entropy) { - return this._addAccountFunctions(Account.create(entropy || utils.randomHex(32))); -}; - -Accounts.prototype.privateKeyToAccount = function privateKeyToAccount(privateKey, targetAddressRaw) { - var { privateKey: prvKey, address, isHumanReadable } = utils.parsePrivateKey(privateKey) - - if (!utils.isValidPrivateKey(prvKey)) throw new Error('Invalid private key') - if (prvKey.slice(0, 2) !== '0x') { - prvKey = `0x${prvKey}` - } - - let account = Account.fromPrivate(prvKey) - - if (targetAddressRaw) { - if(address && address !== targetAddressRaw) { +/** + * _determineAddress determines the priority of the parameters entered and returns the address that should be used for the account. + * + * @method _determineAddress + * @param {Object} legacyAccount Account with a legacy account key extracted from private key to be used for address determination. + * @param {String} addressFromKey Address extracted from key. + * @param {String} userInputAddress Address passed as parameter by user. + * @return {String} + */ +Accounts.prototype._determineAddress = function _determineAddress(legacyAccount, addressFromKey, userInputAddress) { + if (userInputAddress) { + if(addressFromKey && addressFromKey !== userInputAddress) { throw new Error('The address extracted from the private key does not match the address received as the input value.') } - if(!utils.isAddress(targetAddressRaw)) { + if(!utils.isAddress(userInputAddress)) { throw new Error('The address received as the input value is invalid.') } - account.address = targetAddressRaw + return userInputAddress - } else if (address){ - if(!utils.isAddress(address)) { + } else if (addressFromKey){ + if(!utils.isAddress(addressFromKey)) { throw new Error('The address extracted from the private key is invalid.') } - // If targetAddressRaw is undefined and address which is came from private is existed, set address in account. - account.address = address + // If userInputAddress is undefined and address which is came from private is existed, set address in account. + return addressFromKey } + return legacyAccount.address +} - account.address = account.address.toLowerCase() - account.address = '0x' + account.address.replace('0x', '') +/** + * _getRoleKey returns a key that matches the role that should be used according to the transaction. + * + * @method _getRoleKey + * @param {Object} tx transaction object to be sign. + * @param {Object} account Account to be used for signing. + * @return {String|Array} + */ +Accounts.prototype._getRoleKey = function _getRoleKey(tx, account) { + let key + + if (!account) throw new Error(`The account to be used for signing is not defined.`) + if (tx.senderRawTransaction && tx.feePayer) { + key = account.feePayerKey + } else if (tx.type && tx.type.includes('ACCOUNT_UPDATE')) { + key = account.updateKey + } else { + key = account.transactionKey + } + + if (!key) throw new Error(`The key corresponding to the role used for signing is not defined.`) + + return key +} + +/** + * create function creates random account with entropy. + * + * @method create + * @param {Object} entropy A random string to increase entropy. + * @return {Object} + */ +Accounts.prototype.create = function create(entropy) { + return this._addAccountFunctions(Account.fromObject(AccountLib.create(entropy || utils.randomHex(32)))); +}; + +/** + * createAccountKey creates AccountKeyPublic, AccountKeyMultiSig or AccountKeyRoleBased instance with parameter. + * + * @method createAccountKey + * @param {String|Array|Object} accountKey Parameters to be used when creating the AccountKey. + * @return {Object} + */ +Accounts.prototype.createAccountKey = function createAccountKey(accountKey) { + if (Account.isAccountKey(accountKey)) accountKey = accountKey.keys + + if (_.isString(accountKey)) { + accountKey = this.createAccountKeyPublic(accountKey) + } else if (_.isArray(accountKey)) { + accountKey = this.createAccountKeyMultiSig(accountKey) + } else if (_.isObject(accountKey)) { + accountKey = this.createAccountKeyRoleBased(accountKey) + } else { + throw new Error(`Invalid accountKey type: ${typeof accountKey}`) + } + return accountKey +} + +/** + * createAccountKeyPublic creates AccountKeyPublic with a string of private key. + * + * @method createAccountKeyPublic + * @param {String} privateKey Private key string that will be used to create AccountKeyPublic. + * @return {Object} + */ +Accounts.prototype.createAccountKeyPublic = function createAccountKeyPublic(privateKey) { + if (privateKey instanceof AccountKeyPublic) return privateKey + + if (!_.isString(privateKey)) throw new Error('Creating a AccountKeyPublic requires a private key string.') + + const parsed = utils.parsePrivateKey(privateKey) + privateKey = parsed.privateKey + + if (!utils.isValidPrivateKey(privateKey)) throw new Error(`Failed to create AccountKeyPublic. Invalid private key : ${privateKey}`) + + return new AccountKeyPublic(privateKey) +} + +/** + * createAccountKeyMultiSig creates AccountKeyMultiSig with an array of private keys. + * + * @method createAccountKeyMultiSig + * @param {Array} privateKeys An Array of private key strings that will be used to create AccountKeyMultiSig. + * @return {Object} + */ +Accounts.prototype.createAccountKeyMultiSig = function createAccountKeyMultiSig(privateKeys) { + if (privateKeys instanceof AccountKeyMultiSig) return privateKeys + + if (!_.isArray(privateKeys)) throw new Error('Creating a AccountKeyMultiSig requires an array of private key string.') + + for (let p of privateKeys) { + const parsed = utils.parsePrivateKey(p) + p = parsed.privateKey + if (!utils.isValidPrivateKey(p)) throw new Error(`Failed to create AccountKeyMultiSig. Invalid private key : ${p}`) + } + + return new AccountKeyMultiSig(privateKeys) +} + +/** + * createAccountKeyRoleBased creates AccountKeyRoleBased with an obejct of key. + * + * @method createAccountKeyRoleBased + * @param {Object} keyObject Object that defines key for each role to use when creating AccountKeyRoleBased. + * @return {Object} + */ +Accounts.prototype.createAccountKeyRoleBased = function createAccountKeyRoleBased(keyObject) { + if (keyObject instanceof AccountKeyRoleBased) return keyObject + + if (!_.isObject(keyObject) || _.isArray(keyObject)) throw new Error('Creating a AccountKeyRoleBased requires an object.') + + return new AccountKeyRoleBased(keyObject) +} + +/** + * accountKeyToPublicKey creates public key format with AccountKey. + * + * @method accountKeyToPublicKey + * @param {Object} accountKey AccountKey instance for which you want to generate a public key format. + * @return {String|Array|Object} + */ +Accounts.prototype.accountKeyToPublicKey = function accountKeyToPublicKey(accountKey) { + accountKey = this.createAccountKey(accountKey) + return accountKey.toPublicKey(this.privateKeyToPublicKey) +} + +/** + * createWithAccountKey creates Account instance with AccountKey. + * + * @method createWithAccountKey + * @param {String} address The address of account. + * @param {String|Array|Object} accountKey The accountKey of account. + * @return {Object} + */ +Accounts.prototype.createWithAccountKey = function createWithAccountKey(address, accountKey) { + const account = new Account(address, this.createAccountKey(accountKey)) return this._addAccountFunctions(account) } -Accounts.prototype.signTransaction = function signTransaction(tx, privateKey, callback) { - var _this = this, - error = false, - result +/** + * createWithAccountKeyPublic create an account with AccountKeyPublic. + * + * @method createWithAccountKeyPublic + * @param {String} address An address of account. + * @param {String|Object} key Key of account. + * @return {Object} + */ +Accounts.prototype.createWithAccountKeyPublic = function createWithAccountKeyPublic(address, key) { + if (!Account.isAccountKey(key)) key = this.createAccountKeyPublic(key) - callback = callback || function () {} + if (key.type !== AccountKeyEnum.ACCOUNT_KEY_PUBLIC) throw new Error(`Failed to create account with AccountKeyPublic. Invalid account key : ${key.type}`) + + const account = new Account(address, key) + return this._addAccountFunctions(account) +} + +/** + * createWithAccountKeyMultiSig create an account with AccountKeyMultiSig. + * + * @method createWithAccountKeyMultiSig + * @param {String} address An address of account. + * @param {String|Object} keys Key of account. + * @return {Object} + */ +Accounts.prototype.createWithAccountKeyMultiSig = function createWithAccountKeyMultiSig(address, keys) { + if (!Account.isAccountKey(keys)) keys = this.createAccountKeyMultiSig(keys) + + if (keys.type !== AccountKeyEnum.ACCOUNT_KEY_MULTISIG) throw new Error(`Failed to create account with AccountKeyMultiSig. Invalid account key : ${keys.type}`) + + const account = new Account(address, keys) + return this._addAccountFunctions(account) +} + +/** + * createWithAccountKeyRoleBased create an account with AccountKeyRoleBased. + * + * @method createWithAccountKeyRoleBased + * @param {String} address An address of account. + * @param {String|Object} keyObject Key of account. + * @return {Object} + */ +Accounts.prototype.createWithAccountKeyRoleBased = function createWithAccountKeyRoleBased(address, keyObject) { + if (!Account.isAccountKey(keyObject)) keyObject = this.createAccountKeyRoleBased(keyObject) + + if (keyObject.type !== AccountKeyEnum.ACCOUNT_KEY_ROLEBASED) throw new Error(`Failed to create account with AccountKeyRoleBased. Invalid account key : ${keyObject.type}`) - const parsed = utils.parsePrivateKey(privateKey) - privateKey = parsed.privateKey + const account = new Account(address, keyObject) + return this._addAccountFunctions(account) +} - if (!utils.isValidPrivateKey(privateKey)) throw new Error('Invalid private key') - privateKey = privateKey.startsWith('0x') ? privateKey : '0x' + privateKey +/** + * privateKeyToAccount creates and returns an Account through the input passed as parameters. + * + * @method privateKeyToAccount + * @param {String} key The key parameter can be either normal private key or KlaytnWalletKey format. + * @param {String} userInputAddress The address entered by the user for use in creating an account. + * @return {Object} + */ +Accounts.prototype.privateKeyToAccount = function privateKeyToAccount(key, userInputAddress) { + let {legacyAccount: account, klaytnWalletKeyAddress} = this.getLegacyAccount(key) - if (!tx) { - error = new Error('No transaction object given!') + account.address = this._determineAddress(account, klaytnWalletKeyAddress, userInputAddress) + account.address = account.address.toLowerCase() + account.address = utils.addHexPrefix(account.address) + + return account +} + + +/** + * createAccountForUpdate creates an AccountForUpdate instance. + * The AccountForUpdate returned as a result of this function contains only the address and public key used to update the account. + * + * @method createAccountForUpdate + * @param {String} address The address value of AccountForUpdate, a structure that contains data for updating an account. + * @param {String|Array|Object} accountKey Private key or AccountKey to update account. + * @param {Object} options Options to use for setting threshold and weight for multiSig. + * @return {Object} + */ +Accounts.prototype.createAccountForUpdate = function createAccountForUpdate(address, accountKey, options) { + let legacyOrFail + + // Logic for handling cases where legacyKey or failKey is set inside AccountKeyRoleBased object. + if (!_.isArray(accountKey) && _.isObject(accountKey)) { + legacyOrFail = {} + Object.keys(accountKey).map((role) => { + if (accountKey[role] === 'legacyKey' || accountKey[role] === 'failKey') { + legacyOrFail[role] = accountKey[role] + delete accountKey[role] + } + }) + if (Object.keys(accountKey).length === 0) return new AccountForUpdate(address, legacyOrFail, options) + } - callback(error) - return Promise.reject(error) + const publicKey = this.accountKeyToPublicKey(accountKey) + + if (legacyOrFail !== undefined) { + Object.assign(publicKey, legacyOrFail) + } + + return new AccountForUpdate(address, publicKey, options) +} + +/** + * createAccountForUpdateWithPublicKey creates AccountForUpdate instance with public key format. + * + * @method createAccountForUpdateWithPublicKey + * @param {String} address The address value of AccountForUpdate, a structure that contains data for updating an account. + * @param {String|Array|Object} keyForUpdate Public key to update. + * @param {Object} options Options to use for setting threshold and weight for multiSig. + * @return {Object} + */ +Accounts.prototype.createAccountForUpdateWithPublicKey = function createAccountForUpdateWithPublicKey(address, keyForUpdate, options) { + return new AccountForUpdate(address, keyForUpdate, options) +} + +/** + * createAccountForUpdateWithLegacyKey creates AccountForUpdate instance with legacyKey. + * + * @method createAccountForUpdateWithLegacyKey + * @param {String} address The address of account to update with the legacy key. + * @return {Object} + */ +Accounts.prototype.createAccountForUpdateWithLegacyKey = function createAccountForUpdateWithLegacyKey(address) { + return new AccountForUpdate(address, 'legacyKey') +} + +/** + * createAccountForUpdateWithFailKey creates AccountForUpdate instance with failKey. + * + * @method createAccountForUpdateWithFailKey + * @param {String} address The address of account to update with the fail key. + * @return {Object} + */ +Accounts.prototype.createAccountForUpdateWithFailKey = function createAccountForUpdateWithFailKey(address) { + return new AccountForUpdate(address, 'failKey') +} + +/** + * isDecoupled determines whether or not it is decoupled based on the input value. + * + * @method isDecoupled + * @param {String} key The key parameter can be either normal private key or KlaytnWalletKey format. + * @param {String} userInputAddress The address to use when determining whether it is decoupled. + * @return {Boolean} + */ +Accounts.prototype.isDecoupled = function isDecoupled(key, userInputAddress) { + let { legacyAccount, klaytnWalletKeyAddress } = this.getLegacyAccount(key) + let actualAddress = this._determineAddress(legacyAccount, klaytnWalletKeyAddress, userInputAddress) + + return legacyAccount.address.toLowerCase() !== actualAddress.toLowerCase() +} + +/** + * getLegacyAccount extracts the private key from the input key and returns an account with the corresponding legacy account key. + * If the input key is KlaytnWalletKey format, it returns klaytnWalletKeyAddress, which is the address extracted from KlaytnWalletKey. + * + * @method getLegacyAccount + * @param {String} key The key parameter can be either normal private key or KlaytnWalletKey format. + * @return {Object} + */ +Accounts.prototype.getLegacyAccount = function getLegacyAccount(key) { + var { privateKey, address: klaytnWalletKeyAddress, isHumanReadable } = utils.parsePrivateKey(key) + + if (!utils.isValidPrivateKey(privateKey)) throw new Error('Invalid private key') + + privateKey = utils.addHexPrefix(privateKey) + + const account = this._addAccountFunctions(Account.fromObject(AccountLib.fromPrivate(privateKey))) + + return { legacyAccount: account, klaytnWalletKeyAddress } +} + +/** + * signTransaction signs to transaction with private key. + * If there are signatures(feePayerSignatures if the fee payer signs) in tx entered as a parameter, + * the signatures(feePayerSignatures if the fee payer signs) are appended. + * + * @method signTransaction + * @param {String|Object} tx The transaction to sign. + * @param {String|Array} privateKey The private key to use for signing. + * @param {String} callback The callback function to call. + * @return {Object} + */ +Accounts.prototype.signTransaction = function signTransaction() { + let _this = this + let isLegacy = false, isFeePayer = false + let existedSenderSignatures = [], existedFeePayerSignatures = [] + let result, tx, privateKey, callback + + let handleError = (e) => { + e = e instanceof Error? e : new Error(e) + if (callback) callback(e) + return Promise.reject(e) } - function signed(tx) { + try { + let resolved = resolveArgsForSignTransaction(arguments) + tx = resolved.tx + privateKey = resolved.privateKey + callback = resolved.callback + } catch(e) { return handleError(e) } + + // If the user signs an RLP encoded transaction, tx is of type string. + if (_.isString(tx)) { + tx = decodeFromRawTransaction(tx) + } + + // Validate tx object + const error = helpers.validateFunction.validateParams(tx) + if (error) return handleError(error) + + if (tx.senderRawTransaction) { + if (tx.feePayerSignatures) { + existedFeePayerSignatures = existedFeePayerSignatures.concat(tx.feePayerSignatures) + } - if (!tx.senderRawTransaction) { - error = helpers.validateFunction.validateParams(tx) + try { + // Decode senderRawTransaction to get signatures of fee payer + const { senderRawTransaction, feePayer, feePayerSignatures } = splitFeePayer(tx.senderRawTransaction) + + // feePayer !== '0x' means that in senderRawTransaction there are feePayerSignatures + if (feePayer !== '0x') { + // The feePayer inside the tx object does not match the feePayer information contained in the senderRawTransaction. + if (feePayer.toLowerCase() !== tx.feePayer.toLowerCase()) return handleError(`Invalid feePayer: The fee payer(${feePayer}) included in the transaction does not match the fee payer(${tx.feePayer}) you want to sign.`) + existedFeePayerSignatures = existedFeePayerSignatures.concat(feePayerSignatures) + } + + tx.senderRawTransaction = senderRawTransaction + isFeePayer = true + } catch(e) { + return handleError(e) } - if (error) { - callback(error); - return Promise.reject(error); + + } else { + isLegacy = tx.type === undefined || tx.type === 'LEGACY' ? true : false + + if (tx.signatures) { + // if there is existed signatures or feePayerSignatures, those should be preserved. + if (isLegacy) return handleError('Legacy transaction cannot be signed with multiple keys.') + existedSenderSignatures = existedSenderSignatures.concat(tx.signatures) } + + } + // When privateKey is undefined, find Account from Wallet. + if (privateKey === undefined) { + try { + const account = this.wallet.getAccount(isFeePayer? tx.feePayer : tx.from) + if (!account) return handleError('Failed to find get private key to sign. The account you want to use for signing must exist in caver.klay.accounts.wallet or you must pass the private key as a parameter.') + privateKey = this._getRoleKey(tx, account) + } catch(e) { + return handleError(e) + } + } + + let privateKeys = _.isArray(privateKey) ? privateKey : [privateKey] + + try { + for (let i = 0; i < privateKeys.length; i ++) { + const parsed = utils.parsePrivateKey(privateKeys[i]) + privateKeys[i] = parsed.privateKey + privateKeys[i] = utils.addHexPrefix(privateKeys[i]) + + if (!utils.isValidPrivateKey(privateKeys[i])) return handleError('Invalid private key') + } + } catch(e) { + return handleError(e) + } + + // Attempting to sign with a decoupled account into a legacy type transaction should be rejected. + if (isLegacy) { + if (privateKeys.length > 1) return handleError('Legacy transaction cannot signed with multiple keys') + if (_this.isDecoupled(privateKeys[0], tx.from)) return handleError('A legacy transaction must be with a legacy account key') + } + + function signed(tx) { try { // Guarantee all property in transaction is hex. tx = helpers.formatters.inputCallFormatter(tx) @@ -184,21 +652,32 @@ Accounts.prototype.signTransaction = function signTransaction(tx, privateKey, ca const messageHash = Hash.keccak256(rlpEncoded) - const signature = Account.makeSigner(Nat.toNumber(transaction.chainId || "0x1") * 2 + 35)(messageHash, privateKey) - const [v, r, s] = Account.decodeSignature(signature).map(sig => utils.makeEven(utils.trimLeadingZero(sig))) + let sigs = isFeePayer? existedFeePayerSignatures : existedSenderSignatures - const rawTransaction = makeRawTransaction(rlpEncoded, [v, r, s], transaction) + for(const privateKey of privateKeys) { + const signature = AccountLib.makeSigner(Nat.toNumber(transaction.chainId || "0x1") * 2 + 35)(messageHash, privateKey) + const [v, r, s] = AccountLib.decodeSignature(signature).map(sig => utils.makeEven(utils.trimLeadingZero(sig))) + sigs.push([v, r, s]) + } + // makeRawTransaction will return signatures and feePayerSignatures with duplicates removed. + let { rawTransaction, signatures, feePayerSignatures } = makeRawTransaction(rlpEncoded, sigs, transaction) result = { messageHash: messageHash, - v: v, - r: r, - s: s, + v: sigs[0][0], + r: sigs[0][1], + s: sigs[0][2], rawTransaction: rawTransaction, txHash: Hash.keccak256(rawTransaction), senderTxHash: getSenderTxHash(rawTransaction), } + if (isFeePayer) { + result.feePayerSignatures = feePayerSignatures + } else { + result.signatures = signatures + } + } catch(e) { callback(e) return Promise.reject(e) @@ -213,7 +692,7 @@ Accounts.prototype.signTransaction = function signTransaction(tx, privateKey, ca } // When the feePayer signs a transaction, required information is only chainId. - if (tx.senderRawTransaction !== undefined) { + if (isFeePayer) { return Promise.all([ isNot(tx.chainId) ? _this._klaytnCall.getChainId() : tx.chainId, ]).then(function (args) { @@ -221,51 +700,143 @@ Accounts.prototype.signTransaction = function signTransaction(tx, privateKey, ca throw new Error('"chainId" couldn\'t be fetched: '+ JSON.stringify(args)); } return signed(_.extend(tx, {chainId: args[0]})); - }); + }) } // Otherwise, get the missing info from the Klaytn Node return Promise.all([ isNot(tx.chainId) ? _this._klaytnCall.getChainId() : tx.chainId, isNot(tx.gasPrice) ? _this._klaytnCall.getGasPrice() : tx.gasPrice, - isNot(tx.nonce) ? _this._klaytnCall.getTransactionCount(tx.from || _this.privateKeyToAccount(privateKey).address) : tx.nonce + isNot(tx.nonce) ? _this._klaytnCall.getTransactionCount(tx.from) : tx.nonce ]).then(function (args) { if (isNot(args[0]) || isNot(args[1]) || isNot(args[2])) { throw new Error('One of the values "chainId", "gasPrice", or "nonce" couldn\'t be fetched: '+ JSON.stringify(args)); } return signed(_.extend(tx, {chainId: args[0], gasPrice: args[1], nonce: args[2]})); - }); -}; + }) +} + +/** +* feePayerSignTransaction calls signTransaction, creating a format for feePayer to sign the transaction. +* If there are feePayerSignatures in tx entered as a parameter, the signatures for fee payer are appended. +* +* @method feePayerSignTransaction +* @param {Object|String} tx The transaction to sign. +* @param {String} feePayer The address of fee payer. +* @param {String|Array} privateKey The private key to use for signing. +* @param {Function} callback The callback function to call. +* @return {Object} +*/ +Accounts.prototype.feePayerSignTransaction = function feePayerSignTransaction() { + let _this = this + let tx, feePayer, privateKey, callback + + let handleError = (e) => { + e = e instanceof Error? e : new Error(e) + if (callback) callback(e) + return Promise.reject(e) + } + + try { + let resolved = resolveArgsForFeePayerSignTransaction(arguments) + tx = resolved.tx + feePayer = resolved.feePayer + privateKey = resolved.privateKey + callback = resolved.callback + } catch(e) { + return handleError(e) + } + + if (_.isString(tx)) { + return this.signTransaction({ senderRawTransaction: tx, feePayer }, privateKey, callback) + } + + if (!tx.feePayer || tx.feePayer === '0x') { tx.feePayer = feePayer } + + if (!tx.senderRawTransaction) { + if (!tx.type || !tx.type.includes('FEE_DELEGATED')) { + return handleError(`Failed to sign transaction with fee payer: invalid transaction type(${tx.type? tx.type: 'LEGACY'})`) + } + } + + let e = helpers.validateFunction.validateParams(tx) + if (e) { + return handleError(e) + } + + if (tx.feePayer.toLowerCase() !== feePayer.toLowerCase()) { + return handleError(`Invalid parameter: The address of fee payer does not match.`) + } -Accounts.prototype.signTransactionWithSignature = function signTransactionWithSignature(tx, callback) { + if (tx.senderRawTransaction) { + return this.signTransaction(tx, privateKey, callback) + } + + return Promise.all([ + isNot(tx.chainId) ? _this._klaytnCall.getChainId() : tx.chainId, + isNot(tx.gasPrice) ? _this._klaytnCall.getGasPrice() : tx.gasPrice, + isNot(tx.nonce) ? _this._klaytnCall.getTransactionCount(tx.from) : tx.nonce + ]).then(function (args) { + if (isNot(args[0]) || isNot(args[1]) || isNot(args[2])) { + throw new Error('One of the values "chainId", "gasPrice", or "nonce" couldn\'t be fetched: '+ JSON.stringify(args)); + } + let transaction = _.extend(tx, {chainId: args[0], gasPrice: args[1], nonce: args[2]}) + + transaction = helpers.formatters.inputCallFormatter(transaction) + transaction = coverInitialTxValue(transaction) + + const rlpEncoded = encodeRLPByTxType(transaction) + let sig = transaction.signatures? transaction.signatures : [['0x01', '0x', '0x']] + let { rawTransaction } = makeRawTransaction(rlpEncoded, sig, transaction) + + return _this.signTransaction({ senderRawTransaction: rawTransaction, feePayer }, privateKey, callback) + }) +} + +/** + * getRawTransactionWithSignatures returns object which contains rawTransaction. + * + * @method getRawTransactionWithSignatures + * @param {Object} tx The transaction object which contains signatures or feePayerSignatures. + * @param {Function} callback The callback function to call. + * @return {Object} + */ +Accounts.prototype.getRawTransactionWithSignatures = function getRawTransactionWithSignatures(tx, callback) { var _this = this, - error = false, result callback = callback || function () {} - if (!tx) { - error = new Error('No transaction object given!') - - callback(error) - return Promise.reject(error) + let handleError = (e) => { + e = e instanceof Error? e : new Error(e) + if (callback) callback(e) + return Promise.reject(e) } - if (!tx.signature) { - error = new Error('No tx signature given!') + if (!tx || !_.isObject(tx)) return handleError('Invalid parameter: The transaction must be defined as an object') + if (!tx.signatures && !tx.feePayerSignatures) return handleError('There are no signatures or feePayerSignatures defined in the transaction object.') + + let error = helpers.validateFunction.validateParams(tx) + if (error) return handleError(error) + + if (tx.senderRawTransaction) { + tx.feePayerSignatures = tx.feePayerSignatures || [['0x01', '0x', '0x']] + + const decoded = decodeFromRawTransaction(tx.senderRawTransaction) + // feePayer !== '0x' means that in senderRawTransaction there are feePayerSignatures + if (decoded.feePayer !== '0x' && !utils.isEmptySig(decoded.feePayerSignatures)) { + if (decoded.feePayer.toLowerCase() !== tx.feePayer.toLowerCase()) return handleError('Invalid feePayer') + tx.feePayerSignatures = tx.feePayerSignatures.concat(decoded.feePayerSignatures) + } + + decoded.feePayer = tx.feePayer + decoded.feePayerSignatures = tx.feePayerSignatures - callback(error) - return Promise.reject(error) + if (tx.signatures) decoded.signatures = decoded.signatures.concat(tx.signatures) + tx = decoded } function signed(tx) { - if (!tx.senderRawTransaction) { - error = helpers.validateFunction.validateParams(tx) - } - if (error) { - callback(error) - return Promise.reject(error) - } try { // Guarantee all property in transaction is hex. @@ -275,23 +846,24 @@ Accounts.prototype.signTransactionWithSignature = function signTransactionWithSi const rlpEncoded = encodeRLPByTxType(transaction) - const messageHash = Hash.keccak256(rlpEncoded) + let sigs = transaction.signatures? transaction.signatures : ['0x01', '0x', '0x'] - let sig - if (_.isArray(transaction.signature)) { - sig = transaction.signature.map((_sig) => utils.resolveSignature(_sig)) - } else { - sig = utils.resolveSignature(transaction.signature) - } + if (!_.isArray(sigs[0])) sigs = [sigs] - const rawTransaction = makeRawTransaction(rlpEncoded, sig, transaction) + const { rawTransaction, signatures, feePayerSignatures } = makeRawTransaction(rlpEncoded, sigs, transaction) result = { - messageHash: messageHash, - signature: sig, - rawTransaction: rawTransaction, - txHash: Hash.keccak256(rawTransaction), - senderTxHash: getSenderTxHash(rawTransaction), + rawTransaction: rawTransaction, + txHash: Hash.keccak256(rawTransaction), + senderTxHash: getSenderTxHash(rawTransaction), + } + + if (signatures && !utils.isEmptySig(signatures)) { + result.signatures = signatures + } + + if (feePayerSignatures && !utils.isEmptySig(feePayerSignatures)) { + result.feePayerSignatures = feePayerSignatures } } catch(e) { @@ -307,12 +879,11 @@ Accounts.prototype.signTransactionWithSignature = function signTransactionWithSi return Promise.resolve(signed(tx)); } - // Otherwise, get the missing info from the Klaytn Node return Promise.all([ isNot(tx.chainId) ? _this._klaytnCall.getChainId() : tx.chainId, isNot(tx.gasPrice) ? _this._klaytnCall.getGasPrice() : tx.gasPrice, - isNot(tx.nonce) ? _this._klaytnCall.getTransactionCount(tx.feePayer || tx.from) : tx.nonce + isNot(tx.nonce) ? _this._klaytnCall.getTransactionCount(tx.from) : tx.nonce ]).then(function (args) { if (isNot(args[0]) || isNot(args[1]) || isNot(args[2])) { throw new Error('One of the values "chainId", "gasPrice", or "nonce" couldn\'t be fetched: '+ JSON.stringify(args)); @@ -321,6 +892,86 @@ Accounts.prototype.signTransactionWithSignature = function signTransactionWithSi }); }; +/** + * combineSignatures combines RLP encoded raw transaction strings. + * combineSignatures compares transaction before combining, and if values in field are not same, this throws error. + * The comparison allows that the address of the fee payer is '0x'(default value) for some transactions while the other transactions have a specific fee payer. This is for the use case that some transactions do not have the fee payer's information. + * In this case, feePayer field doesn't have to be compared with other transaction. + * + * @method combineSignatures + * @param {Array} rawTransactions The array of raw transaction string to combine. + * @param {Function} callback The callback function to call. + * @return {Object} + */ +Accounts.prototype.combineSignatures = function combineSignatures(rawTransactions, callback) { + let decodedTx + let senders = [] + let feePayers = [] + let feePayer + + callback = callback || function () {} + + let handleError = (e) => { + e = e instanceof Error? e : new Error(e) + if (callback) callback(e) + return Promise.reject(e) + } + + if (!_.isArray(rawTransactions)) return handleError(`The parameter of the combineSignatures function must be an array of RLP encoded transaction strings.`) + + for (const raw of rawTransactions) { + const { senderSignatures, feePayerSignatures, decodedTransaction } = extractSignatures(raw) + + senders = senders.concat(senderSignatures) + feePayers = feePayers.concat(feePayerSignatures) + + if (decodedTx) { + let isSame = true + const keys = Object.keys(decodedTx) + for (const key of keys) { + if (key === 'v' || key === 'r' || key === 's' || key === 'signatures' || key === 'payerV' || key === 'payerR' || key === 'payerS' || key === 'feePayerSignatures') { + continue + } + + // feePayer field can be '0x' when after sender signs to trasnaction. + // For handling this, if feePayer is '0x', don't compare with other transaction + if (key === 'feePayer') { + if (decodedTransaction[key] === '0x') { + continue + } else { + // set feePayer variable with valid feePayer address(not '0x') + feePayer = decodedTransaction[key] + if (decodedTx[key] === '0x') { + // set feePayer field to decodedTx for comparing feePayer address with other transactions + decodedTx[key] = decodedTransaction[key] + } + } + } + + if (decodedTransaction[key] === undefined || decodedTx[key] !== decodedTransaction[key]) { + isSame = false + break + } + } + if (!isSame) return handleError(`Failed to combineSignatures: Signatures that sign to different transaction cannot be combined.`) + } else { + decodedTx = decodedTransaction + } + } + + + const parsedTxObject = decodeFromRawTransaction(rawTransactions[0]) + parsedTxObject.signatures = senders + + if (feePayer) { + parsedTxObject.feePayer = feePayer + if (feePayers.length > 0) { + parsedTxObject.feePayerSignatures = feePayers + } + } + return this.getRawTransactionWithSignatures(parsedTxObject, callback) +} + /** * cav.klay.accounts.recoverTransaction('0xf86180808401ef364594f0109fc8df283027b6285cc889f5aa624eac1f5580801ca031573280d608f75137e33fc14655f097867d691d5c4c44ebe5ae186070ac3d5ea0524410802cdc025034daefcdfa08e7d2ee3f0b9d9ae184b2001fe0aff07603d9'); * > "0xF0109fC8DF283027b6285cc889F5aA624EaC1F55" @@ -340,13 +991,13 @@ Accounts.prototype.recoverTransaction = function recoverTransaction(rawTx) { }) arr.unshift(values[6]) - var signature = Account.encodeSignature(arr); + var signature = AccountLib.encodeSignature(arr); var recovery = Bytes.toNumber(values[6]); var extraData = recovery < 35 ? [] : [Bytes.fromNumber((recovery - 35) >> 1), "0x", "0x"]; var signingData = values.slice(0,6).concat(extraData); var signingDataHex = RLP.encode(signingData); - return Account.recover(Hash.keccak256(signingDataHex), signature); + return AccountLib.recover(Hash.keccak256(signingDataHex), signature); }; /** @@ -392,8 +1043,8 @@ Accounts.prototype.sign = function sign(data, privateKey) { if (!utils.isValidPrivateKey(privateKey)) throw new Error('Invalid private key') const messageHash = this.hashMessage(data) - const signature = Account.sign(messageHash, privateKey) - const [v, r, s] = Account.decodeSignature(signature) + const signature = AccountLib.sign(messageHash, privateKey) + const [v, r, s] = AccountLib.decodeSignature(signature) return { message: data, messageHash, @@ -416,7 +1067,7 @@ Accounts.prototype.recover = function recover(message, signature, preFixed) { if (_.isObject(message)) { return this.recover( message.messageHash, - Account.encodeSignature([message.v, message.r, message.s]), + AccountLib.encodeSignature([message.v, message.r, message.s]), true, ) } @@ -429,21 +1080,21 @@ Accounts.prototype.recover = function recover(message, signature, preFixed) { preFixed = args.slice(-1)[0]; preFixed = _.isBoolean(preFixed) ? !!preFixed : false; - return this.recover(message, Account.encodeSignature(args.slice(1, 4)), preFixed); // v, r, s + return this.recover(message, AccountLib.encodeSignature(args.slice(1, 4)), preFixed); // v, r, s } /** * recover in Account module * const recover = (hash, signature) => { * const vals = decodeSignature(signature); * const vrs = { v: Bytes.toNumber(vals[0]), r: vals[1].slice(2), s: vals[2].slice(2) }; - * const ecPublicKey = secp256k1.recoverPubKey(new Buffer(hash.slice(2), "hex"), vrs, vrs.v < 2 ? vrs.v : 1 - vrs.v % 2); // because odd vals mean v=0... sadly that means v=0 means v=1... I hate that - * const publicKey = "0x" + ecPublicKey.encode("hex", false).slice(2); + * const ecPublicKey = secp256k1.recoverPubKey(Buffer.from(hash.slice(2), 'hex'), vrs, vrs.v < 2 ? vrs.v : 1 - vrs.v % 2); // because odd vals mean v=0... sadly that means v=0 means v=1... I hate that + * const publicKey = "0x" + ecPublicKey.encode('hex', false).slice(2); * const publicHash = keccak256(publicKey); * const address = toChecksum("0x" + publicHash.slice(-40)); * return address; * }; */ - return Account.recover(message, signature); + return AccountLib.recover(message, signature); }; // Taken from https://github.com/ethereumjs/ethereumjs-wallet @@ -454,47 +1105,86 @@ Accounts.prototype.decrypt = function (v3Keystore, password, nonStrict) { var json = (_.isObject(v3Keystore)) ? v3Keystore : JSON.parse(nonStrict ? v3Keystore.toLowerCase() : v3Keystore); - if (json.version !== 3) { - console.warn('This is not a V3 wallet.') + if (json.version !== 3 && json.version !== 4) { + console.warn('This is not a V3 or V4 wallet.') // throw new Error('Not a valid V3 wallet'); } - var derivedKey; - var kdfparams; - /** - * Supported kdf modules are the following: - * 1) pbkdf2 - * 2) scrypt - */ - if (json.crypto.kdf === 'scrypt') { - kdfparams = json.crypto.kdfparams; + if (json.version === 3 && !json.crypto) { + // crypto field should be existed in keystore version 3 + throw new Error(`Invalid keystore V3 format: 'crypto' is not defined.`) + } - // FIXME: support progress reporting callback - derivedKey = scryptsy(new Buffer(password), new Buffer(kdfparams.salt, 'hex'), kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen); - } else if (json.crypto.kdf === 'pbkdf2') { - kdfparams = json.crypto.kdfparams; + if (json.crypto) { + if (json.keyring) throw new Error(`Invalid key store format: 'crypto' can not be with 'keyring'`) + json.keyring = [json.crypto] + delete json.crypto + } - if (kdfparams.prf !== 'hmac-sha256') { - throw new Error('Unsupported parameters to PBKDF2'); - } + if (_.isArray(json.keyring[0]) && json.keyring.length > 3) { + throw new Error(`Invalid key store format`) + } + + let accountKey = {} + + // AccountKeyRoleBased format + if (_.isArray(json.keyring[0])) { + let transactionKey = decryptKey(json.keyring[0]) + if (transactionKey) accountKey.transactionKey = transactionKey + + let updateKey = decryptKey(json.keyring[1]) + if (updateKey) accountKey.updateKey = updateKey - derivedKey = cryp.pbkdf2Sync(new Buffer(password), new Buffer(kdfparams.salt, 'hex'), kdfparams.c, kdfparams.dklen, 'sha256'); + let feePayerKey = decryptKey(json.keyring[2]) + if (feePayerKey) accountKey.feePayerKey = feePayerKey } else { - throw new Error('Unsupported key derivation scheme'); + accountKey = decryptKey(json.keyring) } - var ciphertext = new Buffer(json.crypto.ciphertext, 'hex'); + function decryptKey(encryptedArray) { + if (!encryptedArray || encryptedArray.length === 0) return undefined + + let decryptedArray = [] + for (const encrypted of encryptedArray) { + var derivedKey + var kdfparams + /** + * Supported kdf modules are the following: + * 1) pbkdf2 + * 2) scrypt + */ + if (encrypted.kdf === 'scrypt') { + kdfparams = encrypted.kdfparams; + + // FIXME: support progress reporting callback + derivedKey = scrypt(Buffer.from(password), Buffer.from(kdfparams.salt, 'hex'), kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen); + } else if (encrypted.kdf === 'pbkdf2') { + kdfparams = encrypted.kdfparams; + + if (kdfparams.prf !== 'hmac-sha256') { + throw new Error('Unsupported parameters to PBKDF2'); + } + + derivedKey = cryp.pbkdf2Sync(Buffer.from(password), Buffer.from(kdfparams.salt, 'hex'), kdfparams.c, kdfparams.dklen, 'sha256'); + } else { + throw new Error('Unsupported key derivation scheme'); + } - var mac = utils.sha3(Buffer.concat([ derivedKey.slice(16, 32), ciphertext ])).replace('0x',''); - if (mac !== json.crypto.mac) { - throw new Error('Key derivation failed - possibly wrong password'); - } + var ciphertext = Buffer.from(encrypted.ciphertext, 'hex'); - var decipher = cryp.createDecipheriv(json.crypto.cipher, derivedKey.slice(0, 16), new Buffer(json.crypto.cipherparams.iv, 'hex')); - var seed = '0x'+ Buffer.concat([ decipher.update(ciphertext), decipher.final() ]).toString('hex'); + var mac = utils.sha3(Buffer.concat([ derivedKey.slice(16, 32), ciphertext ])).replace('0x',''); + if (mac !== encrypted.mac) { + throw new Error('Key derivation failed - possibly wrong password'); + } - return this.privateKeyToAccount(seed, json.address); -}; + var decipher = cryp.createDecipheriv(encrypted.cipher, derivedKey.slice(0, 16), Buffer.from(encrypted.cipherparams.iv, 'hex')); + decryptedArray.push('0x'+ Buffer.concat([ decipher.update(ciphertext), decipher.final() ]).toString('hex')) + } + return decryptedArray.length === 1? decryptedArray[0] : decryptedArray + } + + return this.createWithAccountKey(json.address, accountKey); + }; /** * cav.klay.accounts.encrypt(privateKey, password); @@ -544,7 +1234,16 @@ Accounts.prototype.decrypt = function (v3Keystore, password, nonStrict) { "id":"dfde6a32-4b0e-404f-8b9f-2b18f279fe21", } */ -Accounts.prototype.encrypt = function (privateKey, password, options) { +/** + * encrypt encrypts an account and returns a key store object. + * + * @method privateKeyToAccount + * @param {String} key The key parameter can be either normal private key or KlaytnWalletKey format. + * @param {String} password The password to be used for account encryption. The encrypted key store can be decrypted with this password. + * @param {Object} options The options to use when encrypt an account. + * @return {Object} + */ +Accounts.prototype.encrypt = function (key, password, options) { /** * options can include below * { @@ -563,60 +1262,110 @@ Accounts.prototype.encrypt = function (privateKey, password, options) { */ options = options || {}; - var account = this.privateKeyToAccount(privateKey, options.address); - - var salt = options.salt || cryp.randomBytes(32); - var iv = options.iv || cryp.randomBytes(16); + let address, account - var derivedKey; - var kdf = options.kdf || 'scrypt'; - var kdfparams = { - dklen: options.dklen || 32, - salt: salt.toString('hex') - }; - - /** - * Supported kdf modules are the following: - * 1) pbkdf2 - * 2) scrypt - default - */ - if (kdf === 'pbkdf2') { - kdfparams.c = options.c || 262144; - kdfparams.prf = 'hmac-sha256'; - derivedKey = cryp.pbkdf2Sync(new Buffer(password), salt, kdfparams.c, kdfparams.dklen, 'sha256'); - } else if (kdf === 'scrypt') { - // FIXME: support progress reporting callback - kdfparams.n = options.n || 4096; // 2048 4096 8192 16384 - kdfparams.r = options.r || 8; - kdfparams.p = options.p || 1; - derivedKey = scryptsy(new Buffer(password), salt, kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen); + if (key instanceof Account) { + if (options.address && options.address !== key.address) throw new Error(`Address in account is not matched with address in options object`) + address = key.address + account = key + } else if (_.isString(key)) { + account = this.privateKeyToAccount(key, options.address) + address = account.address } else { - throw new Error('Unsupported kdf'); + if (!options.address) throw new Error(`The address must be defined inside the options object.`) + address = options.address } - var cipher = cryp.createCipheriv(options.cipher || 'aes-128-ctr', derivedKey.slice(0, 16), iv); - if (!cipher) { - throw new Error('Unsupported cipher'); + if (!account) account = this.createWithAccountKey(address, key) + + let keyring + + switch(account.accountKeyType) { + case AccountKeyEnum.ACCOUNT_KEY_PUBLIC: + case AccountKeyEnum.ACCOUNT_KEY_MULTISIG: + keyring = encryptKey(account.keys) + break + case AccountKeyEnum.ACCOUNT_KEY_ROLEBASED: + keyring = [] + let transactionKey = encryptKey(account.transactionKey) + let updateKey = encryptKey(account.updateKey) + let feePayerKey = encryptKey(account.feePayerKey) + keyring.push(transactionKey) + keyring.push(updateKey) + keyring.push(feePayerKey) + for (i = keyring.length-1; i >=0; i --) { + if (keyring[i].length !== 0) break + keyring = keyring.slice(0, i) + } + break } - var ciphertext = Buffer.concat([ cipher.update(new Buffer(account.privateKey.replace('0x',''), 'hex')), cipher.final() ]); + function encryptKey(privateKey) { + let encryptedArray = [] + + if (!privateKey) return encryptedArray + + let privateKeyArray = _.isArray(privateKey)? privateKey : [privateKey] + + for (const privateKey of privateKeyArray) { + var salt = options.salt || cryp.randomBytes(32); + var iv = options.iv || cryp.randomBytes(16); + + var derivedKey; + var kdf = options.kdf || 'scrypt'; + var kdfparams = { + dklen: options.dklen || 32, + salt: salt.toString('hex') + }; + + /** + * Supported kdf modules are the following: + * 1) pbkdf2 + * 2) scrypt - default + */ + if (kdf === 'pbkdf2') { + kdfparams.c = options.c || 262144; + kdfparams.prf = 'hmac-sha256'; + derivedKey = cryp.pbkdf2Sync(Buffer.from(password), salt, kdfparams.c, kdfparams.dklen, 'sha256'); + } else if (kdf === 'scrypt') { + // FIXME: support progress reporting callback + kdfparams.n = options.n || 4096; // 2048 4096 8192 16384 + kdfparams.r = options.r || 8; + kdfparams.p = options.p || 1; + derivedKey = scrypt(Buffer.from(password), salt, kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen); + } else { + throw new Error('Unsupported kdf'); + } + + var cipher = cryp.createCipheriv(options.cipher || 'aes-128-ctr', derivedKey.slice(0, 16), iv); + if (!cipher) { + throw new Error('Unsupported cipher'); + } + + var ciphertext = Buffer.concat([cipher.update(Buffer.from(privateKey.replace('0x',''), 'hex')), cipher.final()]); + + var mac = utils.sha3(Buffer.concat([derivedKey.slice(16, 32), Buffer.from(ciphertext, 'hex')])).replace('0x',''); + + encryptedArray.push({ + ciphertext: ciphertext.toString('hex'), + cipherparams: { + iv: iv.toString('hex') + }, + cipher: options.cipher || 'aes-128-ctr', + kdf: kdf, + kdfparams: kdfparams, + mac: mac.toString('hex') + }) + } - var mac = utils.sha3(Buffer.concat([ derivedKey.slice(16, 32), new Buffer(ciphertext, 'hex') ])).replace('0x',''); + return encryptedArray + } return { - version: 3, + version: 4, id: uuid.v4({ random: options.uuid || cryp.randomBytes(16) }), address: account.address.toLowerCase(), - crypto: { - ciphertext: ciphertext.toString('hex'), - cipherparams: { - iv: iv.toString('hex') - }, - cipher: options.cipher || 'aes-128-ctr', - kdf: kdf, - kdfparams: kdfparams, - mac: mac.toString('hex') - } + keyring }; }; @@ -628,7 +1377,7 @@ Accounts.prototype.privateKeyToPublicKey = function (privateKey, compressed = fa if (privateKey.length !== 64) { throw new Error('Received a invalid privateKey. The length of privateKey should be 64.') } - const buffer = new Buffer(privateKey, "hex"); + const buffer = Buffer.from(privateKey, 'hex'); const ecKey = secp256k1.keyFromPrivate(buffer) let publicKey @@ -727,43 +1476,47 @@ Wallet.prototype.create = function (numberOfAccounts, entropy) { encrypt: function(password){...} } */ -Wallet.prototype.add = function (account, targetAddressRaw) { - var klaytnWalletKey - /** - * cav.klay.accounts.wallet.add('0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'); - * - * cav.klay.accounts.wallet.add({ - * privateKey: '0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709', - * address: '0xb8CE9ab6943e0eCED004cDe8e3bBed6568B2Fa01' - * }); - */ - if (_.isString(account)) { - account = this._accounts.privateKeyToAccount(account, targetAddressRaw); - } else if(!_.isObject(account)) { - throw new Error('Invalid private key') - } - - const accountAlreadyExists = !!this[account.address] - - if (accountAlreadyExists) { - throw new Error('Account exists with ' + account.address) - } +Wallet.prototype.add = function (account, userInputAddress) { + let accountForWallet + /** + * cav.klay.accounts.wallet.add('0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'); + * + * cav.klay.accounts.wallet.add({ + * privateKey: '0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709', + * address: '0xb8CE9ab6943e0eCED004cDe8e3bBed6568B2Fa01' + * }); + */ + if (Account.isAccountKey(account)) { + if (!userInputAddress) throw new Error(`Address is not defined. Address cannot be determined from AccountKey`) + accountForWallet = this._accounts.createWithAccountKey(userInputAddress, account) + } else if(account instanceof Account) { + accountForWallet = this._accounts.createWithAccountKey(account.address, account.accountKey) + accountForWallet.address = userInputAddress || account.address + } else if (_.isObject(account) && account.address && account.privateKey) { + accountForWallet = this._accounts.privateKeyToAccount(account.privateKey, userInputAddress || account.address) + } else if (_.isString(account)) { + accountForWallet = this._accounts.privateKeyToAccount(account, userInputAddress); + } else { + const accountKey = this._accounts.createAccountKey(account) + if (!userInputAddress) throw new Error(`Address is not defined. Address cannot be determined from AccountKey format`) + accountForWallet = this._accounts.createWithAccountKey(userInputAddress, accountKey) + } - account = this._accounts.privateKeyToAccount(account.privateKey, targetAddressRaw || account.address) + if (!!this[accountForWallet.address]) throw new Error('Account exists with ' + accountForWallet.address) - account.index = this._findSafeIndex() - this[account.index] = account + accountForWallet.index = this._findSafeIndex() + this[accountForWallet.index] = accountForWallet - this[account.address] = account - this[account.address.toLowerCase()] = account - this[account.address.toUpperCase()] = account - try { - this[utils.toChecksumAddress(account.address)] = account - } catch (e) {} + this[accountForWallet.address] = accountForWallet + this[accountForWallet.address.toLowerCase()] = accountForWallet + this[accountForWallet.address.toUpperCase()] = accountForWallet + try { + this[utils.toChecksumAddress(accountForWallet.address)] = accountForWallet + } catch (e) {} - this.length++ + this.length++ - return account + return accountForWallet } Wallet.prototype.updatePrivateKey = function (privateKey, address) { @@ -776,64 +1529,103 @@ Wallet.prototype.updatePrivateKey = function (privateKey, address) { throw new Error('The private key used for the update is not a valid string.') } + if (!utils.isAddress(address)) throw new Error(`Invalid address : ${address}`) + // If failed to find account through address, return error const accountExists = !!this[address] if (!accountExists) throw new Error('Failed to find account with ' + address) const account = this[address] + if (account.accountKeyType !== AccountKeyEnum.ACCOUNT_KEY_PUBLIC) { + throw new Error('Account using AccountKeyMultiSig or AccountKeyRoleBased must be updated using the caver.klay.accounts.updateAccountKey function.') + } + const parsed = utils.parsePrivateKey(privateKey) if (!utils.isValidPrivateKey(parsed.privateKey)) throw new Error('Invalid private key') - if (parsed.address && parsed.address !== account.address) throw new Error('The address extracted from the private key does not match the address received as the input value.') - this[account.index].privateKey = parsed.privateKey + if (parsed.address && parsed.address !== account.address) { + throw new Error('The address extracted from the private key does not match the address received as the input value.') + } + + const newAccountKeyPublic = new AccountKeyPublic(parsed.privateKey) + this[account.index].accountKey = newAccountKeyPublic + this[account.address].accountKey = newAccountKeyPublic + this[account.address.toLowerCase()].accountKey = newAccountKeyPublic + this[account.address.toUpperCase()].accountKey = newAccountKeyPublic - this[account.address].privateKey = parsed.privateKey - this[account.address.toLowerCase()].privateKey = parsed.privateKey - this[account.address.toUpperCase()].privateKey = parsed.privateKey try { - this[utils.toChecksumAddress(account.address)].privateKey = parsed.privateKey + this[utils.toChecksumAddress(account.address)].accountKey = newAccountKeyPublic } catch (e) {} return account } - Wallet.prototype.remove = function (addressOrIndex) { - var account = this[addressOrIndex] +Wallet.prototype.updateAccountKey = function updateAccountKey(address, accountKey) { + if (address === undefined || accountKey === undefined) { + throw new Error('To update the accountKey in wallet, need to set both address and accountKey.') + } - if (account && account.address) { - // address - this[account.address].privateKey = null - delete this[account.address] + if (!Account.isAccountKey(accountKey)) { + accountKey = this._accounts.createAccountKey(accountKey) + } - if (this[account.address.toLowerCase()]) { - // address lowercase - this[account.address.toLowerCase()].privateKey = null - delete this[account.address.toLowerCase()] - } + if (!utils.isAddress(address)) throw new Error(`Invalid address : ${address}`) - if (this[account.address.toUpperCase()]) { - // address uppercase - this[account.address.toUpperCase()].privateKey = null - delete this[account.address.toUpperCase()] - } + // If failed to find account through address, return error + const accountExists = !!this[address] + if (!accountExists) throw new Error('Failed to find account with ' + address) + + const account = this[address] - try { - this[utils.toChecksumAddress(account.address)].privateKey = null - delete this[utils.toChecksumAddress(account.address)] - } catch (e) {} + this[account.index].accountKey = accountKey + this[account.address].accountKey = accountKey + this[account.address.toLowerCase()].accountKey = accountKey + this[account.address.toUpperCase()].accountKey = accountKey - // index - this[account.index].privateKey = null - delete this[account.index] + try { + this[utils.toChecksumAddress(account.address)].accountKey = accountKey + } catch (e) {} - this.length-- + return account +} + +Wallet.prototype.remove = function (addressOrIndex) { + var account = this[addressOrIndex] + + if (account && account.address) { + // address + this[account.address].accountKey = null + delete this[account.address] + + if (this[account.address.toLowerCase()]) { + // address lowercase + this[account.address.toLowerCase()].accountKey = null + delete this[account.address.toLowerCase()] + } - return true - } else { - return false + if (this[account.address.toUpperCase()]) { + // address uppercase + this[account.address.toUpperCase()].accountKey = null + delete this[account.address.toUpperCase()] } + + try { + this[utils.toChecksumAddress(account.address)].accountKey = null + delete this[utils.toChecksumAddress(account.address)] + } catch (e) {} + + // index + this[account.index].accountKey = null + delete this[account.index] + + this.length-- + + return true + } else { + return false } +} Wallet.prototype.clear = function () { var _this = this; @@ -983,6 +1775,19 @@ Wallet.prototype.getKlaytnWalletKey = function (addressOrIndex) { return genKlaytnWalletKeyStringFromAccount(account) } +Wallet.prototype.getAccount = function (input) { + if (_.isNumber(input)) { + if (this.length <= input) throw new Error(`The index(${input}) is out of range(Wallet length : ${this.length}).`) + return this[input] + } + + if (!_.isString(input)) throw new Error(`Accounts in the Wallet can be searched by only index or address. :${input}`) + + if (!utils.isAddress(input)) throw new Error(`Failed to getAccount from Wallet: invalid address(${input})`) + + return this[input.toLowerCase()] +} + function genKlaytnWalletKeyStringFromAccount(account) { var addressString = account.address var privateKey = account.privateKey diff --git a/packages/caver-klay/caver-klay-accounts/src/makeRawTransaction.js b/packages/caver-klay/caver-klay-accounts/src/makeRawTransaction.js index 1e019c9c..7407c0e8 100644 --- a/packages/caver-klay/caver-klay-accounts/src/makeRawTransaction.js +++ b/packages/caver-klay/caver-klay-accounts/src/makeRawTransaction.js @@ -138,14 +138,17 @@ function makeRawTransaction(rlpEncoded, sig, transaction) { case 'FEE_DELEGATED_VALUE_TRANSFER_MEMO_WITH_RATIO': { if (transaction.senderRawTransaction) { const decoded = decodeFromRawTransaction(transaction.senderRawTransaction) - return _combineFeePayerRawTransaction(rlpEncoded, sig, transaction, [decoded.v, decoded.r, decoded.s]) + return _combineFeePayerRawTransaction(rlpEncoded, sig, transaction, decoded.signatures) + } + if (transaction.feePayer && transaction.feePayer !== '0x' && transaction.feePayerSignatures) { + return _combineFeePayerRawTransaction(rlpEncoded, transaction.feePayerSignatures, transaction, sig) } return _combineSenderRawTransaction(rlpEncoded, sig) } case 'LEGACY': default: - rawTx = decodedValues.slice(0, 6).concat(sig) - return RLP.encode(rawTx) + rawTx = decodedValues.slice(0, 6).concat(sig[0]) + return { rawTransaction: RLP.encode(rawTx), signatures: sig[0], feePayerSignatures: undefined } } } @@ -155,27 +158,93 @@ function _combineSenderRawTransaction(rlpEncoded, sig) { let [data] = decodedValues let [txType, ...rawTx] = RLP.decode(data) - rawTx = [...rawTx, [sig]] + if (!Array.isArray(sig[0])) sig = [sig] + sig = refineSignatures(sig) + + rawTx = [...rawTx, sig] // set default feepayer's information in rawTx const typeString = utils.getTxTypeStringFromRawTransaction(txType) if (typeString !== undefined && typeString.includes('FEE_DELEGATED')) rawTx = [...rawTx, '0x', [['0x01', '0x', '0x']]] - return txType + RLP.encode(rawTx).slice(2) + return { rawTransaction: txType + RLP.encode(rawTx).slice(2), signatures: sig, feePayerSignatures: undefined } } -function _combineFeePayerRawTransaction(rlpEncoded, sig, transaction, [senderV, senderR, senderS]) { +function _combineFeePayerRawTransaction(rlpEncoded, feePayerSignatures, transaction, senderSignature) { const decodedValues = RLP.decode(rlpEncoded) let [data] = decodedValues let [txType, ...rawTx] = RLP.decode(data) - rawTx = [...rawTx, [[senderV, senderR, senderS]], transaction.feePayer.toLowerCase(), [sig]] + if (!Array.isArray(feePayerSignatures[0])) feePayerSignatures = [feePayerSignatures] + senderSignature = refineSignatures(senderSignature) + feePayerSignatures = refineSignatures(feePayerSignatures) - return txType + RLP.encode(rawTx).slice(2) + rawTx = [...rawTx, senderSignature, transaction.feePayer.toLowerCase(), feePayerSignatures] + + return { rawTransaction: txType + RLP.encode(rawTx).slice(2), signatures: senderSignature, feePayerSignatures } +} + +// refineSignatures removes duplication and empty signatures +function refineSignatures(sigArray) { + const set = new Set() + let result = [] + for (const sig of sigArray) { + if (sig.length === 0 || utils.isEmptySig(sig)) continue + + const sigString = sig.join('') + if (!set.has(sigString)) { + set.add(sigString, true) + result.push(sig) + } + } + + if (result.length === 0) result = [['0x01', '0x', '0x']] + + return result +} + +function extractSignatures(rawTransaction) { + let senderSignatures = [] + let feePayerSignatures = [] + + const decoded = _decodeFromRawTransaction(rawTransaction) + senderSignatures = senderSignatures.concat(decoded.signatures) + if (decoded.feePayerSignatures) { + feePayerSignatures = feePayerSignatures.concat(decoded.feePayerSignatures) + } + return { senderSignatures, feePayerSignatures, decodedTransaction: decoded } +} + +function splitFeePayer(rawTransaction) { + const typeString = utils.getTxTypeStringFromRawTransaction(rawTransaction) + + if (!typeString || !typeString.includes('FEE_DELEGATED')) throw new Error(`Failed to split fee payer: not a fee delegated transaction type('${typeString? typeString : 'LEGACY'}')`) + + const txType = rawTransaction.slice(0, 4) + const decodedValues = RLP.decode(utils.addHexPrefix(rawTransaction.slice(4))) + + const detachFeePayer = decodedValues.splice(0, decodedValues.length-2) + detachFeePayer.push('0x') + detachFeePayer.push([['0x01', '0x', '0x']]) + + return { senderRawTransaction: txType + RLP.encode(detachFeePayer).slice(2), feePayer: decodedValues[0], feePayerSignatures: decodedValues[1] } } function decodeFromRawTransaction (rawTransaction, type) { + let decodeResult = _decodeFromRawTransaction(rawTransaction, type) + + switch(decodeResult.type) { + case 'ACCOUNT_UPDATE': + case 'FEE_DELEGATED_ACCOUNT_UPDATE': + case 'FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO': { + decodeResult = parseAccountKey(decodeResult) + } + } + return decodeResult +} + +function _decodeFromRawTransaction(rawTransaction, type) { var typeString = type if (typeString === undefined || typeString !== 'LEGACY') { typeString = utils.getTxTypeStringFromRawTransaction(rawTransaction) @@ -189,86 +258,86 @@ function decodeFromRawTransaction (rawTransaction, type) { switch(typeString) { case 'LEGACY': { const [ nonce, gasPrice, gas, to, value, data, v, r, s ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, data, v, r, s } + return { type: typeString, nonce, gasPrice, gas, to, value, data, v, r, s, signatures: [v, r, s] } } case 'VALUE_TRANSFER': { - const [ nonce, gasPrice, gas, to, value, from, [ [ v, r, s ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, v, r, s } + const [ nonce, gasPrice, gas, to, value, from, signatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures } } case 'FEE_DELEGATED_VALUE_TRANSFER': { - const [ nonce, gasPrice, gas, to, value, from, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, to, value, from, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO': { - const [ nonce, gasPrice, gas, to, value, from, feeRatio, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, feeRatio, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, to, value, from, feeRatio, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, feeRatio, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'VALUE_TRANSFER_MEMO': { - const [ nonce, gasPrice, gas, to, value, from, data, [ [ v, r, s ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, v, r, s } + const [ nonce, gasPrice, gas, to, value, from, data, signatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures } } case 'FEE_DELEGATED_VALUE_TRANSFER_MEMO': { - const [ nonce, gasPrice, gas, to, value, from, data, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, to, value, from, data, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'FEE_DELEGATED_VALUE_TRANSFER_MEMO_WITH_RATIO': { - const [ nonce, gasPrice, gas, to, value, from, data, feeRatio, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, feeRatio, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, to, value, from, data, feeRatio, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, feeRatio, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'ACCOUNT_CREATION': { throw new Error(creationNotSupportError) } case 'ACCOUNT_UPDATE': { - const [ nonce, gasPrice, gas, from, accountKey, [ [ v, r, s ] ] ] = RLP.decode(rawTransaction) - return parseAccountKey({ type: typeString, nonce, gasPrice, gas, from, accountKey, v, r, s }) + const [ nonce, gasPrice, gas, from, accountKey, signatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, from, accountKey, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures } } case 'FEE_DELEGATED_ACCOUNT_UPDATE': { - const [ nonce, gasPrice, gas, from, accountKey, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return parseAccountKey({ type: typeString, nonce, gasPrice, gas, from, accountKey, v, r, s, feePayer, payerV, payerR, payerS }) + const [ nonce, gasPrice, gas, from, accountKey, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, from, accountKey, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO': { - const [ nonce, gasPrice, gas, from, accountKey, feeRatio, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return parseAccountKey({ type: typeString, nonce, gasPrice, gas, from, accountKey, feeRatio, v, r, s, feePayer, payerV, payerR, payerS }) + const [ nonce, gasPrice, gas, from, accountKey, feeRatio, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, from, accountKey, feeRatio, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'SMART_CONTRACT_DEPLOY': { - const [ nonce, gasPrice, gas, to, value, from, data, humanReadable, codeFormat, [ [ v, r, s ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, humanReadable: humanReadable === '0x01'? true: false, codeFormat, v, r, s } + const [ nonce, gasPrice, gas, to, value, from, data, humanReadable, codeFormat, signatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, humanReadable: humanReadable === '0x01'? true: false, codeFormat, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures } } case 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY': { - const [ nonce, gasPrice, gas, to, value, from, data, humanReadable, codeFormat, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, humanReadable: humanReadable === '0x01'? true: false, codeFormat, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, to, value, from, data, humanReadable, codeFormat, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, humanReadable: humanReadable === '0x01'? true: false, codeFormat, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY_WITH_RATIO': { - const [ nonce, gasPrice, gas, to, value, from, data, humanReadable, feeRatio, codeFormat, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, humanReadable: humanReadable === '0x01'? true: false, feeRatio, codeFormat, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, to, value, from, data, humanReadable, feeRatio, codeFormat, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, humanReadable: humanReadable === '0x01'? true: false, feeRatio, codeFormat, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'SMART_CONTRACT_EXECUTION': { - const [ nonce, gasPrice, gas, to, value, from, data, [ [ v, r, s ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, v, r, s } + const [ nonce, gasPrice, gas, to, value, from, data, signatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures } } case 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION': { - const [ nonce, gasPrice, gas, to, value, from, data, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, to, value, from, data, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION_WITH_RATIO': { - const [ nonce, gasPrice, gas, to, value, from, data, feeRatio, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, data, feeRatio, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, to, value, from, data, feeRatio, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, data, feeRatio, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'CANCEL': { - const [ nonce, gasPrice, gas, from, [ [ v, r, s ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, from, v, r, s } + const [ nonce, gasPrice, gas, from, signatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, from, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures } } case 'FEE_DELEGATED_CANCEL': { - const [ nonce, gasPrice, gas, from, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, from, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, from, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, from, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'FEE_DELEGATED_CANCEL_WITH_RATIO': { - const [ nonce, gasPrice, gas, from, feeRatio, [ [ v, r, s ] ], feePayer, [ [ payerV, payerR, payerS ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, from, feeRatio, v, r, s, feePayer, payerV, payerR, payerS } + const [ nonce, gasPrice, gas, from, feeRatio, signatures, feePayer, feePayerSignatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, from, feeRatio, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures, feePayer, payerV: feePayerSignatures[0][0], payerR: feePayerSignatures[0][1], payerS: feePayerSignatures[0][2], feePayerSignatures } } case 'CHAIN_DATA_ANCHORING': { - const [ nonce, gasPrice, gas, to, value, from, data, [ [ v, r, s ] ] ] = RLP.decode(rawTransaction) - return { type: typeString, nonce, gasPrice, gas, to, value, from, anchoredData:data, v, r, s } + const [ nonce, gasPrice, gas, to, value, from, data, signatures ] = RLP.decode(rawTransaction) + return { type: typeString, nonce, gasPrice, gas, to, value, from, anchoredData:data, v: signatures[0][0], r: signatures[0][1], s: signatures[0][2], signatures } } } } @@ -319,4 +388,6 @@ module.exports = { decodeFromRawTransaction, overwriteSignature, getSenderTxHash, -} \ No newline at end of file + splitFeePayer, + extractSignatures, +} diff --git a/packages/caver-klay/caver-klay-accounts/src/scrypt.js b/packages/caver-klay/caver-klay-accounts/src/scrypt.js new file mode 100644 index 00000000..291685b0 --- /dev/null +++ b/packages/caver-klay/caver-klay-accounts/src/scrypt.js @@ -0,0 +1,92 @@ +/* + Modifications copyright 2019 The caver-js Authors + This file is part of web3.js. + + web3.js 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. + + web3.js 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 web3.js. If not, see . + + This file is derived from web3.js/packages/web3-eth-accounts/src/scrpyt.js (2019/09/03). + Modified and improved for the caver-js development. +*/ + +var scryptsy = require('scryptsy'); + +var scrypt; + +var isNode = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'; +if (isNode) { + var NODE_MIN_VER_WITH_BUILTIN_SCRYPT = '10.5.0'; + var NODE_MIN_VER_INCOMPAT_SCRYPT_PKG = '12.0.0'; + var semver = require('semver'); + var useNodeBuiltin = isNode && semver.Range('>=' + NODE_MIN_VER_WITH_BUILTIN_SCRYPT).test(process.version); + + var tryScryptPkg = (function() { + var scryptPkg; + return function() { + if (scryptPkg !== undefined) { return scryptPkg; } + try { + scryptPkg = (function(m) { return require(m); })('scrypt'); + } catch (e) { + if (/was compiled against a different/.test(e.message)) { + throw e; + } + scryptPkg = null; + } + return scryptPkg; + }; + })(); + + var canImprove = function(nodeVer) { + return 'can improve web3\'s peformance when running Node.js versions older than ' + nodeVer + ' by installing the (deprecated) scrypt package in your project'; + }; + + if (useNodeBuiltin) { + var crypto = require('crypto'); + var fallbackCount = 0; + scrypt = function(key, salt, N, r, p, dkLen) { + try { + return crypto.scryptSync(key, salt, dkLen, {N: N, r: r, p: p}); + } catch (e) { + if (/scrypt:memory limit exceeded/.test(e.message)) { + var scryptPkg = tryScryptPkg(); + if (scryptPkg) { + return scryptPkg.hashSync(key, {N: N, r: r, p: p}, dkLen, salt); + } + fallbackCount += 1; + console.warn( + '\x1b[33m%s\x1b[0m', + 'Memory limit exceeded for Node\'s built-in crypto.scrypt, falling back to scryptsy (times: ' + fallbackCount + '), if this happens frequently you ' + canImprove(NODE_MIN_VER_INCOMPAT_SCRYPT_PKG) + ); + return scryptsy(key, salt, N, r, p, dkLen); + } + throw e; + } + }; + } else { + var scryptPkg = tryScryptPkg(); + if (scryptPkg) { + scrypt = function(key, salt, N, r, p, dkLen) { + return scryptPkg.hashSync(key, {N: N, r: r, p: p}, dkLen, salt); + }; + } else { + console.warn( + '\x1b[33m%s\x1b[0m', + 'You ' + canImprove(NODE_MIN_VER_WITH_BUILTIN_SCRYPT) + ); + } + } +} + +scrypt = scrypt || scryptsy; + +module.exports = scrypt; diff --git a/packages/caver-klay/caver-klay-accounts/src/transactionType/account.js b/packages/caver-klay/caver-klay-accounts/src/transactionType/account.js index 364c6205..19b120dc 100644 --- a/packages/caver-klay/caver-klay-accounts/src/transactionType/account.js +++ b/packages/caver-klay/caver-klay-accounts/src/transactionType/account.js @@ -55,7 +55,7 @@ function rlpEncodeForAccountUpdate(transaction) { function rlpEncodeForFeeDelegatedAccountUpdate(transaction) { - if (transaction.feePayer) { + if (transaction.senderRawTransaction) { const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, from, accountKey, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -96,7 +96,7 @@ function rlpEncodeForFeeDelegatedAccountUpdate(transaction) { function rlpEncodeForFeeDelegatedAccountUpdateWithRatio(transaction) { - if (transaction.feePayer) { + if (transaction.senderRawTransaction) { const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, from, accountKey, feeRatio, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -138,6 +138,14 @@ function rlpEncodeForFeeDelegatedAccountUpdateWithRatio(transaction) { } function resolveRawKeyToAccountKey(transaction) { + // Handles the case where AccountForUpdate is set in key field in transaction object to update account. + if (transaction.key) { + if (transaction.from && transaction.from.toLowerCase() !== transaction.key.address.toLowerCase()) { + throw new Error(`The value of the from field of the transaction does not match the address of AccountForUpdate.`) + } + transaction.key.fillUpdateObject(transaction) + } + if (!!transaction.legacyKey) return ACCOUNT_KEY_LEGACY_TAG if (!!transaction.failKey) return ACCOUNT_KEY_FAIL_TAG @@ -167,17 +175,20 @@ function resolveRawKeyToAccountKey(transaction) { } if (transaction.roleTransactionKey || transaction.roleAccountUpdateKey || transaction.roleFeePayerKey) { - transaction.roleTransactionKey = transaction.roleTransactionKey + // Create a new object so as not to damage the input transaction object. + const roleBasedObject = {} + + roleBasedObject.roleTransactionKey = transaction.roleTransactionKey ? resolveRawKeyToAccountKey(transaction.roleTransactionKey) : ACCOUNT_KEY_NIL_TAG - transaction.roleAccountUpdateKey = transaction.roleAccountUpdateKey + roleBasedObject.roleAccountUpdateKey = transaction.roleAccountUpdateKey ? resolveRawKeyToAccountKey(transaction.roleAccountUpdateKey) : ACCOUNT_KEY_NIL_TAG - transaction.roleFeePayerKey = transaction.roleFeePayerKey + roleBasedObject.roleFeePayerKey = transaction.roleFeePayerKey ? resolveRawKeyToAccountKey(transaction.roleFeePayerKey) : ACCOUNT_KEY_NIL_TAG - var keys = [transaction.roleTransactionKey, transaction.roleAccountUpdateKey, transaction.roleFeePayerKey] + var keys = [roleBasedObject.roleTransactionKey, roleBasedObject.roleAccountUpdateKey, roleBasedObject.roleFeePayerKey] return ACCOUNT_KEY_ROLE_BASED_TAG + RLP.encode(keys).slice(2) } diff --git a/packages/caver-klay/caver-klay-accounts/src/transactionType/cancel.js b/packages/caver-klay/caver-klay-accounts/src/transactionType/cancel.js index 2aa8e887..138dc056 100644 --- a/packages/caver-klay/caver-klay-accounts/src/transactionType/cancel.js +++ b/packages/caver-klay/caver-klay-accounts/src/transactionType/cancel.js @@ -44,7 +44,7 @@ function rlpEncodeForCancel(transaction) { } function rlpEncodeForFeeDelegatedCancel(transaction) { - if (transaction.feePayer) { + if (transaction.senderRawTransaction) { const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, from, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -81,7 +81,7 @@ function rlpEncodeForFeeDelegatedCancel(transaction) { } function rlpEncodeForFeeDelegatedCancelWithRatio(transaction) { - if (transaction.feePayer) { + if (transaction.senderRawTransaction) { const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, from, feeRatio, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) diff --git a/packages/caver-klay/caver-klay-accounts/src/transactionType/contract.js b/packages/caver-klay/caver-klay-accounts/src/transactionType/contract.js index d170c305..5636a698 100644 --- a/packages/caver-klay/caver-klay-accounts/src/transactionType/contract.js +++ b/packages/caver-klay/caver-klay-accounts/src/transactionType/contract.js @@ -82,7 +82,7 @@ function rlpEncodeForContractExecution(transaction) { function rlpEncodeForFeeDelegatedSmartContractDeploy(transaction) { - if (transaction.feePayer) { + if (transaction.senderRawTransaction) { const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, to, value, from, data, humanReadable, codeFormat, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -129,7 +129,7 @@ function rlpEncodeForFeeDelegatedSmartContractDeploy(transaction) { function rlpEncodeForFeeDelegatedSmartContractDeployWithRatio(transaction) { - if (transaction.feePayer) { + if (transaction.senderRawTransaction) { const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, to, value, from, data, humanReadable, feeRatio, codeFormat, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -178,7 +178,7 @@ function rlpEncodeForFeeDelegatedSmartContractDeployWithRatio(transaction) { function rlpEncodeForFeeDelegatedSmartContractExecution(transaction) { - if (transaction.feePayer) { + if (transaction.senderRawTransaction) { const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, to, value, from, data, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -221,7 +221,7 @@ function rlpEncodeForFeeDelegatedSmartContractExecution(transaction) { function rlpEncodeForFeeDelegatedSmartContractExecutionWithRatio(transaction) { - if (transaction.feePayer) { + if (transaction.senderRawTransaction) { const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, to, value, from, data, feeRatio, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) diff --git a/packages/caver-klay/caver-klay-accounts/src/transactionType/valueTransfer.js b/packages/caver-klay/caver-klay-accounts/src/transactionType/valueTransfer.js index a39162af..9470b293 100644 --- a/packages/caver-klay/caver-klay-accounts/src/transactionType/valueTransfer.js +++ b/packages/caver-klay/caver-klay-accounts/src/transactionType/valueTransfer.js @@ -67,7 +67,7 @@ function rlpEncodeForValueTransferMemo(transaction) { } function rlpEncodeForFeeDelegatedValueTransfer(transaction) { - if (transaction.feePayer) { // fee payer rlp encoding. + if (transaction.senderRawTransaction) { // fee payer rlp encoding. const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, to, value, from, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -106,7 +106,7 @@ function rlpEncodeForFeeDelegatedValueTransfer(transaction) { } function rlpEncodeForFeeDelegatedValueTransferWithRatio(transaction) { - if (transaction.feePayer) { // fee payer rlp encoding. + if (transaction.senderRawTransaction) { // fee payer rlp encoding. const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, to, value, from, feeRatio, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -147,7 +147,7 @@ function rlpEncodeForFeeDelegatedValueTransferWithRatio(transaction) { } function rlpEncodeForFeeDelegatedValueTransferMemo(transaction) { - if (transaction.feePayer) { // fee payer rlp encoding. + if (transaction.senderRawTransaction) { // fee payer rlp encoding. const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, to, value, from, data, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) @@ -188,7 +188,7 @@ function rlpEncodeForFeeDelegatedValueTransferMemo(transaction) { } function rlpEncodeForFeeDelegatedValueTransferMemoWithRatio(transaction) { - if (transaction.feePayer) { // fee payer rlp encoding. + if (transaction.senderRawTransaction) { // fee payer rlp encoding. const typeDetacehdRawTransaction = '0x' + transaction.senderRawTransaction.slice(4) const [ nonce, gasPrice, gas, to, value, from, data, feeRatio, [ [ v, r, s ] ] ] = utils.rlpDecode(typeDetacehdRawTransaction) diff --git a/packages/caver-klay/src/index.js b/packages/caver-klay/src/index.js index 08c831bf..3e7ecf54 100644 --- a/packages/caver-klay/src/index.js +++ b/packages/caver-klay/src/index.js @@ -50,7 +50,7 @@ var Klay = function Klay(...args) { // overwrite setProvider var setProvider = this.setProvider; - this.setProvider = function () { + this.setProvider = function (...args) { setProvider.apply(_this, args); _this.net.setProvider.apply(_this, args); _this.personal.setProvider.apply(_this, args); diff --git a/packages/caver-utils/src/index.js b/packages/caver-utils/src/index.js index 107d86af..26da72ff 100644 --- a/packages/caver-utils/src/index.js +++ b/packages/caver-utils/src/index.js @@ -378,7 +378,8 @@ function _flattenTypes (includeTuple, puts) { * @return {bool} */ var isHexPrefixed = function (str) { - return isHexParameter(str) + if (typeof str !== 'string') return false + return str.slice(0, 2) === '0x' } /** @@ -390,7 +391,7 @@ var isHexPrefixed = function (str) { var addHexPrefix = function (str) { if (typeof str !== 'string') return str - return isHexParameter(str) ? str : '0x' + str + return isHexPrefixed(str) ? str : '0x' + str } /** @@ -402,7 +403,7 @@ var addHexPrefix = function (str) { var stripHexPrefix = function (str) { if (typeof str !== 'string') return str - return isHexParameter(str) ? str.slice(2) : str + return isHexPrefixed(str) ? str.slice(2) : str } /** @@ -505,6 +506,8 @@ module.exports = { padRight: utils.rightPad, rightPad: utils.rightPad, toTwosComplement: utils.toTwosComplement, + isTxHash: utils.isTxHash, + isTxHashStrict: utils.isTxHashStrict, // Moved promiEvent to utils, promiEvent: promiEvent, Iban: Iban, @@ -532,6 +535,12 @@ module.exports = { txTypeToString: utils.txTypeToString, trimLeadingZero: utils.trimLeadingZero, makeEven: utils.makeEven, + isValidPublicKey: utils.isValidPublicKey, isCompressedPublicKey: utils.isCompressedPublicKey, compressPublicKey: utils.compressPublicKey, + + // For account key + isValidRole: utils.isValidRole, + + isEmptySig: utils.isEmptySig, }; diff --git a/packages/caver-utils/src/utils.js b/packages/caver-utils/src/utils.js index c4fc3d55..b743ddff 100644 --- a/packages/caver-utils/src/utils.js +++ b/packages/caver-utils/src/utils.js @@ -57,6 +57,8 @@ const txTypeToString = { '0x48': 'CHAIN_DATA_ANCHORING', } +const TRANSACTION_HASH_LENGTH = 66 + /** * Returns true if object is BN, otherwise false * @@ -398,6 +400,21 @@ var isHex = function (hex) { return ((_.isString(hex) || _.isNumber(hex)) && /^(-0x|0x)?[0-9a-f]*$/i.test(hex)); }; +/** + * Checks if the given string is a hexadecimal transaction hash with or without prefix 0x + * @method isTxHash + * @param {String} tx given hexadecimal transaction hash + * @return {Boolean} + */ +const isTxHash = (tx) => new RegExp(`^(0x|0X)?[0-9a-fA-F]{${TRANSACTION_HASH_LENGTH - 2}}$`).test(tx) + +/** + * Checks if the given string is a hexadecimal transaction hash that starts with 0x + * @method isTxHashStrict + * @param {String} tx given hexadecimal transaction hash + * @return {Boolean} + */ +const isTxHashStrict = (tx) => new RegExp(`^(0x|0X)[0-9a-fA-F]{${TRANSACTION_HASH_LENGTH - 2}}$`).test(tx) /** * Returns true if given string is a valid Klaytn block header bloom. @@ -501,6 +518,8 @@ var sha3 = function (value) { sha3._Hash = Hash; function parsePrivateKey(privateKey) { + if (typeof privateKey !== 'string') throw new Error(`The private key must be of type string`) + const has0xPrefix = privateKey.slice(0, 2) === '0x' privateKey = has0xPrefix ? privateKey.slice(2) : privateKey @@ -632,9 +651,25 @@ const getTxTypeStringFromRawTransaction = (rawTransaction) => { return typeString } +const isValidPublicKey = (publicKey) => { + publicKey = publicKey.replace('0x', '') + + if (publicKey.length !== 66 && publicKey.length !== 128) return false + + if (publicKey.length === 66 && !isCompressedPublicKey(publicKey)) return false + + if (publicKey.length === 128) { + const xyPoints = xyPointFromPublicKey(publicKey) + if (xyPoints === undefined || !xyPoints.length) return false + } + + return true +} + const isCompressedPublicKey = (publicKey) => { const compressedIndicators = ['02', '03'] - return publicKey.replace('0x', '').length === 66 && compressedIndicators.includes(publicKey.slice(2, 4)) + const withoutPrefix = publicKey.replace('0x', '') + return withoutPrefix.length === 66 && compressedIndicators.includes(withoutPrefix.slice(0, 2)) } const compressPublicKey = (uncompressedPublicKey) => { @@ -675,6 +710,35 @@ const isContractDeployment = (txObject) => { } +const isValidRole = (role) => { + switch(role) { + case 'transactionKey': + case 'updateKey': + case 'feePayerKey': + return true + } + return false +} + +const isEmptySig = (sig) => { + if (!Array.isArray(sig)) return false + + function isEmpty (s) { + if (s.length !== 3) throw new Error(`Invalid signatures length: ${s.length}`) + + if (s[0] === '0x01' && s[1] === '0x' && s[2] === '0x') return true + return false + } + + if (Array.isArray(sig[0])) { + // [[v,r,s]] + if (sig.length !== 1) return false + return isEmpty(sig[0]) + } + + return isEmpty(sig) +} + module.exports = { BN: BN, isBN: isBN, @@ -715,6 +779,13 @@ module.exports = { trimLeadingZero, makeEven, txTypeToString, + isValidPublicKey, isCompressedPublicKey, compressPublicKey, + isTxHash, + isTxHashStrict, + + isValidRole: isValidRole, + + isEmptySig: isEmptySig, }; diff --git a/test/decodeTransaction.js b/test/decodeTransaction.js index 74da64be..fb172653 100644 --- a/test/decodeTransaction.js +++ b/test/decodeTransaction.js @@ -46,7 +46,7 @@ describe('Decode Transaction', () => { } let ret = await caver.klay.accounts.signTransaction(txObj, sender.privateKey) - let decodedTx = await caver.klay.decodeTransaction(ret.rawTransaction) + let decodedTx = caver.klay.decodeTransaction(ret.rawTransaction) expect(decodedTx.type).to.equals(txObj.type) expect(decodedTx.nonce).to.equals(txObj.nonce) @@ -58,6 +58,10 @@ describe('Decode Transaction', () => { expect(decodedTx.v).not.to.undefined expect(decodedTx.r).not.to.undefined expect(decodedTx.s).not.to.undefined + expect(decodedTx.signatures).not.to.be.undefined + expect(decodedTx.signatures[0][0]).to.equals(decodedTx.v) + expect(decodedTx.signatures[0][1]).to.equals(decodedTx.r) + expect(decodedTx.signatures[0][2]).to.equals(decodedTx.s) expect(decodedTx.feePayer).to.equals('0x') expect(decodedTx.payerV).to.equals('0x01') expect(decodedTx.payerR).to.equals('0x') @@ -68,7 +72,7 @@ describe('Decode Transaction', () => { feePayer: payer.address, }, payer.privateKey) - decodedTx = await caver.klay.decodeTransaction(ret.rawTransaction) + decodedTx = caver.klay.decodeTransaction(ret.rawTransaction) expect(decodedTx.type).to.equals(txObj.type) expect(decodedTx.nonce).to.equals(txObj.nonce) @@ -80,9 +84,71 @@ describe('Decode Transaction', () => { expect(decodedTx.v).not.to.undefined expect(decodedTx.r).not.to.undefined expect(decodedTx.s).not.to.undefined - expect(decodedTx.feePayer).to.equals(payer.address) + expect(decodedTx.signatures).not.to.be.undefined + expect(decodedTx.signatures[0][0]).to.equals(decodedTx.v) + expect(decodedTx.signatures[0][1]).to.equals(decodedTx.r) + expect(decodedTx.signatures[0][2]).to.equals(decodedTx.s) + expect(decodedTx.feePayer.toLowerCase()).to.equals(payer.address.toLowerCase()) expect(decodedTx.payerV).not.to.undefined expect(decodedTx.payerR).not.to.undefined expect(decodedTx.payerS).not.to.undefined + expect(decodedTx.feePayerSignatures).not.to.be.undefined + expect(decodedTx.feePayerSignatures[0][0]).to.equals(decodedTx.payerV) + expect(decodedTx.feePayerSignatures[0][1]).to.equals(decodedTx.payerR) + expect(decodedTx.feePayerSignatures[0][2]).to.equals(decodedTx.payerS) + }).timeout(10000) + + it('CAVERJS-UNIT-SER-064: Decode sender multi signature transaction', () => { + const rawTransaction = '0x08f8c6028505d21dba00830dbba094342a2853b442c66e47cc0aff29836983050bd1850294cde32e19cfa95b0f03de3d09c549d636e43bed22f88ef845824e43a0edb3620ea3a317e36000ab8177342770d245c27c0a641593ffef57a16532578ba028ecaf81729774b97d7c859c064c84095b9d575278dc1b7cc45cd88a29c0cf91f845824e43a0b2874877cb71c847ad33af3d4cb0861ce2b32c6d7649a3c99a213724871cb37ca00c3e960b277623d6298b9ebd5711083321f7caa162aec10cf2eb49e042081cdd' + let decodedTx = caver.klay.decodeTransaction(rawTransaction) + + expect(decodedTx.type).to.equals('VALUE_TRANSFER') + expect(caver.utils.hexToNumber(decodedTx.nonce)).to.equals(2) + expect(caver.utils.hexToNumber(decodedTx.gasPrice)).to.equals(25000000000) + expect(caver.utils.hexToNumber(decodedTx.gas)).to.equals(900000) + expect(decodedTx.to).to.equals('0x342a2853b442c66e47cc0aff29836983050bd185') + expect(caver.utils.hexToNumber(decodedTx.value)).to.equals(2) + expect(decodedTx.from).to.equals('0xcde32e19cfa95b0f03de3d09c549d636e43bed22') + expect(decodedTx.v).not.to.undefined + expect(decodedTx.r).not.to.undefined + expect(decodedTx.s).not.to.undefined + expect(decodedTx.signatures).not.to.be.undefined + expect(Array.isArray(decodedTx.signatures)).to.be.true + expect(decodedTx.signatures.length).to.equals(2) + expect(decodedTx.signatures[0][0]).to.equals(decodedTx.v) + expect(decodedTx.signatures[0][1]).to.equals(decodedTx.r) + expect(decodedTx.signatures[0][2]).to.equals(decodedTx.s) + }).timeout(10000) + + it('CAVERJS-UNIT-SER-065: Decode sender and feePayer multi signature transaction', () => { + const rawTransaction = '0x09f9016b018505d21dba00830dbba094ca4f2df6e617e340eb2004453e3cc449a8e51d9803946b0f4bb65b4bb4c92d55b1e8574cf8059f3b2da8f88ef845824e43a0cc14fd91517649de4f3e1e2729fa63dfb2ae401e5da54fa52f305fff445d803fa07e134086a557f28847aa689207bc4375bb69cae64ba6356dedc60d8c93929131f845824e44a07d90c9385ae713199f9c4016e06da63af4956294cd66329edc6bf925f03dbfc3a04802101f506df137b218a0880e5b78585c8b7074ecc246950b8e59473d8816de946b0f4bb65b4bb4c92d55b1e8574cf8059f3b2da8f88ef845824e43a09696eec79df68c33ef2dd43302ebb3e18193266d49d805897d0591c6a7e07de0a051b2467e9f84f75c7f1473c3d709df3c13c6824fdf2061c13fe1c41c6ea24155f845824e43a0f7615987a2eeed696d90405b950e26dde93d35ff2fd9d6d94838dc71a209a017a05811cf04dce40d76873ca9ce02a72bcd5b2e748274dc11cd6cfcd34c14cf49e1' + let decodedTx = caver.klay.decodeTransaction(rawTransaction) + + expect(decodedTx.type).to.equals('FEE_DELEGATED_VALUE_TRANSFER') + expect(caver.utils.hexToNumber(decodedTx.nonce)).to.equals(1) + expect(caver.utils.hexToNumber(decodedTx.gasPrice)).to.equals(25000000000) + expect(caver.utils.hexToNumber(decodedTx.gas)).to.equals(900000) + expect(decodedTx.to).to.equals('0xca4f2df6e617e340eb2004453e3cc449a8e51d98') + expect(caver.utils.hexToNumber(decodedTx.value)).to.equals(3) + expect(decodedTx.from).to.equals('0x6b0f4bb65b4bb4c92d55b1e8574cf8059f3b2da8') + expect(decodedTx.v).not.to.undefined + expect(decodedTx.r).not.to.undefined + expect(decodedTx.s).not.to.undefined + expect(decodedTx.signatures).not.to.be.undefined + expect(Array.isArray(decodedTx.signatures)).to.be.true + expect(decodedTx.signatures.length).to.equals(2) + expect(decodedTx.signatures[0][0]).to.equals(decodedTx.v) + expect(decodedTx.signatures[0][1]).to.equals(decodedTx.r) + expect(decodedTx.signatures[0][2]).to.equals(decodedTx.s) + expect(decodedTx.feePayer).to.equals('0x6b0f4bb65b4bb4c92d55b1e8574cf8059f3b2da8') + expect(decodedTx.payerV).not.to.undefined + expect(decodedTx.payerR).not.to.undefined + expect(decodedTx.payerS).not.to.undefined + expect(decodedTx.feePayerSignatures).not.to.be.undefined + expect(Array.isArray(decodedTx.feePayerSignatures)).to.be.true + expect(decodedTx.feePayerSignatures.length).to.equals(2) + expect(decodedTx.feePayerSignatures[0][0]).to.equals(decodedTx.payerV) + expect(decodedTx.feePayerSignatures[0][1]).to.equals(decodedTx.payerR) + expect(decodedTx.feePayerSignatures[0][2]).to.equals(decodedTx.payerS) }).timeout(10000) }) \ No newline at end of file diff --git a/test/intTest.js b/test/intTest.js index 029a95a8..058f4612 100644 --- a/test/intTest.js +++ b/test/intTest.js @@ -320,7 +320,7 @@ const getSignedRawTransaction = async (t) => { rawTransaction = signedRawTx.rawTransaction if (t.tx.v !== undefined || t.tx.r !== undefined || t.tx.s !== undefined) { - const txObj = await caver.klay.decodeTransaction(rawTransaction, t.tx.type) + const txObj = caver.klay.decodeTransaction(rawTransaction, t.tx.type) if (t.tx.v !== undefined) { txObj.v = t.tx.v } if (t.tx.r !== undefined) { txObj.r = t.tx.r } if (t.tx.s !== undefined) { txObj.s = t.tx.s } @@ -350,11 +350,11 @@ const getSignedRawTransaction = async (t) => { // If v or r or s value is set in test case, overwrite with that. if (t.tx.payerV !== undefined || t.tx.payerR !== undefined || t.tx.payerS !== undefined) { - const txObj = await caver.klay.decodeTransaction(rawTransaction, t.tx.type) + const txObj = caver.klay.decodeTransaction(rawTransaction, t.tx.type) if (t.tx.payerV !== undefined) { txObj.payerV = t.tx.payerV } if (t.tx.payerR !== undefined) { txObj.payerR = t.tx.payerR } if (t.tx.payerS !== undefined) { txObj.payerS = t.tx.payerS } - rawTransaction = overwriteSignature(rawTransaction, txObj, undefined, [t.tx.payerV, t.tx.payerR, t.tx.payerS]) + rawTransaction = overwriteSignature(rawTransaction, txObj, undefined, [txObj.payerV, txObj.payerR, txObj.payerS]) } } diff --git a/test/isCompressedPublicKey.js b/test/isCompressedPublicKey.js index 85f585dd..8d4923de 100644 --- a/test/isCompressedPublicKey.js +++ b/test/isCompressedPublicKey.js @@ -32,6 +32,9 @@ describe('caver.utils.isCompressedPublicKey', (done) => { expect(caver.utils.isCompressedPublicKey(uncompressedPublicKey1)).to.false expect(caver.utils.isCompressedPublicKey(uncompressedPublicKey2)).to.false expect(caver.utils.isCompressedPublicKey(uncompressedPublicKey3)).to.false + expect(caver.utils.isCompressedPublicKey(uncompressedPublicKey1.slice(2))).to.false + expect(caver.utils.isCompressedPublicKey(uncompressedPublicKey2.slice(2))).to.false + expect(caver.utils.isCompressedPublicKey(uncompressedPublicKey3.slice(2))).to.false }) it('CAVERJS-UNIT-SER-026 : Should return true if the argument is compressed public key', () => { @@ -45,5 +48,8 @@ describe('caver.utils.isCompressedPublicKey', (done) => { expect(caver.utils.isCompressedPublicKey(compressedPublicKey1)).to.true expect(caver.utils.isCompressedPublicKey(compressedPublicKey2)).to.true expect(caver.utils.isCompressedPublicKey(compressedPublicKey3)).to.true + expect(caver.utils.isCompressedPublicKey(compressedPublicKey1.slice(2))).to.true + expect(caver.utils.isCompressedPublicKey(compressedPublicKey2.slice(2))).to.true + expect(caver.utils.isCompressedPublicKey(compressedPublicKey3.slice(2))).to.true }) }) diff --git a/test/packages/caver.klay.accounts.js b/test/packages/caver.klay.accounts.js index af49e013..d33f5797 100644 --- a/test/packages/caver.klay.accounts.js +++ b/test/packages/caver.klay.accounts.js @@ -32,10 +32,11 @@ beforeEach(() => { caver = new Caver(testRPCURL) }) -function isAccount(data, { privateKey, address } = {}) { +function isAccount(data, { keys, address } = {}) { // account object keys - const keys = [ + const objectKeys = [ 'address', + 'accountKey', 'privateKey', 'signTransaction', 'sign', @@ -43,16 +44,16 @@ function isAccount(data, { privateKey, address } = {}) { 'getKlaytnWalletKey' ] - expect(Object.getOwnPropertyNames(data)).to.deep.equal(keys) + expect(Object.getOwnPropertyNames(data)).to.deep.equal(objectKeys) expect(caver.utils.isAddress(data.address)).to.equal(true) - if (privateKey !== undefined) { - expect(data.privateKey).to.equal(privateKey) + if (keys !== undefined) { + compareAccountKey(data.accountKey, keys) } if (address !== undefined) { - expect(data.address).to.equal(address.toLowerCase()) + expect(data.address.toLowerCase()).to.equal(address.toLowerCase()) } } @@ -62,9 +63,11 @@ function checkHashMessage(hashed, originMessage) { expect(hashed).to.equal(originHashed) } -function isKeystoreV3(data, { address }) { - const keys = ['version', 'id', 'address', 'crypto'] - expect(Object.getOwnPropertyNames(data)).to.deep.equal(keys) +function isKeystoreV4(data, { address }) { + const objectKeys = ['version', 'id', 'address', 'keyring'] + expect(Object.getOwnPropertyNames(data)).to.deep.equal(objectKeys) + + expect(data.version).to.equals(4) expect(caver.utils.isAddress(data.address)).to.equal(true) @@ -85,19 +88,59 @@ function isWallet(data, { accounts } = {}) { expect(data.length).to.equal(accounts.length) for (let i = 0; i < data.length; i++) { - let accountObj = Object.assign({}, data[i]) - delete accountObj.index + let accountObj = caver.klay.accounts.createWithAccountKey(data[i].address, data[i].accountKey) - isAccount(accountObj, { privateKey: accounts[i].privateKey, address: accounts[i].address }) + isAccount(accountObj, { keys: accounts[i].keys, address: accounts[i].address }) - accountObj = Object.assign({}, data[accountObj.address]) - delete accountObj.index + accountObj = caver.klay.accounts.createWithAccountKey(data[i].address, data[accountObj.address].accountKey) - isAccount(accountObj, { privateKey: accounts[i].privateKey, address: accounts[i].address }) + isAccount(accountObj, { keys: accounts[i].keys, address: accounts[i].address }) } } } +function compareAccountKey(keyFromAccount, key) { + if (!keyFromAccount && !key) return + + if (typeof keyFromAccount.keys === 'string') { + expect(isSameKeyString(keyFromAccount.keys, key)).to.be.true + expect(isSameKeyString(keyFromAccount.transactionKey, key)).to.be.true + expect(isSameKeyString(keyFromAccount.updateKey, key)).to.be.true + expect(isSameKeyString(keyFromAccount.feePayerKey, key)).to.be.true + } else if (Array.isArray(keyFromAccount.keys)) { + expect(isSameKeyArray(keyFromAccount.keys, key)).to.be.true + expect(isSameKeyArray(keyFromAccount.transactionKey, key)).to.be.true + expect(isSameKeyArray(keyFromAccount.updateKey, key)).to.be.true + expect(isSameKeyArray(keyFromAccount.feePayerKey, key)).to.be.true + } else { + expect(isSameKeyObject(keyFromAccount.keys, key)).to.be.true + compareAccountKey(keyFromAccount._transactionKey, key.transactionKey) + compareAccountKey(keyFromAccount._updateKey, key.updateKey) + compareAccountKey(keyFromAccount._feePayerKey, key.feePayerKey) + } +} + +function isSameKeyString(str1, str2) { + return str1.toLowerCase() === str2.toLowerCase() +} + +function isSameKeyArray(arr1, arr2) { + if (arr1.length !== arr2.length) return false + for (let i = 0; i < arr1.length; i ++) { + if (arr1[i].toLowerCase() !== arr2[i].toLowerCase()) return false + } + return true +} + +function isSameKeyObject(obj1, obj2) { + const keys = Object.keys(obj1) + keys.map((r)=> { + if (typeof obj1[r] === 'string' && !isSameKeyString(obj1[r], obj2[r])) return false + if (Array.isArray(obj1[r]) && !isSameKeyArray(obj1[r], obj2[r])) return false + }) + return true +} + describe('caver.klay.accounts.create', () => { context('CAVERJS-UNIT-WALLET-021 : input: no parameter', () => { it('should return valid account', () => { @@ -137,20 +180,35 @@ describe('caver.klay.accounts.privateKeyToAccount', () => { }) describe('caver.klay.accounts.signTransaction', () => { - const txObj = { - from: setting.toAddress, - nonce: '0x0', - to: setting.toAddress, - gas: setting.gas, - gasPrice: setting.gasPrice, - value: '0x1', - chainId: 2019, - } - - let account + let txObj, vtTx, account, feePayer, sender beforeEach(() => { account = caver.klay.accounts.create() + caver.klay.accounts.wallet.add(account) + feePayer = caver.klay.accounts.wallet.add('0x3bdc858e890c3c845ea9ca24b1e9ed183a56eb78d4bf5463da219a74f708eff6') + sender = caver.klay.accounts.wallet.add('0x66de1a1fa104b008c3c34c1695415d71435384f3b68df03dda82e74f9d85064d') + + txObj = { + from: account.address, + nonce: '0x0', + to: setting.toAddress, + gas: setting.gas, + gasPrice: setting.gasPrice, + value: '0x1', + chainId: 2019, + } + + + vtTx = { + type: 'VALUE_TRANSFER', + from: account.address, + to: '0xd05c5926b0a2f31aadcc9a9cbd3868a50104d834', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } }) context('CAVERJS-UNIT-WALLET-023 : input: tx, privateKey', () => { @@ -160,7 +218,7 @@ describe('caver.klay.accounts.signTransaction', () => { account.privateKey ) - const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash'] + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) expect(caver.klay.accounts.recoverTransaction(result.rawTransaction)).to.equal(account.address) @@ -174,7 +232,7 @@ describe('caver.klay.accounts.signTransaction', () => { const result = await caver.klay.accounts.signTransaction(tx, account.privateKey) - const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash'] + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) expect(caver.klay.accounts.recoverTransaction(result.rawTransaction)).to.equal(account.address) @@ -188,7 +246,7 @@ describe('caver.klay.accounts.signTransaction', () => { const result = await caver.klay.accounts.signTransaction(tx, account.privateKey) - const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash'] + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) expect(caver.klay.accounts.recoverTransaction(result.rawTransaction)).to.equal(account.address) @@ -202,7 +260,7 @@ describe('caver.klay.accounts.signTransaction', () => { const result = await caver.klay.accounts.signTransaction(tx, account.privateKey) - const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash'] + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) expect(caver.klay.accounts.recoverTransaction(result.rawTransaction)).to.equal(account.address) @@ -235,13 +293,12 @@ describe('caver.klay.accounts.signTransaction', () => { }) context('CAVERJS-UNIT-WALLET-025 : input: tx, privateKey:invalid', () => { - it('should throw an error', () => { + it('should throw an error', async () => { const invalidPrivateKey = caver.utils.randomHex(31) // 31bytes const errorMessage = 'Invalid private key' - expect(() => caver.klay.accounts.signTransaction(txObj, invalidPrivateKey)) - .to.throw(errorMessage) + await expect(caver.klay.accounts.signTransaction(txObj, invalidPrivateKey)).to.be.rejectedWith(errorMessage) }) }) @@ -251,7 +308,7 @@ describe('caver.klay.accounts.signTransaction', () => { txObj, account.privateKey, (error, result) => { - const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash'] + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) expect(caver.klay.accounts.recoverTransaction(result.rawTransaction)).to.equal(account.address) @@ -276,355 +333,4134 @@ describe('caver.klay.accounts.signTransaction', () => { expect(result.senderTxHash).to.equal('0x1b7c0f2fc7548056e90d9690e8c397acf99eb38e622ac91ee22c2085065f8a55') }) }) -}) - -describe('caver.klay.accounts.recoverTransaction', () => { - let account - let rawTx - - beforeEach(async () => { - account = caver.klay.accounts.create() - - const txObj = { - from: setting.fromAddress, - nonce: '0x0', - to: setting.toAddress, - gas: setting.gas, - gasPrice: setting.gasPrice, - value: '0x1' - } - const signedTx = await account.signTransaction(txObj) - rawTx = signedTx.rawTransaction - }) - - context('CAVERJS-UNIT-WALLET-029 : rawTransaction', () => { - it('should return valid address', () => { - const result = caver.klay.accounts.recoverTransaction(rawTx) - expect(result).to.equal(account.address) - }) - }) - context('CAVERJS-UNIT-WALLET-030 : rawTransaction:invalid', () => { - it('should not equal to account.address', () => { - const invalid = rawTx.slice(0, -2) - const result = caver.klay.accounts.recoverTransaction(invalid) - expect(result).to.not.equal(account.addrss) - }) - }) + context('CAVERJS-UNIT-WALLET-122 : input: legacyTx, privateKey of decoupled account', () => { + it('should return signature and rawTransaction', async () => { + const decoupledAccount = caver.klay.accounts.privateKeyToAccount(caver.klay.accounts.create().privateKey, caver.klay.accounts.create().address) - context('CAVERJS-UNIT-WALLET-104 : rawTransaction: leading zeroes of the signature are trimmed', () => { - it('should equal to account.address', async () => { - const transaction = { - from: '0x13b0d8316F0c3cE0C3C51Ebb586A14d7d90112fD', + let tx = { + from: decoupledAccount.address, nonce: '0x0', - to: '0x30d8d4217145ba3f6cde24ec28c64c9120f2bdfb', - gas: 900000, - gasPrice: 25000000000, + to: setting.toAddress, + gas: setting.gas, + gasPrice: setting.gasPrice, value: '0x1', - chainId: 10000, + chainId: 2019, } - account.address = '0x13b0d8316F0c3cE0C3C51Ebb586A14d7d90112fD' - account.privateKey = '0x72d72a46401220f08ccb1b17b550feb816840f2f8ce86361e7ee54ac7a9ee6d8' - - const signed = await caver.klay.accounts.signTransaction(transaction, account.privateKey) - const rlpDecoded = caver.utils.rlpDecode(signed.rawTransaction) - expect(rlpDecoded[7].length).not.to.equals(rlpDecoded[8].length) + const errorMessage = 'A legacy transaction must be with a legacy account key' - const result = caver.klay.accounts.recoverTransaction(signed.rawTransaction) - expect(result).to.not.equal(account.addrss) + await expect(caver.klay.accounts.signTransaction(tx, decoupledAccount.privateKey)).to.be.rejectedWith(errorMessage) }) }) - context('CAVERJS-UNIT-WALLET-105 : rawTransaction: Non-LEGACY transactions.', () => { - it('should throw error', async () => { - const transaction = { - type: 'VALUE_TRANSFER', - from: '0x13b0d8316F0c3cE0C3C51Ebb586A14d7d90112fD', - nonce: '0x0', - to: '0x30d8d4217145ba3f6cde24ec28c64c9120f2bdfb', - gas: 900000, - gasPrice: 25000000000, - value: '0x1', - chainId: 10000, - } - account.address = '0x13b0d8316F0c3cE0C3C51Ebb586A14d7d90112fD' - account.privateKey = '0x72d72a46401220f08ccb1b17b550feb816840f2f8ce86361e7ee54ac7a9ee6d8' - - const signed = await caver.klay.accounts.signTransaction(transaction, account.privateKey) + context('CAVERJS-UNIT-WALLET-123 : input: if there are invalid number of parameters then signTrasnaction should reject', () => { + it('should reject when there is no parameter', async () => { + const errorMessage = 'Invalid parameter: The number of parameters is invalid.' + await expect(caver.klay.accounts.signTransaction()).to.be.rejectedWith(errorMessage) + }) - const errorMessage = 'recoverTransaction only supports transactions of type "LEGACY".' - expect(()=>caver.klay.accounts.recoverTransaction(signed.rawTransaction)).to.throws(errorMessage) + it('should reject when there are more than three parameters', async () => { + const errorMessage = 'Invalid parameter: The number of parameters is invalid.' + await expect(caver.klay.accounts.signTransaction({}, 'privateKey', ()=>{}, 'one more')).to.be.rejectedWith(errorMessage) }) }) -}) -describe('caver.klay.accounts.hashMessage', () => { - it('CAVERJS-UNIT-WALLET-031, CAVERJS-UNIT-WALLET-032 : result should be same with keccak256(MessagePrefix + originMessage.length + originMessage)', () => { - const message = 'Hello World' - let result = caver.klay.accounts.hashMessage(message) - checkHashMessage(result, message) + context('CAVERJS-UNIT-WALLET-124 : input: if there are valid number of parameters then signTrasnaction should set properly', () => { + it('should sign to transaction parameter with private key in wallet', async () => { + const result = await caver.klay.accounts.signTransaction(txObj) - const decoded = caver.utils.utf8ToHex(message) - result = caver.klay.accounts.hashMessage(decoded) - checkHashMessage(result, message) - }) -}) + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) -describe('caver.klay.accounts.sign', () => { - let account + expect(typeof result.signatures[0]).to.equals('string') - beforeEach(() => { - account = caver.klay.accounts.create() - }) + expect(caver.klay.accounts.recoverTransaction(result.rawTransaction)).to.equal(account.address) + }) - context('CAVERJS-UNIT-WALLET-033 : input: data, privateKey', () => { - it('should recover valid address', () => { - const data = 'Some data' - let result = caver.klay.accounts.sign(data, account.privateKey) + it('should sign to transaction parameter with private key in wallet with callback', async () => { + let isCalled = false + const result = await caver.klay.accounts.signTransaction(txObj, (error, result) => isCalled = true) - const keys = ['message', 'messageHash', 'v', 'r', 's', 'signature'] + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) - if (data != result.message) { - expect(data).to.equal(caver.utils.utf8ToHex(result.message)) - } + expect(typeof result.signatures[0]).to.equals('string') - const decoded = caver.utils.utf8ToHex(data) - result = caver.klay.accounts.sign(decoded, account.privateKey) - checkHashMessage(result.messageHash, data) + expect(caver.klay.accounts.recoverTransaction(result.rawTransaction)).to.equal(account.address) - expect(caver.klay.accounts.recover(result)).to.equal(account.address) + expect(isCalled).to.be.true }) - }) - context('CAVERJS-UNIT-WALLET-034 : input: data, privateKey:invalid', () => { - it('should throw an error', () => { - const data = 'Some data' - const invalid = caver.utils.randomHex(31) // 31bytes + it('should sign to transaction parameter with private key parameter', async () => { + const result = await caver.klay.accounts.signTransaction(vtTx, account.privateKey) - const errorMessage = 'Invalid private key' - expect(() => caver.klay.accounts.sign(data, invalid)).to.throw(errorMessage) - }) - }) -}) + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) -// caver.klay.accounts.recover -describe('caver.klay.accounts.recover', () => { - let account + expect(Array.isArray(result.signatures[0])).to.be.true + expect(result.signatures.length).to.equals(1) + }) - beforeEach(() => { - account = caver.klay.accounts.create() - }) + it('should sign to transaction parameter with private key array', async () => { + const privateKeyArray = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const result = await caver.klay.accounts.signTransaction(vtTx, privateKeyArray) - context('CAVERJS-UNIT-WALLET-035 : input: signatureObject', () => { - it('result should be same with account.address', () => { - const message = 'Some data' - const sigObj = account.sign(message) + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) - let result = caver.klay.accounts.recover(sigObj) - expect(result).to.equal(account.address) + expect(Array.isArray(result.signatures[0])).to.be.true + expect(result.signatures.length).to.equals(2) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(2) + expect(decoded.signatures[0][0]).to.equals(result.signatures[0][0]) + expect(decoded.signatures[0][1]).to.equals(result.signatures[0][1]) + expect(decoded.signatures[0][2]).to.equals(result.signatures[0][2]) + expect(decoded.signatures[1][0]).to.equals(result.signatures[1][0]) + expect(decoded.signatures[1][1]).to.equals(result.signatures[1][1]) + expect(decoded.signatures[1][2]).to.equals(result.signatures[1][2]) }) }) - context('CAVERJS-UNIT-WALLET-036 : input: message, signature', () => { - it('result should be same with account.address', () => { - const message = 'Some data' - const sigObj = account.sign(message) - - let result = caver.klay.accounts.recover(sigObj.message, sigObj.signature) - expect(result).to.equal(account.address) + context('CAVERJS-UNIT-WALLET-130 : input: txObject', () => { + it('should sign with feePayer and return feePayerSignatures', async () => { + const feeDelegatedTx = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: '0x76d1cc1cdb081de8627cab2c074f02ebc7bce0d0', + to: '0xd05c5926b0a2f31aadcc9a9cbd3868a50104d834', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + const senderSigned = await caver.klay.accounts.signTransaction(feeDelegatedTx, '1881a973628dba6ab07b6b47c8f3fb50d8e7cbf71fef3b4739155619a3c126fa') + + const feePayerTransaction = { + senderRawTransaction: senderSigned.rawTransaction, + feePayer: account.address + } + const feePayerSigned = await caver.klay.accounts.signTransaction(feePayerTransaction) + expect(feePayerSigned.feePayerSignatures).not.to.be.undefined + expect(Array.isArray(feePayerSigned.feePayerSignatures)).to.be.true + + const decoded = caver.klay.decodeTransaction(feePayerSigned.rawTransaction) + expect(decoded.signatures.length).to.equals(1) + expect(decoded.signatures[0][0]).to.equals(senderSigned.signatures[0][0]) + expect(decoded.signatures[0][1]).to.equals(senderSigned.signatures[0][1]) + expect(decoded.signatures[0][2]).to.equals(senderSigned.signatures[0][2]) + expect(decoded.feePayerSignatures[0][0]).to.equals(feePayerSigned.feePayerSignatures[0][0]) + expect(decoded.feePayerSignatures[0][1]).to.equals(feePayerSigned.feePayerSignatures[0][1]) + expect(decoded.feePayerSignatures[0][2]).to.equals(feePayerSigned.feePayerSignatures[0][2]) }) }) - context('CAVERJS-UNIT-WALLET-037 : input: message, signature, prefixed', () => { - it('result should be same with account.address', () => { - const message = 'Some data' - const sigObj = account.sign(message) - const prefixed = true - - const messageHash = caver.klay.accounts.hashMessage(message) + context('CAVERJS-UNIT-WALLET-131 : input: txObject for fee payer without feePayer field', () => { + it('should reject', async () => { + const feeDelegatedTx = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: '0x76d1cc1cdb081de8627cab2c074f02ebc7bce0d0', + to: '0xd05c5926b0a2f31aadcc9a9cbd3868a50104d834', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + const senderSigned = await caver.klay.accounts.signTransaction(feeDelegatedTx, '1881a973628dba6ab07b6b47c8f3fb50d8e7cbf71fef3b4739155619a3c126fa') + + const feePayerTransaction = { + senderRawTransaction: senderSigned.rawTransaction, + } - let result = caver.klay.accounts.recover(messageHash, sigObj.signature, prefixed) - expect(result).to.equal(account.address) + const errorMessage = `Invalid fee payer: ${feePayerTransaction.feePayer}` + await expect(caver.klay.accounts.signTransaction(feePayerTransaction)).to.be.rejectedWith(errorMessage) }) }) - context('CAVERJS-UNIT-WALLET-038 : input: message, v, r, s', () => { - it('result should be same with account.address', () => { - const message = 'Some data' - const sigObj = account.sign(message) - - let result = caver.klay.accounts.recover(sigObj.message, sigObj.v, sigObj.r, sigObj.s) - expect(result).to.equal(account.address) + context('CAVERJS-UNIT-WALLET-132 : input: txObject without private key', () => { + it('when fail to find account, should reject with expected error message', async () => { + const feeDelegatedTx = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: caver.klay.accounts.create().address, + to: '0xd05c5926b0a2f31aadcc9a9cbd3868a50104d834', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + const errorMessage = 'Failed to find get private key to sign. The account you want to use for signing must exist in caver.klay.accounts.wallet or you must pass the private key as a parameter.' + await expect(caver.klay.accounts.signTransaction(feeDelegatedTx)).to.be.rejectedWith(errorMessage) }) }) - context('CAVERJS-UNIT-WALLET-039 : input: message, v, r, s, prefixed', () => { - it('result should be same with account.address', () => { - const message = 'Some data' - const sigObj = account.sign(message) - const prefixed = true - - const messageHash = caver.klay.accounts.hashMessage(message) + context('CAVERJS-UNIT-WALLET-225: input: rawTransaction without other signatures', () => { + it('should sign to transaction', async () => { + const rawTransaction = '0x08f83c819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194f63c07602e64ca5e2ffb325fdbe4b76015d56f1cc4c3018080' + const result = await caver.klay.accounts.signTransaction(rawTransaction, account.privateKey) - let result = caver.klay.accounts.recover(messageHash, sigObj.v, sigObj.r, sigObj.s, prefixed) - expect(result).to.equal(account.address) + expect(result.signatures.length).to.equals(1) + expect(result.feePayerSignatures).to.be.undefined }) }) -}) - -// caver.klay.accounts.encrypt -describe('caver.klay.accounts.encrypt', () => { - let account - beforeEach(() => { - account = caver.klay.accounts.create() + context('CAVERJS-UNIT-WALLET-226: input: rawTransaction with other sender signatures', () => { + it('should sign with private key and append to signatures', async () => { + const rawTransaction = '0x08f880819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194f63c07602e64ca5e2ffb325fdbe4b76015d56f1cf847f845824e44a068e480ad868cdbe509d3f6419f872d5f0bfe5c81dd6b56463df73f2225353ef0a005836c1c756bcc5262dfcb4aa1c1b69858475c389a770170f25105f58e23bc85' + const result = await caver.klay.accounts.signTransaction(rawTransaction, account.privateKey) + + expect(result.signatures.length).to.equals(2) + expect(result.feePayerSignatures).to.be.undefined + }) }) - context('CAVERJS-UNIT-WALLET-040 : input: privateKey, password', () => { - it('should encrypt password with privateKey', () => { - const password = 'klaytn!@' - - let result = caver.klay.accounts.encrypt(account.privateKey, password) - - isKeystoreV3(result, account) + context('CAVERJS-UNIT-WALLET-227: input: rawTransaction without signatures of sender and fee payer', () => { + it('should sign with fee payer', async () => { + const rawTransaction = '0x09f842819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149c4c301808080c4c3018080' + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: feePayer.address + } + const result = await caver.klay.accounts.signTransaction(feePayerTx, feePayer.privateKey) - const decryptedAccount = caver.klay.accounts.decrypt(result, password) - isAccount(decryptedAccount, account) + expect(result.signatures).to.be.undefined + expect(result.feePayerSignatures.length).to.equals(1) }) }) - context('CAVERJS-UNIT-WALLET-041 : input: privateKey:invalid, password', () => { - it('should throw an error', () => { - const invalid = caver.utils.randomHex(31) // 31bytes - const password = 'klaytn!@' - - const errorMessage = 'Invalid private key' - expect(() => caver.klay.accounts.encrypt(invalid, password)).to.throw(errorMessage) + context('CAVERJS-UNIT-WALLET-228: input: rawTransaction with signatures of fee payer', () => { + it('should sign with sender and include existed signatures of fee payer', async () => { + const rawTransaction = '0x09f89a819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149c4c3018080944a804669b2637b18d46e62109ed8edc0dc8526c7f847f845824e43a0113bf0986a48768e38daeff685ce56766aacf449f2aec8a0c77165777970f954a00717c4d4720fe706a075374f8eb0432f6c9f70c0a6e6267483d0b0cc8100ec07' + const result = await caver.klay.accounts.signTransaction(rawTransaction, account.privateKey) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + + expect(result.signatures.length).to.equals(1) + expect(result.feePayerSignatures).to.be.undefined + expect(decoded.signatures.length).to.equals(1) + expect(decoded.feePayerSignatures.length).to.equals(1) }) }) - context('CAVERJS-UNIT-WALLET-096 : input: privateKey:KlaytnWalletKey, password', () => { - it('should encrypt password with privateKey', () => { - const password = 'klaytn!@' - - let result = caver.klay.accounts.encrypt(account.getKlaytnWalletKey(), password) - - isKeystoreV3(result, account) + context('CAVERJS-UNIT-WALLET-229: input: rawTransaction with signatures of sender and fee payer', () => { + it('should append signatures of sender to existed signatures', async () => { + const rawTransaction = '0x09f8de819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149f847f845824e44a030accfbec3b577a53103c843744754a98408e7518391a31399bc757610ad1fc5a00eecc65e8f6f1795f7665513442fe30f881ddce12ceb687a8a7d9b65a0eee595944a804669b2637b18d46e62109ed8edc0dc8526c7f847f845824e43a0113bf0986a48768e38daeff685ce56766aacf449f2aec8a0c77165777970f954a00717c4d4720fe706a075374f8eb0432f6c9f70c0a6e6267483d0b0cc8100ec07' + const result = await caver.klay.accounts.signTransaction(rawTransaction, sender.privateKey) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + + expect(result.signatures.length).to.equals(2) + expect(result.feePayerSignatures).to.be.undefined + expect(decoded.signatures.length).to.equals(2) + expect(decoded.feePayerSignatures.length).to.equals(1) + }) + }) - const decryptedAccount = caver.klay.accounts.decrypt(result, password) - isAccount(decryptedAccount, account) + context('CAVERJS-UNIT-WALLET-230: input: rawTransaction with signatures of sender and fee payer', () => { + it('should append signatures of fee payer to existed feePayerSignatures', async () => { + const rawTransaction = '0x09f90125819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149f88ef845824e44a030accfbec3b577a53103c843744754a98408e7518391a31399bc757610ad1fc5a00eecc65e8f6f1795f7665513442fe30f881ddce12ceb687a8a7d9b65a0eee595f845824e44a006400dc68ab85d838a02e202e2ca52c6a30f11f7fa84f4046e08a940f4c95d1ea061a09e1a2dd6b823c42552d1cf0a4c0e408b4ada2b0236ef14ba0f036e6da262944a804669b2637b18d46e62109ed8edc0dc8526c7f847f845824e43a0113bf0986a48768e38daeff685ce56766aacf449f2aec8a0c77165777970f954a00717c4d4720fe706a075374f8eb0432f6c9f70c0a6e6267483d0b0cc8100ec07' + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: feePayer.address + } + const result = await caver.klay.accounts.signTransaction(feePayerTx, account.privateKey) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + + expect(result.signatures).to.be.undefined + expect(result.feePayerSignatures.length).to.equals(2) + expect(decoded.signatures.length).to.equals(2) + expect(decoded.feePayerSignatures.length).to.equals(2) }) }) - context('CAVERJS-UNIT-WALLET-097 : input: privateKey:KlaytnWalletKey, password, {address:valid}', () => { - it('should encrypt password with privateKey', () => { + context('CAVERJS-UNIT-WALLET-231: input: rawTransaction with signatures of sender and fee payer', () => { + it('should remove duplicated signatures of sender', async () => { + const tx = { + type: 'VALUE_TRANSFER', + from: account.address, + to: '0xd05c5926b0a2f31aadcc9a9cbd3868a50104d834', + value: '0x1', + gas: '0xdbba0', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + const {rawTransaction} = await caver.klay.accounts.signTransaction(tx, [account.privateKey, caver.klay.accounts.create().privateKey]) + const result = await caver.klay.accounts.signTransaction(rawTransaction, account.privateKey) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + + expect(result.signatures.length).to.equals(2) + expect(result.feePayerSignatures).to.be.undefined + expect(decoded.signatures.length).to.equals(2) + }) + }) + + context('CAVERJS-UNIT-WALLET-232: input: rawTransaction with signatures of sender and fee payer', () => { + it('should remove duplicated signatures of fee payer', async () => { + const rawTransaction = '0x09f9016c819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149f88ef845824e44a030accfbec3b577a53103c843744754a98408e7518391a31399bc757610ad1fc5a00eecc65e8f6f1795f7665513442fe30f881ddce12ceb687a8a7d9b65a0eee595f845824e44a006400dc68ab85d838a02e202e2ca52c6a30f11f7fa84f4046e08a940f4c95d1ea061a09e1a2dd6b823c42552d1cf0a4c0e408b4ada2b0236ef14ba0f036e6da262944a804669b2637b18d46e62109ed8edc0dc8526c7f88ef845824e43a0113bf0986a48768e38daeff685ce56766aacf449f2aec8a0c77165777970f954a00717c4d4720fe706a075374f8eb0432f6c9f70c0a6e6267483d0b0cc8100ec07f845824e43a00553c26e9e69541f7feca9484a5af8997c6f55b04bcac315be9c4888213709c4a00d0df6543f53afd0f131283c9a52f3c0a389b65b7a54ffe4c88004ddab74dd58' + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: feePayer.address, + chainId: 10000 + } + const result = await caver.klay.accounts.signTransaction(feePayerTx, feePayer.privateKey) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + + expect(result.signatures).to.be.undefined + expect(result.feePayerSignatures.length).to.equals(2) + expect(decoded.signatures.length).to.equals(2) + expect(decoded.feePayerSignatures.length).to.equals(2) + }) + }) + + context('CAVERJS-UNIT-WALLET-233: input: transaction object with signatures of sender', () => { + it('should append signatures when signatures is defined in transaction object', async () => { + vtTx.signatures = [ + [ + '0x4e44', + '0x30accfbec3b577a53103c843744754a98408e7518391a31399bc757610ad1fc5', + '0x0eecc65e8f6f1795f7665513442fe30f881ddce12ceb687a8a7d9b65a0eee595' + ] + ] + const result = await caver.klay.accounts.signTransaction(vtTx, account.privateKey) + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + + expect(result.signatures.length).to.equals(2) + expect(result.feePayerSignatures).to.be.undefined + expect(decoded.signatures.length).to.equals(2) + }) + }) + + context('CAVERJS-UNIT-WALLET-234: input: rawTransaction with signatures of sender and fee payer', () => { + it('should append feePayerSignatures when feePayerSignatures is defined in transaction object', async () => { + const rawTransaction = '0x09f8de819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149f847f845824e44a030accfbec3b577a53103c843744754a98408e7518391a31399bc757610ad1fc5a00eecc65e8f6f1795f7665513442fe30f881ddce12ceb687a8a7d9b65a0eee595944a804669b2637b18d46e62109ed8edc0dc8526c7f847f845824e43a0113bf0986a48768e38daeff685ce56766aacf449f2aec8a0c77165777970f954a00717c4d4720fe706a075374f8eb0432f6c9f70c0a6e6267483d0b0cc8100ec07' + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: feePayer.address, + feePayerSignatures: [ + [ + '0x4e44', + '0xe5465dd2d07aaf56a43a1ee0dd01583105d8f34f335e27e0ae5321a913871d0d', + '0x77f6c873a2a2d94d3501fd8ccc9c9d9cfdcedbde2ce645605c6849bb64be0fcf' + ] + ] + } + const result = await caver.klay.accounts.signTransaction(feePayerTx, account.privateKey) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + + expect(result.signatures).to.be.undefined + expect(result.feePayerSignatures.length).to.equals(3) + expect(decoded.signatures.length).to.equals(1) + expect(decoded.feePayerSignatures.length).to.equals(3) + }) + }) + + context('CAVERJS-UNIT-WALLET-235: input: transaction object with from account accountKeyMultiSig', () => { + it('should sign with multiple private key in wallet', async () => { + const multiSigKey = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const multiSigAddress = caver.klay.accounts.create().address + const multiSigAccount = caver.klay.accounts.createWithAccountKey(multiSigAddress, multiSigKey) + caver.klay.accounts.wallet.add(multiSigAccount) + + vtTx.from = multiSigAddress + + const result = await caver.klay.accounts.signTransaction(vtTx) + + expect(result.signatures.length).to.equals(2) + expect(result.feePayerSignatures).to.be.undefined + }) + }) + + context('CAVERJS-UNIT-WALLET-236: input: transaction object with from account accountKeyRoleBased', () => { + it('should sign with transactionKey', async () => { + const keyObject = { + transactionKey: [caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey,caver.klay.accounts.create().privateKey] + } + const roleBasedAddress = caver.klay.accounts.create().address + const roleBasedAccount = caver.klay.accounts.createWithAccountKey(roleBasedAddress, keyObject) + caver.klay.accounts.wallet.add(roleBasedAccount) + + vtTx.from = roleBasedAddress + + const result = await caver.klay.accounts.signTransaction(vtTx) + + expect(result.signatures.length).to.equals(1) + expect(result.feePayerSignatures).to.be.undefined + }) + }) + + context('CAVERJS-UNIT-WALLET-237: input: transaction object with from account accountKeyRoleBased', () => { + it('should sign with updateKey when transaction is for account update', async () => { + const keyObject = { + transactionKey: [caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey,caver.klay.accounts.create().privateKey] + } + const roleBasedAddress = caver.klay.accounts.create().address + const roleBasedAccount = caver.klay.accounts.createWithAccountKey(roleBasedAddress, keyObject) + caver.klay.accounts.wallet.add(roleBasedAccount) + + const updator = caver.klay.accounts.createAccountForUpdate(roleBasedAddress, '0x19d3e96ab579566fa7cbe735cbcad18e2382d44b5e1cb8e8284d0d6e7b37094e') + const updateTx = { + type: 'ACCOUNT_UPDATE', + from: roleBasedAddress, + key: updator, + gas: 90000, + } + + const result = await caver.klay.accounts.signTransaction(updateTx) + + expect(result.signatures.length).to.equals(2) + expect(result.feePayerSignatures).to.be.undefined + }) + }) + + context('CAVERJS-UNIT-WALLET-238: input: fee payer transaction object with fee payer account accountKeyRoleBased', () => { + it('should sign with feePayerKey when transaction object is fee payer format', async () => { + const keyObject = { + transactionKey: [caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey,caver.klay.accounts.create().privateKey] + } + const roleBasedAddress = caver.klay.accounts.create().address + const roleBasedAccount = caver.klay.accounts.createWithAccountKey(roleBasedAddress, keyObject) + caver.klay.accounts.wallet.add(roleBasedAccount) + + const rawTransaction = '0x09f842819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149c4c301808080c4c3018080' + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: roleBasedAddress + } + + const result = await caver.klay.accounts.signTransaction(feePayerTx) + + expect(result.signatures).to.be.undefined + expect(result.feePayerSignatures.length).to.equals(3) + }) + }) + + context('CAVERJS-UNIT-WALLET-239: input: rawTransaction with signatures of sender and fee payer with different fee payer', () => { + it('should remove duplicated signatures of fee payer', async () => { + const rawTransaction = '0x09f9016c819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149f88ef845824e44a030accfbec3b577a53103c843744754a98408e7518391a31399bc757610ad1fc5a00eecc65e8f6f1795f7665513442fe30f881ddce12ceb687a8a7d9b65a0eee595f845824e44a006400dc68ab85d838a02e202e2ca52c6a30f11f7fa84f4046e08a940f4c95d1ea061a09e1a2dd6b823c42552d1cf0a4c0e408b4ada2b0236ef14ba0f036e6da262944a804669b2637b18d46e62109ed8edc0dc8526c7f88ef845824e43a0113bf0986a48768e38daeff685ce56766aacf449f2aec8a0c77165777970f954a00717c4d4720fe706a075374f8eb0432f6c9f70c0a6e6267483d0b0cc8100ec07f845824e43a00553c26e9e69541f7feca9484a5af8997c6f55b04bcac315be9c4888213709c4a00d0df6543f53afd0f131283c9a52f3c0a389b65b7a54ffe4c88004ddab74dd58' + const newFeePayer = caver.klay.accounts.create() + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: newFeePayer.address + } + + const errorMessage = `Invalid feePayer: The fee payer(${feePayer.address}) included in the transaction does not match the fee payer(${newFeePayer.address}) you want to sign.` + await expect(caver.klay.accounts.signTransaction(feePayerTx, newFeePayer.privateKey)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-240: input: legacy rawTransaction with signatures of sender', () => { + it('should throw error becuase encoded legacy transaction do not contain from', async () => { + const rawTransaction = '0xf867808505d21dba00830dbba09430d8d4217145ba3f6cde24ec28c64c9120f2bdfb0180820feaa03ae52bd8b105a138f179ecc85c94296c851922775ef15d9d775b6cc1971ad19ca07164eff9bf7ac3f9a80d1578ee48ccaa08fe127d21ce00a5b3110b774289695b' + + const errorMessage = `"from" is missing` + await expect(caver.klay.accounts.signTransaction(rawTransaction, account.privateKey)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-241: input: legacy rawTransaction with signatures of sender', () => { + it('should throw error becuase encoded legacy transaction do not contain from', async () => { + txObj.signatures = [ + '0x0fea', + '0x3ae52bd8b105a138f179ecc85c94296c851922775ef15d9d775b6cc1971ad19c', + '0x7164eff9bf7ac3f9a80d1578ee48ccaa08fe127d21ce00a5b3110b774289695b' + ] + + const errorMessage = `Legacy transaction cannot be signed with multiple keys.` + await expect(caver.klay.accounts.signTransaction(txObj, account.privateKey)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-242: input: rawTransaction without fee payer in tx object', () => { + it('should throw error when fee payer is not defined', async () => { + const rawTransaction = '0x09f9016c819a8505d21dba00830dbba094d05c5926b0a2f31aadcc9a9cbd3868a50104d8340194127a24ec811aa1e45071a669e5d117a475e68149f88ef845824e44a030accfbec3b577a53103c843744754a98408e7518391a31399bc757610ad1fc5a00eecc65e8f6f1795f7665513442fe30f881ddce12ceb687a8a7d9b65a0eee595f845824e44a006400dc68ab85d838a02e202e2ca52c6a30f11f7fa84f4046e08a940f4c95d1ea061a09e1a2dd6b823c42552d1cf0a4c0e408b4ada2b0236ef14ba0f036e6da262944a804669b2637b18d46e62109ed8edc0dc8526c7f88ef845824e43a0113bf0986a48768e38daeff685ce56766aacf449f2aec8a0c77165777970f954a00717c4d4720fe706a075374f8eb0432f6c9f70c0a6e6267483d0b0cc8100ec07f845824e43a00553c26e9e69541f7feca9484a5af8997c6f55b04bcac315be9c4888213709c4a00d0df6543f53afd0f131283c9a52f3c0a389b65b7a54ffe4c88004ddab74dd58' + const feePayerTx = { senderRawTransaction: rawTransaction } + + const errorMessage = `Invalid fee payer: undefined` + await expect(caver.klay.accounts.signTransaction(feePayerTx, account.privateKey)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-243: input: update transaction object with AccountForUpdate with mismatched address', () => { + it('should throw error when address is not matched', async () => { + const updator = caver.klay.accounts.createAccountForUpdate(caver.klay.accounts.create().address, '0x19d3e96ab579566fa7cbe735cbcad18e2382d44b5e1cb8e8284d0d6e7b37094e') + const updateTx = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 90000 + } + + const errorMessage = `The value of the from field of the transaction does not match the address of AccountForUpdate.` + await expect(caver.klay.accounts.signTransaction(updateTx)).to.be.rejectedWith(errorMessage) + }) + }) +}) + +describe('caver.klay.accounts.feePayerSignTransaction', () => { + let txObj + let sender, feePayer + let withoutSig = '0x09f842819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6c4c301808080c4c3018080' + let withSenderSig = '0x09f90114819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6f8d5f845820feaa06d2b6c9530a9f311a3ea42b2fc474ce0decefc65a88510161a392eef029714b8a0360fff97c9818dabb7d25ce4dc1afb4a5cca00409d79c0e3f76e151dec542701f845820feaa0fb24ef24dd6d10a9410417c56a5a8b09575d0611251f6f03b9199b4004cee087a02a1b040cc80942deda8523c2bf24364b3b2ea1a6d33165fde35d86a1c247ddfef845820fe9a0ae0d77d98aec5880efc7bd943fb58ea691e3023975e757a720586d3781284d9aa077072cfa045f872a1e33840e28ed2704f8a6d77f5077171b6b45e3ec7a671ddf80c4c3018080' + let withFeePayerSig = '0x09f90128819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6c4c3018080944a804669b2637b18d46e62109ed8edc0dc8526c7f8d5f845824e43a003df110e3d328d75ac8b05ff29e3b00b65c4402bc0f2556590077e3ffd699f85a0395252d8b2bf6a5b1b997d41694bb84b6e30bc846263b6fc55a023a66ef68630f845824e44a08eb3eb4414fe1b5f0f1baaa0192a9ee018b6132b8fc965918318bdd7087acb42a0211741eae45dae25659894ada38c0c5b03483337148182d2951e7386cb2c2ab8f845824e44a0691eaea2dead54efce368395f2394a9cbc7b3d68effd5c5b3ba9bee7b57dfa59a00b7cbe6b8ebcf013a197f6ee81e3c3e180cf62c940fd8f9282d3f6814d710c9d' + let withBothSig = '0x09f901fa819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6f8d5f845820feaa06d2b6c9530a9f311a3ea42b2fc474ce0decefc65a88510161a392eef029714b8a0360fff97c9818dabb7d25ce4dc1afb4a5cca00409d79c0e3f76e151dec542701f845820feaa0fb24ef24dd6d10a9410417c56a5a8b09575d0611251f6f03b9199b4004cee087a02a1b040cc80942deda8523c2bf24364b3b2ea1a6d33165fde35d86a1c247ddfef845820fe9a0ae0d77d98aec5880efc7bd943fb58ea691e3023975e757a720586d3781284d9aa077072cfa045f872a1e33840e28ed2704f8a6d77f5077171b6b45e3ec7a671ddf944a804669b2637b18d46e62109ed8edc0dc8526c7f8d5f845824e43a0e0cd799758d93f3ac9ff1fd5055bff9e7c7e664599f5615c5016c88b7c8edea5a00353e206c246a10a5ac4388924e8eb42fedbbbfb674efa8441f0b0c4957cf05df845824e43a04c5c84dcace452a5bde411d7888d116f0a993a579b11a79cc9ed7fa6e9adb421a023d33c71fced04801643d4d58c9fbb184625cbfaa8dab5a8e25ec5e84d25452af845824e44a0051fad2c19ee4936721b5985ebdf354d069f3e9e3d3c832751caf20f69202c20a03f340e42613e6868cff9b3312fa0f671523b340d469d7d69f6573724bd6f6047' + + beforeEach(() => { + let senderRoleBasedKey = { transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + let feePayerRoleBasedKey = { feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + + sender = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey('0x6f9a8851feca74f6694a12d11c9684f0b5c1d3b6', senderRoleBasedKey)) + feePayer = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey('0x4a804669b2637b18d46e62109ed8edc0dc8526c7', feePayerRoleBasedKey)) + + txObj = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: sender.address, + to: '0xd2553e0508d481892aa1b481b3ac29aba5c7fb4d', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + }) + + context('CAVERJS-UNIT-WALLET-273: input: tx object without signatures and feePayer', () => { + it('should sign with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const result = await caver.klay.accounts.feePayerSignTransaction(txObj, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + }) + }) + + context('CAVERJS-UNIT-WALLET-274: input: tx object(signatures) and feePayer', () => { + it('should sign with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + txObj.signatures = [ + [ + '0x0fea', + '0x6d2b6c9530a9f311a3ea42b2fc474ce0decefc65a88510161a392eef029714b8', + '0x360fff97c9818dabb7d25ce4dc1afb4a5cca00409d79c0e3f76e151dec542701' + ] + ] + const result = await caver.klay.accounts.feePayerSignTransaction(txObj, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(txObj.signatures.length) + expect(decoded.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + }) + }) + + context('CAVERJS-UNIT-WALLET-275: input: tx object(feePayer/feePayerSignatures) and feePayer', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + txObj.feePayer = feePayer.address + txObj.feePayerSignatures = [ + [ + '0x4e44', + '0x7328aa537646bfffebfd6f006f9a6e0520d077cb898225e4fa44f52c54c4c2f2', + '0x6302349a19b4b7b9d82798eac1d4716cdaa239e490fd8427f49c9bca4dd4b6a2' + ] + ] + const result = await caver.klay.accounts.feePayerSignTransaction(txObj, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length + 1) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length + 1) + }) + }) + + context('CAVERJS-UNIT-WALLET-276: input: tx object(feePayerSignatures) and feePayer', () => { + it('should set feePayer with value of feePayer parameter and append feePayerSignatures', async () => { + txObj.feePayerSignatures = [ + [ + '0x4e44', + '0x7328aa537646bfffebfd6f006f9a6e0520d077cb898225e4fa44f52c54c4c2f2', + '0x6302349a19b4b7b9d82798eac1d4716cdaa239e490fd8427f49c9bca4dd4b6a2' + ] + ] + const result = await caver.klay.accounts.feePayerSignTransaction(txObj, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length + 1) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length + 1) + }) + }) + + context('CAVERJS-UNIT-WALLET-277: input: tx object(signatures/feePayer/feePayerSignatures) and feePayer', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + txObj.signatures = [ + [ + '0x0fea', + '0x6d2b6c9530a9f311a3ea42b2fc474ce0decefc65a88510161a392eef029714b8', + '0x360fff97c9818dabb7d25ce4dc1afb4a5cca00409d79c0e3f76e151dec542701' + ] + ] + txObj.feePayer = feePayer.address + txObj.feePayerSignatures = [ + [ + '0x4e44', + '0x7328aa537646bfffebfd6f006f9a6e0520d077cb898225e4fa44f52c54c4c2f2', + '0x6302349a19b4b7b9d82798eac1d4716cdaa239e490fd8427f49c9bca4dd4b6a2' + ] + ] + const result = await caver.klay.accounts.feePayerSignTransaction(txObj, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length + 1) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(txObj.signatures.length) + expect(decoded.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length + 1) + }) + }) + + context('CAVERJS-UNIT-WALLET-278: input: tx object(signatures/feePayer/feePayerSignatures), feePayer and privateKey', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + txObj.signatures = [ + [ + '0x0fea', + '0x6d2b6c9530a9f311a3ea42b2fc474ce0decefc65a88510161a392eef029714b8', + '0x360fff97c9818dabb7d25ce4dc1afb4a5cca00409d79c0e3f76e151dec542701' + ] + ] + txObj.feePayer = feePayer.address + txObj.feePayerSignatures = [ + [ + '0x4e44', + '0x7328aa537646bfffebfd6f006f9a6e0520d077cb898225e4fa44f52c54c4c2f2', + '0x6302349a19b4b7b9d82798eac1d4716cdaa239e490fd8427f49c9bca4dd4b6a2' + ] + ] + const result = await caver.klay.accounts.feePayerSignTransaction(txObj, feePayer.address, feePayer.feePayerKey[0]) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(2) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(txObj.signatures.length) + expect(decoded.feePayerSignatures.length).to.equals(2) + }) + }) + + context('CAVERJS-UNIT-WALLET-279: input: tx object(feePayer), feePayer and privateKey', () => { + it('should throw error when feePayer is not matched', async () => { + txObj.feePayer = caver.klay.accounts.create().address + + const errorMessage = 'Invalid parameter: The address of fee payer does not match.' + + await expect(caver.klay.accounts.feePayerSignTransaction(txObj, feePayer.address)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-280: input: tx object(without nonce) and feePayer', () => { + it('should return signature and rawTransaction', async () => { + const tx = Object.assign({}, txObj) + delete tx.nonce + + const result = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + }) + }) + + context('CAVERJS-UNIT-WALLET-281: input: tx object(without nonce), feePayer and privateKey', () => { + it('should return signature and rawTransaction', async () => { + const tx = Object.assign({}, txObj) + delete tx.nonce + + const result = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address, feePayer.feePayerKey[0]) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(1) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(1) + }) + }) + + context('CAVERJS-UNIT-WALLET-282: input: tx object(without gasPrice) and feePayer', () => { + it('should return signature and rawTransaction', async () => { + const tx = Object.assign({}, txObj) + delete tx.gasPrice + + const result = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + }) + }) + + context('CAVERJS-UNIT-WALLET-283: input: tx object(without gasPrice), feePayer and privateKey', () => { + it('should return signature and rawTransaction', async () => { + const tx = Object.assign({}, txObj) + delete tx.gasPrice + + const result = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address, feePayer.feePayerKey[0]) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(1) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(1) + }) + }) + + context('CAVERJS-UNIT-WALLET-284: input: tx object(without chainId) and feePayer', () => { + it('should return signature and rawTransaction', async () => { + const tx = Object.assign({}, txObj) + delete tx.chainId + + const result = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + }) + }) + + context('CAVERJS-UNIT-WALLET-285: input: tx object(without chainId), feePayer and privateKey', () => { + it('should return signature and rawTransaction', async () => { + const tx = Object.assign({}, txObj) + delete tx.chainId + + const result = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address, feePayer.feePayerKey[0]) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(1) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(1) + }) + }) + + context('CAVERJS-UNIT-WALLET-286: input: tx object, feePayer and invalid privateKey', () => { + it('should throw error when private key is invalid', async () => { + const invalid = '0x01' + const errorMessage = `Invalid private key(${invalid.slice(2)})` + + await expect(caver.klay.accounts.feePayerSignTransaction(txObj, feePayer.address, invalid)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-287: input: tx object, invalid feePayer address', () => { + it('should throw error when private key is invalid', async () => { + const invalid = 'feePayer' + const errorMessage = `Invalid fee payer address : ${invalid}` + + await expect(caver.klay.accounts.feePayerSignTransaction(txObj, invalid)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-288: input: tx object(without from) and feePayer', () => { + it('should throw error when invalid transaction', async () => { + const invalid = Object.assign({}, txObj) + delete invalid.from + + const errorMessage = '"from" is missing' + await expect(caver.klay.accounts.feePayerSignTransaction(invalid, feePayer.address)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-289: input: tx object(without from), feePayer and privateKey', () => { + it('should throw error when invalid transaction', async () => { + const invalid = Object.assign({}, txObj) + delete invalid.from + + const errorMessage = '"from" is missing' + await expect(caver.klay.accounts.feePayerSignTransaction(invalid, feePayer.address, feePayer.feePayerKey[0])).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-290: input: RLP encoded rawTransaction(without signatures) string and feePayer', () => { + it('should sign with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const result = await caver.klay.accounts.feePayerSignTransaction(withoutSig, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(3) + }) + }) + + context('CAVERJS-UNIT-WALLET-291: input: RLP encoded rawTransaction(with signatures of sender) string and feePayer', () => { + it('should sign with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const result = await caver.klay.accounts.feePayerSignTransaction(withSenderSig, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(3) + }) + }) + + context('CAVERJS-UNIT-WALLET-292: input: RLP encoded rawTransaction(with signatures of fee payer) string and feePayer', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const result = await caver.klay.accounts.feePayerSignTransaction(withFeePayerSig, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(6) + }) + }) + + context('CAVERJS-UNIT-WALLET-293: input: RLP encoded rawTransaction(with signatures of sender and fee payer) string and feePayer', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const result = await caver.klay.accounts.feePayerSignTransaction(withBothSig, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(6) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(6) + }) + }) + + context('CAVERJS-UNIT-WALLET-294: input: RLP encoded rawTransaction(with signatures of sender and fee payer) string, feePayer and privateKey', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const result = await caver.klay.accounts.feePayerSignTransaction(withBothSig, feePayer.address, feePayer.feePayerKey[0]) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(4) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(4) + }) + }) + + context('CAVERJS-UNIT-WALLET-295: input: RLP encoded rawTransaction(with signatures of sender and fee payer) string with invalid feePayer', () => { + it('should throw error when address of fee payer is invalid', async () => { + const invalid = 'feePayer' + const errorMessage = `Invalid fee payer address : ${invalid}` + + await expect(caver.klay.accounts.feePayerSignTransaction(withBothSig, invalid)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-296: input: RLP encoded rawTransaction(with signatures of sender and fee payer) string, feePayer and invalid private key', () => { + it('should throw error when private key is invalid', async () => { + const invalid = '0x01' + const errorMessage = `Invalid private key(${invalid.slice(2)})` + + await expect(caver.klay.accounts.feePayerSignTransaction(withBothSig, feePayer.address, invalid)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-297: input: fee payer format transaction(without signatures) string and feePayer', () => { + it('should sign with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const feePayerTx = { + senderRawTransaction: withoutSig, + feePayer: feePayer.address, + } + const result = await caver.klay.accounts.feePayerSignTransaction(feePayerTx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(3) + }) + }) + + context('CAVERJS-UNIT-WALLET-298: input: fee payer format transaction(with signatures of sender) string and feePayer', () => { + it('should sign with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const feePayerTx = { + senderRawTransaction: withSenderSig, + feePayer: feePayer.address, + } + const result = await caver.klay.accounts.feePayerSignTransaction(feePayerTx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(3) + }) + }) + + context('CAVERJS-UNIT-WALLET-299: input: fee payer format transaction(with signatures of fee payer) string and feePayer', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const feePayerTx = { + senderRawTransaction: withFeePayerSig, + feePayer: feePayer.address, + } + const result = await caver.klay.accounts.feePayerSignTransaction(feePayerTx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(6) + }) + }) + + context('CAVERJS-UNIT-WALLET-300: input: fee payer format transaction(with signatures of sender and fee payer) string and feePayer', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const feePayerTx = { + senderRawTransaction: withBothSig, + feePayer: feePayer.address, + } + const result = await caver.klay.accounts.feePayerSignTransaction(feePayerTx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(6) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(6) + }) + }) + + context('CAVERJS-UNIT-WALLET-301: input: fee payer format transaction(with signatures of sender and fee payer) string, feePayer and privateKey', () => { + it('should append with feePayerKey of feePayer and return feePayerSignatures and rawTransaction', async () => { + const feePayerTx = { + senderRawTransaction: withBothSig, + feePayer: feePayer.address, + } + const result = await caver.klay.accounts.feePayerSignTransaction(feePayerTx, feePayer.address, feePayer.feePayerKey[0]) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(4) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(4) + }) + }) + + context('CAVERJS-UNIT-WALLET-302: input: fee payer tx object(without feePayer) and feePayer', () => { + it('should set feePayer information through feePayer parameter', async () => { + const feePayerTx = { senderRawTransaction: withoutSig } + + const result = await caver.klay.accounts.feePayerSignTransaction(feePayerTx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + }) + }) + + context('CAVERJS-UNIT-WALLET-303: input: fee payer tx object(with 0x feePayer) and feePayer', () => { + it('should set feePayer information through feePayer parameter', async () => { + const feePayerTx = { senderRawTransaction: withoutSig, feePayer: '0x' } + + const result = await caver.klay.accounts.feePayerSignTransaction(feePayerTx, feePayer.address) + + const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(result.feePayerSignatures.length).to.equals(feePayer.feePayerKey.length) + }) + }) + + context('CAVERJS-UNIT-WALLET-304: input: fee payer format transaction(with signatures of sender and fee payer) string with invalid feePayer', () => { + it('should throw error when address of fee payer is invalid', async () => { + const invalid = 'feePayer' + const feePayerTx = { + senderRawTransaction: withBothSig, + feePayer: invalid, + } + const errorMessage = `Invalid fee payer address : ${invalid}` + + await expect(caver.klay.accounts.feePayerSignTransaction(feePayerTx, invalid)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-305: input: fee payer format transaction(with signatures of sender and fee payer) string with not matched feePayer', () => { + it('should throw error when address of fee payer in transaction object is not matched with fee payer parameter', async () => { + const address = caver.klay.accounts.create().address + const feePayerTx = { + senderRawTransaction: withBothSig, + feePayer: caver.klay.accounts.create().address, + } + const errorMessage = `Invalid parameter: The address of fee payer does not match.` + + await expect(caver.klay.accounts.feePayerSignTransaction(feePayerTx, address)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-306: input: fee payer format transaction(with signatures of sender and fee payer) string, feePayer and invalid private key', () => { + it('should throw error when private key is invalid', async () => { + const invalid = '0x01' + const feePayerTx = { + senderRawTransaction: withBothSig, + feePayer: feePayer.address, + } + const errorMessage = `Invalid private key(${invalid.slice(2)})` + + await expect(caver.klay.accounts.feePayerSignTransaction(feePayerTx, feePayer.address, invalid)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-376: input: non fee delegated transaction, fee payer address', () => { + it('should throw error when private key is invalid', async () => { + const nonFeeDelegated = { + type: 'VALUE_TRANSFER', + from: sender.address, + to: '0xd2553e0508d481892aa1b481b3ac29aba5c7fb4d', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + const errorMessage = `Failed to sign transaction with fee payer: invalid transaction type(VALUE_TRANSFER)` + + await expect(caver.klay.accounts.feePayerSignTransaction(nonFeeDelegated, feePayer.address)).to.be.rejectedWith(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-377: input: non fee delegated transaction, fee payer address', () => { + it('should throw error when private key is invalid', async () => { + const nonFeeDelegated = { + type: 'VALUE_TRANSFER', + from: sender.address, + to: '0xd2553e0508d481892aa1b481b3ac29aba5c7fb4d', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + const { rawTransaction } = await caver.klay.accounts.signTransaction(nonFeeDelegated) + const errorMessage = `Failed to split fee payer: not a fee delegated transaction type('VALUE_TRANSFER')` + + await expect(caver.klay.accounts.feePayerSignTransaction(rawTransaction, feePayer.address)).to.be.rejectedWith(errorMessage) + }) + }) +}) + +describe('caver.klay.accounts.getRawTransactionWithSignatures', () => { + let vtTx, feeDelegatedTx + let sender, feePayer + + beforeEach(() => { + let senderRoleBasedKey = { transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + let feePayerRoleBasedKey = { feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + + sender = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey('0x6f9a8851feca74f6694a12d11c9684f0b5c1d3b6', senderRoleBasedKey)) + feePayer = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey('0x4a804669b2637b18d46e62109ed8edc0dc8526c7', feePayerRoleBasedKey)) + + vtTx = { + type: 'VALUE_TRANSFER', + from: sender.address, + to: '0xd2553e0508d481892aa1b481b3ac29aba5c7fb4d', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + + feeDelegatedTx = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: sender.address, + to: '0xd2553e0508d481892aa1b481b3ac29aba5c7fb4d', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + }) + + context('CAVERJS-UNIT-WALLET-307: input: value transfer tx object with signatures', () => { + it('should return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(vtTx) + vtTx.signatures = signResult.signatures + + let result = await caver.klay.accounts.getRawTransactionWithSignatures(vtTx) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(signResult.rawTransaction).to.equals(result.rawTransaction) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-308: input: fee delegated value transfer tx object with signatures', () => { + it('should return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx) + feeDelegatedTx.signatures = signResult.signatures + + let result = await caver.klay.accounts.getRawTransactionWithSignatures(feeDelegatedTx) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + expect(signResult.rawTransaction).to.equals(result.rawTransaction) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-309: input: fee delegated value transfer tx object with feePayerSignatures', () => { + it('should return valid rawTransaction', async () => { + const tx = Object.assign({}, feeDelegatedTx) + + let feePayerSignResult = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address) + + tx.feePayer = feePayer.address + tx.feePayerSignatures = feePayerSignResult.feePayerSignatures + + let result = await caver.klay.accounts.getRawTransactionWithSignatures(tx) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.feePayerSignatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(caver.utils.isEmptySig(decoded.signatures)).to.be.true + expect(decoded.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-310: input: fee delegated value transfer tx object with signatures and feePayerSignatures', () => { + it('should return valid rawTransaction', async () => { + const tx = Object.assign({}, feeDelegatedTx) + + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx) + + let feePayerSignResult = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address) + + tx.signatures = signResult.signatures + tx.feePayer = feePayer.address + tx.feePayerSignatures = feePayerSignResult.feePayerSignatures + + let result = await caver.klay.accounts.getRawTransactionWithSignatures(tx) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.signatures.length).to.equals(3) + expect(result.feePayerSignatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-311: input: fee payer tx format(includes signatures) object with signatures', () => { + it('should return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[0]) + let signResult2 = await caver.klay.accounts.signTransaction(feeDelegatedTx, [sender.transactionKey[1], sender.transactionKey[2]]) + + const feePayerTx = { + senderRawTransaction: signResult.rawTransaction, + feePayer: feePayer.address, + signatures: signResult2.signatures + } + let result = await caver.klay.accounts.getRawTransactionWithSignatures(feePayerTx) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.signatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-312: input: fee payer tx format(includes signatures and feePayerSignatures) object with signatures', () => { + it('should return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[0]) + let signResult2 = await caver.klay.accounts.signTransaction(feeDelegatedTx, [sender.transactionKey[1], sender.transactionKey[2]]) + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(signResult.rawTransaction, feePayer.address, feePayer.feePayerKey[0]) + + const feePayerTx = { + senderRawTransaction: feePayerSigned.rawTransaction, + feePayer: feePayer.address, + signatures: signResult2.signatures + } + let result = await caver.klay.accounts.getRawTransactionWithSignatures(feePayerTx) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.signatures.length).to.equals(3) + expect(result.feePayerSignatures.length).to.equals(1) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(1) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-313: input: fee payer tx format(includes signatures and feePayerSignatures) object with signatures and feePayerSignatures', () => { + it('should return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[0]) + let signResult2 = await caver.klay.accounts.signTransaction(feeDelegatedTx, [sender.transactionKey[1], sender.transactionKey[2]]) + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(signResult.rawTransaction, feePayer.address, feePayer.feePayerKey[0]) + let feePayerSigned2 = await caver.klay.accounts.feePayerSignTransaction(signResult.rawTransaction, feePayer.address, [feePayer.feePayerKey[1], feePayer.feePayerKey[2]]) + + const feePayerTx = { + senderRawTransaction: feePayerSigned.rawTransaction, + feePayer: feePayer.address, + signatures: signResult2.signatures, + feePayerSignatures: feePayerSigned2.feePayerSignatures + } + let result = await caver.klay.accounts.getRawTransactionWithSignatures(feePayerTx) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.signatures.length).to.equals(3) + expect(result.feePayerSignatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-314: input: fee delegated value transfer tx object without signatures and feePayerSignatures', () => { + it('should throw error when there is no signatures information', async () => { + const errorMessage = `There are no signatures or feePayerSignatures defined in the transaction object.` + + await expect(caver.klay.accounts.getRawTransactionWithSignatures(feeDelegatedTx)).to.be.rejectedWith(errorMessage) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-315: input: fee delegated value transfer tx object without feePayerSignatures only(no feePayer)', () => { + it('should throw error when tx defines feePayerSignatures only without feePayer', async () => { + const tx = Object.assign({}, feeDelegatedTx) + let feePayerSignResult = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address) + tx.feePayerSignatures = feePayerSignResult.feePayerSignatures + + const errorMessage = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.getRawTransactionWithSignatures(tx)).to.be.rejectedWith(errorMessage) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-316: input: value transfer tx object with feePayer', () => { + it('should throw error when non-feeDelegated tx defines feePayer', async () => { + let signResult = await caver.klay.accounts.signTransaction(vtTx) + vtTx.signatures = signResult.signatures + vtTx.feePayer = feePayer.address + + const errorMessage = `"feePayer" cannot be used with ${vtTx.type} transaction` + + await expect(caver.klay.accounts.getRawTransactionWithSignatures(vtTx)).to.be.rejectedWith(errorMessage) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-317: input: value transfer tx object with feePayerSignatures', () => { + it('should throw error when non-feeDelegated tx defines feePayerSignatures', async () => { + let feePayerSignResult = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address) + vtTx.feePayerSignatures = feePayerSignResult.feePayerSignatures + + const errorMessage = `"feePayerSignatures" cannot be used with ${vtTx.type} transaction` + + await expect(caver.klay.accounts.getRawTransactionWithSignatures(vtTx)).to.be.rejectedWith(errorMessage) + }).timeout(200000) + }) +}) + +describe('caver.klay.accounts.combineSignatures', () => { + let feeDelegatedTx + let sender, feePayer + + beforeEach(() => { + let senderRoleBasedKey = { transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + let feePayerRoleBasedKey = { feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + + sender = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey('0x6f9a8851feca74f6694a12d11c9684f0b5c1d3b6', senderRoleBasedKey)) + feePayer = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey('0x4a804669b2637b18d46e62109ed8edc0dc8526c7', feePayerRoleBasedKey)) + + feeDelegatedTx = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: sender.address, + to: '0xd2553e0508d481892aa1b481b3ac29aba5c7fb4d', + value: '0x1', + gas: '0xdbba0', + chainId: '0x7e3', + gasPrice: '0x5d21dba00', + nonce: '0x9a', + } + }) + + context('CAVERJS-UNIT-WALLET-318: input: RLP encoded raw transaction string(includes signatures of sender only)', () => { + it('should combine signatures and return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[0]) + let signResult2 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[1]) + let signResult3 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[2]) + + let result = await caver.klay.accounts.combineSignatures([signResult.rawTransaction, signResult2.rawTransaction, signResult3.rawTransaction]) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.signatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-319: input: RLP encoded raw transaction string(includes signatures of fee payer only)', () => { + it('should combine signatures and return valid rawTransaction', async () => { + let feePayerSignResult = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address, feePayer.feePayerKey[0]) + let feePayerSignResult2 = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address, feePayer.feePayerKey[1]) + let feePayerSignResult3 = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address, feePayer.feePayerKey[2]) + + let result = await caver.klay.accounts.combineSignatures([feePayerSignResult.rawTransaction, feePayerSignResult2.rawTransaction, feePayerSignResult3.rawTransaction]) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.feePayerSignatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-320: input: RLP encoded raw transaction string(includes signatures of sender and fee payer)', () => { + it('should combine signatures and return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx) + let feePayerSignResult = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address) + + let result = await caver.klay.accounts.combineSignatures([signResult.rawTransaction, feePayerSignResult.rawTransaction]) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.signatures.length).to.equals(3) + expect(result.feePayerSignatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-321: input: RLP encoded raw transaction string(includes duplicated signatures of sender and fee payer)', () => { + it('should remove duplicated signatures return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx) + let signResult2 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[0]) + let signResult3 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[1]) + let signResult4 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[2]) + + let feePayerSignResult = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address) + let feePayerSignResult2 = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address, feePayer.feePayerKey[0]) + let feePayerSignResult3 = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address, feePayer.feePayerKey[1]) + let feePayerSignResult4 = await caver.klay.accounts.feePayerSignTransaction(feeDelegatedTx, feePayer.address, feePayer.feePayerKey[2]) + + let rawArray = [ + signResult.rawTransaction, + signResult2.rawTransaction, + signResult3.rawTransaction, + signResult4.rawTransaction, + feePayerSignResult.rawTransaction, + feePayerSignResult2.rawTransaction, + feePayerSignResult3.rawTransaction, + feePayerSignResult4.rawTransaction + ] + let result = await caver.klay.accounts.combineSignatures(rawArray) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures', 'feePayerSignatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.signatures.length).to.equals(3) + expect(result.feePayerSignatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + expect(decoded.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-386: input: RLP encoded raw transaction string(includes signatures of sender only)', () => { + it('should remove duplicated signatures return valid rawTransaction', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx) + let signResult2 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[0]) + let signResult3 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[1]) + let signResult4 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[2]) + + let rawArray = [ + signResult.rawTransaction, + signResult2.rawTransaction, + signResult3.rawTransaction, + signResult4.rawTransaction, + ] + let result = await caver.klay.accounts.combineSignatures(rawArray) + + const keys = ['rawTransaction', 'txHash', 'senderTxHash', 'signatures'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + expect(result.signatures.length).to.equals(3) + + const decoded = caver.klay.decodeTransaction(result.rawTransaction) + expect(decoded.signatures.length).to.equals(3) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-387: input: not array', () => { + it('should throw error when parameter is not array', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx) + + const expectedError = 'The parameter of the combineSignatures function must be an array of RLP encoded transaction strings.' + await expect(caver.klay.accounts.combineSignatures(signResult.rawTransaction, (error, result) => { + expect(error).not.to.be.undefined + expect(result).to.be.undefined + })).to.be.rejectedWith(expectedError) + }).timeout(200000) + }) + + context('CAVERJS-UNIT-WALLET-388: input: different RLP encoded transaction', () => { + it('should throw error when contents of transaction is not same', async () => { + let signResult = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[0]) + feeDelegatedTx.value = 2 + let signResult2 = await caver.klay.accounts.signTransaction(feeDelegatedTx, sender.transactionKey[2]) + + const expectedError = 'Failed to combineSignatures: Signatures that sign to different transaction cannot be combined.' + await expect(caver.klay.accounts.combineSignatures([signResult.rawTransaction, signResult2.rawTransaction], (error, result) => { + expect(error).not.to.be.undefined + expect(result).to.be.undefined + })).to.be.rejectedWith(expectedError) + }).timeout(200000) + }) +}) + +describe('caver.klay.accounts.recoverTransaction', () => { + let account + let rawTx + + beforeEach(async () => { + account = caver.klay.accounts.create() + + const txObj = { + from: account.address, + nonce: '0x0', + to: setting.toAddress, + gas: setting.gas, + gasPrice: setting.gasPrice, + value: '0x1' + } + const signedTx = await account.signTransaction(txObj) + rawTx = signedTx.rawTransaction + }) + + context('CAVERJS-UNIT-WALLET-029 : rawTransaction', () => { + it('should return valid address', () => { + const result = caver.klay.accounts.recoverTransaction(rawTx) + expect(result).to.equal(account.address) + }) + }) + + context('CAVERJS-UNIT-WALLET-030 : rawTransaction:invalid', () => { + it('should not equal to account.address', () => { + const invalid = rawTx.slice(0, -2) + const result = caver.klay.accounts.recoverTransaction(invalid) + expect(result).to.not.equal(account.addrss) + }) + }) + + context('CAVERJS-UNIT-WALLET-104 : rawTransaction: leading zeroes of the signature are trimmed', () => { + it('should equal to account.address', async () => { + const transaction = { + from: '0x13b0d8316F0c3cE0C3C51Ebb586A14d7d90112fD', + nonce: '0x0', + to: '0x30d8d4217145ba3f6cde24ec28c64c9120f2bdfb', + gas: 900000, + gasPrice: 25000000000, + value: '0x1', + chainId: 10000, + } + + let privateKey = '0x72d72a46401220f08ccb1b17b550feb816840f2f8ce86361e7ee54ac7a9ee6d8' + account = caver.klay.accounts.privateKeyToAccount(privateKey) + + const signed = await caver.klay.accounts.signTransaction(transaction, account.privateKey) + + const rlpDecoded = caver.utils.rlpDecode(signed.rawTransaction) + expect(rlpDecoded[7].length).not.to.equals(rlpDecoded[8].length) + + const result = caver.klay.accounts.recoverTransaction(signed.rawTransaction) + expect(result).to.not.equal(account.addrss) + }) + }) + + context('CAVERJS-UNIT-WALLET-105 : rawTransaction: Non-LEGACY transactions.', () => { + it('should throw error', async () => { + const transaction = { + type: 'VALUE_TRANSFER', + from: '0x13b0d8316F0c3cE0C3C51Ebb586A14d7d90112fD', + nonce: '0x0', + to: '0x30d8d4217145ba3f6cde24ec28c64c9120f2bdfb', + gas: 900000, + gasPrice: 25000000000, + value: '0x1', + chainId: 10000, + } + address = '0x13b0d8316F0c3cE0C3C51Ebb586A14d7d90112fD' + privateKey = '0x72d72a46401220f08ccb1b17b550feb816840f2f8ce86361e7ee54ac7a9ee6d8' + caver.klay.accounts.privateKeyToAccount(privateKey, address) + + const signed = await caver.klay.accounts.signTransaction(transaction, account.privateKey) + + const errorMessage = 'recoverTransaction only supports transactions of type "LEGACY".' + expect(()=>caver.klay.accounts.recoverTransaction(signed.rawTransaction)).to.throws(errorMessage) + }) + }) +}) + +describe('caver.klay.accounts.hashMessage', () => { + it('CAVERJS-UNIT-WALLET-031, CAVERJS-UNIT-WALLET-032 : result should be same with keccak256(MessagePrefix + originMessage.length + originMessage)', () => { + const message = 'Hello World' + let result = caver.klay.accounts.hashMessage(message) + checkHashMessage(result, message) + + const decoded = caver.utils.utf8ToHex(message) + result = caver.klay.accounts.hashMessage(decoded) + checkHashMessage(result, message) + }) +}) + +describe('caver.klay.accounts.sign', () => { + let account + + beforeEach(() => { + account = caver.klay.accounts.create() + }) + + context('CAVERJS-UNIT-WALLET-033 : input: data, privateKey', () => { + it('should recover valid address', () => { + const data = 'Some data' + let result = caver.klay.accounts.sign(data, account.privateKey) + + const keys = ['message', 'messageHash', 'v', 'r', 's', 'signature'] + expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) + + if (data != result.message) { + expect(data).to.equal(caver.utils.utf8ToHex(result.message)) + } + + const decoded = caver.utils.utf8ToHex(data) + result = caver.klay.accounts.sign(decoded, account.privateKey) + checkHashMessage(result.messageHash, data) + + expect(caver.klay.accounts.recover(result)).to.equal(account.address) + }) + }) + + context('CAVERJS-UNIT-WALLET-034 : input: data, privateKey:invalid', () => { + it('should throw an error', () => { + const data = 'Some data' + const invalid = caver.utils.randomHex(31) // 31bytes + + const errorMessage = 'Invalid private key' + expect(() => caver.klay.accounts.sign(data, invalid)).to.throw(errorMessage) + }) + }) +}) + +// caver.klay.accounts.recover +describe('caver.klay.accounts.recover', () => { + let account + + beforeEach(() => { + account = caver.klay.accounts.create() + }) + + context('CAVERJS-UNIT-WALLET-035 : input: signatureObject', () => { + it('result should be same with account.address', () => { + const message = 'Some data' + const sigObj = account.sign(message) + + let result = caver.klay.accounts.recover(sigObj) + expect(result).to.equal(account.address) + }) + }) + + context('CAVERJS-UNIT-WALLET-036 : input: message, signature', () => { + it('result should be same with account.address', () => { + const message = 'Some data' + const sigObj = account.sign(message) + + let result = caver.klay.accounts.recover(sigObj.message, sigObj.signature) + expect(result).to.equal(account.address) + }) + }) + + context('CAVERJS-UNIT-WALLET-037 : input: message, signature, prefixed', () => { + it('result should be same with account.address', () => { + const message = 'Some data' + const sigObj = account.sign(message) + const prefixed = true + + const messageHash = caver.klay.accounts.hashMessage(message) + + let result = caver.klay.accounts.recover(messageHash, sigObj.signature, prefixed) + expect(result).to.equal(account.address) + }) + }) + + context('CAVERJS-UNIT-WALLET-038 : input: message, v, r, s', () => { + it('result should be same with account.address', () => { + const message = 'Some data' + const sigObj = account.sign(message) + + let result = caver.klay.accounts.recover(sigObj.message, sigObj.v, sigObj.r, sigObj.s) + expect(result).to.equal(account.address) + }) + }) + + context('CAVERJS-UNIT-WALLET-039 : input: message, v, r, s, prefixed', () => { + it('result should be same with account.address', () => { + const message = 'Some data' + const sigObj = account.sign(message) + const prefixed = true + + const messageHash = caver.klay.accounts.hashMessage(message) + + let result = caver.klay.accounts.recover(messageHash, sigObj.v, sigObj.r, sigObj.s, prefixed) + expect(result).to.equal(account.address) + }) + }) +}) + +// caver.klay.accounts.encrypt +describe('caver.klay.accounts.encrypt', () => { + let account + + beforeEach(() => { + account = caver.klay.accounts.create() + }) + + context('CAVERJS-UNIT-WALLET-040 : input: privateKey, password', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let result = caver.klay.accounts.encrypt(account.privateKey, password) + + isKeystoreV4(result, account) + + const decryptedAccount = caver.klay.accounts.decrypt(result, password) + isAccount(decryptedAccount, {keys: account.keys, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-041 : input: privateKey:invalid, password', () => { + it('should throw an error', () => { + const invalid = caver.utils.randomHex(31) // 31bytes + const password = 'klaytn!@' + + const errorMessage = 'Invalid private key' + expect(() => caver.klay.accounts.encrypt(invalid, password)).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-096 : input: privateKey:KlaytnWalletKey, password', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let result = caver.klay.accounts.encrypt(account.getKlaytnWalletKey(), password) + + isKeystoreV4(result, account) + + const decryptedAccount = caver.klay.accounts.decrypt(result, password) + isAccount(decryptedAccount, {keys: account.keys, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-097 : input: privateKey:KlaytnWalletKey, password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let result = caver.klay.accounts.encrypt(account.getKlaytnWalletKey(), password, {address: account.address}) + + isKeystoreV4(result, account) + + const decryptedAccount = caver.klay.accounts.decrypt(result, password) + isAccount(decryptedAccount, {keys: account.keys, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-098 : input: privateKey:KlaytnWalletKey, password, {address:invalid}', () => { + it('should throw an error', () => { + const password = 'klaytn!@' + + const errorMessage = 'The address extracted from the private key does not match the address received as the input value.' + expect(() => caver.klay.accounts.encrypt(account.getKlaytnWalletKey(), password, {address: caver.klay.accounts.create().address})).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-099 : input: privateKey:KlaytnWalletKey(decoupled), password', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, caver.klay.accounts.create().privateKey) + + let result = caver.klay.accounts.encrypt(testAccount.getKlaytnWalletKey(), password) + + isKeystoreV4(result, testAccount) + + const decryptedAccount = caver.klay.accounts.decrypt(result, password) + isAccount(decryptedAccount, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-100 : input: privateKey:KlaytnWalletKey(decoupled), password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, caver.klay.accounts.create().privateKey) + + let result = caver.klay.accounts.encrypt(testAccount.getKlaytnWalletKey(), password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + + const decryptedAccount = caver.klay.accounts.decrypt(result, password) + isAccount(decryptedAccount, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-101 : input: privateKey:KlaytnWalletKey(decoupled), password, {address:invalid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, caver.klay.accounts.create().privateKey) + + const errorMessage = 'The address extracted from the private key does not match the address received as the input value.' + expect(() => caver.klay.accounts.encrypt(testAccount.getKlaytnWalletKey(), password, {address: caver.klay.accounts.create().address})).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-351: input: array of private key string, password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, key) + + let result = caver.klay.accounts.encrypt(testAccount.keys, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(key.length) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-352: input: array of private key string, password', () => { + it('should throw error when address is not defined', () => { + const password = 'klaytn!@' + + let key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, key) + + const errorMessage = 'The address must be defined inside the options object.' + expect(() => caver.klay.accounts.encrypt(testAccount.keys, password)).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-353: input: key object(transactionKey, updateKey and feePayerKey are defined), password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: caver.klay.accounts.create().privateKey, + } + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, key) + + let result = caver.klay.accounts.encrypt(testAccount.keys, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(3) + expect(result.keyring[0].length).to.equals(1) + expect(result.keyring[1].length).to.equals(1) + expect(result.keyring[2].length).to.equals(1) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-354: input: key object(transactionKey, updateKey and feePayerKey are defined with array of private key), password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, key) + + let result = caver.klay.accounts.encrypt(testAccount.keys, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(3) + expect(result.keyring[0].length).to.equals(key.transactionKey.length) + expect(result.keyring[1].length).to.equals(key.updateKey.length) + expect(result.keyring[2].length).to.equals(key.feePayerKey.length) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-355: input: key object(transactionKey is defined), password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let key = { + transactionKey: caver.klay.accounts.create().privateKey, + } + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, key) + + let result = caver.klay.accounts.encrypt(testAccount.keys, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(1) + expect(result.keyring[0].length).to.equals(1) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-356: input: key object(updateKey is defined), password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let key = { + updateKey: caver.klay.accounts.create().privateKey, + } + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, key) + + let result = caver.klay.accounts.encrypt(testAccount.keys, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(2) + expect(result.keyring[0].length).to.equals(0) + expect(result.keyring[1].length).to.equals(1) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-357: input: key object(feePayerKey is defined), password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let key = { + feePayerKey: caver.klay.accounts.create().privateKey, + } + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, key) + + let result = caver.klay.accounts.encrypt(testAccount.keys, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(3) + expect(result.keyring[0].length).to.equals(0) + expect(result.keyring[1].length).to.equals(0) + expect(result.keyring[2].length).to.equals(1) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-358: input: key object, password', () => { + it('should throw error when address is not defined', () => { + const password = 'klaytn!@' + + let key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: caver.klay.accounts.create().privateKey, + } + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, key) + + const errorMessage = 'The address must be defined inside the options object.' + expect(() => caver.klay.accounts.encrypt(testAccount.keys, password)).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-359: input: AccountKeyPublic, password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let key = caver.klay.accounts.create().privateKey + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + let result = caver.klay.accounts.encrypt(accountKey, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(1) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-360: input: AccountKeyPublic, password', () => { + it('should throw error', () => { + const password = 'klaytn!@' + + let key = caver.klay.accounts.create().privateKey + let accountKey = caver.klay.accounts.createAccountKey(key) + + const errorMessage = 'The address must be defined inside the options object.' + expect(() => caver.klay.accounts.encrypt(accountKey, password)).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-361: input: AccountKeyMultiSig, password, {address:valid}', () => { + it('should encrypt key with password', () => { + const password = 'klaytn!@' + + let key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + let result = caver.klay.accounts.encrypt(accountKey, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(key.length) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-362: input: AccountKeyMultiSig, password', () => { + it('should throw error', () => { + const password = 'klaytn!@' + + let key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + let accountKey = caver.klay.accounts.createAccountKey(key) + + const errorMessage = 'The address must be defined inside the options object.' + expect(() => caver.klay.accounts.encrypt(accountKey, password)).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-363: input: AccountKeyRoleBased, password, {address:valid}', () => { + it('should encrypt key with password', () => { + const password = 'klaytn!@' + + let key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + let result = caver.klay.accounts.encrypt(accountKey, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(3) + expect(result.keyring[0].length).to.equals(1) + expect(result.keyring[1].length).to.equals(key.updateKey.length) + expect(result.keyring[2].length).to.equals(key.feePayerKey.length) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-364: input: AccountKeyMultiSig, password', () => { + it('should throw error', () => { + const password = 'klaytn!@' + + let key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + let accountKey = caver.klay.accounts.createAccountKey(key) + + const errorMessage = 'The address must be defined inside the options object.' + expect(() => caver.klay.accounts.encrypt(accountKey, password)).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-365: input: Account with AccountKeyPublic, password, {address:valid}', () => { + it('should encrypt password with privateKey', () => { + const password = 'klaytn!@' + + let key = caver.klay.accounts.create().privateKey + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + let result = caver.klay.accounts.encrypt(testAccount, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(1) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-366: input: Account with AccountKeyPublic, password, {address:different address}', () => { + it('should throw error when addresses are not matched', () => { + const password = 'klaytn!@' + + let key = caver.klay.accounts.create().privateKey + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + const errorMessage = 'Address in account is not matched with address in options object' + expect(() => caver.klay.accounts.encrypt(testAccount, password, {address: caver.klay.accounts.create().address})).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-367: input: Account with AccountKeyMultiSig, password, {address:valid}', () => { + it('should encrypt key with password', () => { + const password = 'klaytn!@' + + let key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + let result = caver.klay.accounts.encrypt(accountKey, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(key.length) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-368: input: Account with AccountKeyMultiSig, password, {address:different address}', () => { + it('should throw error when addresses are not matched', () => { + const password = 'klaytn!@' + + let key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + const errorMessage = 'Address in account is not matched with address in options object' + expect(() => caver.klay.accounts.encrypt(testAccount, password, {address: caver.klay.accounts.create().address})).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-369: input: Account with AccountKeyRoleBased, password, {address:valid}', () => { + it('should encrypt key with password', () => { + const password = 'klaytn!@' + + let key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + let result = caver.klay.accounts.encrypt(accountKey, password, {address: testAccount.address}) + + isKeystoreV4(result, testAccount) + expect(result.keyring.length).to.equals(3) + expect(result.keyring[0].length).to.equals(1) + expect(result.keyring[1].length).to.equals(key.updateKey.length) + expect(result.keyring[2].length).to.equals(key.feePayerKey.length) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-370: input: Account with AccountKeyRoleBased, password, {address:different address}', () => { + it('should throw error when addresses are not matched', () => { + const password = 'klaytn!@' + + let key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + let accountKey = caver.klay.accounts.createAccountKey(key) + let testAccount = caver.klay.accounts.createWithAccountKey(account.address, accountKey) + + const errorMessage = 'Address in account is not matched with address in options object' + expect(() => caver.klay.accounts.encrypt(testAccount, password, {address: caver.klay.accounts.create().address})).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-385: input: Account with AccountKeyMultiSig, password, option', () => { + it('should throw error when addresses are not matched', () => { + const password = 'password' + + let testAccount = caver.klay.accounts.createWithAccountKey('0xf725a2950dc959638fa09f9d9b5426ad3dd8cd90', { + transactionKey: '0x7dc66dca0e5d56940c99ad01903a8ba5fd9e1f7a51a8ab07cf81ccd1d3c4be16', + updateKey: ['0x5fc3216454ab841ffa2bed0933a27bcdf2965238372bff3ec4fe56cbf5389a87', '0x79fe0616e7624314611b8e9c716b8d9c0c8c8c20f654021ff5fa7c46dc50709b'], + feePayerKey: '0xfac188dc156ef58d529ea14ac95379f502a390d5720a9575b87545e36b3f758e', + }) + + let encryptOption = { + salt: 'e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a', + iv: Buffer.from('38aa896fc128075425e512f01e4b206c', 'hex'), + kdf: 'scrypt', + dklen: 32, + n: 4096, + r: 8, + p: 1, + cipher: 'aes-128-ctr', + uuid: Buffer.from('e7c4605ad8200e0d93cd67f9d82fb997', 'hex'), + } + + const expectedKeystore = { + version: 4, + id: 'e7c4605a-d820-4e0d-93cd-67f9d82fb997', + address: '0xf725a2950dc959638fa09f9d9b5426ad3dd8cd90', + keyring:[ + [ + { + ciphertext: '5e2f95f61d7af3bebf4ff9f5d5813690c80b0b5aaebd6e8b22d0f928ff06776a', + cipherparams: { iv: '38aa896fc128075425e512f01e4b206c' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: 'e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a', + n: 4096, + r: 8, + p: 1 + }, + mac: 'fb86255428e24ba701201d5815f2f2114214cbd34fe4bc7a24b948a8ceac9f9b' + } + ], + [ + { + ciphertext: '7c2ad958478c213549fdb9fd7619c6f8c7034618c83e3ab229af6332d9fa53fb', + cipherparams: { iv: '38aa896fc128075425e512f01e4b206c' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: 'e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a', + n: 4096, + r: 8, + p: 1 + }, + mac: 'e6c3897772916c69f7c778ac2e0e60b786c55f17367c68b086486bd68fea9517' + }, + { + ciphertext: '5a17fe2af445e63ed2cdda6834d030a9391998000941c79318ab49bff092b9e7', + cipherparams: { iv: '38aa896fc128075425e512f01e4b206c' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: 'e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a', + n: 4096, + r: 8, + p: 1 + }, + mac: '633f91994f33541fbf1c3c3e973e539c12f1dd98f2757f64e3b63de986f367e0' + } + ], + [ + { + ciphertext: 'd92870e0064950a7e148f5be8ce8c4c0373684f58d1f50f95524701a47fdbcf2', + cipherparams: { iv: '38aa896fc128075425e512f01e4b206c' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: 'e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a', + n: 4096, + r: 8, + p: 1 + }, + mac: 'b2f0245036e7bbdea712819dfbe019c8bfb237684c67d61fa82638868a7ae752' + } + ] + ], + } + + let result = caver.klay.accounts.encrypt(testAccount, password, encryptOption) + + expect(result.version).to.equals(expectedKeystore.version) + expect(result.id).to.equals(expectedKeystore.id) + expect(result.address).to.equals(expectedKeystore.address) + compareEncrypted(result.keyring[0][0], expectedKeystore.keyring[0][0]) + compareEncrypted(result.keyring[1][0], expectedKeystore.keyring[1][0]) + compareEncrypted(result.keyring[1][1], expectedKeystore.keyring[1][1]) + compareEncrypted(result.keyring[2][0], expectedKeystore.keyring[2][0]) + + function compareEncrypted(ret, exp) { + expect(ret.ciphertext).to.equals(exp.ciphertext) + expect(ret.cipherparams.iv).to.equals(exp.cipherparams.iv) + expect(ret.cipher).to.equals(exp.cipher) + expect(ret.kdf).to.equals(exp.kdf) + expect(ret.kdfparams.dklen).to.equals(exp.kdfparams.dklen) + expect(ret.kdfparams.salt).to.equals(exp.kdfparams.salt) + expect(ret.kdfparams.n).to.equals(exp.kdfparams.n) + expect(ret.kdfparams.r).to.equals(exp.kdfparams.r) + expect(ret.kdfparams.p).to.equals(exp.kdfparams.p) + expect(ret.mac).to.equals(exp.mac) + } + + isKeystoreV4(result, testAccount) + }) + }) + + context('CAVERJS-UNIT-WALLET-389: input: Account with AccountKeyMultiSig, password, option(pbkdf2)', () => { + it('should throw error when addresses are not matched', () => { + const password = 'password' + + let testAccount = caver.klay.accounts.createWithAccountKey('0xf725a2950dc959638fa09f9d9b5426ad3dd8cd90', { + transactionKey: '0x7dc66dca0e5d56940c99ad01903a8ba5fd9e1f7a51a8ab07cf81ccd1d3c4be16', + updateKey: ['0x5fc3216454ab841ffa2bed0933a27bcdf2965238372bff3ec4fe56cbf5389a87', '0x79fe0616e7624314611b8e9c716b8d9c0c8c8c20f654021ff5fa7c46dc50709b'], + feePayerKey: '0xfac188dc156ef58d529ea14ac95379f502a390d5720a9575b87545e36b3f758e', + }) + + let encryptOption = { + salt: 'e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a', + iv: Buffer.from('38aa896fc128075425e512f01e4b206c', 'hex'), + kdf: 'pbkdf2', + dklen: 32, + c: 262144, + cipher: 'aes-128-ctr', + uuid: Buffer.from('e7c4605ad8200e0d93cd67f9d82fb997', 'hex'), + } + + let result = caver.klay.accounts.encrypt(testAccount.keys, password, Object.assign({address: testAccount.address, encryptOption})) + + isKeystoreV4(result, testAccount) + + expect(result.keyring.length).to.equals(3) + expect(result.keyring[0].length).to.equals(1) + expect(result.keyring[1].length).to.equals(2) + expect(result.keyring[2].length).to.equals(1) + + const decrypted = caver.klay.accounts.decrypt(result, password) + isAccount(decrypted, {keys: testAccount.keys, address: testAccount.address}) + }) + }) +}) + +describe('caver.klay.accounts.decrypt', () => { + let account + + beforeEach(() => { + account = caver.klay.accounts.create() + }) + + context('CAVERJS-UNIT-WALLET-042 : input: keystoreJsonV4, password', () => { + it('After decrypting, should return valid account', () => { + const password = 'klaytn!@' + const keystoreJsonV4 = caver.klay.accounts.encrypt(account.privateKey, password) + + let result = caver.klay.accounts.decrypt(keystoreJsonV4, password) + isKeystoreV4(keystoreJsonV4, result) + + isAccount(result, {keys: account.keys, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-103 : input: keystoreJsonV4(without 0x address format), password', () => { + it('After decrypting, should return valid account', () => { + const password = 'klaytn!@' + const keystoreJsonV4 = caver.klay.accounts.encrypt(account.privateKey, password) + keystoreJsonV4.address = keystoreJsonV4.address.replace('0x', '') + + let result = caver.klay.accounts.decrypt(keystoreJsonV4, password) + + expect(result.address.slice(0, 2)).to.equals('0x') + }) + }) + + context('CAVERJS-UNIT-WALLET-371: input: keystoreJsonV4 that encrypts Account with AccountKeyMultiSig, password', () => { + it('After decrypting, should return valid account', () => { + const password = 'klaytn!@' + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const keystoreJsonV4 = caver.klay.accounts.encrypt(key, password, { address: account.address }) + + let result = caver.klay.accounts.decrypt(keystoreJsonV4, password) + + isAccount(result, {keys: key, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-372: input: keystoreJsonV4 that encrypts Account with AccountKeyRoleBased, password', () => { + it('After decrypting, should return valid account', () => { + const password = 'klaytn!@' + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const keystoreJsonV4 = caver.klay.accounts.encrypt(key, password, { address: account.address }) + + let result = caver.klay.accounts.decrypt(keystoreJsonV4, password) + + isAccount(result, {keys: key, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-373: input: keystoreJsonV4 that encrypts Account with AccountKeyRoleBased(transactionKey only), password', () => { + it('After decrypting, should return valid account', () => { + const password = 'klaytn!@' + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + } + const keystoreJsonV4 = caver.klay.accounts.encrypt(key, password, { address: account.address }) + + let result = caver.klay.accounts.decrypt(keystoreJsonV4, password) + + isAccount(result, {keys: key, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-374: input: keystoreJsonV4 that encrypts Account with AccountKeyRoleBased(updateKey only), password', () => { + it('After decrypting, should return valid account', () => { + const password = 'klaytn!@' + const key = { + updateKey: caver.klay.accounts.create().privateKey, + } + const keystoreJsonV4 = caver.klay.accounts.encrypt(key, password, { address: account.address }) + + let result = caver.klay.accounts.decrypt(keystoreJsonV4, password) + + isAccount(result, {keys: key, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-375: input: keystoreJsonV4 that encrypts Account with AccountKeyRoleBased(feePayerKey only), password', () => { + it('After decrypting, should return valid account', () => { + const password = 'klaytn!@' + const key = { + feePayerKey: caver.klay.accounts.create().privateKey, + } + const keystoreJsonV4 = caver.klay.accounts.encrypt(key, password, { address: account.address }) + + let result = caver.klay.accounts.decrypt(keystoreJsonV4, password) + + isAccount(result, {keys: key, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-378: input: keystoreJsonV4 that encrypts Account, password', () => { + it('After decrypting, should return valid account', () => { const password = 'klaytn!@' + const key = caver.klay.accounts.create().privateKey + const keystore = caver.klay.accounts.encrypt(key, password, { address: account.address }) + keystore.version = 3 + keystore.crypto = keystore.keyring[0] + delete keystore.keyring + + let result = caver.klay.accounts.decrypt(keystore, password) + + isAccount(result, {keys: key, address: account.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-379: input: keystoreJsonV4 that encrypts Account, password', () => { + it('should throw error with invalid keystore v3 which not defines crypto', () => { + const password = 'klaytn!@' + const key = caver.klay.accounts.create().privateKey + const keystore = caver.klay.accounts.encrypt(key, password, { address: account.address }) + keystore.version = 3 + + const expectedError = `Invalid keystore V3 format: 'crypto' is not defined.` + + expect(() => caver.klay.accounts.decrypt(keystore, password)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-380: input: keystoreJsonV4 that encrypts Account, password', () => { + it('should throw error with invalid keystore v3 which defines crypto and keyring', () => { + const password = 'klaytn!@' + const key = caver.klay.accounts.create().privateKey + const keystore = caver.klay.accounts.encrypt(key, password, { address: account.address }) + keystore.version = 3 + keystore.crypto = keystore.keyring[0] + + const expectedError = `Invalid key store format: 'crypto' can not be with 'keyring'` + + expect(() => caver.klay.accounts.decrypt(keystore, password)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-381: input: keystoreJsonV4 that encrypts Account, password', () => { + it('should throw error with invalid keystore v3 which defines crypto and keyring', () => { + const password = 'klaytn!@' + const key = caver.klay.accounts.create().privateKey + const keystore = caver.klay.accounts.encrypt(key, password, { address: account.address }) + keystore.crypto = keystore.keyring[0] + + const expectedError = `Invalid key store format: 'crypto' can not be with 'keyring'` + + expect(() => caver.klay.accounts.decrypt(keystore, password)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-382: input: keystoreJsonV4 that encrypts Account, password', () => { + it('should throw error with invalid length of key', () => { + const password = 'klaytn!@' + const key = caver.klay.accounts.create().privateKey + const keystore = caver.klay.accounts.encrypt(key, password, { address: account.address }) + keystore.keyring = [[], [], [], []] + + const expectedError = `Invalid key store format` + + expect(() => caver.klay.accounts.decrypt(keystore, password)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-383: input: hard coded keystoreJsonV4 that encrypts Account, password', () => { + it('should decrypt and return valid account', () => { + const keystoreJsonV4 = { + version: 4, + id: '55da3f9c-6444-4fc1-abfa-f2eabfc57501', + address: '0x86bce8c859f5f304aa30adb89f2f7b6ee5a0d6e2', + keyring:[ + [ + { + ciphertext: '93dd2c777abd9b80a0be8e1eb9739cbf27c127621a5d3f81e7779e47d3bb22f6', + cipherparams: { iv: '84f90907f3f54f53d19cbd6ae1496b86' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: '69bf176a136c67a39d131912fb1e0ada4be0ed9f882448e1557b5c4233006e10', + n: 4096, + r: 8, + p: 1 + }, + mac: '8f6d1d234f4a87162cf3de0c7fb1d4a8421cd8f5a97b86b1a8e576ffc1eb52d2' + }, + { + ciphertext: '53d50b4e86b550b26919d9b8cea762cd3c637dfe4f2a0f18995d3401ead839a6', + cipherparams: { iv: 'd7a6f63558996a9f99e7daabd289aa2c' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: '966116898d90c3e53ea09e4850a71e16df9533c1f9e1b2e1a9edec781e1ad44f', + n: 4096, + r: 8, + p: 1 + }, + mac: 'bca7125e17565c672a110ace9a25755847d42b81aa7df4bb8f5ce01ef7213295' + } + ], + [ + { + ciphertext: 'f16def98a70bb2dae053f791882f3254c66d63416633b8d91c2848893e7876ce', + cipherparams: { iv: 'f5006128a4c53bc02cada64d095c15cf' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: '0d8a2f71f79c4880e43ff0795f6841a24cb18838b3ca8ecaeb0cda72da9a72ce', + n: 4096, + r: 8, + p: 1 + }, + mac: '38b79276c3805b9d2ff5fbabf1b9d4ead295151b95401c1e54aed782502fc90a' + } + ], + [ + { + ciphertext: '544dbcc327942a6a52ad6a7d537e4459506afc700a6da4e8edebd62fb3dd55ee', + cipherparams: { iv: '05dd5d25ad6426e026818b6fa9b25818' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: '3a9003c1527f65c772c54c6056a38b0048c2e2d58dc0e584a1d867f2039a25aa', + n: 4096, + r: 8, + p: 1 + }, + mac: '19a698b51409cc9ac22d63d329b1201af3c89a04a1faea3111eec4ca97f2e00f' + }, + { + ciphertext: 'dd6b920f02cbcf5998ed205f8867ddbd9b6b088add8dfe1774a9fda29ff3920b', + cipherparams: { iv: 'ac04c0f4559dad80dc86c975d1ef7067' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: '22279c6dbcc706d7daa120022a236cfe149496dca8232b0f8159d1df999569d6', + n: 4096, + r: 8, + p: 1 + }, + mac: '1c54f7378fa279a49a2f790a0adb683defad8535a21bdf2f3dadc48a7bddf517' + } + ] + ], + } + const password = 'password' + const expectedAccount = caver.klay.accounts.createWithAccountKey('0x86bce8c859f5f304aa30adb89f2f7b6ee5a0d6e2', { + transactionKey: ['0xd1e9f8f00ef9f93365f5eabccccb3f3c5783001b61a40f0f74270e50158c163d', '0x4bd8d0b0c1575a7a35915f9af3ef8beb11ad571337ec9b6aca7c88ca7458ef5c'], + updateKey: '0xdc2690ac6017e32ef17ea219c2a2fd14a2bb73e7a0a253dfd69abba3eb8d7d91', + feePayerKey: ['0xf17bf8b7bee09ffc50a401b7ba8e633b9e55eedcf776782f2a55cf7cc5c40aa8', '0x4f8f1e9e1466609b836dba611a0a24628aea8ee11265f757aa346bde3d88d548'] + }) + + let result = caver.klay.accounts.decrypt(keystoreJsonV4, password) + + isAccount(result, {keys: expectedAccount.keys, address: expectedAccount.address}) + }) + }) + + context('CAVERJS-UNIT-WALLET-384: input: hard coded keystoreJsonV3 that encrypts Account, password', () => { + it('should decrypt and return valid account', () => { + const keystoreJsonV3 = { + version: 3, + id: '7a0a8557-22a5-4c90-b554-d6f3b13783ea', + address: '0x86bce8c859f5f304aa30adb89f2f7b6ee5a0d6e2', + crypto: { + ciphertext: '696d0e8e8bd21ff1f82f7c87b6964f0f17f8bfbd52141069b59f084555f277b7', + cipherparams: { iv: '1fd13e0524fa1095c5f80627f1d24cbd' }, + cipher: 'aes-128-ctr', + kdf: 'scrypt', + kdfparams: { + dklen: 32, + salt: '7ee980925cef6a60553cda3e91cb8e3c62733f64579f633d0f86ce050c151e26', + n: 4096, + r: 8, + p: 1 + }, + mac: '8684d8dc4bf17318cd46c85dbd9a9ec5d9b290e04d78d4f6b5be9c413ff30ea4' + } + } + const password = 'password' + const expectedAccount = caver.klay.accounts.privateKeyToAccount('0x36e0a792553f94a7660e5484cfc8367e7d56a383261175b9abced7416a5d87df', '0x86bce8c859f5f304aa30adb89f2f7b6ee5a0d6e2') + + let result = caver.klay.accounts.decrypt(keystoreJsonV3, password) + + isAccount(result, {keys: expectedAccount.keys, address: expectedAccount.address}) + }) + }) + + + /* + it('keystoreJsonV3, password:invalid [KLAYTN-52]', () => { + const invalid = '' + const keystoreJsonV3 = caver.klay.accounts.encrypt(account.privateKey, invalid) + + utils.log('input', keystoreJsonV3, invalid) + + const expectedError = { + name: 'Error', + message: '' + } + validateErrorCodeblock(() => caver.klay.accounts.decrypt(keystoreJsonV3, invalid), expectedError) + }) + */ +}) + +describe('caver.klay.accounts.getLegacyAccount', () => { + context('CAVERJS-UNIT-WALLET-106 : input: valid privateKey', () => { + it('should return account which is derived from private key', () => { + const testAccount = caver.klay.accounts.create() + let result = caver.klay.accounts.getLegacyAccount(testAccount.privateKey) + + expect(result.klaytnWalletKeyAddress).to.equals('') + expect(result.legacyAccount.address).to.equals(testAccount.address) + expect(result.legacyAccount.privateKey).to.equals(testAccount.privateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-107 : input: nonDecoupled valid KlaytnWalletKey format', () => { + it('should return account which is derived from private key and address from KlaytnWalletKey format', () => { + const testAccount = caver.klay.accounts.create() + let result = caver.klay.accounts.getLegacyAccount(testAccount.getKlaytnWalletKey()) + + expect(result.klaytnWalletKeyAddress).to.equals(testAccount.address) + expect(result.legacyAccount.address).to.equals(result.klaytnWalletKeyAddress) + expect(result.legacyAccount.privateKey).to.equals(testAccount.privateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-108 : input: decoupled valid KlaytnWalletKey format', () => { + it('should return account which is derived from private key and address from KlaytnWalletKey format', () => { + // decoupled + const privateKey = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const testAccount = caver.klay.accounts.privateKeyToAccount(privateKey, address) + + let result = caver.klay.accounts.getLegacyAccount(testAccount.getKlaytnWalletKey()) + + expect(result.klaytnWalletKeyAddress).to.equals(testAccount.address) + expect(result.legacyAccount.address).not.to.equals(result.klaytnWalletKeyAddress) + expect(result.legacyAccount.privateKey).to.equals(testAccount.privateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-109 : input: invalid privateKey', () => { + it('should throw error if input is invalid privateKey string', () => { + const expectedError = 'Invalid private key' + + expect(() => caver.klay.accounts.getLegacyAccount('0x')).to.throws(expectedError) + expect(() => caver.klay.accounts.getLegacyAccount('1')).to.throws(expectedError) + expect(() => caver.klay.accounts.getLegacyAccount('a')).to.throws(expectedError) + expect(() => caver.klay.accounts.getLegacyAccount('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140FF')).to.throws(expectedError) + }) + + it('should throw error if input is invalid privateKey type', () => { + const expectedError = 'The private key must be of type string' + + expect(() => caver.klay.accounts.getLegacyAccount(1234)).to.throws(expectedError) + expect(() => caver.klay.accounts.getLegacyAccount({})).to.throws(expectedError) + expect(() => caver.klay.accounts.getLegacyAccount()).to.throws(expectedError) + expect(() => caver.klay.accounts.getLegacyAccount(undefined)).to.throws(expectedError) + expect(() => caver.klay.accounts.getLegacyAccount(null)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-110 : input: invalid KlaytnWalletKey format', () => { + it('should throw error if input is invalid KlaytnWalletKey string', () => { + const expectedError = 'Invalid private key' + expect(() => caver.klay.accounts.getLegacyAccount(caver.klay.accounts.create().privateKey+'0x000x00')).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.isDecoupled', () => { + context('CAVERJS-UNIT-WALLET-111 : input: valid privateKey and decoupled address', () => { + it('should return true if input is decoupled private and address', () => { + const privateKey = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const testAccount = caver.klay.accounts.privateKeyToAccount(privateKey, address) + + expect(caver.klay.accounts.isDecoupled(testAccount.privateKey, testAccount.address)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-112 : input: valid KlaytnWalletKey', () => { + it('should return true if input is decoupled KlaytnWalletKey', () => { + const privateKey = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const testAccount = caver.klay.accounts.privateKeyToAccount(privateKey, address) + + expect(caver.klay.accounts.isDecoupled(testAccount.getKlaytnWalletKey())).to.be.true + expect(caver.klay.accounts.isDecoupled(testAccount.getKlaytnWalletKey(), testAccount.address)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-113 : input: valid privateKey', () => { + it('should return false if input is valid privateKey', () => { + expect(caver.klay.accounts.isDecoupled(caver.klay.accounts.create().privateKey)).to.be.false + expect(caver.klay.accounts.isDecoupled(caver.klay.accounts.create().privateKey.slice(2))).to.be.false + }) + }) + + context('CAVERJS-UNIT-WALLET-114 : input: valid KlaytnWalletKey', () => { + it('should return true if input is nonDecoupled KlaytnWalletKey', () => { + const testAccount = caver.klay.accounts.create() + expect(caver.klay.accounts.isDecoupled(testAccount.getKlaytnWalletKey())).to.be.false + expect(caver.klay.accounts.isDecoupled(testAccount.getKlaytnWalletKey(), testAccount.address)).to.be.false + }) + }) + + context('CAVERJS-UNIT-WALLET-115 : input: invalid privateKey', () => { + it('should throw error if input is invalid privateKey string', () => { + const expectedError = 'Invalid private key' + + expect(() => caver.klay.accounts.isDecoupled('0x')).to.throws(expectedError) + expect(() => caver.klay.accounts.isDecoupled('1')).to.throws(expectedError) + expect(() => caver.klay.accounts.isDecoupled('a')).to.throws(expectedError) + expect(() => caver.klay.accounts.isDecoupled('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140FF')).to.throws(expectedError) + }) + + it('should throw error if input is invalid privateKey type', () => { + const expectedError = 'The private key must be of type string' + + expect(() => caver.klay.accounts.isDecoupled(1234)).to.throws(expectedError) + expect(() => caver.klay.accounts.isDecoupled({})).to.throws(expectedError) + expect(() => caver.klay.accounts.isDecoupled()).to.throws(expectedError) + expect(() => caver.klay.accounts.isDecoupled(undefined)).to.throws(expectedError) + expect(() => caver.klay.accounts.isDecoupled(null)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-116 : input: not match address with KlaytnWalletKey and input', () => { + it('should throw error if input is invalid privateKey string', () => { + const privateKey = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const testAccount = caver.klay.accounts.privateKeyToAccount(privateKey, address) + + const expectedError = 'The address extracted from the private key does not match the address received as the input value.' + + expect(() => caver.klay.accounts.isDecoupled(testAccount.getKlaytnWalletKey(), caver.klay.accounts.create().address)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts._getRoleKey', () => { + let account + + beforeEach(() => { + const keyObject = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + account = caver.klay.accounts.createWithAccountKey(caver.klay.accounts.create().address, keyObject) + }) + + context('CAVERJS-UNIT-WALLET-133: input: legacy tx and account', () => { + it('should return transactionKey', () => { + const tx = {} + const roleKey = caver.klay.accounts._getRoleKey(tx, account) + expect(isSameKeyArray(roleKey, account.transactionKey)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-134: input: update tx and account', () => { + it('should return updateKey', () => { + const tx = { type: 'FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO' } + const roleKey = caver.klay.accounts._getRoleKey(tx, account) + expect(isSameKeyArray(roleKey, account.updateKey)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-135: input: tx for fee payer and account', () => { + it('should return feePayerKey', () => { + const tx = { senderRawTransaction: '0x', feePayer: account.address } + const roleKey = caver.klay.accounts._getRoleKey(tx, account) + expect(isSameKeyArray(roleKey, account.feePayerKey)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-136: input: tx and account', () => { + it('should throw error if there is not key matched with role', () => { + let testAccount = {updateKey: caver.klay.accounts.create().privateKey} + const expectedError = `The key corresponding to the role used for signing is not defined.` + expect(() => caver.klay.accounts._getRoleKey({}, testAccount)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-137: input: tx and account', () => { + it('should throw error if there is not key matched with role', () => { + let testAccount = {transactionKey: caver.klay.accounts.create().privateKey} + const expectedError = `The key corresponding to the role used for signing is not defined.` + expect(() => caver.klay.accounts._getRoleKey({type: 'ACCOUNT_UPDATE'}, testAccount)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-138: input: tx and account', () => { + it('should throw error if there is not key matched with role', () => { + let testAccount = {transactionKey: caver.klay.accounts.create().privateKey} + const expectedError = `The key corresponding to the role used for signing is not defined.` + expect(() => caver.klay.accounts._getRoleKey({ senderRawTransaction: '0x', feePayer: account.address }, testAccount)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createAccountKey', () => { + context('CAVERJS-UNIT-WALLET-139: input: private key string`', () => { + it('should return AccountKeyPublic', () => { + const key = caver.klay.accounts.create().privateKey + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyPublic') + expect(accountKey.defaultKey).to.equals(key) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-140: input: array of private key string', () => { + it('should return accountKeyMultiSig', () => { + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyMultiSig') + expect(accountKey.defaultKey).to.equals(key[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-141: input: keyObject(transactionKey is defined with private key string)', () => { + it('should return accountKeyRoleBased', () => { + const key = { + transactionKey: caver.klay.accounts.create().privateKey + } + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.transactionKey) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-142: input: keyObject(transactionKey is defined with array of private key string)', () => { + it('should return accountKeyRoleBased', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.transactionKey[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-143: input: keyObject(updateKey is defined with private key string)', () => { + it('should return accountKeyRoleBased', () => { + const key = { + updateKey: caver.klay.accounts.create().privateKey + } + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.updateKey) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-144: input: keyObject(updateKey is defined with array of private key string)', () => { + it('should return accountKeyRoleBased', () => { + const key = { + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.updateKey[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-145: input: keyObject(feePayerKey is defined with private key string)', () => { + it('should return accountKeyRoleBased', () => { + const key = { + feePayerKey: caver.klay.accounts.create().privateKey + } + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.feePayerKey) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-146: input: keyObject(feePayerKey is defined with array of private key string)', () => { + it('should return accountKeyRoleBased', () => { + const key = { + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.feePayerKey[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-147: input: keyObject(transactionKey, updateKey, feePayerKey are defined)', () => { + it('should return accountKeyRoleBased', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + + const accountKey = caver.klay.accounts.createAccountKey(key) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.transactionKey[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-148: input: invalid type of parameter', () => { + it('should throw Error', () => { + let param = 1 + let expectedError = `Invalid accountKey type: ${typeof param}` + expect(() => caver.klay.accounts.createAccountKey(param)).to.throws(expectedError) + + param = undefined + expectedError = `Invalid accountKey type: ${typeof param}` + expect(() => caver.klay.accounts.createAccountKey(param)).to.throws(expectedError) + + param = null + expectedError = `Invalid accountKey type: ${typeof param}` + expect(() => caver.klay.accounts.createAccountKey(param)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-149: input: invalid parameter', () => { + it('should throw Error', () => { + let param = 'invalidString' + let expectedError = `Invalid private key` + expect(() => caver.klay.accounts.createAccountKey(param)).to.throws(expectedError) + + param = ['invalidString'] + expectedError = `Invalid private key` + expect(() => caver.klay.accounts.createAccountKey(param)).to.throws(expectedError) + + param = {} + expectedError = `Failed to create AccountKeyRoleBased: empty object` + expect(() => caver.klay.accounts.createAccountKey(param)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createAccountKeyPublic', () => { + context('CAVERJS-UNIT-WALLET-150: input: private key string`', () => { + it('should return AccountKeyPublic', () => { + const key = caver.klay.accounts.create().privateKey + const accountKey = caver.klay.accounts.createAccountKeyPublic(key) + + expect(accountKey.type).to.equals('AccountKeyPublic') + expect(accountKey.defaultKey).to.equals(key) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-151: input: AccountKeyPublic', () => { + it('should return AccountKeyPublic', () => { + const key = caver.klay.accounts.create().privateKey + const accountKey = caver.klay.accounts.createAccountKeyPublic(caver.klay.accounts.createAccountKeyPublic(key)) + + expect(accountKey.type).to.equals('AccountKeyPublic') + expect(accountKey.defaultKey).to.equals(key) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-152: input: invalid type of parameter', () => { + it('should throw Error', () => { + let param = 1 + let expectedError = `Creating a AccountKeyPublic requires a private key string.` + expect(() => caver.klay.accounts.createAccountKeyPublic(param)).to.throws(expectedError) + + param = [] + expectedError = `Creating a AccountKeyPublic requires a private key string.` + expect(() => caver.klay.accounts.createAccountKeyPublic(param)).to.throws(expectedError) + + param = {} + expectedError = `Creating a AccountKeyPublic requires a private key string.` + expect(() => caver.klay.accounts.createAccountKeyPublic(param)).to.throws(expectedError) + + param = undefined + expectedError = `Creating a AccountKeyPublic requires a private key string.` + expect(() => caver.klay.accounts.createAccountKeyPublic(param)).to.throws(expectedError) + + param = null + expectedError = `Creating a AccountKeyPublic requires a private key string.` + expect(() => caver.klay.accounts.createAccountKeyPublic(param)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-153: input: invalid parameter', () => { + it('should throw Error', () => { + const param = 'invalidString' + const expectedError = `Invalid private key` + expect(() => caver.klay.accounts.createAccountKeyPublic(param)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createAccountKeyMultiSig', () => { + context('CAVERJS-UNIT-WALLET-154: input: private key string`', () => { + it('should return AccountKeyMultiSig', () => { + const key = [caver.klay.accounts.create().privateKey] + const accountKey = caver.klay.accounts.createAccountKeyMultiSig(key) + + expect(accountKey.type).to.equals('AccountKeyMultiSig') + expect(accountKey.defaultKey).to.equals(key[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-155: input: AccountKeyMultiSig', () => { + it('should return AccountKeyMultiSig', () => { + const key = [caver.klay.accounts.create().privateKey] + const accountKey = caver.klay.accounts.createAccountKeyMultiSig(caver.klay.accounts.createAccountKeyMultiSig(key)) + + expect(accountKey.type).to.equals('AccountKeyMultiSig') + expect(accountKey.defaultKey).to.equals(key[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-156: input: invalid type of parameter', () => { + it('should throw Error', () => { + let param = 1 + let expectedError = `Creating a AccountKeyMultiSig requires an array of private key string.` + expect(() => caver.klay.accounts.createAccountKeyMultiSig(param)).to.throws(expectedError) + + param = 'string' + expectedError = `Creating a AccountKeyMultiSig requires an array of private key string.` + expect(() => caver.klay.accounts.createAccountKeyMultiSig(param)).to.throws(expectedError) + + param = {} + expectedError = `Creating a AccountKeyMultiSig requires an array of private key string.` + expect(() => caver.klay.accounts.createAccountKeyMultiSig(param)).to.throws(expectedError) + + param = undefined + expectedError = `Creating a AccountKeyMultiSig requires an array of private key string.` + expect(() => caver.klay.accounts.createAccountKeyMultiSig(param)).to.throws(expectedError) + + param = null + expectedError = `Creating a AccountKeyMultiSig requires an array of private key string.` + expect(() => caver.klay.accounts.createAccountKeyMultiSig(param)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-157: input: invalid parameter', () => { + it('should throw Error', () => { + const param = ['invalidString'] + const expectedError = `Invalid private key` + expect(() => caver.klay.accounts.createAccountKeyMultiSig(param)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createAccountKeyRoleBased', () => { + context('CAVERJS-UNIT-WALLET-158: input: private key string', () => { + it('should return AccountKeyRoleBased', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: caver.klay.accounts.create().privateKey, + } + const accountKey = caver.klay.accounts.createAccountKeyRoleBased(key) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.transactionKey[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-159: input: AccountKeyRoleBased', () => { + it('should return AccountKeyRoleBased', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: caver.klay.accounts.create().privateKey, + } + const accountKey = caver.klay.accounts.createAccountKeyRoleBased(caver.klay.accounts.createAccountKeyRoleBased(key)) + + expect(accountKey.type).to.equals('AccountKeyRoleBased') + expect(accountKey.defaultKey).to.equals(key.transactionKey[0]) + compareAccountKey(accountKey, key) + expect(typeof accountKey.toPublicKey).to.equals('function') + expect(typeof accountKey.update).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-160: input: invalid type of parameter', () => { + it('should throw Error', () => { + let param = 1 + let expectedError = `Creating a AccountKeyRoleBased requires an object.` + expect(() => caver.klay.accounts.createAccountKeyRoleBased(param)).to.throws(expectedError) + + param = 'string' + expectedError = `Creating a AccountKeyRoleBased requires an object.` + expect(() => caver.klay.accounts.createAccountKeyRoleBased(param)).to.throws(expectedError) + + param = [] + expectedError = `Creating a AccountKeyRoleBased requires an object.` + expect(() => caver.klay.accounts.createAccountKeyRoleBased(param)).to.throws(expectedError) + + param = undefined + expectedError = `Creating a AccountKeyRoleBased requires an object.` + expect(() => caver.klay.accounts.createAccountKeyRoleBased(param)).to.throws(expectedError) + + param = null + expectedError = `Creating a AccountKeyRoleBased requires an object.` + expect(() => caver.klay.accounts.createAccountKeyRoleBased(param)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-161: input: empty object', () => { + it('should throw Error', () => { + let param = {} + let expectedError = `Failed to create AccountKeyRoleBased: empty object` + expect(() => caver.klay.accounts.createAccountKeyRoleBased(param)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-162: input: invalid role is defined', () => { + it('should throw Error', () => { + let param = { invalidRole: 'invalidString' } + expectedError = `Failed to create AccountKeyRoleBased. Invalid role is defined : invalidRole` + expect(() => caver.klay.accounts.createAccountKeyRoleBased(param)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-163: input: invalid parameter', () => { + it('should throw Error', () => { + let param = { transactionKey: 'invalidString' } + let expectedError = `Invalid private key` + expect(() => caver.klay.accounts.createAccountKeyRoleBased(param)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.accountKeyToPublicKey', () => { + context('CAVERJS-UNIT-WALLET-164: input: private key string', () => { + it('should return string of public key', () => { + const key = caver.klay.accounts.create().privateKey + const expectedPublicKey = caver.klay.accounts.privateKeyToPublicKey(key) + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(key) + + expect(expectedPublicKey).to.equals(publicKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-165: input: AccountKeyPublic', () => { + it('should return string of public key', () => { + const key = caver.klay.accounts.create().privateKey + const accountKey = caver.klay.accounts.createAccountKeyPublic(key) + const expectedPublicKey = caver.klay.accounts.privateKeyToPublicKey(key) + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(accountKey) + + expect(expectedPublicKey).to.equals(publicKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-166: input: array of private key string', () => { + it('should return array of public key string', () => { + const key = [caver.klay.accounts.create().privateKey] + const expectedPublicKey = [caver.klay.accounts.privateKeyToPublicKey(key[0])] + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(key) + + expect(isSameKeyArray(expectedPublicKey, publicKey)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-167: input: AccountKeyMultiSig', () => { + it('should return array of public key string', () => { + const key = [caver.klay.accounts.create().privateKey] + const accountKey = caver.klay.accounts.createAccountKeyMultiSig(key) + const expectedPublicKey = [caver.klay.accounts.privateKeyToPublicKey(key[0])] + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(accountKey) + + expect(isSameKeyArray(expectedPublicKey, publicKey)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-168: input: object defines key', () => { + it('should return object of public key string', () => { + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey], + } + const expectedPublicKey = { + transactionKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey), + updateKey: caver.klay.accounts.privateKeyToPublicKey(key.updateKey), + feePayerKey: [caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey[0])], + } + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(key) + + expect(publicKey.transactionKey).to.equals(expectedPublicKey.transactionKey) + expect(publicKey.updateKey).to.equals(expectedPublicKey.updateKey) + expect(isSameKeyArray(expectedPublicKey.feePayerKey, publicKey.feePayerKey)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-169: input: AccountKeyRoleBased', () => { + it('should return array of public key string', () => { + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey], + } + const accountKey = caver.klay.accounts.createAccountKeyRoleBased(key) + const expectedPublicKey = { + transactionKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey), + updateKey: caver.klay.accounts.privateKeyToPublicKey(key.updateKey), + feePayerKey: [caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey[0])], + } + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(accountKey) + + expect(publicKey.transactionKey).to.equals(expectedPublicKey.transactionKey) + expect(publicKey.updateKey).to.equals(expectedPublicKey.updateKey) + expect(isSameKeyArray(expectedPublicKey.feePayerKey, publicKey.feePayerKey)).to.be.true + }) + }) + + context('CAVERJS-UNIT-WALLET-170: input: object defines transactionKey only', () => { + it('should return object of public key string', () => { + const key = { transactionKey: caver.klay.accounts.create().privateKey, } + const expectedPublicKey = { + transactionKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey), + } + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(key) + + expect(publicKey.transactionKey).to.equals(expectedPublicKey.transactionKey) + expect(publicKey.updateKey).to.be.undefined + expect(publicKey.feePayerKey).to.be.undefined + }) + }) + + context('CAVERJS-UNIT-WALLET-171: input: object defines updateKey only', () => { + it('should return object of public key string', () => { + const key = { updateKey: caver.klay.accounts.create().privateKey, } + const expectedPublicKey = { + updateKey: caver.klay.accounts.privateKeyToPublicKey(key.updateKey), + } + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(key) + + expect(publicKey.transactionKey).to.be.undefined + expect(publicKey.updateKey).to.equals(expectedPublicKey.updateKey) + expect(publicKey.feePayerKey).to.be.undefined + }) + }) + + context('CAVERJS-UNIT-WALLET-172: input: object defines feePayerKey only', () => { + it('should return object of public key string', () => { + const key = { feePayerKey: caver.klay.accounts.create().privateKey, } + const expectedPublicKey = { + feePayerKey: caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey), + } + + const publicKey = caver.klay.accounts.accountKeyToPublicKey(key) + + expect(publicKey.transactionKey).to.be.undefined + expect(publicKey.updateKey).to.be.undefined + expect(publicKey.feePayerKey).to.equals(expectedPublicKey.feePayerKey) + }) + }) +}) + +describe('caver.klay.accounts.createWithAccountKey', () => { + context('CAVERJS-UNIT-WALLET-173: input: address and private key string', () => { + it('should return Account with AccountKeyPublic', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + + const account = caver.klay.accounts.createWithAccountKey(address, key) + + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key) + expect(account.accountKeyType).to.equals('AccountKeyPublic') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-174: input: address and array of private key string', () => { + it('should return Account with AccountKeyMultiSig', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + + const account = caver.klay.accounts.createWithAccountKey(address, key) + + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key[0]) + expect(account.accountKeyType).to.equals('AccountKeyMultiSig') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-175: input: address and object of private key', () => { + it('should return Account with AccountKeyRoleBased', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey] + } + + const account = caver.klay.accounts.createWithAccountKey(address, key) + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key.transactionKey) + expect(account.accountKeyType).to.equals('AccountKeyRoleBased') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-176: input: address and object defines only transactionKey', () => { + it('should return Account with AccountKeyRoleBased', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + } + + const account = caver.klay.accounts.createWithAccountKey(address, key) + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key.transactionKey) + expect(account.accountKeyType).to.equals('AccountKeyRoleBased') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-177: input: address and object defines only updateKey', () => { + it('should return Account with AccountKeyRoleBased', () => { + const address = caver.klay.accounts.create().address + const key = { + updateKey: caver.klay.accounts.create().privateKey, + } + + const account = caver.klay.accounts.createWithAccountKey(address, key) + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key.updateKey) + expect(account.accountKeyType).to.equals('AccountKeyRoleBased') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-178: input: address and object defines only feePayerKey', () => { + it('should return Account with AccountKeyRoleBased', () => { + const address = caver.klay.accounts.create().address + const key = { + feePayerKey: caver.klay.accounts.create().privateKey, + } + + const account = caver.klay.accounts.createWithAccountKey(address, key) + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key.feePayerKey) + expect(account.accountKeyType).to.equals('AccountKeyRoleBased') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) +}) + +describe('caver.klay.accounts.createWithAccountKeyPublic', () => { + context('CAVERJS-UNIT-WALLET-179: input: address and private key string', () => { + it('should return Account with AccountKeyPublic', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + + const account = caver.klay.accounts.createWithAccountKeyPublic(address, key) + + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key) + expect(account.accountKeyType).to.equals('AccountKeyPublic') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-180: input: address and AccountKeyPublic', () => { + it('should return Account with AccountKeyPublic', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + const accountKey = caver.klay.accounts.createAccountKey(key) + + const account = caver.klay.accounts.createWithAccountKeyPublic(address, accountKey) + + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key) + expect(account.accountKeyType).to.equals('AccountKeyPublic') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-181: input: address and array of private key string', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + + const expectedError = 'Creating a AccountKeyPublic requires a private key string.' + + expect(() => caver.klay.accounts.createWithAccountKeyPublic(address, key)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-182: input: address and object', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = {transactionKey: caver.klay.accounts.create().privateKey} + + const expectedError = 'Creating a AccountKeyPublic requires a private key string.' + + expect(() => caver.klay.accounts.createWithAccountKeyPublic(address, key)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-183: input: address and AccountKeyMultiSig', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const accountKey = caver.klay.accounts.createAccountKey(key) + + const expectedError = `Failed to create account with AccountKeyPublic. Invalid account key : ${accountKey.type}` + + expect(() => caver.klay.accounts.createWithAccountKeyPublic(address, accountKey)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-184: input: address and AccountKeyRoleBased', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = {transactionKey: caver.klay.accounts.create().privateKey} + const accountKey = caver.klay.accounts.createAccountKey(key) + + const expectedError = `Failed to create account with AccountKeyPublic. Invalid account key : ${accountKey.type}` + + expect(() => caver.klay.accounts.createWithAccountKeyPublic(address, accountKey)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createWithAccountKeyMultiSig', () => { + context('CAVERJS-UNIT-WALLET-185: input: address and array of private key string', () => { + it('should return Account with AccountKeyMultiSig', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey] + + const account = caver.klay.accounts.createWithAccountKeyMultiSig(address, key) + + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key[0]) + expect(account.accountKeyType).to.equals('AccountKeyMultiSig') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-186: input: address and AccountKeyMultiSig', () => { + it('should return Account with AccountKeyMultiSig', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey] + const accountKey = caver.klay.accounts.createAccountKey(key) + + const account = caver.klay.accounts.createWithAccountKeyMultiSig(address, accountKey) + + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key[0]) + expect(account.accountKeyType).to.equals('AccountKeyMultiSig') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-187: input: address and private key string', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + + const expectedError = 'Creating a AccountKeyMultiSig requires an array of private key string.' + + expect(() => caver.klay.accounts.createWithAccountKeyMultiSig(address, key)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-188: input: address and object', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = {transactionKey: caver.klay.accounts.create().privateKey} + + const expectedError = 'Creating a AccountKeyMultiSig requires an array of private key string.' + + expect(() => caver.klay.accounts.createWithAccountKeyMultiSig(address, key)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-189: input: address and AccountKeyPublic', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + const accountKey = caver.klay.accounts.createAccountKey(key) + + const expectedError = `Failed to create account with AccountKeyMultiSig. Invalid account key : ${accountKey.type}` + + expect(() => caver.klay.accounts.createWithAccountKeyMultiSig(address, accountKey)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-190: input: address and AccountKeyRoleBased', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = {transactionKey: caver.klay.accounts.create().privateKey} + const accountKey = caver.klay.accounts.createAccountKey(key) + + const expectedError = `Failed to create account with AccountKeyMultiSig. Invalid account key : ${accountKey.type}` + + expect(() => caver.klay.accounts.createWithAccountKeyMultiSig(address, accountKey)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createWithAccountKeyRoleBased', () => { + context('CAVERJS-UNIT-WALLET-191: input: address and object of key', () => { + it('should return Account with AccountKeyRoleBased', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey + } + + const account = caver.klay.accounts.createWithAccountKeyRoleBased(address, key) + + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key.transactionKey) + expect(account.accountKeyType).to.equals('AccountKeyRoleBased') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-192: input: address and AccountKeyRoleBased', () => { + it('should return Account with AccountKeyRoleBased', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey + } + const accountKey = caver.klay.accounts.createAccountKey(key) + + const account = caver.klay.accounts.createWithAccountKeyRoleBased(address, accountKey) + + isAccount(account, {keys: key, address}) + expect(account.privateKey).to.equals(key.transactionKey) + expect(account.accountKeyType).to.equals('AccountKeyRoleBased') + expect(typeof account.toPublicKey).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-193: input: address and private key string', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + + const expectedError = 'Creating a AccountKeyRoleBased requires an object.' + + expect(() => caver.klay.accounts.createWithAccountKeyRoleBased(address, key)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-194: input: address and array of private key string', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey] + + const expectedError = 'Creating a AccountKeyRoleBased requires an object.' + + expect(() => caver.klay.accounts.createWithAccountKeyRoleBased(address, key)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-195: input: address and AccountKeyPublic', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + const accountKey = caver.klay.accounts.createAccountKey(key) + + const expectedError = `Failed to create account with AccountKeyRoleBased. Invalid account key : ${accountKey.type}` + + expect(() => caver.klay.accounts.createWithAccountKeyRoleBased(address, accountKey)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-196: input: address and AccountKeyMultiSig', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const accountKey = caver.klay.accounts.createAccountKey(key) + + const expectedError = `Failed to create account with AccountKeyRoleBased. Invalid account key : ${accountKey.type}` + + expect(() => caver.klay.accounts.createWithAccountKeyRoleBased(address, accountKey)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createAccountForUpdate', () => { + context('CAVERJS-UNIT-WALLET-197: input: address and private key string', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + const publicKey = caver.klay.accounts.privateKeyToPublicKey(key) + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, key) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.publicKey).to.equals(publicKey) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-198: input: address and AccountKeyPublic', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + const accountKey = caver.klay.accounts.createAccountKeyPublic(key) + const publicKey = caver.klay.accounts.privateKeyToPublicKey(key) + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, accountKey) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.publicKey).to.equals(publicKey) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-199: input: address and array of private key string', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const options = { threshold: 2, weight: [1,1] } + const publicKey = [caver.klay.accounts.privateKeyToPublicKey(key[0]), caver.klay.accounts.privateKeyToPublicKey(key[1])] + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, key, options) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.multisig.threshold).to.equals(options.threshold) + expect(accountForUpdate.keyForUpdate.multisig.keys[0].weight).to.equals(options.weight[0]) + expect(accountForUpdate.keyForUpdate.multisig.keys[0].publicKey).to.equals(publicKey[0]) + expect(accountForUpdate.keyForUpdate.multisig.keys[1].weight).to.equals(options.weight[1]) + expect(accountForUpdate.keyForUpdate.multisig.keys[1].publicKey).to.equals(publicKey[1]) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-200: input: address and AccountKeyMultiSig', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const accountKey = caver.klay.accounts.createAccountKeyMultiSig(key) + const options = { threshold: 2, weight: [1,1] } + const publicKey = [caver.klay.accounts.privateKeyToPublicKey(key[0]), caver.klay.accounts.privateKeyToPublicKey(key[1])] + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, accountKey, options) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.multisig.threshold).to.equals(options.threshold) + expect(accountForUpdate.keyForUpdate.multisig.keys[0].weight).to.equals(options.weight[0]) + expect(accountForUpdate.keyForUpdate.multisig.keys[0].publicKey).to.equals(publicKey[0]) + expect(accountForUpdate.keyForUpdate.multisig.keys[1].weight).to.equals(options.weight[1]) + expect(accountForUpdate.keyForUpdate.multisig.keys[1].publicKey).to.equals(publicKey[1]) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-201: input: address and object', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey, + } + const options = { updateKey: { threshold: 2, weight: [1,1] } } + const publicKey = { + transactionKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey), + updateKey: [caver.klay.accounts.privateKeyToPublicKey(key.updateKey[0]), caver.klay.accounts.privateKeyToPublicKey(key.updateKey[1])], + feePayerKey: caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey) + } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, key, options) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey.publicKey).to.equals(publicKey.transactionKey) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.threshold).to.equals(options.updateKey.threshold) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[0].weight).to.equals(options.updateKey.weight[0]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[0].publicKey).to.equals(publicKey.updateKey[0]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[1].weight).to.equals(options.updateKey.weight[1]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[1].publicKey).to.equals(publicKey.updateKey[1]) + expect(accountForUpdate.keyForUpdate.roleFeePayerKey.publicKey).to.equals(publicKey.feePayerKey) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-202: input: address and AccountKeyRoleBased', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey, + } + const accountKey = caver.klay.accounts.createAccountKeyRoleBased(key) + const options = { updateKey: { threshold: 2, weight: [1,1] } } + const publicKey = { + transactionKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey), + updateKey: [caver.klay.accounts.privateKeyToPublicKey(key.updateKey[0]), caver.klay.accounts.privateKeyToPublicKey(key.updateKey[1])], + feePayerKey: caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey) + } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, accountKey, options) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey.publicKey).to.equals(publicKey.transactionKey) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.threshold).to.equals(options.updateKey.threshold) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[0].weight).to.equals(options.updateKey.weight[0]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[0].publicKey).to.equals(publicKey.updateKey[0]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[1].weight).to.equals(options.updateKey.weight[1]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[1].publicKey).to.equals(publicKey.updateKey[1]) + expect(accountForUpdate.keyForUpdate.roleFeePayerKey.publicKey).to.equals(publicKey.feePayerKey) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-203: input: address and object defines transactionKey only', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { transactionKey: caver.klay.accounts.create().privateKey } + const publicKey = { transactionKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey) } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, key) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey.publicKey).to.equals(publicKey.transactionKey) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleFeePayerKey).to.be.undefined + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-204: input: address and AccountKeyRoleBased defines transactionKey only', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { transactionKey: caver.klay.accounts.create().privateKey } + const accountKey = caver.klay.accounts.createAccountKeyRoleBased(key) + const publicKey = { transactionKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey) } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, accountKey) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey.publicKey).to.equals(publicKey.transactionKey) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleFeePayerKey).to.be.undefined + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-205: input: address and object defines updateKey only', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { updateKey: caver.klay.accounts.create().privateKey } + const publicKey = { updateKey: caver.klay.accounts.privateKeyToPublicKey(key.updateKey) } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, key) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.publicKey).to.equals(publicKey.updateKey) + expect(accountForUpdate.keyForUpdate.roleFeePayerKey).to.be.undefined + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-206: input: address and AccountKeyRoleBased defines updateKey only', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { updateKey: caver.klay.accounts.create().privateKey } + const accountKey = caver.klay.accounts.createAccountKeyRoleBased(key) + const publicKey = { updateKey: caver.klay.accounts.privateKeyToPublicKey(key.updateKey) } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, accountKey) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.publicKey).to.equals(publicKey.updateKey) + expect(accountForUpdate.keyForUpdate.roleFeePayerKey).to.be.undefined + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) - let result = caver.klay.accounts.encrypt(account.getKlaytnWalletKey(), password, {address: account.address}) + context('CAVERJS-UNIT-WALLET-207: input: address and object defines feePayerKey only', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { feePayerKey: caver.klay.accounts.create().privateKey } + const publicKey = { feePayerKey: caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey) } - isKeystoreV3(result, account) + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, key) - const decryptedAccount = caver.klay.accounts.decrypt(result, password) - isAccount(decryptedAccount, account) + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleFeePayerKey.publicKey).to.equals(publicKey.feePayerKey) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') }) }) - context('CAVERJS-UNIT-WALLET-098 : input: privateKey:KlaytnWalletKey, password, {address:invalid}', () => { - it('should throw an error', () => { - const password = 'klaytn!@' + context('CAVERJS-UNIT-WALLET-208: input: address and AccountKeyRoleBased defines feePayerKey only', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { feePayerKey: caver.klay.accounts.create().privateKey } + const accountKey = caver.klay.accounts.createAccountKeyRoleBased(key) + const publicKey = { feePayerKey: caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey) } - const errorMessage = 'The address extracted from the private key does not match the address received as the input value.' - expect(() => caver.klay.accounts.encrypt(account.getKlaytnWalletKey(), password, {address: caver.klay.accounts.create().address})).to.throw(errorMessage) + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, accountKey) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleFeePayerKey.publicKey).to.equals(publicKey.feePayerKey) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') }) }) - context('CAVERJS-UNIT-WALLET-099 : input: privateKey:KlaytnWalletKey(decoupled), password', () => { - it('should encrypt password with privateKey', () => { - const password = 'klaytn!@' - caver.klay.accounts.wallet.add(account) - var updatedAccount = caver.klay.accounts.wallet.updatePrivateKey(caver.klay.accounts.create().privateKey, account.address) - let result = caver.klay.accounts.encrypt(updatedAccount.getKlaytnWalletKey(), password) + context('CAVERJS-UNIT-WALLET-209: input: address and array of private key without options', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] - isKeystoreV3(result, updatedAccount) + const expectedError = 'For AccountKeyMultiSig, threshold and weight should be defined in options object.' - const decryptedAccount = caver.klay.accounts.decrypt(result, password) - isAccount(decryptedAccount, updatedAccount) + expect(() => caver.klay.accounts.createAccountForUpdate(address, key)).to.throws(expectedError) }) }) - context('CAVERJS-UNIT-WALLET-100 : input: privateKey:KlaytnWalletKey(decoupled), password, {address:valid}', () => { - it('should encrypt password with privateKey', () => { - const password = 'klaytn!@' - caver.klay.accounts.wallet.add(account) - var updatedAccount = caver.klay.accounts.wallet.updatePrivateKey(caver.klay.accounts.create().privateKey, account.address) - let result = caver.klay.accounts.encrypt(updatedAccount.getKlaytnWalletKey(), password, {address: updatedAccount.address}) + context('CAVERJS-UNIT-WALLET-210: input: address and array of private key with invalid options(weight sum < threshold)', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const options = { threshold: 4, weight: [1,1] } - isKeystoreV3(result, updatedAccount) + const expectedError = 'Invalid options for AccountKeyMultiSig: The sum of weights is less than the threshold.' - const decryptedAccount = caver.klay.accounts.decrypt(result, password) - isAccount(decryptedAccount, updatedAccount) + expect(() => caver.klay.accounts.createAccountForUpdate(address, key, options)).to.throws(expectedError) }) }) - context('CAVERJS-UNIT-WALLET-101 : input: privateKey:KlaytnWalletKey(decoupled), password, {address:invalid}', () => { - it('should encrypt password with privateKey', () => { - const password = 'klaytn!@' - caver.klay.accounts.wallet.add(account) - var updatedAccount = caver.klay.accounts.wallet.updatePrivateKey(caver.klay.accounts.create().privateKey, account.address) + context('CAVERJS-UNIT-WALLET-211: input: address and array of private key with invalid options(weight is not array)', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const options = { threshold: 4, weight: 1 } - const errorMessage = 'The address extracted from the private key does not match the address received as the input value.' - expect(() => caver.klay.accounts.encrypt(updatedAccount.getKlaytnWalletKey(), password, {address: caver.klay.accounts.create().address})).to.throw(errorMessage) + const expectedError = 'The weight should be defined as a array.' + + expect(() => caver.klay.accounts.createAccountForUpdate(address, key, options)).to.throws(expectedError) }) }) -}) -describe('caver.klay.accounts.decrypt', () => { - let account + context('CAVERJS-UNIT-WALLET-212: input: address and array of private key with invalid options(weight length is not matched with key array)', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const options = { threshold: 2, weight: [1,1,1,1] } - beforeEach(() => { - account = caver.klay.accounts.create() + const expectedError = 'The length of keys in AccountKeyMultiSig and the length of weight array do not match.' + + expect(() => caver.klay.accounts.createAccountForUpdate(address, key, options)).to.throws(expectedError) + }) }) - context('CAVERJS-UNIT-WALLET-042 : input: keystoreJsonV3, password', () => { - it('After decrypting, should return valid account', () => { - const password = 'klaytn!@' - const keystoreJsonV3 = caver.klay.accounts.encrypt(account.privateKey, password) + context('CAVERJS-UNIT-WALLET-213: input: address and object has multisig without options', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = { transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } - let result = caver.klay.accounts.decrypt(keystoreJsonV3, password) - isKeystoreV3(keystoreJsonV3, result) + const expectedError = 'For AccountKeyMultiSig, threshold and weight should be defined in options object.' - isAccount(result, account) + expect(() => caver.klay.accounts.createAccountForUpdate(address, key)).to.throws(expectedError) }) }) - context('CAVERJS-UNIT-WALLET-103 : input: keystoreJsonV3(without 0x address format), password', () => { - it('After decrypting, should return valid account', () => { - const password = 'klaytn!@' - const keystoreJsonV3 = caver.klay.accounts.encrypt(account.privateKey, password) - keystoreJsonV3.address = keystoreJsonV3.address.replace('0x', '') + context('CAVERJS-UNIT-WALLET-214: input: address and array of private key with invalid options(weight sum < threshold)', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = { transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + const options = { transactionKey: { threshold: 4, weight: [1,1] } } + + const expectedError = 'Invalid options for AccountKeyMultiSig: The sum of weights is less than the threshold.' + + expect(() => caver.klay.accounts.createAccountForUpdate(address, key, options)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-215: input: address and array of private key with invalid options(weight is not array)', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = { transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + const options = { transactionKey: { threshold: 4, weight: 1 } } + + const expectedError = 'The weight should be defined as a array.' + + expect(() => caver.klay.accounts.createAccountForUpdate(address, key, options)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-216: input: address and array of private key with invalid options(weight length is not matched with key array)', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = { transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + const options = { transactionKey: { threshold: 2, weight: [1,1,1,1] } } + + const expectedError = 'The length of keys in AccountKeyMultiSig and the length of weight array do not match.' + + expect(() => caver.klay.accounts.createAccountForUpdate(address, key, options)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-348: input: address and key object which has legacyKey and failKey', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { transactionKey: 'legacyKey', feePayerKey: 'failKey' } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, key) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey.legacyKey).to.be.true + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey).to.be.undefined + expect(accountForUpdate.keyForUpdate.roleFeePayerKey.failKey).to.be.true + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-349: input: address and key object which has transactionKey(legacyKey), updateKey(multiSigKey) and feePayerKey(failKey)', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const updateKey = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const updatePublicKey = [caver.klay.accounts.privateKeyToPublicKey(updateKey[0]), caver.klay.accounts.privateKeyToPublicKey(updateKey[1])] + const key = { transactionKey: 'legacyKey', updateKey: updateKey ,feePayerKey: 'failKey' } + const options = { updateKey: { threshold: 2, weight: [1, 1] } } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdate(address, key, options) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey.legacyKey).to.be.true + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.threshold).to.equals(options.updateKey.threshold) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys.length).to.equals(updateKey.length) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[0].publicKey).to.equals(updatePublicKey[0]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[1].publicKey).to.equals(updatePublicKey[1]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[0].weight).to.equals(options.updateKey.weight[0]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[1].weight).to.equals(options.updateKey.weight[1]) + expect(accountForUpdate.keyForUpdate.roleFeePayerKey.failKey).to.be.true + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-350: input: address and key object which has legacyKey and failKey with options', () => { + it('should throw error', () => { + const address = caver.klay.accounts.create().address + const key = { transactionKey: 'legacyKey', feePayerKey: 'failKey' } + const options = { transactionKey: { threshold: 2, weight: [1,1,1,1] } } + + const expectedError = 'Failed to keyFormatter for AccountForUpdate: AccountKeyPublic/legacyKey/failKey cannot have options' + + expect(() => caver.klay.accounts.createAccountForUpdate(address, key, options)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createAccountForUpdateWithPublicKey', () => { + context('CAVERJS-UNIT-WALLET-217: input: address and public key string', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + const publicKey = caver.klay.accounts.privateKeyToPublicKey(key) + + const accountForUpdate = caver.klay.accounts.createAccountForUpdateWithPublicKey(address, publicKey) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.publicKey).to.equals(publicKey) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-218: input: address and array of public key string', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const options = { threshold: 2, weight: [1,1] } + const publicKey = [caver.klay.accounts.privateKeyToPublicKey(key[0]), caver.klay.accounts.privateKeyToPublicKey(key[1])] + + const accountForUpdate = caver.klay.accounts.createAccountForUpdateWithPublicKey(address, publicKey, options) - let result = caver.klay.accounts.decrypt(keystoreJsonV3, password) + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.multisig.threshold).to.equals(options.threshold) + expect(accountForUpdate.keyForUpdate.multisig.keys[0].weight).to.equals(options.weight[0]) + expect(accountForUpdate.keyForUpdate.multisig.keys[0].publicKey).to.equals(publicKey[0]) + expect(accountForUpdate.keyForUpdate.multisig.keys[1].weight).to.equals(options.weight[1]) + expect(accountForUpdate.keyForUpdate.multisig.keys[1].publicKey).to.equals(publicKey[1]) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-219: input: address and object of public key', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey, + } + + const options = { updateKey: { threshold: 2, weight: [1,1] } } + const publicKey = { + transactionKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey), + updateKey: [caver.klay.accounts.privateKeyToPublicKey(key.updateKey[0]), caver.klay.accounts.privateKeyToPublicKey(key.updateKey[1])], + feePayerKey: caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey) + } + + const accountForUpdate = caver.klay.accounts.createAccountForUpdateWithPublicKey(address, publicKey, options) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.roleTransactionKey.publicKey).to.equals(publicKey.transactionKey) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.threshold).to.equals(options.updateKey.threshold) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[0].weight).to.equals(options.updateKey.weight[0]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[0].publicKey).to.equals(publicKey.updateKey[0]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[1].weight).to.equals(options.updateKey.weight[1]) + expect(accountForUpdate.keyForUpdate.roleAccountUpdateKey.multisig.keys[1].publicKey).to.equals(publicKey.updateKey[1]) + expect(accountForUpdate.keyForUpdate.roleFeePayerKey.publicKey).to.equals(publicKey.feePayerKey) + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) + + context('CAVERJS-UNIT-WALLET-220: input: address and object of public key with invalid role', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey, + } - expect(result.address.slice(0, 2)).to.equals('0x') + const options = { updateKey: { threshold: 2, weight: [1,1] } } + const publicKey = { + invalidRoleKey: caver.klay.accounts.privateKeyToPublicKey(key.transactionKey), + updateKey: [caver.klay.accounts.privateKeyToPublicKey(key.updateKey[0]), caver.klay.accounts.privateKeyToPublicKey(key.updateKey[1])], + feePayerKey: caver.klay.accounts.privateKeyToPublicKey(key.feePayerKey) + } + + const expectedError = `Invalid role is defined: invalidRoleKey` + + expect(() => caver.klay.accounts.createAccountForUpdateWithPublicKey(address, publicKey, options)).to.throws(expectedError) }) }) - /* - it('keystoreJsonV3, password:invalid [KLAYTN-52]', () => { - const invalid = '' - const keystoreJsonV3 = caver.klay.accounts.encrypt(account.privateKey, invalid) + context('CAVERJS-UNIT-WALLET-221: input: address and array of public key string without options', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const publicKey = [caver.klay.accounts.privateKeyToPublicKey(key[0]), caver.klay.accounts.privateKeyToPublicKey(key[1])] - utils.log('input', keystoreJsonV3, invalid) + const expectedError = 'For AccountKeyMultiSig, threshold and weight should be defined in options object.' - const expectedError = { - name: 'Error', - message: '' - } - validateErrorCodeblock(() => caver.klay.accounts.decrypt(keystoreJsonV3, invalid), expectedError) + expect(() => caver.klay.accounts.createAccountForUpdateWithPublicKey(address, publicKey)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-222: input: address and object of public key string without options', () => { + it('should return AccountForUpdate', () => { + const address = caver.klay.accounts.create().address + const key = { updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] } + const publicKey = [caver.klay.accounts.privateKeyToPublicKey(key.updateKey[0]), caver.klay.accounts.privateKeyToPublicKey(key.updateKey[1])] + + const expectedError = 'For AccountKeyMultiSig, threshold and weight should be defined in options object.' + + expect(() => caver.klay.accounts.createAccountForUpdateWithPublicKey(address, publicKey)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.createAccountForUpdateWithLegacyKey', () => { + context('CAVERJS-UNIT-WALLET-223: input: address', () => { + it('should return AccountForUpdate with legacy key setting', () => { + const address = caver.klay.accounts.create().address + + const accountForUpdate = caver.klay.accounts.createAccountForUpdateWithLegacyKey(address) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.legacyKey).to.be.true + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) + }) +}) + +describe('caver.klay.accounts.createAccountForUpdateWithFailKey', () => { + context('CAVERJS-UNIT-WALLET-224: input: address', () => { + it('should return AccountForUpdate with fail key setting', () => { + const address = caver.klay.accounts.create().address + + const accountForUpdate = caver.klay.accounts.createAccountForUpdateWithFailKey(address) + + expect(accountForUpdate.address).to.equals(address) + expect(accountForUpdate.keyForUpdate.failKey).to.be.true + expect(typeof accountForUpdate.fillUpdateObject).to.equals('function') + }) }) - */ }) describe('caver.klay.accounts.wallet', () => { @@ -651,14 +4487,11 @@ describe('caver.klay.accounts.wallet.create', () => { isWallet(result) expect(result.length).to.equal(numberOfAccounts) for (let i = 0; i < result.length; i++) { - const accountByIndex = Object.assign({}, result[i]) - const accountByAddress = Object.assign({}, result[accountByIndex.address]) + const accountByIndex = caver.klay.accounts.createWithAccountKey(result[i].address, result[i].accountKey) + const accountByAddress = caver.klay.accounts.createWithAccountKey(result[accountByIndex.address].address, result[accountByIndex.address].accountKey) - delete accountByIndex.index - delete accountByAddress.index - - isAccount(accountByIndex, { privateKey: accountByAddress.privateKey, address: accountByAddress.address }) - isAccount(accountByAddress, { privateKey: accountByIndex.privateKey, address: accountByIndex.address }) + isAccount(accountByIndex, { keys: accountByAddress.keys, address: accountByAddress.address }) + isAccount(accountByAddress, { keys: accountByIndex.keys, address: accountByIndex.address }) } } @@ -693,13 +4526,12 @@ describe('caver.klay.accounts.wallet.add', () => { const validateCheckForWalletAddition = (data, { account, wallet }) => { const accounts = [] - accounts.push(Object.assign({}, data)) - accounts.push(Object.assign({}, wallet[data.index])) - accounts.push(Object.assign({}, wallet[data.address])) + accounts.push(caver.klay.accounts.createWithAccountKey(data.address, data.accountKey)) + accounts.push(caver.klay.accounts.createWithAccountKey(wallet[data.index].address, wallet[data.index].accountKey)) + accounts.push(caver.klay.accounts.createWithAccountKey(wallet[data.address].address, wallet[data.address].accountKey)) for (v of accounts) { - delete v.index - isAccount(v, { privateKey: account.privateKey, address: account.address }) + isAccount(v, { keys: account.keys, address: account.address }) } } @@ -753,52 +4585,387 @@ describe('caver.klay.accounts.wallet.add', () => { validateCheckForWalletAddition(result, { account: account, wallet: caver.klay.accounts.wallet }) - account = caver.klay.accounts.create() - address = '0x6a61736d696e652e6b6c6179746e000000000000' - account.address = address - result = caver.klay.accounts.wallet.add(account.privateKey, address) + account = caver.klay.accounts.create() + address = '0x6a61736d696e652e6b6c6179746e000000000000' + account.address = address + result = caver.klay.accounts.wallet.add(account.privateKey, address) + + validateCheckForWalletAddition(result, { account: account, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-053 CAVERJS-UNIT-WALLET-054 : input: KlaytnWalletKey, address', () => { + it('should have valid wallet instance after addition', () => { + klaytnWalletKey = '0xc1ad21b3da99cbb6a57cf181ec3e36af77ae37112585f700c81db19115f74b110x000xc4f88823f5030e4343c1494b502d534e9f15152d' + account = caver.klay.accounts.privateKeyToAccount(klaytnWalletKey) + result = caver.klay.accounts.wallet.add(klaytnWalletKey, account.address) + + validateCheckForWalletAddition(result, { account: account, wallet: caver.klay.accounts.wallet }) + + // decoupled + klaytnWalletKey = '0xc1ad21b3da99cbb6a57cf181ec3e36af77ae37112585f700c81db19115f74b110x000x95e024d64534948a89748d4c3e82e02d05721beb' + account = caver.klay.accounts.privateKeyToAccount(klaytnWalletKey) + result = caver.klay.accounts.wallet.add(klaytnWalletKey, account.address) + + validateCheckForWalletAddition(result, { account: account, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-055 : input: KlaytnWalletKey, invalid address', () => { + it('should have valid wallet instance after addition', () => { + klaytnWalletKey = '0x2d21dc5d73e29177af17a1376f3e5769b94086479735e711670c2d599c3f97ab0x000x53b0e6ebd395093d83ac9b0e1e47b4b31b7c18c4' + account = caver.klay.accounts.privateKeyToAccount(klaytnWalletKey) + expect(()=> caver.klay.accounts.wallet.add(klaytnWalletKey, '0x95e024d64534948a89748d4c3e82e02d05721beb')).to.throw() + }) + }) + + context('CAVERJS-UNIT-WALLET-056 : input: invalid KlaytnWalletKey', () => { + it('should have valid wallet instance after addition', () => { + klaytnWalletKey = '0x39d87f15c695ec94d6d7107b48dee85e252f21fedd371e1c6badc4afa71efbdf0x010x167bcdef96658b7b7a94ac398a8e7275e719a10c' + expect(()=> caver.klay.accounts.wallet.add(klaytnWalletKey)).to.throw() + }) + }) + + context('CAVERJS-UNIT-WALLET-049 : input: account:invalid', () => { + it('should throw an error', () => { + const invalid = -1 + const errorMessage = 'Invalid accountKey type: number' + expect(() => caver.klay.accounts.wallet.add(invalid)).to.throw(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-244: input: userInputAddress and AccountKeyPublic', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const accountKeyPublic = caver.klay.accounts.createAccountKeyPublic(key) + const accountWithPublic = caver.klay.accounts.createWithAccountKey(address, accountKeyPublic) + + let accountInWallet = caver.klay.accounts.wallet.add(accountKeyPublic, address) + validateCheckForWalletAddition(accountInWallet, { account: accountWithPublic, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-245: input: userInputAddress and AccountKeyMultiSig', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const address = caver.klay.accounts.create().address + const accountKeyMultiSig = caver.klay.accounts.createAccountKeyMultiSig(key) + const accountWithMultiSig = caver.klay.accounts.createWithAccountKey(address, accountKeyMultiSig) + + let accountInWallet = caver.klay.accounts.wallet.add(accountKeyMultiSig, address) + validateCheckForWalletAddition(accountInWallet, { account: accountWithMultiSig, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-246: input: userInputAddress and AccountKeyRoleBased which defines transactionKey, updateKey and feePayerKey', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const accountKeyRoleBased = caver.klay.accounts.createAccountKeyRoleBased(key) + const accountWithRoleBased = caver.klay.accounts.createWithAccountKey(address, accountKeyRoleBased) + + let accountInWallet = caver.klay.accounts.wallet.add(accountKeyRoleBased, address) + validateCheckForWalletAddition(accountInWallet, { account: accountWithRoleBased, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-247: input: userInputAddress and AccountKeyRoleBased which defines transactionKey only', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const accountKeyRoleBased = caver.klay.accounts.createAccountKeyRoleBased(key) + const accountWithRoleBased = caver.klay.accounts.createWithAccountKey(address, accountKeyRoleBased) + + let accountInWallet = caver.klay.accounts.wallet.add(accountKeyRoleBased, address) + validateCheckForWalletAddition(accountInWallet, { account: accountWithRoleBased, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-248: input: userInputAddress and AccountKeyRoleBased which defines updateKey only', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = { + updateKey: caver.klay.accounts.create().privateKey + } + const address = caver.klay.accounts.create().address + const accountKeyRoleBased = caver.klay.accounts.createAccountKeyRoleBased(key) + const accountWithRoleBased = caver.klay.accounts.createWithAccountKey(address, accountKeyRoleBased) + + let accountInWallet = caver.klay.accounts.wallet.add(accountKeyRoleBased, address) + validateCheckForWalletAddition(accountInWallet, { account: accountWithRoleBased, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-249: input: userInputAddress and AccountKeyRoleBased which defines feePayerKey only', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = { + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const accountKeyRoleBased = caver.klay.accounts.createAccountKeyRoleBased(key) + const accountWithRoleBased = caver.klay.accounts.createWithAccountKey(address, accountKeyRoleBased) + + let accountInWallet = caver.klay.accounts.wallet.add(accountKeyRoleBased, address) + validateCheckForWalletAddition(accountInWallet, { account: accountWithRoleBased, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-250: input: AccountKeyPublic without userInputAddress', () => { + it('should throw error', () => { + const accountKey = caver.klay.accounts.createAccountKey(caver.klay.accounts.create().privateKey) + + const expectedError = `Address is not defined. Address cannot be determined from AccountKey` + expect(() => caver.klay.accounts.wallet.add(accountKey)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-251: input: AccountKeyMultiSig without userInputAddress', () => { + it('should throw error', () => { + const accountKey = caver.klay.accounts.createAccountKey([caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey]) + + const expectedError = `Address is not defined. Address cannot be determined from AccountKey` + expect(() => caver.klay.accounts.wallet.add(accountKey)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-252: input: AccountKeyRoleBased without userInputAddress', () => { + it('should throw error', () => { + const accountKey = caver.klay.accounts.createAccountKey({transactionKey: caver.klay.accounts.create().privateKey}) + + const expectedError = `Address is not defined. Address cannot be determined from AccountKey` + expect(() => caver.klay.accounts.wallet.add(accountKey)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-253: input: Account with AccountKeyPublic', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const accountWithPublic = caver.klay.accounts.createWithAccountKey(address, key) + + let accountInWallet = caver.klay.accounts.wallet.add(accountWithPublic) + validateCheckForWalletAddition(accountInWallet, { account: accountWithPublic, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-254: input: Account with AccountKeyMultiSig', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const address = caver.klay.accounts.create().address + const accountWithMultiSig = caver.klay.accounts.createWithAccountKey(address, key) + + let accountInWallet = caver.klay.accounts.wallet.add(accountWithMultiSig) + validateCheckForWalletAddition(accountInWallet, { account: accountWithMultiSig, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-255: input: Account with AccountKeyRoleBased which defines transactionKey, updateKey and feePayerKey', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const accountWithRoleBased = caver.klay.accounts.createWithAccountKey(address, key) + + let accountInWallet = caver.klay.accounts.wallet.add(accountWithRoleBased) + validateCheckForWalletAddition(accountInWallet, { account: accountWithRoleBased, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-256: input: Account with AccountKeyRoleBased which defines transactionKey only', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const accountWithRoleBased = caver.klay.accounts.createWithAccountKey(address, key) + + let accountInWallet = caver.klay.accounts.wallet.add(accountWithRoleBased) + validateCheckForWalletAddition(accountInWallet, { account: accountWithRoleBased, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-257: input: Account with AccountKeyRoleBased which defines updateKey only', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = { + updateKey: caver.klay.accounts.create().privateKey + } + const address = caver.klay.accounts.create().address + const accountWithRoleBased = caver.klay.accounts.createWithAccountKey(address, key) + + let accountInWallet = caver.klay.accounts.wallet.add(accountWithRoleBased) + validateCheckForWalletAddition(accountInWallet, { account: accountWithRoleBased, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-258: input: Account with AccountKeyRoleBased which defines feePayerKey only', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = { + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const accountWithRoleBased = caver.klay.accounts.createWithAccountKey(address, key) + + let accountInWallet = caver.klay.accounts.wallet.add(accountWithRoleBased) + validateCheckForWalletAddition(accountInWallet, { account: accountWithRoleBased, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-259: input: Account with AccountKeyPublic and userInputAddress', () => { + it('should have valid Account instance with userInputAddress as an address in wallet after addition', () => { + const key = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const accountWithPublic = caver.klay.accounts.createWithAccountKey(address, key) + const userInputAddrees = caver.klay.accounts.create().address + + let accountInWallet = caver.klay.accounts.wallet.add(accountWithPublic, userInputAddrees) + accountWithPublic.address = userInputAddrees + + validateCheckForWalletAddition(accountInWallet, { account: accountWithPublic, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-260: input: object which has address and privateKey', () => { + it('should have valid Account instance in wallet after addition', () => { + const key = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const accountWithPublic = caver.klay.accounts.createWithAccountKey(address, key) + + const inputObject = { + address: accountWithPublic.address, + privateKey: accountWithPublic.privateKey, + } + + let accountInWallet = caver.klay.accounts.wallet.add(inputObject) + + validateCheckForWalletAddition(accountInWallet, { account: accountWithPublic, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-261: input: userInputAddress and object which has address and privateKey', () => { + it('should have valid Account instance with userInputAddress as an address in wallet after addition', () => { + const key = caver.klay.accounts.create().privateKey + const address = caver.klay.accounts.create().address + const accountWithPublic = caver.klay.accounts.createWithAccountKey(address, key) + const userInputAddrees = caver.klay.accounts.create().address + + const inputObject = { + address: accountWithPublic.address, + privateKey: accountWithPublic.privateKey, + } + + let accountInWallet = caver.klay.accounts.wallet.add(inputObject, userInputAddrees) + accountWithPublic.address = userInputAddrees + + validateCheckForWalletAddition(accountInWallet, { account: accountWithPublic, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-262: input: userInputAddress and array of private key string', () => { + it('should have valid Account instance with userInputAddress as an address in wallet after addition', () => { + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const address = caver.klay.accounts.create().address + const account = caver.klay.accounts.createWithAccountKey(address, key) + + let accountInWallet = caver.klay.accounts.wallet.add(key, address) + + validateCheckForWalletAddition(accountInWallet, { account: account, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-263: input: userInputAddress and key object which defines transactionKey, updateKey and feePayerKey', () => { + it('should have valid Account instance with userInputAddress as an address in wallet after addition', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const account = caver.klay.accounts.createWithAccountKey(address, key) - validateCheckForWalletAddition(result, { account: account, wallet: caver.klay.accounts.wallet }) + let accountInWallet = caver.klay.accounts.wallet.add(key, address) + + validateCheckForWalletAddition(accountInWallet, { account: account, wallet: caver.klay.accounts.wallet }) }) }) - context('CAVERJS-UNIT-WALLET-053 CAVERJS-UNIT-WALLET-054 : input: KlaytnWalletKey, address', () => { - it('should have valid wallet instance after addition', () => { - klaytnWalletKey = '0xc1ad21b3da99cbb6a57cf181ec3e36af77ae37112585f700c81db19115f74b110x000xc4f88823f5030e4343c1494b502d534e9f15152d' - account = caver.klay.accounts.privateKeyToAccount(klaytnWalletKey) - result = caver.klay.accounts.wallet.add(klaytnWalletKey, account.address) + context('CAVERJS-UNIT-WALLET-264: input: userInputAddress and key object which defines transactionKey only', () => { + it('should have valid Account instance with userInputAddress as an address in wallet after addition', () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const account = caver.klay.accounts.createWithAccountKey(address, key) - validateCheckForWalletAddition(result, { account: account, wallet: caver.klay.accounts.wallet }) + let accountInWallet = caver.klay.accounts.wallet.add(key, address) + + validateCheckForWalletAddition(accountInWallet, { account: account, wallet: caver.klay.accounts.wallet }) + }) + }) - // decoupled - klaytnWalletKey = '0xc1ad21b3da99cbb6a57cf181ec3e36af77ae37112585f700c81db19115f74b110x000x95e024d64534948a89748d4c3e82e02d05721beb' - account = caver.klay.accounts.privateKeyToAccount(klaytnWalletKey) - result = caver.klay.accounts.wallet.add(klaytnWalletKey, account.address) + context('CAVERJS-UNIT-WALLET-265: input: userInputAddress and key object which defines updateKey only', () => { + it('should have valid Account instance with userInputAddress as an address in wallet after addition', () => { + const key = { + updateKey: caver.klay.accounts.create().privateKey + } + const address = caver.klay.accounts.create().address + const account = caver.klay.accounts.createWithAccountKey(address, key) - validateCheckForWalletAddition(result, { account: account, wallet: caver.klay.accounts.wallet }) + let accountInWallet = caver.klay.accounts.wallet.add(key, address) + + validateCheckForWalletAddition(accountInWallet, { account: account, wallet: caver.klay.accounts.wallet }) }) }) - context('CAVERJS-UNIT-WALLET-055 : input: KlaytnWalletKey, invalid address', () => { - it('should have valid wallet instance after addition', () => { - klaytnWalletKey = '0x2d21dc5d73e29177af17a1376f3e5769b94086479735e711670c2d599c3f97ab0x000x53b0e6ebd395093d83ac9b0e1e47b4b31b7c18c4' - account = caver.klay.accounts.privateKeyToAccount(klaytnWalletKey) - expect(()=> caver.klay.accounts.wallet.add(klaytnWalletKey, '0x95e024d64534948a89748d4c3e82e02d05721beb')).to.throw() + context('CAVERJS-UNIT-WALLET-266: input: userInputAddress and key object which defines feePayerKey only', () => { + it('should have valid Account instance with userInputAddress as an address in wallet after addition', () => { + const key = { + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const address = caver.klay.accounts.create().address + const account = caver.klay.accounts.createWithAccountKey(address, key) + + let accountInWallet = caver.klay.accounts.wallet.add(key, address) + + validateCheckForWalletAddition(accountInWallet, { account: account, wallet: caver.klay.accounts.wallet }) }) }) - context('CAVERJS-UNIT-WALLET-056 : input: invalid KlaytnWalletKey', () => { - it('should have valid wallet instance after addition', () => { - klaytnWalletKey = '0x39d87f15c695ec94d6d7107b48dee85e252f21fedd371e1c6badc4afa71efbdf0x010x167bcdef96658b7b7a94ac398a8e7275e719a10c' - expect(()=> caver.klay.accounts.wallet.add(klaytnWalletKey)).to.throw() + context('CAVERJS-UNIT-WALLET-267: input: invalid type parameter', () => { + it('should throw error', () => { + let invalid = {} + + let expectedError = `Failed to create AccountKeyRoleBased: empty object` + expect(() => caver.klay.accounts.wallet.add(invalid)).to.throws(expectedError) + + invalid = undefined + expectedError = `Invalid accountKey type: ${typeof invalid}` + expect(() => caver.klay.accounts.wallet.add(invalid)).to.throws(expectedError) + + invalid = null + expectedError = `Invalid accountKey type: ${typeof invalid}` + expect(() => caver.klay.accounts.wallet.add(invalid)).to.throws(expectedError) + + invalid = 1 + expectedError = `Invalid accountKey type: ${typeof invalid}` + expect(() => caver.klay.accounts.wallet.add(invalid)).to.throws(expectedError) }) }) - context('CAVERJS-UNIT-WALLET-049 : input: account:invalid', () => { - it('should throw an error', () => { - const invalid = -1 - const errorMessage = 'Invalid private key' - expect(() => caver.klay.accounts.wallet.add(invalid)).to.throw(errorMessage) + context('CAVERJS-UNIT-WALLET-268: input: invalid role defined', () => { + it('should throw error', () => { + const invalid = {invalidRole: caver.klay.accounts.create().privateKey} + + const expectedError = `Failed to create AccountKeyRoleBased. Invalid role is defined : invalidRole` + expect(() => caver.klay.accounts.wallet.add(invalid)).to.throws(expectedError) }) }) }) @@ -845,6 +5012,73 @@ describe('caver.klay.accounts.wallet.remove', () => { validateCheckForWalletRemove(result, { expected: false }) }) }) + + context('CAVERJS-UNIT-WALLET-269: input: Account with AccountKeyPublic', () => { + it('should remove wallet instance', () => { + const address = caver.klay.accounts.create().address + const key = caver.klay.accounts.create().privateKey + const account = caver.klay.accounts.createWithAccountKey(address, key) + let accountInWallet = caver.klay.accounts.wallet.add(account) + + let result = caver.klay.accounts.wallet.remove(accountInWallet.index) + + expect(accountInWallet.accountKey).to.be.null + validateCheckForWalletRemove(result, { account: accountInWallet, wallet: caver.klay.accounts.wallet }) + + accountInWallet = caver.klay.accounts.wallet.add(account) + + result = caver.klay.accounts.wallet.remove(accountInWallet.address) + + expect(accountInWallet.accountKey).to.be.null + validateCheckForWalletRemove(result, { account: accountInWallet, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-270: input: Account with AccountKeyMultiSig', () => { + it('should remove wallet instance', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const account = caver.klay.accounts.createWithAccountKey(address, key) + let accountInWallet = caver.klay.accounts.wallet.add(account) + + let result = caver.klay.accounts.wallet.remove(accountInWallet.index) + + expect(accountInWallet.accountKey).to.be.null + validateCheckForWalletRemove(result, { account: accountInWallet, wallet: caver.klay.accounts.wallet }) + + accountInWallet = caver.klay.accounts.wallet.add(account) + + result = caver.klay.accounts.wallet.remove(accountInWallet.address) + + expect(accountInWallet.accountKey).to.be.null + validateCheckForWalletRemove(result, { account: accountInWallet, wallet: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-271: input: Account with AccountKeyRoleBased', () => { + it('should remove wallet instance', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: caver.klay.accounts.create().privateKey + } + const account = caver.klay.accounts.createWithAccountKey(address, key) + let accountInWallet = caver.klay.accounts.wallet.add(account) + + let result = caver.klay.accounts.wallet.remove(accountInWallet.index) + + expect(accountInWallet.accountKey).to.be.null + validateCheckForWalletRemove(result, { account: accountInWallet, wallet: caver.klay.accounts.wallet }) + + accountInWallet = caver.klay.accounts.wallet.add(account) + + result = caver.klay.accounts.wallet.remove(accountInWallet.address) + + expect(accountInWallet.accountKey).to.be.null + validateCheckForWalletRemove(result, { account: accountInWallet, wallet: caver.klay.accounts.wallet }) + }) + }) }) describe('caver.klay.accounts.wallet.clear', () => { @@ -869,7 +5103,7 @@ describe('caver.klay.accounts.wallet.clear', () => { describe('caver.klay.accounts.wallet.encrypt', () => { context('CAVERJS-UNIT-WALLET-062 : input: password', () => { - it('should encrypted as v3Keystore', () => { + it('should encrypted as v4Keystore', () => { const password = 'klaytn!@' const numberOfAccounts = Math.floor(Math.random() * 5) + 1 @@ -879,13 +5113,44 @@ describe('caver.klay.accounts.wallet.encrypt', () => { expect(result.length).to.equal(caver.klay.accounts.wallet.length) result.forEach((v, i) => { - isKeystoreV3(v, { address: caver.klay.accounts.wallet[i].address }) + isKeystoreV4(v, { address: caver.klay.accounts.wallet[i].address }) + }) + const decryptedWallet = caver.klay.accounts.wallet.decrypt(result, password) + isWallet(decryptedWallet, { accounts: caver.klay.accounts.wallet }) + }) + }) + + context('CAVERJS-UNIT-WALLET-272: input: password', () => { + it('should throw error if there is Account with AccountKeyMultiSig or AccountKeyRoleBased', () => { + const password = 'klaytn!@' + + // AccountKeyMultiSig + let address = caver.klay.accounts.create().address + let key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey(address, key)) + + // AccountKeyRoleBased + let roleBasedaddress = caver.klay.accounts.create().address + let roleBasedkey = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: caver.klay.accounts.create().privateKey + } + caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey(roleBasedaddress, roleBasedkey)) + + let result = caver.klay.accounts.wallet.encrypt(password) + + expect(result.length).to.equal(caver.klay.accounts.wallet.length) + result.forEach((v, i) => { + isKeystoreV4(v, { address: caver.klay.accounts.wallet[i].address }) }) const decryptedWallet = caver.klay.accounts.wallet.decrypt(result, password) isWallet(decryptedWallet, { accounts: caver.klay.accounts.wallet }) + }) }) + /* it('password:invalid [KLAYTN-52]', () => { const invalid = '' @@ -907,12 +5172,26 @@ describe('caver.klay.accounts.wallet.encrypt', () => { describe('caver.klay.accounts.wallet.decrypt', () => { context('CAVERJS-UNIT-WALLET-063 : input: keystoreArray, password', () => { - it('should decrypt v3Keystore to account instance', () => { + it('should decrypt v4Keystore to account instance', () => { const password = 'klaytn!@' const numberOfAccounts = Math.floor(Math.random() * 5) + 1 caver.klay.accounts.wallet.create(numberOfAccounts) + // AccountKeyMultiSig + let address = caver.klay.accounts.create().address + let key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey(address, key)) + + // AccountKeyRoleBased + let roleBasedaddress = caver.klay.accounts.create().address + let roleBasedkey = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: caver.klay.accounts.create().privateKey + } + caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey(roleBasedaddress, roleBasedkey)) + const encryptedKeystore = caver.klay.accounts.wallet.encrypt(password) caver.klay.accounts.wallet.clear() @@ -940,3 +5219,426 @@ describe('caver.klay.accounts.wallet.decrypt', () => { }) */ }) + +describe('caver.klay.accounts.wallet.getAccount', () => { + context('CAVERJS-UNIT-WALLET-125 : input: number', () => { + it('should return Account from Wallet', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + + const fromWallet = caver.klay.accounts.wallet.getAccount(testAccount.index) + + expect(testAccount.address).to.equals(fromWallet.address) + expect(testAccount.privateKey).to.equals(fromWallet.privateKey) + expect(testAccount.index).to.equals(fromWallet.index) + }) + }) + + context('CAVERJS-UNIT-WALLET-126 : input: invalid number index', () => { + it('should throw Error', () => { + const errorMessage = `The index(${caver.klay.accounts.wallet.length}) is out of range(Wallet length : ${caver.klay.accounts.wallet.length}).` + + expect(() => caver.klay.accounts.wallet.getAccount(caver.klay.accounts.wallet.length)).to.throws(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-127 : input: invalid parameter type for getAccount', () => { + it('should throw Error', () => { + const errorMessage = `Accounts in the Wallet can be searched by only index or address.` + + expect(() => caver.klay.accounts.wallet.getAccount({})).to.throws(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-128 : input: invalid address for getAccount', () => { + it('should throw Error', () => { + const input = 'address' + const errorMessage = `Failed to getAccount from Wallet: invalid address(${input})` + + expect(() => caver.klay.accounts.wallet.getAccount(input)).to.throws(errorMessage) + }) + }) + + context('CAVERJS-UNIT-WALLET-129 : input: validAddress', () => { + it('should return Account from Wallet', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + + let fromWallet = caver.klay.accounts.wallet.getAccount(testAccount.address) + + expect(testAccount.address).to.equals(fromWallet.address) + expect(testAccount.privateKey).to.equals(fromWallet.privateKey) + expect(testAccount.index).to.equals(fromWallet.index) + + fromWallet = caver.klay.accounts.wallet.getAccount(testAccount.address.toLowerCase()) + + expect(testAccount.address).to.equals(fromWallet.address) + expect(testAccount.privateKey).to.equals(fromWallet.privateKey) + expect(testAccount.index).to.equals(fromWallet.index) + + fromWallet = caver.klay.accounts.wallet.getAccount(testAccount.address.toUpperCase()) + + expect(testAccount.address).to.equals(fromWallet.address) + expect(testAccount.privateKey).to.equals(fromWallet.privateKey) + expect(testAccount.index).to.equals(fromWallet.index) + }) + }) +}) + +describe('caver.klay.accounts.wallet.updatePrivateKey', () => { + context('CAVERJS-UNIT-WALLET-322: input: private key string and address', () => { + it('should update the private key of account by updating the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const originalPrivateKey = testAccount.privateKey + const updatePrivateKey = caver.klay.accounts.create().privateKey + + let account = caver.klay.accounts.wallet.getAccount(testAccount.address) + expect(account.privateKey).to.equals(originalPrivateKey) + + account = caver.klay.accounts.wallet.updatePrivateKey(updatePrivateKey, testAccount.address) + expect(account.privateKey).to.equals(updatePrivateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-323: input: KlaytnWalletKey and address', () => { + it('should update the private key of account by updating the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const originalPrivateKey = testAccount.privateKey + const updatedAccount = caver.klay.accounts.createWithAccountKey(testAccount.address, caver.klay.accounts.create().privateKey) + const klaytnWalletKey = updatedAccount.getKlaytnWalletKey() + const updatedPrivateKey = updatedAccount.privateKey + + let account = caver.klay.accounts.wallet.getAccount(testAccount.address) + expect(account.privateKey).to.equals(originalPrivateKey) + + account = caver.klay.accounts.wallet.updatePrivateKey(klaytnWalletKey, testAccount.address) + expect(account.privateKey).to.equals(updatedPrivateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-324: input: private key or address are undefined', () => { + it('should throw error', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = caver.klay.accounts.create().privateKey + + const expectedError = `To update the privatKey in wallet, need to set both privateKey and address.` + + expect(() => caver.klay.accounts.wallet.updatePrivateKey()).to.throws(expectedError) + expect(() => caver.klay.accounts.wallet.updatePrivateKey(updatePrivateKey)).to.throws(expectedError) + expect(() => caver.klay.accounts.wallet.updatePrivateKey(undefined, testAccount.address)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-325: input: invalid type of privateKey', () => { + it('should throw error', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + + let invalid = '' + let expectedError = `Invalid private key(${invalid})` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(invalid, testAccount.address)).to.throws(expectedError) + + invalid = {} + expectedError = `The private key used for the update is not a valid string.` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(invalid, testAccount.address)).to.throws(expectedError) + + invalid = 1 + expectedError = `The private key used for the update is not a valid string.` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(invalid, testAccount.address)).to.throws(expectedError) + + invalid = [] + expectedError = `The private key used for the update is not a valid string.` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(invalid, testAccount.address)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-326: input: private key string and address', () => { + it('should throw error when accountKey in Account is AccountKeyMultiSig', () => { + const address = caver.klay.accounts.create().address + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey(address, key)) + const updatePrivateKey = caver.klay.accounts.create().privateKey + + let expectedError = `Account using AccountKeyMultiSig or AccountKeyRoleBased must be updated using the caver.klay.accounts.updateAccountKey function.` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(updatePrivateKey, testAccount.address)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-327: input: private key string and address', () => { + it('should throw error when accountKey in Account is AccountKeyRoleBased', () => { + const address = caver.klay.accounts.create().address + const key = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: caver.klay.accounts.create().privateKey + } + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey(address, key)) + const updatePrivateKey = caver.klay.accounts.create().privateKey + + let expectedError = `Account using AccountKeyMultiSig or AccountKeyRoleBased must be updated using the caver.klay.accounts.updateAccountKey function.` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(updatePrivateKey, testAccount.address)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-328: input: invalid private key string and address', () => { + it('should throw error', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + let invalid = '0x01' + + let expectedError = `Invalid private key(${invalid.slice(2)})` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(invalid, testAccount.address)).to.throws(expectedError) + + invalid = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364142' + expectedError = `Invalid private key` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(invalid, testAccount.address)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-329: input: KlaytnWalletKey and address', () => { + it('should throw error when address from KlaytnWalletKey is not matched with address parameter', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatedAccount = caver.klay.accounts.createWithAccountKey(caver.klay.accounts.create().address, caver.klay.accounts.create().privateKey) + const klaytnWalletKey = updatedAccount.getKlaytnWalletKey() + + let expectedError = `The address extracted from the private key does not match the address received as the input value.` + expect(() => caver.klay.accounts.wallet.updatePrivateKey(klaytnWalletKey, testAccount.address)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-330: input: not existed address of account', () => { + it('should throw error when account cannot be found with address', () => { + const notExistAddress = caver.klay.accounts.create().address + const updatePrivateKey = caver.klay.accounts.create().privateKey + + const expectedError = `Failed to find account with ${notExistAddress}` + + expect(() => caver.klay.accounts.wallet.updatePrivateKey(updatePrivateKey, notExistAddress)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-331: input: invalid address', () => { + it('should throw error when account cannot be found with address', () => { + const invalidAddress = 'invalidAddress' + const updatePrivateKey = caver.klay.accounts.create().privateKey + + const expectedError = `Invalid address : ${invalidAddress}` + + expect(() => caver.klay.accounts.wallet.updatePrivateKey(updatePrivateKey, invalidAddress)).to.throws(expectedError) + }) + }) +}) + +describe('caver.klay.accounts.wallet.updateAccountKey', () => { + context('CAVERJS-UNIT-WALLET-332: input: address and private key string', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = caver.klay.accounts.create().privateKey + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-333: input: address and AccountKeyPublic', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = caver.klay.accounts.createAccountKeyPublic(caver.klay.accounts.create().privateKey) + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey.keys) + }) + }) + + context('CAVERJS-UNIT-WALLET-334: input: address and array of private key string', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-335: input: address and AccountKeyMultiSig', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = caver.klay.accounts.createAccountKeyMultiSig([caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey]) + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey.keys) + }) + }) + + context('CAVERJS-UNIT-WALLET-336: input: address and key object which defines transactionKey, updateKey and feePayerKey', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey, + } + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-337: input: address and AccountKeyRoleBased which defines transactionKey, updateKey and feePayerKey', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const keyObject = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey, + } + const updatePrivateKey = caver.klay.accounts.createAccountKeyRoleBased(keyObject) + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey.keys) + }) + }) + + context('CAVERJS-UNIT-WALLET-338: input: address and key object which defines transactionKey only', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = { + transactionKey: caver.klay.accounts.create().privateKey, + } + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-339: input: address and AccountKeyRoleBased which defines transactionKey only', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const keyObject = { + transactionKey: caver.klay.accounts.create().privateKey, + } + const updatePrivateKey = caver.klay.accounts.createAccountKeyRoleBased(keyObject) + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey.keys) + }) + }) + + context('CAVERJS-UNIT-WALLET-340: input: address and key object which defines updateKey only', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = { + updateKey: caver.klay.accounts.create().privateKey, + } + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-341: input: address and AccountKeyRoleBased which defines updateKey only', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const keyObject = { + updateKey: caver.klay.accounts.create().privateKey, + } + const updatePrivateKey = caver.klay.accounts.createAccountKeyRoleBased(keyObject) + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey.keys) + }) + }) + + context('CAVERJS-UNIT-WALLET-342: input: address and key object which defines feePayerKey only', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const updatePrivateKey = { + feePayerKey: caver.klay.accounts.create().privateKey, + } + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-343: input: address and AccountKeyRoleBased which defines feePayerKey only', () => { + it('should update the accountKey', () => { + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + const keyObject = { + updateKey: caver.klay.accounts.create().privateKey, + } + const updatePrivateKey = caver.klay.accounts.createAccountKeyRoleBased(keyObject) + + account = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + compareAccountKey(account.accountKey, updatePrivateKey.keys) + }) + }) + + context('CAVERJS-UNIT-WALLET-344: input: address of role based account and key object which defines role key partially', () => { + it('should update the key defined inside the AccountKey only', () => { + const originalKeyObject = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey, + } + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey(caver.klay.accounts.create().address, originalKeyObject)) + + const keyObject = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + + let updatedAccount = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, keyObject) + + const expectedUpdatedKey = { + transactionKey: keyObject.transactionKey, + updateKey: keyObject.updateKey, + feePayerKey: originalKeyObject.feePayerKey + } + compareAccountKey(updatedAccount.accountKey, expectedUpdatedKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-345: input: address of role based account and AccountKeyRoleBased which defines role key partially', () => { + it('should update the key defined inside the AccountKey only', () => { + const originalKeyObject = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: caver.klay.accounts.create().privateKey, + } + const testAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.createWithAccountKey(caver.klay.accounts.create().address, originalKeyObject)) + + const keyObject = { + transactionKey: caver.klay.accounts.create().privateKey, + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + + const updatePrivateKey = caver.klay.accounts.createAccountKeyRoleBased(keyObject) + + let updatedAccount = caver.klay.accounts.wallet.updateAccountKey(testAccount.address, updatePrivateKey) + + const expectedUpdatedKey = { + transactionKey: keyObject.transactionKey, + updateKey: keyObject.updateKey, + feePayerKey: originalKeyObject.feePayerKey + } + compareAccountKey(updatedAccount.accountKey, expectedUpdatedKey) + }) + }) + + context('CAVERJS-UNIT-WALLET-346: input: not existed address of account', () => { + it('should throw error when account cannot be found with address', () => { + const notExistAddress = caver.klay.accounts.create().address + const updatePrivateKey = caver.klay.accounts.create().privateKey + + const expectedError = `Failed to find account with ${notExistAddress}` + + expect(() => caver.klay.accounts.wallet.updateAccountKey(notExistAddress, updatePrivateKey)).to.throws(expectedError) + }) + }) + + context('CAVERJS-UNIT-WALLET-347: input: invalid address', () => { + it('should throw error when account cannot be found with address', () => { + const invalidAddress = 'invalidAddress' + const updatePrivateKey = caver.klay.accounts.create().privateKey + + const expectedError = `Invalid address : ${invalidAddress}` + + expect(() => caver.klay.accounts.wallet.updateAccountKey(invalidAddress, updatePrivateKey)).to.throws(expectedError) + }) + }) +}) \ No newline at end of file diff --git a/test/packages/caver.klay.utils.js b/test/packages/caver.klay.utils.js deleted file mode 100644 index 1539540d..00000000 --- a/test/packages/caver.klay.utils.js +++ /dev/null @@ -1,925 +0,0 @@ -/* - Copyright 2018 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 . -*/ - -require('it-each')({ testPerIteration: true }) -const testRPCURL = require('../testrpc') -const { expect } = require('../extendedChai') - -const setting = require('./setting') -const utils = require('./utils') -const Caver = require('../../index.js') -const BN = require('bn.js') - -let caver -beforeEach(() => { - caver = new Caver(testRPCURL) -}) - -describe('caver.utils.randomHex', () => { - context('input: valid value', () => { - it.each( - [0, 1, 2, 4, 32, 64], - 'should match with regex', - (size) => { - const data = caver.utils.randomHex(size) - const regExp = new RegExp(`^0x[0-9a-f]{${size * 2}}$`) - expect(data).to.match(regExp) - } - ) - }) - - context('input: invalid value', () => { - it('should throw an error: Invalid size: It must be >=0 && <= 65536', () => { - const expectedErrorMessage = 'Invalid size: It must be >=0 && <= 65536' - - expect(() => caver.utils.randomHex(-1)).to.throw(expectedErrorMessage) - expect(() => caver.utils.randomHex(65537)).to.throw(expectedErrorMessage) - }) - }) -}) - -describe('caver.utils.isBN', () => { - context('input: BN type', () => { - it.each([ - ['new BN(255)', new BN(255), true], - [`new BN('ff', 16)`, new BN('ff', 16), true], - [`new BN('377', 8)`, new BN('377', 8), true], - [`new BN('11111111', 2)`, new BN('11111111', 2), true], - ], - 'should return true', - ([_, bn, expected]) => { - const data = caver.utils.isBN(bn) - expect(data).to.be.equal(expected) - } - ) - }) - - context('input: not a BN type', () => { - it.each([ - ['255', 255, false], - ['0xff', 0xff, false], - ['0377', 0o377, false], - ['0b11111111', 0b11111111, false], - ], - 'should return false', - ([_, bn, expected]) => { - const data = caver.utils.isBN(bn) - expect(data).to.be.equal(expected) - } - ) - }) -}) - -describe('caver.utils.isBigNumber', () => { - const BigNumber = require('bignumber.js') - - context('input: BigNumber type', () => { - it.each([ - ['new BigNumber(1.0000000000000001)', new BigNumber(1.0000000000000001), true], - ['new BigNumber(88259496234518.57)', new BigNumber(88259496234518.57), true], - ['new BigNumber(99999999999999999999)', new BigNumber(99999999999999999999), true], - ['new BigNumber(2e+308)', new BigNumber(2e+308), true], - ], - 'should return true', - ([_, bigNumber, expected]) => { - const data = caver.utils.isBigNumber(bigNumber) - expect(data).to.be.equal(expected) - } - ) - }) - - context('input: not a BigNumber type', () => { - it.each([ - ['1.0000000000000001', 1.0000000000000001, false], - ['88259496234518.57', 88259496234518.57, false], - ['99999999999999999999', 99999999999999999999, false], - ['2e+308', 2e+308, false], - ], - 'should return false', - ([_, bn, expected]) => { - const data = caver.utils.isBigNumber(bn) - expect(data).to.be.equal(expected) - } - ) - }) -}) - -describe('caver.utils.sha3', () => { - - context('input: BigNumber type', () => { - it.each([ - ['new BN(\'234\')', new BN('234'), '0xc1912fee45d61c87cc5ea59dae311904cd86b84fee17cc96966216f811ce6a79'], - ], - 'should return 32 bytes hexstring', - ([_, sha3Input, expected]) => { - const data = caver.utils.sha3(sha3Input) - expect(data).to.be.equal(expected) - } - ) - }) - - context('input: number type', () => { - it.each([ - ['234', 234, null], - ['0xea', 0xea, null], - ], - 'should return null', - ([_, sha3Input, expected]) => { - const data = caver.utils.sha3(sha3Input) - expect(data).to.be.equal(expected) - } - ) - }) - - context('input: String | HexString type', () => { - it.each([ - ['\'234\'', '234', '0xc1912fee45d61c87cc5ea59dae311904cd86b84fee17cc96966216f811ce6a79'], - ['\'0xea\'', '0xea', '0x2f20677459120677484f7104c76deb6846a2c071f9b3152c103bb12cd54d1a4a'], - ], - 'should return 32 bytes hexstring', - ([_, sha3Input, expected]) => { - const data = caver.utils.sha3(sha3Input) - expect(data).to.be.equal(expected) - } - ) - }) -}) - -describe('caver.utils.soliditySha3', () => { - it.each([ - ['\'234564535\', \'0xfff23243\', true, -10', ['234564535', '0xfff23243', true, -10], '0x3e27a893dc40ef8a7f0841d96639de2f58a132be5ae466d40087a2cfa83b7179'], - ['\'Hello!%\'', ['Hello!%'], '0x661136a4267dba9ccdf6bfddb7c00e714de936674c4bdb065a531cf1cb15c7fc'], - ['\'234\'', ['234'], '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'], - ['0xea', [0xea], '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'], - ['new BN(\'234\')', [new BN('234')], '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'], - ['{ type: \'uint256\', value: \'234\' }', [{ type: 'uint256', value: '234' }], '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'], - ['{ t: \'uint256\', v: \'234\' }', [{ t: 'uint', v: new BN('234') }], '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'], - ['\'0x407D73d8a49eeb85D32Cf465507dd71d507100c1\'', ['0x407D73d8a49eeb85D32Cf465507dd71d507100c1'], '0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b'], - ['{ t: \'bytes\', v: \'0x407D73d8a49eeb85D32Cf465507dd71d507100c1\' }', [{ t: 'bytes', v: '0x407D73d8a49eeb85D32Cf465507dd71d507100c1' }], '0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b'], - ['{ t: \'address\', v: \'0x407D73d8a49eeb85D32Cf465507dd71d507100c1\' }', [{ t: 'address', v: '0x407D73d8a49eeb85D32Cf465507dd71d507100c1' }], '0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b'], - ['{ t: \'bytes32\', v: \'0x407D73d8a49eeb85D32Cf465507dd71d507100c1\' }', [{ t: 'bytes32', v: '0x407D73d8a49eeb85D32Cf465507dd71d507100c1' }], '0x3c69a194aaf415ba5d6afca734660d0a3d45acdc05d54cd1ca89a8988e7625b4'], - ['{ t: \'string\', v: \'Hello! % \' }, { t: \'int8\', v: -23 }, { t: \'address\', v: \'0x85F43D8a49eeB85d32Cf465507DD71d507100C1d\' }', [{ t: 'string', v: 'Hello!%' }, { t: 'int8', v: -23 }, { t: 'address', v: '0x85F43D8a49eeB85d32Cf465507DD71d507100C1d' }], '0xa13b31627c1ed7aaded5aecec71baf02fe123797fffd45e662eac8e06fbe4955'], - ], - 'should return 32 bytes hexstring', - ([_, soliditySha3Input, expected]) => { - const data = caver.utils.soliditySha3(...soliditySha3Input) - expect(data).to.be.equal(expected) - } - ) -}) - -describe('caver.utils.isHex', () => { - context('input: hexString', () => { - it.each([ - ['\'0xc1912\'', '0xc1912', true], - ['0xc1912', 0xc1912, true], - ['\'c1912\'', 'c1912', true], - ['345', 345, true], - ], - 'should return true', - ([_, hex, expected]) => { - const data = caver.utils.isHex(hex) - expect(data).to.be.equal(expected) - }) - }) - - context('input: invalid hexString', () => { - it.each([ - ['\'0xZ1912\'', '0xZ1912', false], - ['\'Hello\'', 'Hello', false], - ], - 'should return false', - ([_, hex, expected]) => { - const data = caver.utils.isHex(hex) - expect(data).to.be.equal(expected) - }) - }) -}) - -describe('caver.utils.isHexStrict', () => { - context('input: strict hexString', () => { - it.each([ - ['\'0xc1912\'', '0xc1912', true], - ], - 'should return true', - ([_, hex, expected]) => { - const data = caver.utils.isHexStrict(hex) - expect(data).to.be.equal(expected) - }) - }) - - context('input: not strict hexString', () => { - it.each([ - ['0xc1912', 0xc1912, false], - ['\'c1912\'', 'c1912', false], - ['345', 345, false], - ['\'0xZ1912\'', '0xZ1912', false], - ['\'Hello\'', 'Hello', false], - ], - 'should return false', - ([_, hex, expected]) => { - const data = caver.utils.isHexStrict(hex) - expect(data).to.be.equal(expected) - }) - }) -}) - -describe('caver.utils.isAddress', () => { - context('input: valid address', () => { - it.each([ - ['0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', true], - ['c1912fee45d61c87cc5ea59dae31190fffff232d', true], - ['0xc1912fee45d61c87cc5ea59dae31190fffff232d', true], - ['0XC1912FEE45D61C87CC5EA59DAE31190FFFFF232D', true], - ], - 'should return true', - ([address, expected]) => { - const data = caver.utils.isAddress(address) - expect(data).to.be.equal(expected) - }) - }) - - context('input: invalid address', () => { - it.each([ - ['0xC1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', false] - ], - 'should return false', - ([address, expected]) => { - const data = caver.utils.isAddress(address) - expect(data).to.be.equal(expected) - }) - }) -}) - -describe('caver.utils.toChecksumAddress', () => { - context('input: valid address', () => { - it.each([ - ['0xc1912fee45d61c87cc5ea59dae31190fffff232D', '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d'], - ['0XC1912FEE45D61C87CC5EA59DAE31190FFFFF232D', '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d'], - ['0xC1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d'] - ], - 'should return checksum address', - ([address, expected]) => { - const data = caver.utils.toChecksumAddress(address) - expect(data).to.be.equal(expected) - }) - }) - - context('input: invalid address', () => { - it('should throw an error', () => { - const invalidAddress = 'zzzz' - const errorMessage = `Given address "${invalidAddress}" is not a valid Klaytn address.` - expect(() => caver.utils.toChecksumAddress(invalidAddress)).to.throw(errorMessage) - }) - }) -}) - -describe('caver.utils.checkAddressChecksum', () => { - context('input: valid checksum address', () => { - it.each([ - ['0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', true], - ], - 'should return true', - ([address, expected]) => { - const result = caver.utils.checkAddressChecksum(address) - expect(result).to.be.equal(expected) - } - ) - }) - - context('input: invalid checksum address', () => { - it.each([ - ['0xc1912fee45d61c87cc5ea59dae31190fffff232d', false], - ['c1912fee45d61c87cc5ea59dae31190fffff232d', false], - ['0XC1912FEE45D61C87CC5EA59DAE31190FFFFF232D', false], - ['0xC1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', false], - ], - 'should return false', - ([address, expected]) => { - const result = caver.utils.checkAddressChecksum(address) - expect(result).to.be.equal(expected) - } - ) - }) -}) - -describe('caver.utils.toHex', () => { - const BN = require('bn.js') - const BigNumber = require('bignumber.js') - - it.each([ - ['\'234\'', '234', '0xea'], - ['234', 234, '0xea'], - ['new BN(\'234\')', new BN('234'), '0xea'], - ['new BigNumber(\'234\')', new BigNumber('234'), '0xea'], - ['\'I have 100€\'', 'I have 100€', '0x49206861766520313030e282ac'], - ], - 'should return hexstring', - ([_, hex, expected]) => { - const result = caver.utils.toHex(hex) - expect(result).to.be.equal(expected) - } - ) -}) - -describe('caver.utils.toBN', () => { - const BN = require('bn.js') - const BigNumber = require('bignumber.js') - - context('input: valid value', () => { - it.each([ - ['1234', 1234, new BN(1234)], - ['\'1234\'', '1234', new BN('1234')], - ['0xea', 0xea, new BN(0xea)], - ['\'0xea\'', '0xea', new BN('ea', 16)], - ['new BN(234)', new BN(234), new BN(234)], - ['new BN(\'234\')', new BN('234'), new BN('234')], - ['new BigNumber(234)', new BigNumber(234), new BN(234)], - ['new BigNumber(\'234\')', new BigNumber('234'), new BN('234')] - ], - 'should return BigNumber type', - ([_, number, expected]) => { - const result = caver.utils.toBN(number) - expect(result.toString()).to.be.equal(expected.toString()) - } - ) - }) - - context('input: invalid value', () => { - it('should throw an error', () => { - let invalid = 'zzzz' - const errorMessage = `Error: [number-to-bn] while converting number "${invalid}" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported. Given value: "${invalid}"` - expect(() => caver.utils.toBN(invalid)).to.throw(errorMessage) - }) - }) -}) - -describe('caver.utils.hexToNumberString', () => { - - context('input: number', () => { - it.each([ - ['1234', 1234, (1234).toString()], - ['0x1234', 0x1234, (0x1234).toString()], - ['0xea', 0xea, (0xea).toString()], - ], - 'should return numberString', - ([_, hex, expected]) => { - const result = caver.utils.hexToNumberString(hex) - expect(result).to.be.equal(expected) - } - ) - }) - - context('input: numberString', () => { - it.each([ - ['\'1234\'', '1234', (1234).toString()], - ], - 'should return numberString', - ([_, hex, expected]) => { - const result = caver.utils.hexToNumberString(hex) - expect(result).to.be.equal(expected) - } - ) - }) - - context('input: hexString', () => { - it.each([ - ['\'0x1234\'', '0x1234', (0x1234).toString()], - ['\'0xea\'', '0xea', (0xea).toString()] - ], - 'should return numberString', - ([_, hex, expected]) => { - const result = caver.utils.hexToNumberString(hex) - expect(result).to.be.equal(expected) - } - ) - }) - - context('input: invalid hexString', () => { - it('should throw an error', () => { - let invalid = 'zzzz' - const errorMessage = `Error: [number-to-bn] while converting number "${invalid}" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported. Given value: "${invalid}"` - expect(() => caver.utils.hexToNumberString(invalid)).to.throw(errorMessage) - }) - }) -}) - - - -// caver.utils.hexToNumber -describe('caver.utils.hexToNumber', () => { - context('input: valid value', () => { - it.each([ - ['1234', 1234, 1234], - ['\'1234\'', '1234', 1234], - ['0x1234', 0x1234, 4660], - ['\'0x1234\'', '0x1234', 4660], - ['0xea', 0xea, 234], - ['\'0xea\'', '0xea', 234] - ], - 'should return number', - ([_, hex, expected]) => { - const result = caver.utils.hexToNumber(hex) - expect(result).to.be.equal(expected) - } - ) - }) - - context('input: invalid value', () => { - it('should throw an error', () => { - const invalid = 'zzzz' - const errorMessage = `Error: [number-to-bn] while converting number "${invalid}" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported. Given value: "${invalid}"` - expect(() => caver.utils.hexToNumber(invalid)).to.throw(errorMessage) - }) - }) -}) - -describe('caver.utils.numberToHex', () => { - const BN = require('bn.js') - const BigNumber = require('bignumber.js') - - const toHexStr = (number) => '0x' + number.toString(16).toLowerCase() - - context('input: valid number', () => { - it.each([ - ['1234', 1234, toHexStr(1234)], - ['\'1234\'', '1234', toHexStr(1234)], - ['0x1234', 0x1234, toHexStr(4660)], - ['\'0x1234\'', '0x1234', toHexStr(4660)], - ['new BN(234)', new BN(234), toHexStr(234)], - ['new BN(\'234\')', new BN('234'), toHexStr(234)], - ['new BigNumber(234)', new BigNumber(234), toHexStr(234)], - ['new BigNumber(\'234\')', new BigNumber('234'), toHexStr(234)], - ], - 'should return hexString', - ([_, number, expected]) => { - const result = caver.utils.numberToHex(number) - expect(result).to.equal(expected) - } - ) - }) - - context('input: invalid number', () => { - it('should throw an error', () => { - const invalid = 'zzzz' - const errorMessage = `Given input "${invalid}" is not a number.` - - expect(() => caver.utils.numberToHex(invalid)).to.throw(errorMessage) - }) - }) -}) - -describe('caver.utils.hexToUtf8', () => { - context('input: valid hexString', () => { - it.each([ - ['0x49206861766520313030e282ac', 'I have 100€'], - ['0x48656c6c6f2c204b6c6179746e', 'Hello, Klaytn'] - ], - 'should return utf8 string', - ([hex, expected]) => { - const result = caver.utils.hexToUtf8(hex) - expect(result).to.be.equal(expected) - } - ) - }) - - context('input: invalid hexString', () => { - it('should throw an error', () => { - const invalid = 'zzzz' - const errorMessage = `The parameter "${invalid}" must be a valid HEX string.` - - expect(() => caver.utils.hexToUtf8(invalid)).to.throw(errorMessage) - }) - }) -}) - -describe('caver.utils.hexToAscii', () => { - - context('input: valid hexString', () => { - it.each([ - ['0x4920686176652031303021', 'I have 100!'], - ['0x48656c6c6f2c204b6c6179746e', 'Hello, Klaytn'], - ], - 'should return Ascii string', - ([hex, expected]) => { - const result = caver.utils.hexToAscii(hex) - expect(result).to.be.equal(expected) - } - ) - }) - - context('input: invalid hexString', () => { - it('should throw an error', () => { - const invalid = 'zzzz' - const errorMessage = `The parameter must be a valid HEX string.` - - expect(() => caver.utils.hexToAscii(invalid)).to.throw(errorMessage) - }) - }) -}) - -describe('caver.utils.utf8ToHex', () => { - it.each([ - ['I have 100€', '0x49206861766520313030e282ac'], - ['Hello, Klaytn', '0x48656c6c6f2c204b6c6179746e'] - ], - 'should return hexString', - ([string, expected]) => { - const result = caver.utils.utf8ToHex(string) - expect(result).to.be.equal(expected) - } - ) -}) - -describe('caver.utils.asciiToHex', () => { - it.each([ - ['I have 100!', '0x4920686176652031303021'], - ['Hello, Klaytn', '0x48656c6c6f2c204b6c6179746e'], - ], - 'should return hex String', - ([string, expected]) => { - const result = caver.utils.asciiToHex(string) - expect(result).to.be.equal(expected) - } - ) -}) - - -describe('caver.utils.hexToBytes', () => { - - context('input: hexString \'0x000000ea\'', () => { - it('should return bytes', () => { - const hex = '0x000000ea' - - const expected = [0, 0, 0, 234] - const result = caver.utils.hexToBytes(hex) - expect(result).to.deep.equal(expected) - }) - }) - - context('input: invalid hexString', () => { - it('should throw an error', () => { - let invalid = 0x000000ea - let errorMessage = `Given value "${invalid.toString(16)}" is not a valid hex string.` - expect(() => caver.utils.hexToBytes(invalid)).to.throw(errorMessage) - - invalid = 'zzzz' - errorMessage = `Given value "${invalid}" is not a valid hex string.` - expect(() => caver.utils.hexToBytes(invalid)).to.throw(errorMessage) - }) - }) -}) - -describe('caver.utils.bytesToHex', () => { - - it.each([ - ['[0, 0, 0, 234]', [0, 0, 0, 234], '0x000000ea'], - ['[234]', [234], '0xea'] - ], - 'should return byteArray', - ([_, byteArray, expected]) => { - const result = caver.utils.bytesToHex(byteArray) - expect(result).deep.equal(expected) - } - ) -}) - -describe('caver.utils.toPeb', () => { - const BN = require('bn.js') - const BigNumber = require('bignumber.js') - - const unitMap = utils.unitMap - - context('input: various type', () => { - it.each([ - ['1', 1, unitMap.KLAY], - ['\'1\'', '1', unitMap.KLAY], - ['123456789', 123456789, (new BigNumber(unitMap.KLAY * 123456789)).toFixed(0)], - ['\'123456789\'', '123456789', (new BigNumber(unitMap.KLAY * 123456789)).toFixed(0)], - ['new BN(1)', new BN(1), unitMap.KLAY], - ['new BN(\'1\')', new BN('1'), unitMap.KLAY], - ['new BN(123456789)', new BN(123456789), (new BigNumber(unitMap.KLAY * 123456789)).toFixed(0)], - ['new BN(\'123456789\')', new BN('123456789'), (new BigNumber(unitMap.KLAY * 123456789)).toFixed(0)] - ], - 'should return string', - ([_, number, expected]) => { - const result = caver.utils.toPeb(number) - expect(result.toString()).to.be.equal(expected.toString()) - } - ) - }) - - context('input: base unitmap', () => { - it.each([ - ['1', 'peb', 1, unitMap.peb], - ['1', 'kpeb', 1, unitMap.kpeb], - ['1', 'Mpeb', 1, unitMap.Mpeb], - ['1', 'Gpeb', 1, unitMap.Gpeb], - ['1', 'uKLAY', 1, unitMap.uKLAY], - ['1', 'mKLAY', 1, unitMap.mKLAY], - ['1', 'KLAY', 1, unitMap.KLAY], - ['1', 'kKLAY', 1, unitMap.kKLAY], - ['1', 'MKLAY', 1, unitMap.MKLAY], - ], - 'should return string', - ([_, unit, number, expected]) => { - const result = caver.utils.toPeb(number, unit) - expect(result).to.be.equal(expected) - } - ) - }) -}) - -describe('caver.utils.fromPeb', () => { - const BN = require('bn.js') - const BigNumber = require('bignumber.js') - - const unitMap = utils.unitMap - - - it.each([ - ['1', 1, unitMap.KLAY], - ['\'1\'', '1', unitMap.KLAY], - ['123456789', 123456789, unitMap.KLAY], - ['\'123456789\'', '123456789', unitMap.KLAY], - ['new BN(1)', new BN(1), unitMap.KLAY], - ['new BN(\'1\')', new BN('1'), unitMap.KLAY], - ['new BN(123456789)', new BN(123456789), unitMap.KLAY], - ['new BN(\'123456789\')', new BN('123456789'), unitMap.KLAY] - ], - 'should return string based on unitMap', - ([_, number, peb]) => { - - const bn = new BigNumber(peb) - const expected = (Math.pow(0.1, bn.e) * number).toFixed(bn.e) - - const result = caver.utils.fromPeb(number) - expect(result).to.be.equal(expected) - } - ) - - it.each([ - ['1', 'peb', 1, unitMap.peb], - ['1', 'kpeb', 1, unitMap.kpeb], - ['1', 'Mpeb', 1, unitMap.Mpeb], - ['1', 'Gpeb', 1, unitMap.Gpeb], - ['1', 'uKLAY', 1, unitMap.uKLAY], - ['1', 'mKLAY', 1, unitMap.mKLAY], - ['1', 'KLAY', 1, unitMap.KLAY], - ['1', 'kKLAY', 1, unitMap.kKLAY], - ['1', 'MKLAY', 1, unitMap.MKLAY] - ], - 'should return string based on unitMap', - ([_, unit, number, peb]) => { - const bn = new BigNumber(peb) - const expected = (Math.pow(0.1, bn.e) * number).toFixed(bn.e) - - const result = caver.utils.fromPeb(number, unit) - expect(result).to.be.equal(expected) - } - ) -}) - -describe('caver.utils.unitMap', () => { - const unitMap = utils.unitMap - - it('should return unitMap', () => { - const result = caver.utils.unitMap - expect(result).to.deep.equal(unitMap) - }) -}) - -describe('caver.utils.padLeft', () => { - context('input: hexString', () => { - it.each([ - ['0x3456ff', 20, '0x000000000000003456ff'], - [0x3456ff, 20, '0x000000000000003456ff'], - ], - 'should be left-padded with 0', - ([string, characterAmount, expected]) => { - const result = caver.utils.padLeft(string, characterAmount) - expect(result).to.equal(expected) - } - ) - }) - - context('input: string', () => { - it.each([ - ['Hello', 20, 'x', 'xxxxxxxxxxxxxxxHello'], - ], - 'should be left padded with x', - ([string, characterAmount, sign, expected]) => { - const result = caver.utils.padLeft(string, characterAmount, sign) - expect(result).to.equal(expected) - } - ) - }) -}) - -describe('caver.utils.padRight', () => { - context('input: hexString', () => { - it.each([ - ['0x3456ff', 20, '0x3456ff00000000000000'], - [0x3456ff, 20, '0x3456ff00000000000000'], - ], - 'should be right padded with 0', - ([string, characterAmount, expected]) => { - const result = caver.utils.padRight(string, characterAmount) - expect(result).to.equal(expected) - } - ) - }) - - context('input: string', () => { - it.each([ - ['Hello', 20, 'x', 'Helloxxxxxxxxxxxxxxx'] - ], - 'should be right padded with x', - ([string, characterAmount, sign, expected]) => { - const result = caver.utils.padRight(string, characterAmount, sign) - expect(result).to.equal(expected) - } - ) - }) -}) - -describe('caver.utils.toTwosComplement', () => { - - it.each([ - ['\'-1\'', '-1', '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'], - ['-1', -1, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'], - ['\'0x1\'', '0x1', '0x0000000000000000000000000000000000000000000000000000000000000001'], - ['-15', -15, '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1'], - ['\'-0x1\'', '-0x1', '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'], - ], - 'should return TwosComplement', - ([_, number, expected]) => { - const result = caver.utils.toTwosComplement(number) - expect(result).to.equal(result) - } - ) -}) - -describe('caver.utils.isHexPrefixed', () => { - it('caver.utils.isHexPrefixed should return boolean depends on parameter', ()=>{ - expect(caver.utils.isHexPrefixed('0x')).to.be.true - expect(caver.utils.isHexPrefixed('01')).to.be.false - expect(caver.utils.isHexPrefixed({})).to.be.false - }) -}) - -describe('caver.utils.addHexPrefix', () => { - it('caver.utils.addHexPrefix should return 0x hex format string', ()=>{ - expect(caver.utils.addHexPrefix('0x')).to.equals('0x') - expect(caver.utils.addHexPrefix('01')).to.equals('0x01') - expect(typeof(caver.utils.addHexPrefix({}))).to.equals('object') - }) -}) - -describe('caver.utils.stripHexPrefix', () => { - it('caver.utils.stripHexPrefix should strip 0x prefix and return string', ()=>{ - expect(caver.utils.stripHexPrefix('0x')).to.equals('') - expect(caver.utils.stripHexPrefix('01')).to.equals('01') - expect(caver.utils.stripHexPrefix('0x01')).to.equals('01') - expect(typeof(caver.utils.stripHexPrefix({}))).to.equals('object') - }) -}) - -describe('caver.utils.toBuffer', () => { - it('caver.utils.toBuffer should return input when input is Buffer', ()=>{ - expect(caver.utils.toBuffer(Buffer.from('test Buffer'))).to.deep.equal(Buffer.from('test Buffer')) - }) - it('caver.utils.toBuffer should convert null or undefined to buffer', ()=>{ - expect(caver.utils.toBuffer(null)).to.deep.equal(Buffer.alloc(0)) - expect(caver.utils.toBuffer(undefined)).to.deep.equal(Buffer.alloc(0)) - }) - it('caver.utils.toBuffer should convert Array to buffer', ()=>{ - expect(caver.utils.toBuffer([1,2,3,4,5])).to.deep.equal(Buffer.from([1,2,3,4,5])) - expect(caver.utils.toBuffer([])).to.deep.equal(Buffer.alloc(0)) - }) - it('caver.utils.toBuffer should convert BN to buffer', ()=>{ - expect(caver.utils.toBuffer(new BN(1))).to.deep.equal(Buffer.from([1])) - expect(caver.utils.toBuffer(new BN(255)).toString('hex')).to.deep.equal('ff') - expect(caver.utils.toBuffer(new BN('ff', 16)).toString('hex')).to.deep.equal('ff') - expect(caver.utils.toBuffer(new BN('377', 8)).toString('hex')).to.deep.equal('ff') - expect(caver.utils.toBuffer(new BN('11111111', 2)).toString('hex')).to.deep.equal('ff') - }) - it('caver.utils.toBuffer should convert Object has toArray function to buffer', ()=>{ - expect(caver.utils.toBuffer({toArray: function () {return [1, 2, 3, 4, 5]}})).to.deep.equal(Buffer.from([1,2,3,4,5])) - }) - it('caver.utils.toBuffer should convert String to buffer', ()=>{ - expect(caver.utils.toBuffer('0x01').toString('hex')).to.deep.equal('01') - expect(caver.utils.toBuffer('0x1').toString('hex')).to.deep.equal('01') - expect(caver.utils.toBuffer('0x1234').toString('hex')).to.deep.equal('1234') - expect(caver.utils.toBuffer('0x12345').toString('hex')).to.deep.equal('012345') - expect(caver.utils.toBuffer('0x11')).to.deep.equal(Buffer.from([17])) - expect(caver.utils.toBuffer('0x')).to.deep.equal(Buffer.from([])) - }) - it('caver.utils.toBuffer should convert Number to buffer', ()=>{ - expect(caver.utils.toBuffer(1)).to.deep.equal(Buffer.from([1])) - expect(caver.utils.toBuffer(1).toString('hex')).to.deep.equal('01') - expect(caver.utils.toBuffer(100).toString('hex')).to.deep.equal('64') - }) - - it('caver.utils.toBuffer should throw error when input type is not supported with toBuffer function', ()=>{ - expect(()=>caver.utils.toBuffer({})).to.throw('To convert an object to a buffer, the toArray function must be implemented inside the object') - expect(()=>caver.utils.toBuffer({toArray: [1,2,3,4,5]})).to.throw('To convert an object to a buffer, the toArray function must be implemented inside the object') - }) - it('caver.utils.toBuffer should throw error when String is not 0x-prefixed', ()=>{ - expect(()=>caver.utils.toBuffer('010x')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) - expect(()=>caver.utils.toBuffer('01')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) - expect(()=>caver.utils.toBuffer('')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) - expect(()=>caver.utils.toBuffer('0xqwer')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) - expect(()=>caver.utils.toBuffer('qwer')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) - }) -}) - -describe('caver.utils.numberToBuffer', () => { - it('caver.utils.numberToBuffer should convert number to buffer', ()=>{ - expect(caver.utils.numberToBuffer(6003400).toString('hex')).to.equals('5b9ac8') - expect(caver.utils.numberToBuffer(1).toString('hex')).to.equals('01') - expect(caver.utils.numberToBuffer(12345).toString('hex')).to.equals('3039') - expect(caver.utils.numberToBuffer(123456789).toString('hex')).to.equals('075bcd15') - expect(caver.utils.numberToBuffer(100000000).toString('hex')).to.equals('05f5e100') - expect(caver.utils.numberToBuffer(819263839023).toString('hex')).to.equals('bebfee1b2f') - }) -}) - -describe('caver.utils.isHexParameter', () => { - it('caver.utils.isHexParameter should return true if input is hex string', ()=>{ - expect(caver.utils.isHexParameter('0x01')).to.be.true - expect(caver.utils.isHexParameter('0xa')).to.be.true - expect(caver.utils.isHexParameter('0x256d774a7a1bbd469d4fb08545d171df1c755a78')).to.be.true - expect(caver.utils.isHexParameter('0x256d774a7a1bbd469d4fb08545d171df1c755a78171df1c755a78')).to.be.true - }) - - it('caver.utils.isHexParameter should return false if input is not hex string', ()=>{ - // string type input - expect(caver.utils.isHexParameter('')).to.be.false - expect(caver.utils.isHexParameter('1')).to.be.false - expect(caver.utils.isHexParameter('0xqwer')).to.be.false - expect(caver.utils.isHexParameter('10x')).to.be.false - expect(caver.utils.isHexParameter('0x14qr')).to.be.false - expect(caver.utils.isHexParameter('0x1!')).to.be.false - expect(caver.utils.isHexParameter(' 0x256d774a7a1bbd469d4fb08545d171df1c755a78')).to.be.false - // not string type input - expect(caver.utils.isHexParameter(null)).to.be.false - expect(caver.utils.isHexParameter(undefined)).to.be.false - expect(caver.utils.isHexParameter(true)).to.be.false - expect(caver.utils.isHexParameter(1)).to.be.false - expect(caver.utils.isHexParameter({})).to.be.false - expect(caver.utils.isHexParameter([])).to.be.false - expect(caver.utils.isHexParameter(Buffer.alloc(0))).to.be.false - expect(caver.utils.isHexParameter(new BN())).to.be.false - }) -}) - -describe('caver.utils.xyPointFromPublicKey', () => { - it('caver.utils.xyPointFromPublicKey should return x, y point from publicKey', ()=>{ - const account = caver.klay.accounts.create() - const publicKey = caver.klay.accounts.privateKeyToPublicKey(account.privateKey) - const xyPoint = caver.utils.xyPointFromPublicKey(publicKey) - expect(Array.isArray(xyPoint)).to.be.true - expect(xyPoint.length).to.equals(2) - expect(publicKey).to.equals(caver.utils.leftPad(xyPoint[0], 64)+caver.utils.leftPad(xyPoint[1], 64).slice(2)) - }) - - it('caver.utils.xyPointFromPublicKey should return x, y point remove leading zeros', ()=>{ - const publicKey1 = '0x046241c7524030e5b44fff78021e35227d708c8630757b35090d56527b615f605b8d366782c86dee49356be574e1172f75ef5ce5d03b6e8c17dbf10f3fa2d9a3' - const publicKey2 = '0xba7135b75cae89b958e7bb78009bda52f6a348150757cc078e3e5e5d25519c500ed4ccec1f78ba4e1c21c7b1e57751cec4cf42e3997a476e3ecbf360ad095336' - const publicKey3 = '0x12b97e6756861ac0257a240d985d761cee9ca7719a29c233c644cfcc421885000c8e4c69cdb71665377b9e8ffb702355ca53917e66c7444619049c3dd0252ab6' - const publicKey4 = '0x05b3b58259770871a1cc18534f2d438935fa2dcdb04116cbfbde8adfe858c23e50047c5aea3c2f55de7de04203f8fe8ccc3b491029338d038a7ef6d6903b302e' - - const xyPoint1 = caver.utils.xyPointFromPublicKey(publicKey1) - const xyPoint2 = caver.utils.xyPointFromPublicKey(publicKey2) - const xyPoint3 = caver.utils.xyPointFromPublicKey(publicKey3) - const xyPoint4 = caver.utils.xyPointFromPublicKey(publicKey4) - - expect(xyPoint1[0]).to.equals('0x46241c7524030e5b44fff78021e35227d708c8630757b35090d56527b615f60') - expect(xyPoint1[1]).to.equals('0x5b8d366782c86dee49356be574e1172f75ef5ce5d03b6e8c17dbf10f3fa2d9a3') - - expect(xyPoint2[0]).to.equals('0xba7135b75cae89b958e7bb78009bda52f6a348150757cc078e3e5e5d25519c50') - expect(xyPoint2[1]).to.equals('0xed4ccec1f78ba4e1c21c7b1e57751cec4cf42e3997a476e3ecbf360ad095336') - - expect(xyPoint3[0]).to.equals('0x12b97e6756861ac0257a240d985d761cee9ca7719a29c233c644cfcc42188500') - expect(xyPoint3[1]).to.equals('0xc8e4c69cdb71665377b9e8ffb702355ca53917e66c7444619049c3dd0252ab6') - - expect(xyPoint4[0]).to.equals('0x5b3b58259770871a1cc18534f2d438935fa2dcdb04116cbfbde8adfe858c23e') - expect(xyPoint4[1]).to.equals('0x50047c5aea3c2f55de7de04203f8fe8ccc3b491029338d038a7ef6d6903b302e') - }) -}) \ No newline at end of file diff --git a/test/packages/caver.utils.js b/test/packages/caver.utils.js new file mode 100644 index 00000000..96030e8d --- /dev/null +++ b/test/packages/caver.utils.js @@ -0,0 +1,1130 @@ +/* + Copyright 2018 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 . +*/ + +require('it-each')({ testPerIteration: true }) +const testRPCURL = require('../testrpc') +const { expect } = require('../extendedChai') + +const setting = require('./setting') +const utils = require('./utils') +const Caver = require('../../index.js') +const BN = require('bn.js') +const BigNumber = require('bignumber.js') + +let caver +beforeEach(() => { + caver = new Caver(testRPCURL) +}) + +describe('caver.utils.randomHex', () => { + context('CAVERJS-UNIT-ETC-097: input: valid value', () => { + const tests = [0, 1, 2, 4, 32, 64] + it.each(tests, 'should match with regex', (size) => { + const data = caver.utils.randomHex(size) + const regExp = new RegExp(`^0x[0-9a-f]{${size * 2}}$`) + expect(data).to.match(regExp) + }) + }) + + context('CAVERJS-UNIT-ETC-098: input: invalid value', () => { + it('should throw an error: Invalid size: It must be >=0 && <= 65536', () => { + const expectedErrorMessage = 'Invalid size: It must be >=0 && <= 65536' + + expect(() => caver.utils.randomHex(-1)).to.throw(expectedErrorMessage) + expect(() => caver.utils.randomHex(65537)).to.throw(expectedErrorMessage) + }) + }) +}) + +describe('caver.utils.isBN', () => { + context('CAVERJS-UNIT-ETC-099: input: BN type', () => { + const tests = [ + {value: new BN(255), expected: true}, + {value: new BN('ff', 16), expected: true}, + {value: new BN('377', 8), expected: true}, + {value: new BN('11111111', 2), expected: true}, + ] + it.each(tests, 'should return true', (test) => { + expect(caver.utils.isBN(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-100: input: not a BN type', () => { + const tests = [ + {value: 255, expected: false}, + {value: 0xff, expected: false}, + {value: 0o377, expected: false}, + {value: 0b11111111, expected: false}, + ] + it.each(tests, 'should return false', (test) => { + expect(caver.utils.isBN(test.value)).to.be.equal(test.expected) + }) + }) +}) + +describe('caver.utils.isBigNumber', () => { + context('CAVERJS-UNIT-ETC-101: input: BigNumber type', () => { + const tests = [ + {value: new BigNumber(1.0000000000000001), expected: true}, + {value: new BigNumber(88259496234518.57), expected: true}, + {value: new BigNumber(99999999999999999999), expected: true}, + {value: new BigNumber(2e+308), expected: true}, + ] + it.each(tests, 'should return true', (test) => { + expect(caver.utils.isBigNumber(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-102: input: not a BigNumber type', () => { + const tests = [ + {value: 1.0000000000000001, expected: false}, + {value: 88259496234518.57, expected: false}, + {value: 99999999999999999999, expected: false}, + {value: 2e+308, expected: false}, + ] + it.each(tests, 'should return false', (test) => { + expect(caver.utils.isBigNumber(test.value)).to.be.equal(test.expected) + }) + }) +}) + +describe('caver.utils.sha3', () => { + context('CAVERJS-UNIT-ETC-103: input: BN type', () => { + const tests = [ + {value: new BN('234'), expected: '0xc1912fee45d61c87cc5ea59dae311904cd86b84fee17cc96966216f811ce6a79'}, + ] + it.each(tests, 'should return 32 bytes hexstring', (test) => { + expect(caver.utils.sha3(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-104: input: number type', () => { + const tests = [ + {value: 234, expected: null}, + {value: 0xea, expected: null}, + ] + it.each(tests, 'should return null', (test) => { + expect(caver.utils.sha3(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-105: input: String | HexString type', () => { + const tests = [ + {value: '234', expected: '0xc1912fee45d61c87cc5ea59dae311904cd86b84fee17cc96966216f811ce6a79'}, + {value: '0xea', expected: '0x2f20677459120677484f7104c76deb6846a2c071f9b3152c103bb12cd54d1a4a'}, + ] + it.each(tests, 'should return 32 bytes hexstring', (test) => { + expect(caver.utils.sha3(test.value)).to.be.equal(test.expected) + }) + }) +}) + +describe('CAVERJS-UNIT-ETC-106: caver.utils.soliditySha3', () => { + const tests = [ + {values: ['234564535', '0xfff23243', true, -10], expected: '0x3e27a893dc40ef8a7f0841d96639de2f58a132be5ae466d40087a2cfa83b7179'}, + {values: ['Hello!%'], expected: '0x661136a4267dba9ccdf6bfddb7c00e714de936674c4bdb065a531cf1cb15c7fc'}, + {values: ['234'], expected: '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'}, + {values: [0xea], expected: '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'}, + {values: [new BN('234')], expected: '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'}, + {values: [{ type: 'uint256', value: '234' }], expected: '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'}, + {values: [{ t: 'uint', v: new BN('234') }], expected: '0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2'}, + {values: ['0x407D73d8a49eeb85D32Cf465507dd71d507100c1'], expected: '0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b'}, + {values: [{ t: 'bytes', v: '0x407D73d8a49eeb85D32Cf465507dd71d507100c1' }], expected: '0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b'}, + {values: [{ t: 'address', v: '0x407D73d8a49eeb85D32Cf465507dd71d507100c1' }], expected: '0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b'}, + {values: [{ t: 'bytes32', v: '0x407D73d8a49eeb85D32Cf465507dd71d507100c1' }], expected: '0x3c69a194aaf415ba5d6afca734660d0a3d45acdc05d54cd1ca89a8988e7625b4'}, + {values: [{ t: 'string', v: 'Hello!%' }, { t: 'int8', v: -23 }, { t: 'address', v: '0x85F43D8a49eeB85d32Cf465507DD71d507100C1d' }], expected: '0xa13b31627c1ed7aaded5aecec71baf02fe123797fffd45e662eac8e06fbe4955'}, + ] + it.each(tests, 'should return 32 bytes hexstring', (test) => { + expect(caver.utils.soliditySha3(...test.values)).to.be.equal(test.expected) + }) +}) + +describe('caver.utils.isHex', () => { + context('CAVERJS-UNIT-ETC-107: input: hexString', () => { + const tests = [ + {value: '0xc1912', expected: true}, + {value: 0xc1912, expected: true}, + {value: 'c1912', expected: true}, + {value: 345, expected: true}, + ] + it.each(tests, 'should return true', (test) => { + expect(caver.utils.isHex(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-108: input: invalid hexString', () => { + const tests = [ + {value: '0xZ1912', expected: false}, + {value: 'Hello', expected: false}, + ] + it.each(tests, 'should return false', (test) => { + expect(caver.utils.isHex(test.value)).to.be.equal(test.expected) + }) + }) +}) + +describe('caver.utils.isHexStrict', () => { + context('CAVERJS-UNIT-ETC-109: input: strict hexString', () => { + const tests = [ + {value: '0xc1912', expected: true}, + ] + it.each(tests, 'should return true', (test) => { + expect(caver.utils.isHexStrict(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-110: input: not strict hexString', () => { + const tests = [ + {value: 0xc1912, expected: false}, + {value: 'c1912', expected: false}, + {value: 345, expected: false}, + {value: '0xZ1912', expected: false}, + {value: 'Hello', expected: false}, + ] + it.each(tests, 'should return false', (test) => { + expect(caver.utils.isHexStrict(test.value)).to.be.equal(test.expected) + }) + }) +}) + +describe('caver.utils.isAddress', () => { + context('CAVERJS-UNIT-ETC-111: input: valid address', () => { + const tests = [ + {address: '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', expected: true}, + {address: 'c1912fee45d61c87cc5ea59dae31190fffff232d', expected: true}, + {address: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', expected: true}, + {address: '0XC1912FEE45D61C87CC5EA59DAE31190FFFFF232D', expected: true}, + ] + it.each(tests, 'should return true', (test) => { + expect(caver.utils.isAddress(test.address)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-112: input: invalid address', () => { + const tests = [ + {address: '0xC1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', expected: false} + ] + it.each(tests, 'should return false', (test) => { + expect(caver.utils.isAddress(test.address)).to.be.equal(test.expected) + }) + }) +}) + +describe('caver.utils.toChecksumAddress', () => { + context('CAVERJS-UNIT-ETC-113: input: valid address', () => { + const tests = [ + {address: '0xc1912fee45d61c87cc5ea59dae31190fffff232D', expected: '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d'}, + {address: '0XC1912FEE45D61C87CC5EA59DAE31190FFFFF232D', expected: '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d'}, + {address: '0xC1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', expected: '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d'} + ] + it.each(tests, 'should return checksum address', (test) => { + expect(caver.utils.toChecksumAddress(test.address)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-114: input: invalid address', () => { + it('should throw an error', () => { + const invalidAddress = 'zzzz' + const errorMessage = `Given address "${invalidAddress}" is not a valid Klaytn address.` + expect(() => caver.utils.toChecksumAddress(invalidAddress)).to.throw(errorMessage) + }) + }) +}) + +describe('caver.utils.checkAddressChecksum', () => { + context('CAVERJS-UNIT-ETC-115: input: valid checksum address', () => { + const tests = [ + {address: '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', expected: true}, + ] + it.each(tests, 'should return true', (test) => { + expect(caver.utils.checkAddressChecksum(test.address)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-116: input: invalid checksum address', () => { + const tests = [ + {address: '0xc1912fee45d61c87cc5ea59dae31190fffff232d', expected: false}, + {address: 'c1912fee45d61c87cc5ea59dae31190fffff232d', expected: false}, + {address: '0XC1912FEE45D61C87CC5EA59DAE31190FFFFF232D', expected: false}, + {address: '0xC1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', expected: false}, + ] + it.each(tests,'should return false', (test) => { + expect(caver.utils.checkAddressChecksum(test.address)).to.be.equal(test.expected) + }) + }) +}) + +describe('CAVERJS-UNIT-ETC-117: caver.utils.toHex', () => { + const tests = [ + {value: '234', expected: '0xea'}, + {value: 234, expected: '0xea'}, + {value: new BN('234'), expected: '0xea'}, + {value: 'I have 100€', expected: '0x49206861766520313030e282ac'}, + {value: '234', expected: '0xea'}, + {value: '234', expected: '0xea'}, + {value: 1, expected: '0x1'}, + {value: '1', expected: '0x1'}, + {value: '0x1', expected: '0x1'}, + {value: '15', expected: '0xf'}, + {value: '0xf', expected: '0xf'}, + {value: -1, expected: '-0x1'}, + {value: '-1', expected: '-0x1'}, + {value: '-0x1', expected: '-0x1'}, + {value: '-15', expected: '-0xf'}, + {value: '-0xf', expected: '-0xf'}, + {value: '0x657468657265756d', expected: '0x657468657265756d'}, + { + value: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', + expected: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd' + }, + { + value: '-0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + expected: '-0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + }, + { + value: '-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', + expected: '-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd' + }, + {value: 0, expected: '0x0'}, + {value: '0', expected: '0x0'}, + {value: '0x0', expected: '0x0'}, + {value: -0, expected: '0x0'}, + {value: '-0', expected: '0x0'}, + {value: '-0x0', expected: '0x0'}, + {value: [1, 2, 3, {test: 'data'}], expected: '0x5b312c322c332c7b2274657374223a2264617461227d5d'}, + {value: {test: 'test'}, expected: '0x7b2274657374223a2274657374227d'}, + {value: '{"test": "test"}', expected: '0x7b2274657374223a202274657374227d'}, + {value: 'myString', expected: '0x6d79537472696e67'}, + {value: 'myString 34534!', expected: '0x6d79537472696e6720333435333421'}, + {value: new BN(15), expected: '0xf'}, + { + value: 'Heeäööä👅D34ɝɣ24Єͽ-.,äü+#/', + expected: '0x486565c3a4c3b6c3b6c3a4f09f9185443334c99dc9a33234d084cdbd2d2e2cc3a4c3bc2b232f' + }, + {value: true, expected: '0x01'}, + {value: false, expected: '0x00'}, + { + value: + 'ff\u0003\u0000\u0000\u00005èÆÕL]\u0012|Î¾ž\u001a7«›\u00052\u0011(ЗY\n<\u0010\u0000\u0000\u0000\u0000\u0000\u0000e!ßd/ñõì\f:z¦Î¦±ç·÷Í¢Ëß\u00076*…\bŽ—ñžùC1ÉUÀé2\u001aӆBŒ', + expected: + '0x66660300000035c3a8c386c3954c5d127cc29dc38ec2bec29e1a37c2abc29b05321128c390c297590a3c100000000000006521c39f642fc3b1c3b5c3ac0c3a7ac2a6c38ec2a6c2b1c3a7c2b7c3b7c38dc2a2c38bc39f07362ac28508c28ec297c3b1c29ec3b94331c38955c380c3a9321ac393c28642c28c' + }, + { + value: + '\u0003\u0000\u0000\u00005èÆÕL]\u0012|Î¾ž\u001a7«›\u00052\u0011(ЗY\n<\u0010\u0000\u0000\u0000\u0000\u0000\u0000e!ßd/ñõì\f:z¦Î¦±ç·÷Í¢Ëß\u00076*…\bŽ—ñžùC1ÉUÀé2\u001aӆBŒ', + expected: + '0x0300000035c3a8c386c3954c5d127cc29dc38ec2bec29e1a37c2abc29b05321128c390c297590a3c100000000000006521c39f642fc3b1c3b5c3ac0c3a7ac2a6c38ec2a6c2b1c3a7c2b7c3b7c38dc2a2c38bc39f07362ac28508c28ec297c3b1c29ec3b94331c38955c380c3a9321ac393c28642c28c' + }, + ] + + it.each(tests, 'should return hexstring', (test) => { + expect(caver.utils.toHex(test.value)).to.be.equal(test.expected) + }) +}) + +describe('caver.utils.isTxHashStrict', () => { + const transactionHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' + context('CAVERJS-UNIT-ETC-162: input: valid strict transaction hex', () => { + const tests = [ + {hash: transactionHash, expected: true}, // all lower + {hash: transactionHash.toUpperCase(), expected: true}, // all upper + {hash: transactionHash.slice(0, 10) + transactionHash.slice(10).toUpperCase(), expected: true}, // mixed + ] + it.each(tests, 'should return true', (test) => { + expect(caver.utils.isTxHashStrict(test.hash)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-163: input: invalid strict transaction hex', () => { + const tests = [ + {hash: `00${transactionHash.slice(2)}`, expected: false}, // doesn't start with 0x + {hash: transactionHash.slice(2), expected: false}, // doesn't start with 0x + {hash: `${transactionHash.slice(0, 64)}ZZ`, expected: false}, // not hex + {hash: transactionHash.slice(0, 10), expected: false}, // length is not enough + ] + it.each(tests, 'should return false', (test) => { + expect(caver.utils.isTxHashStrict(test.hash)).to.be.equal(test.expected) + }) + }) +}) + +describe('caver.utils.isTxHash', () => { + const transactionHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' + context('CAVERJS-UNIT-ETC-164: input: valid transaction hex', () => { + const tests = [ + {hash: transactionHash, expected: true}, // all lower long + {hash: transactionHash.slice(2), expected: true}, // all lower short + {hash: transactionHash.toUpperCase(), expected: true}, // all upper long + {hash: transactionHash.slice(2).toUpperCase(), expected: true}, // all upper short + {hash: transactionHash.slice(0, 10) + transactionHash.slice(10).toUpperCase(), expected: true}, // mixed long + {hash: transactionHash.slice(2, 10) + transactionHash.slice(10).toUpperCase(), expected: true}, // mixed short + ] + it.each(tests, 'should return true', (test) => { + expect(caver.utils.isTxHash(test.hash)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-165: input: invalid transaction hex', () => { + const tests = [ + {hash: transactionHash.slice(4), expected: false}, // length is not enough (62) + {hash: `${transactionHash.slice(0, 62)}ZZ`, expected: false}, // not hex + {hash: `${transactionHash.slice(2)}00`, expected: false}, // length is too long (66 without 0x) + {hash: `${transactionHash}00`, expected: false}, // length is too long (68) + ] + it.each(tests, 'should return false', (test) => { + expect(caver.utils.isTxHash(test.tx)).to.be.equal(test.expected) + }) + }) +}) + +describe('caver.utils.toBN', () => { + context('CAVERJS-UNIT-ETC-118: input: valid value', () => { + const tests = [ + {value: 1, expected: '1'}, + {value: '1', expected: '1'}, + {value: '0x1', expected: '1'}, + {value: '0x01', expected: '1'}, + {value: 15, expected: '15'}, + {value: '15', expected: '15'}, + {value: '0xf', expected: '15'}, + {value: '0x0f', expected: '15'}, + {value: new BN('f', 16), expected: '15'}, + {value: -1, expected: '-1'}, + {value: '-1', expected: '-1'}, + {value: '-0x1', expected: '-1'}, + {value: '-0x01', expected: '-1'}, + {value: -15, expected: '-15'}, + {value: '-15', expected: '-15'}, + {value: '-0xf', expected: '-15'}, + {value: '-0x0f', expected: '-15'}, + { + value: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + expected: '115792089237316195423570985008687907853269984665640564039457584007913129639935' + }, + { + value: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', + expected: '115792089237316195423570985008687907853269984665640564039457584007913129639933' + }, + { + value: '-0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + expected: '-115792089237316195423570985008687907853269984665640564039457584007913129639935' + }, + { + value: '-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', + expected: '-115792089237316195423570985008687907853269984665640564039457584007913129639933' + }, + {value: 0, expected: '0'}, + {value: '0', expected: '0'}, + {value: '0x0', expected: '0'}, + {value: -0, expected: '0'}, + {value: '-0', expected: '0'}, + {value: '-0x0', expected: '0'}, + {value: new BN(0), expected: '0'} + ] + it.each(tests, 'should return BigNumber type', (test) => { + const bn = caver.utils.toBN(test.value) + expect(caver.utils.isBN(bn)).to.be.true + expect(bn.toString()).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-119: input: invalid value', () => { + it('should throw an error', () => { + let invalid = 'zzzz' + const errorMessage = `Error: [number-to-bn] while converting number "${invalid}" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported. Given value: "${invalid}"` + expect(() => caver.utils.toBN(invalid)).to.throw(errorMessage) + }) + }) +}) + +describe('caver.utils.hexToNumberString', () => { + context('CAVERJS-UNIT-ETC-120: input: number', () => { + const tests = [ + {value: 1234, expected: (1234).toString()}, + {value: 0x1234, expected: (0x1234).toString()}, + {value: 0xea, expected: (0xea).toString()}, + {value: 100000, expected: '100000'}, + ] + it.each(tests, 'should return numberString', (test) => { + expect(caver.utils.hexToNumberString(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-121: input: numberString', () => { + const tests = [ + {value: '1234', expected: (1234).toString()}, + ] + it.each(tests, 'should return numberString', (test) => { + expect(caver.utils.hexToNumberString(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-122: input: hexString', () => { + const tests = [ + {value: '0x1234', expected: (0x1234).toString()}, + {value: '0x3e8', expected: '1000'}, + {value: '0x1f0fe294a36', expected: '2134567897654'}, + ] + it.each(tests, 'should return numberString', (test) => { + expect(caver.utils.hexToNumberString(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-123: input: invalid hexString', () => { + it('should throw an error', () => { + let invalid = 'zzzz' + const errorMessage = `Error: [number-to-bn] while converting number "${invalid}" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported. Given value: "${invalid}"` + expect(() => caver.utils.hexToNumberString(invalid)).to.throw(errorMessage) + }) + }) +}) + + + +// caver.utils.hexToNumber +describe('caver.utils.hexToNumber', () => { + context('CAVERJS-UNIT-ETC-124: input: valid value', () => { + const tests = [ + {value: 1234, expected: 1234}, + {value: '1234', expected: 1234}, + {value: 0x1234, expected: 4660}, + {value: 0xea, expected: 234}, + {value: '0xea', expected: 234}, + ] + it.each(tests, 'should return number', (test) => { + expect(caver.utils.hexToNumber(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-125: input: invalid value', () => { + it('should throw an error', () => { + const invalid = 'zzzz' + const errorMessage = `Error: [number-to-bn] while converting number "${invalid}" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported. Given value: "${invalid}"` + expect(() => caver.utils.hexToNumber(invalid)).to.throw(errorMessage) + }) + }) +}) + +describe('caver.utils.numberToHex', () => { + const toHexStr = (number) => '0x' + number.toString(16).toLowerCase() + + context('CAVERJS-UNIT-ETC-126: input: valid number', () => { + const tests = [ + {value: 1234, expected: toHexStr(1234)}, + {value: '1234', expected: toHexStr(1234)}, + {value: 0x1234, expected: toHexStr(4660)}, + {value: '0x1234', expected: toHexStr(4660)}, + {value: new BN(234), expected: toHexStr(234)}, + {value: new BN('234'), expected: toHexStr(234)}, + {value: new BigNumber(234), expected: toHexStr(234)}, + {value: new BigNumber('234'), expected: toHexStr(234)}, + {value: 1, expected: '0x1'}, + {value: '21345678976543214567869765432145647586', expected: '0x100f073a3d694d13d1615dc9bc3097e2'}, + {value: '1', expected: '0x1'}, + {value: '0x1', expected: '0x1'}, + {value: '0x01', expected: '0x1'}, + {value: 15, expected: '0xf'}, + {value: '15', expected: '0xf'}, + {value: '0xf', expected: '0xf'}, + {value: '0x0f', expected: '0xf'}, + {value: -1, expected: '-0x1'}, + {value: '-1', expected: '-0x1'}, + {value: '-0x1', expected: '-0x1'}, + {value: '-0x01', expected: '-0x1'}, + {value: -15, expected: '-0xf'}, + {value: '-15', expected: '-0xf'}, + {value: '-0xf', expected: '-0xf'}, + {value: '-0x0f', expected: '-0xf'}, + { + value: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + expected: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + }, + { + value: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', + expected: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd' + }, + { + value: '-0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + expected: '-0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + }, + { + value: '-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', + expected: '-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd' + }, + {value: 0, expected: '0x0'}, + {value: '0', expected: '0x0'}, + {value: '0x0', expected: '0x0'}, + {value: -0, expected: '0x0'}, + {value: '-0', expected: '0x0'}, + {value: '-0x0', expected: '0x0'} + ] + it.each(tests, 'should return hexString', (test) => { + expect(caver.utils.numberToHex(test.value)).to.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-127: input: invalid number', () => { + it('should throw an error', () => { + const invalid = 'zzzz' + const errorMessage = `Given input "${invalid}" is not a number.` + + expect(() => caver.utils.numberToHex(invalid)).to.throw(errorMessage) + }) + }) +}) + +describe('caver.utils.hexToUtf8', () => { + context('CAVERJS-UNIT-ETC-128: input: valid hexString', () => { + const tests = [ + {value: '0x49206861766520313030e282ac', expected: 'I have 100€'}, + {value: '0x48656c6c6f2c204b6c6179746e', expected: 'Hello, Klaytn'}, + {value: '0x486565c3a4c3b6c3b6c3a4f09f9185443334c99dc9a33234d084cdbd2d2e2cc3a4c3bc2b232f', expected: 'Heeäööä👅D34ɝɣ24Єͽ-.,äü+#/'}, + {value: '0x6d79537472696e67', expected: 'myString'}, + {value: '0x6d79537472696e6700', expected: 'myString'}, + {value: '0x65787065637465642076616c7565000000000000000000000000000000000000', expected: 'expected value'}, + {value: '0x000000000000000000000000000000000000657870656374000065642076616c7565', expected: 'expect\u0000\u0000ed value'} + ] + it.each(tests, 'should return utf8 string', (test) => { + expect(caver.utils.hexToUtf8(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-129: input: invalid hexString', () => { + it('should throw an error', () => { + const invalid = 'zzzz' + const errorMessage = `The parameter "${invalid}" must be a valid HEX string.` + + expect(() => caver.utils.hexToUtf8(invalid)).to.throw(errorMessage) + }) + }) +}) + +describe('caver.utils.hexToAscii', () => { + + context('CAVERJS-UNIT-ETC-130: input: valid hexString', () => { + const tests = [ + {value: '0x4920686176652031303021', expected: 'I have 100!'}, + {value: '0x48656c6c6f2c204b6c6179746e', expected: 'Hello, Klaytn'}, + {value: '0x6d79537472696e67', expected: 'myString'}, + {value: '0x6d79537472696e6700', expected: 'myString\u0000'}, + { + value: '0x0300000035e8c6d54c5d127c9dcebe9e1a37ab9b05321128d097590a3c100000000000006521df642ff1f5ec0c3a7aa6cea6b1e7b7f7cda2cbdf07362a85088e97f19ef94331c955c0e9321ad386428c', + expected: '\u0003\u0000\u0000\u00005èÆÕL]\u0012|Î¾ž\u001a7«›\u00052\u0011(ЗY\n<\u0010\u0000\u0000\u0000\u0000\u0000\u0000e!ßd/ñõì\f:z¦Î¦±ç·÷Í¢Ëß\u00076*…\bŽ—ñžùC1ÉUÀé2\u001aӆBŒ' + } + ] + it.each(tests, 'should return Ascii string', (test) => { + expect(caver.utils.hexToAscii(test.value)).to.be.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-131: input: invalid hexString', () => { + it('should throw an error', () => { + const invalid = 'zzzz' + const errorMessage = `The parameter must be a valid HEX string.` + + expect(() => caver.utils.hexToAscii(invalid)).to.throw(errorMessage) + }) + }) +}) + +describe('CAVERJS-UNIT-ETC-132: caver.utils.utf8ToHex', () => { + const tests = [ + {value: 'I have 100€', expected: '0x49206861766520313030e282ac'}, + {value: 'Hello, Klaytn', expected: '0x48656c6c6f2c204b6c6179746e'}, + {value: 'Heeäööä👅D34ɝɣ24Єͽ-.,äü+#/', expected: '0x486565c3a4c3b6c3b6c3a4f09f9185443334c99dc9a33234d084cdbd2d2e2cc3a4c3bc2b232f'}, + {value: 'myString', expected: '0x6d79537472696e67'}, + {value: 'myString\u0000', expected: '0x6d79537472696e67'}, + {value: 'expected value\u0000\u0000\u0000', expected: '0x65787065637465642076616c7565'}, + {value: 'expect\u0000\u0000ed value\u0000\u0000\u0000', expected: '0x657870656374000065642076616c7565'}, + ] + it.each(tests, 'should return hexString', (test) => { + expect(caver.utils.utf8ToHex(test.value)).to.be.equal(test.expected) + }) +}) + +describe('CAVERJS-UNIT-ETC-133: caver.utils.asciiToHex', () => { + const tests = [ + {value: 'I have 100!', expected: '0x4920686176652031303021'}, + {value: 'Hello, Klaytn', expected: '0x48656c6c6f2c204b6c6179746e'}, + {value: 'myString', expected: '0x6d79537472696e67'}, + {value: 'myString\u0000', expected: '0x6d79537472696e6700'}, + { + value: '\u0003\u0000\u0000\u00005èÆÕL]\u0012|Î¾ž\u001a7«›\u00052\u0011(ЗY\n<\u0010\u0000\u0000\u0000\u0000\u0000\u0000e!ßd/ñõì\f:z¦Î¦±ç·÷Í¢Ëß\u00076*…\bŽ—ñžùC1ÉUÀé2\u001aӆBŒ', + expected: '0x0300000035e8c6d54c5d127c9dcebe9e1a37ab9b05321128d097590a3c100000000000006521df642ff1f5ec0c3a7aa6cea6b1e7b7f7cda2cbdf07362a85088e97f19ef94331c955c0e9321ad386428c' + } + ] + it.each(tests, 'should return hex String', (test) => { + expect(caver.utils.asciiToHex(test.value)).to.be.equal(test.expected) + }) +}) + + +describe('caver.utils.hexToBytes', () => { + + context('CAVERJS-UNIT-ETC-134: input: hexString \'0x000000ea\'', () => { + it('should return bytes', () => { + const hex = '0x000000ea' + + const expected = [0, 0, 0, 234] + const result = caver.utils.hexToBytes(hex) + expect(result).to.deep.equal(expected) + }) + }) + + context('CAVERJS-UNIT-ETC-135: input: invalid hexString', () => { + it('should throw an error', () => { + let invalid = 0x000000ea + let errorMessage = `Given value "${invalid.toString(16)}" is not a valid hex string.` + expect(() => caver.utils.hexToBytes(invalid)).to.throw(errorMessage) + + invalid = 'zzzz' + errorMessage = `Given value "${invalid}" is not a valid hex string.` + expect(() => caver.utils.hexToBytes(invalid)).to.throw(errorMessage) + }) + }) +}) + +describe('CAVERJS-UNIT-ETC-136: caver.utils.bytesToHex', () => { + const tests = [ + {value: [0, 0, 0, 234], expected: '0x000000ea'}, + {value: [234], expected: '0xea'} + ] + it.each(tests, 'should return byteArray', (test) => { + expect(caver.utils.bytesToHex(test.value)).deep.equal(test.expected) + }) +}) + +describe('caver.utils.toPeb', () => { + const unitMap = utils.unitMap + + context('CAVERJS-UNIT-ETC-137: input: various type', () => { + const tests = [ + {value: 1, expected: unitMap.KLAY}, + {value: '1', expected: unitMap.KLAY}, + {value: 123456789, expected: (new BigNumber(unitMap.KLAY * 123456789)).toFixed(0)}, + {value: '123456789', expected: (new BigNumber(unitMap.KLAY * 123456789)).toFixed(0)}, + {value: new BN(1), expected: unitMap.KLAY}, + {value: new BN('1'), expected: unitMap.KLAY}, + {value: new BN(123456789), expected: (new BigNumber(unitMap.KLAY * 123456789)).toFixed(0)}, + {value: new BN('123456789'), expected: (new BigNumber(unitMap.KLAY * 123456789)).toFixed(0)} + ] + it.each(tests, 'should return string', (test) => { + expect(caver.utils.toPeb(test.value).toString()).to.be.equal(test.expected.toString()) + }) + }) + + context('CAVERJS-UNIT-ETC-138: input: base unitmap', () => { + const tests = [ + {value: 1, unit: 'peb', expected: unitMap.peb}, + {value: 1, unit: 'kpeb', expected: unitMap.kpeb}, + {value: 1, unit: 'Mpeb', expected: unitMap.Mpeb}, + {value: 1, unit: 'Gpeb', expected: unitMap.Gpeb}, + {value: 1, unit: 'uKLAY', expected: unitMap.uKLAY}, + {value: 1, unit: 'mKLAY', expected: unitMap.mKLAY}, + {value: 1, unit: 'KLAY', expected: unitMap.KLAY}, + {value: 1, unit: 'kKLAY', expected: unitMap.kKLAY}, + {value: 1, unit: 'MKLAY', expected: unitMap.MKLAY}, + ] + it.each(tests, 'should return string', (test) => { + expect(caver.utils.toPeb(test.value, test.unit)).to.be.equal(test.expected) + }) + }) +}) + +describe('caver.utils.fromPeb', () => { + const unitMap = utils.unitMap + + context('CAVERJS-UNIT-ETC-139: fromPeb without unit', () => { + const tests = [ + {value: 1, peb: unitMap.KLAY}, + {value: '1', peb: unitMap.KLAY}, + {value: 123456789, peb: unitMap.KLAY}, + {value: '123456789', peb: unitMap.KLAY}, + {value: new BN(1), peb: unitMap.KLAY}, + {value: new BN('1'), peb: unitMap.KLAY}, + {value: new BN(123456789), peb: unitMap.KLAY}, + {value: new BN('123456789'), peb: unitMap.KLAY} + ] + it.each(tests, 'should return string based on unitMap', (test) => { + const bn = new BigNumber(test.peb) + const expected = (Math.pow(0.1, bn.e) * test.value).toFixed(bn.e) + + expect(caver.utils.fromPeb(test.value)).to.be.equal(expected) + } + ) + }) + + context('CAVERJS-UNIT-ETC-140: fromPeb with unit', () => { + const tests = [ + {value: 1, unit: 'peb', peb: unitMap.peb}, + {value: 1, unit: 'kpeb', peb: unitMap.kpeb}, + {value: 1, unit: 'Mpeb', peb: unitMap.Mpeb}, + {value: 1, unit: 'Gpeb', peb: unitMap.Gpeb}, + {value: 1, unit: 'uKLAY', peb: unitMap.uKLAY}, + {value: 1, unit: 'mKLAY', peb: unitMap.mKLAY}, + {value: 1, unit: 'KLAY', peb: unitMap.KLAY}, + {value: 1, unit: 'kKLAY', peb: unitMap.kKLAY}, + {value: 1, unit: 'MKLAY', peb: unitMap.MKLAY}, + ] + it.each(tests, 'should return string based on unitMap', (test) => { + const bn = new BigNumber(test.peb) + const expected = (Math.pow(0.1, bn.e) * test.value).toFixed(bn.e) + + expect(caver.utils.fromPeb(test.value, test.unit)).to.be.equal(expected) + } + ) + }) +}) + +describe('caver.utils.unitMap', () => { + const unitMap = utils.unitMap + + it('CAVERJS-UNIT-ETC-141: should return valid unitMap', () => { + const result = caver.utils.unitMap + expect(result).to.deep.equal(unitMap) + expect(result.peb).to.equals('1') + expect(result.kpeb).to.equals('1000') + expect(result.Mpeb).to.equals('1000000') + expect(result.Gpeb).to.equals('1000000000') + expect(result.Ston).to.equals('1000000000') + expect(result.uKLAY).to.equals('1000000000000') + expect(result.mKLAY).to.equals('1000000000000000') + expect(result.KLAY).to.equals('1000000000000000000') + expect(result.kKLAY).to.equals('1000000000000000000000') + expect(result.MKLAY).to.equals('1000000000000000000000000') + }) +}) + +describe('caver.utils.padLeft', () => { + context('CAVERJS-UNIT-ETC-142: input: hex', () => { + const tests = [ + {value: '0x3456ff', length: 20, expected: '0x000000000000003456ff'}, + {value: 0x3456ff, length: 20, expected: '0x000000000000003456ff'} + ] + it.each(tests, 'should be left-padded with 0', (test) => { + expect(caver.utils.padLeft(test.value, test.length)).to.equal(test.expected) + }) + }) + + context('CAVERJS-UNIT-ETC-143: input: string', () => { + const tests = [ + {value: 'Hello', length: 20, sign: 'x', expected: 'xxxxxxxxxxxxxxxHello'}, + ] + it.each(tests, 'should be left padded with x', (test) => { + expect(caver.utils.padLeft(test.value, test.length, test.sign)).to.equal(test.expected) + }) + }) +}) + +describe('caver.utils.padRight', () => { + context('input: hex', () => { + const tests = [ + {value: '0x3456ff', length: 20, expected: '0x3456ff00000000000000'}, + {value: 0x3456ff, length: 20, expected: '0x3456ff00000000000000'}, + ] + it.each(tests, 'should be right padded with 0', (test) => { + expect(caver.utils.padRight(test.value, test.length)).to.equal(test.expected) + }) + }) + + context('input: string', () => { + const tests = [ + {value: 'Hello', length: 20, sign: 'x', expected: 'Helloxxxxxxxxxxxxxxx'} + ] + it.each(tests, 'should be right padded with x', (test) => { + expect(caver.utils.padRight(test.value, test.length, test.sign)).to.equal(test.expected) + }) + }) +}) + +describe('CAVERJS-UNIT-ETC-144: caver.utils.toTwosComplement', () => { + const tests = [ + {value: '-1', expected: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'}, + {value: -1, expected: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'}, + {value: '0x1', expected: '0x0000000000000000000000000000000000000000000000000000000000000001'}, + {value: -15, expected: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1'}, + {value: '-0x1', expected: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'}, + {value: 1, expected: '0x0000000000000000000000000000000000000000000000000000000000000001'}, + {value: '1', expected: '0x0000000000000000000000000000000000000000000000000000000000000001'}, + {value: '0x01', expected: '0x0000000000000000000000000000000000000000000000000000000000000001'}, + {value: 15, expected: '0x000000000000000000000000000000000000000000000000000000000000000f'}, + {value: '15', expected: '0x000000000000000000000000000000000000000000000000000000000000000f'}, + {value: '0xf', expected: '0x000000000000000000000000000000000000000000000000000000000000000f'}, + {value: '0x0f', expected: '0x000000000000000000000000000000000000000000000000000000000000000f'}, + {value: new BN(0), expected: '0x0000000000000000000000000000000000000000000000000000000000000000'} + ] + it.each(tests, 'should return TwosComplement', (test) => { + expect(caver.utils.toTwosComplement(test.value)).to.equal(test.expected) + }) +}) + +describe('CAVERJS-UNIT-ETC-145: caver.utils.isHexPrefixed', () => { + it('caver.utils.isHexPrefixed should return boolean depends on parameter', ()=>{ + expect(caver.utils.isHexPrefixed('0x')).to.be.true + expect(caver.utils.isHexPrefixed('0x0x')).to.be.true + expect(caver.utils.isHexPrefixed('01')).to.be.false + expect(caver.utils.isHexPrefixed({})).to.be.false + }) +}) + +describe('CAVERJS-UNIT-ETC-146: caver.utils.addHexPrefix', () => { + it('caver.utils.addHexPrefix should return 0x hex format string', ()=>{ + expect(caver.utils.addHexPrefix('0x')).to.equals('0x') + expect(caver.utils.addHexPrefix('01')).to.equals('0x01') + expect(caver.utils.addHexPrefix('x')).to.equals('0xx') + expect(typeof(caver.utils.addHexPrefix({}))).to.equals('object') + }) +}) + +describe('CAVERJS-UNIT-ETC-147: caver.utils.stripHexPrefix', () => { + it('caver.utils.stripHexPrefix should strip 0x prefix and return string', ()=>{ + expect(caver.utils.stripHexPrefix('0x')).to.equals('') + expect(caver.utils.stripHexPrefix('01')).to.equals('01') + expect(caver.utils.stripHexPrefix('0x01')).to.equals('01') + expect(caver.utils.stripHexPrefix('0xx')).to.equals('x') + expect(typeof(caver.utils.stripHexPrefix({}))).to.equals('object') + }) +}) + +describe('caver.utils.toBuffer', () => { + it('CAVERJS-UNIT-ETC-148: caver.utils.toBuffer should return input when input is Buffer', ()=>{ + expect(caver.utils.toBuffer(Buffer.from('test Buffer'))).to.deep.equal(Buffer.from('test Buffer')) + }) + it('CAVERJS-UNIT-ETC-149: caver.utils.toBuffer should convert null or undefined to buffer', ()=>{ + expect(caver.utils.toBuffer(null)).to.deep.equal(Buffer.alloc(0)) + expect(caver.utils.toBuffer(undefined)).to.deep.equal(Buffer.alloc(0)) + }) + it('CAVERJS-UNIT-ETC-150: caver.utils.toBuffer should convert Array to buffer', ()=>{ + expect(caver.utils.toBuffer([1,2,3,4,5])).to.deep.equal(Buffer.from([1,2,3,4,5])) + expect(caver.utils.toBuffer([])).to.deep.equal(Buffer.alloc(0)) + }) + it('CAVERJS-UNIT-ETC-151: caver.utils.toBuffer should convert BN to buffer', ()=>{ + expect(caver.utils.toBuffer(new BN(1))).to.deep.equal(Buffer.from([1])) + expect(caver.utils.toBuffer(new BN(255)).toString('hex')).to.deep.equal('ff') + expect(caver.utils.toBuffer(new BN('ff', 16)).toString('hex')).to.deep.equal('ff') + expect(caver.utils.toBuffer(new BN('377', 8)).toString('hex')).to.deep.equal('ff') + expect(caver.utils.toBuffer(new BN('11111111', 2)).toString('hex')).to.deep.equal('ff') + }) + it('CAVERJS-UNIT-ETC-152: caver.utils.toBuffer should convert Object has toArray function to buffer', ()=>{ + expect(caver.utils.toBuffer({toArray: function () {return [1, 2, 3, 4, 5]}})).to.deep.equal(Buffer.from([1,2,3,4,5])) + }) + it('CAVERJS-UNIT-ETC-153: caver.utils.toBuffer should convert String to buffer', ()=>{ + expect(caver.utils.toBuffer('0x01').toString('hex')).to.deep.equal('01') + expect(caver.utils.toBuffer('0x1').toString('hex')).to.deep.equal('01') + expect(caver.utils.toBuffer('0x1234').toString('hex')).to.deep.equal('1234') + expect(caver.utils.toBuffer('0x12345').toString('hex')).to.deep.equal('012345') + expect(caver.utils.toBuffer('0x11')).to.deep.equal(Buffer.from([17])) + expect(caver.utils.toBuffer('0x')).to.deep.equal(Buffer.from([])) + }) + it('CAVERJS-UNIT-ETC-154: caver.utils.toBuffer should convert Number to buffer', ()=>{ + expect(caver.utils.toBuffer(1)).to.deep.equal(Buffer.from([1])) + expect(caver.utils.toBuffer(1).toString('hex')).to.deep.equal('01') + expect(caver.utils.toBuffer(100).toString('hex')).to.deep.equal('64') + }) + + it('CAVERJS-UNIT-ETC-155: caver.utils.toBuffer should throw error when input type is not supported with toBuffer function', ()=>{ + expect(()=>caver.utils.toBuffer({})).to.throw('To convert an object to a buffer, the toArray function must be implemented inside the object') + expect(()=>caver.utils.toBuffer({toArray: [1,2,3,4,5]})).to.throw('To convert an object to a buffer, the toArray function must be implemented inside the object') + }) + it('CAVERJS-UNIT-ETC-156: caver.utils.toBuffer should throw error when String is not 0x-prefixed', ()=>{ + expect(()=>caver.utils.toBuffer('010x')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) + expect(()=>caver.utils.toBuffer('01')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) + expect(()=>caver.utils.toBuffer('')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) + expect(()=>caver.utils.toBuffer('0xqwer')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) + expect(()=>caver.utils.toBuffer('qwer')).to.throw(`Failed to convert string to Buffer. 'toBuffer' function only supports 0x-prefixed hex string`) + }) +}) + +describe('CAVERJS-UNIT-ETC-157: caver.utils.numberToBuffer', () => { + it('caver.utils.numberToBuffer should convert number to buffer', ()=>{ + expect(caver.utils.numberToBuffer(6003400).toString('hex')).to.equals('5b9ac8') + expect(caver.utils.numberToBuffer(1).toString('hex')).to.equals('01') + expect(caver.utils.numberToBuffer(12345).toString('hex')).to.equals('3039') + expect(caver.utils.numberToBuffer(123456789).toString('hex')).to.equals('075bcd15') + expect(caver.utils.numberToBuffer(100000000).toString('hex')).to.equals('05f5e100') + expect(caver.utils.numberToBuffer(819263839023).toString('hex')).to.equals('bebfee1b2f') + }) +}) + +describe('caver.utils.isHexParameter', () => { + it('CAVERJS-UNIT-ETC-158: caver.utils.isHexParameter should return true if input is hex string', ()=>{ + expect(caver.utils.isHexParameter('0x01')).to.be.true + expect(caver.utils.isHexParameter('0xa')).to.be.true + expect(caver.utils.isHexParameter('0x256d774a7a1bbd469d4fb08545d171df1c755a78')).to.be.true + expect(caver.utils.isHexParameter('0x256d774a7a1bbd469d4fb08545d171df1c755a78171df1c755a78')).to.be.true + }) + + it('CAVERJS-UNIT-ETC-159: caver.utils.isHexParameter should return false if input is not hex string', ()=>{ + // string type input + expect(caver.utils.isHexParameter('')).to.be.false + expect(caver.utils.isHexParameter('1')).to.be.false + expect(caver.utils.isHexParameter('0xqwer')).to.be.false + expect(caver.utils.isHexParameter('10x')).to.be.false + expect(caver.utils.isHexParameter('0x14qr')).to.be.false + expect(caver.utils.isHexParameter('0x1!')).to.be.false + expect(caver.utils.isHexParameter(' 0x256d774a7a1bbd469d4fb08545d171df1c755a78')).to.be.false + // not string type input + expect(caver.utils.isHexParameter(null)).to.be.false + expect(caver.utils.isHexParameter(undefined)).to.be.false + expect(caver.utils.isHexParameter(true)).to.be.false + expect(caver.utils.isHexParameter(1)).to.be.false + expect(caver.utils.isHexParameter({})).to.be.false + expect(caver.utils.isHexParameter([])).to.be.false + expect(caver.utils.isHexParameter(Buffer.alloc(0))).to.be.false + expect(caver.utils.isHexParameter(new BN())).to.be.false + }) +}) + +describe('caver.utils.xyPointFromPublicKey', () => { + it('CAVERJS-UNIT-ETC-160: caver.utils.xyPointFromPublicKey should return x, y point from publicKey', ()=>{ + const account = caver.klay.accounts.create() + const publicKey = caver.klay.accounts.privateKeyToPublicKey(account.privateKey) + const xyPoint = caver.utils.xyPointFromPublicKey(publicKey) + expect(Array.isArray(xyPoint)).to.be.true + expect(xyPoint.length).to.equals(2) + expect(publicKey).to.equals(caver.utils.leftPad(xyPoint[0], 64)+caver.utils.leftPad(xyPoint[1], 64).slice(2)) + }) + + it('CAVERJS-UNIT-ETC-161: caver.utils.xyPointFromPublicKey should return x, y point remove leading zeros', ()=>{ + const publicKey1 = '0x046241c7524030e5b44fff78021e35227d708c8630757b35090d56527b615f605b8d366782c86dee49356be574e1172f75ef5ce5d03b6e8c17dbf10f3fa2d9a3' + const publicKey2 = '0xba7135b75cae89b958e7bb78009bda52f6a348150757cc078e3e5e5d25519c500ed4ccec1f78ba4e1c21c7b1e57751cec4cf42e3997a476e3ecbf360ad095336' + const publicKey3 = '0x12b97e6756861ac0257a240d985d761cee9ca7719a29c233c644cfcc421885000c8e4c69cdb71665377b9e8ffb702355ca53917e66c7444619049c3dd0252ab6' + const publicKey4 = '0x05b3b58259770871a1cc18534f2d438935fa2dcdb04116cbfbde8adfe858c23e50047c5aea3c2f55de7de04203f8fe8ccc3b491029338d038a7ef6d6903b302e' + + const xyPoint1 = caver.utils.xyPointFromPublicKey(publicKey1) + const xyPoint2 = caver.utils.xyPointFromPublicKey(publicKey2) + const xyPoint3 = caver.utils.xyPointFromPublicKey(publicKey3) + const xyPoint4 = caver.utils.xyPointFromPublicKey(publicKey4) + + expect(xyPoint1[0]).to.equals('0x46241c7524030e5b44fff78021e35227d708c8630757b35090d56527b615f60') + expect(xyPoint1[1]).to.equals('0x5b8d366782c86dee49356be574e1172f75ef5ce5d03b6e8c17dbf10f3fa2d9a3') + + expect(xyPoint2[0]).to.equals('0xba7135b75cae89b958e7bb78009bda52f6a348150757cc078e3e5e5d25519c50') + expect(xyPoint2[1]).to.equals('0xed4ccec1f78ba4e1c21c7b1e57751cec4cf42e3997a476e3ecbf360ad095336') + + expect(xyPoint3[0]).to.equals('0x12b97e6756861ac0257a240d985d761cee9ca7719a29c233c644cfcc42188500') + expect(xyPoint3[1]).to.equals('0xc8e4c69cdb71665377b9e8ffb702355ca53917e66c7444619049c3dd0252ab6') + + expect(xyPoint4[0]).to.equals('0x5b3b58259770871a1cc18534f2d438935fa2dcdb04116cbfbde8adfe858c23e') + expect(xyPoint4[1]).to.equals('0x50047c5aea3c2f55de7de04203f8fe8ccc3b491029338d038a7ef6d6903b302e') + }) +}) + +describe('caver.utils.isValidPublicKey', () => { + it('CAVERJS-UNIT-ETC-171: caver.utils.isValidPublicKey should true with valid uncompressed public key', ()=>{ + const account = caver.klay.accounts.create() + + const unCompressedPublicKey = caver.klay.accounts.privateKeyToPublicKey(account.privateKey) + + let isValid = caver.utils.isValidPublicKey(unCompressedPublicKey) + expect(isValid).to.be.true + }) + + it('CAVERJS-UNIT-ETC-172: caver.utils.isValidPublicKey should true with valid compressed public key', ()=>{ + const account = caver.klay.accounts.create() + + const unCompressedPublicKey = caver.klay.accounts.privateKeyToPublicKey(account.privateKey) + const compressed = caver.utils.compressPublicKey(unCompressedPublicKey) + + let isValid = caver.utils.isValidPublicKey(compressed) + expect(isValid).to.be.true + }) + + it('CAVERJS-UNIT-ETC-173: caver.utils.isValidPublicKey should false with invalid uncompressed public key', ()=>{ + const account = caver.klay.accounts.create() + + const unCompressedPublicKey = caver.klay.accounts.privateKeyToPublicKey(account.privateKey) + + let isValid = caver.utils.isValidPublicKey(unCompressedPublicKey.slice(1)) + expect(isValid).to.be.false + }) + + it('CAVERJS-UNIT-ETC-174: caver.utils.isValidPublicKey should false with invalid compressed public key', ()=>{ + const account = caver.klay.accounts.create() + + const unCompressedPublicKey = caver.klay.accounts.privateKeyToPublicKey(account.privateKey) + const compressed = caver.utils.compressPublicKey(unCompressedPublicKey) + + let isValid = caver.utils.isValidPublicKey(compressed.slice(1)) + expect(isValid).to.be.false + }) + + it('CAVERJS-UNIT-ETC-175: caver.utils.isValidPublicKey should false with invalid indicated compressed public key', ()=>{ + const account = caver.klay.accounts.create() + + const unCompressedPublicKey = caver.klay.accounts.privateKeyToPublicKey(account.privateKey) + let compressed = caver.utils.compressPublicKey(unCompressedPublicKey) + compressed = compressed.replace('0x', '') + compressed = compressed.slice(2) + compressed = '05' + compressed + + let isValid = caver.utils.isValidPublicKey(compressed) + expect(isValid).to.be.false + }) +}) + +describe('caver.utils.isValidRole', () => { + it('CAVERJS-UNIT-ETC-176: caver.utils.isValidRole should true with valid role', ()=>{ + let isValid = caver.utils.isValidRole('transactionKey') + expect(isValid).to.be.true + + isValid = caver.utils.isValidRole('updateKey') + expect(isValid).to.be.true + + isValid = caver.utils.isValidRole('feePayerKey') + expect(isValid).to.be.true + }) + + it('CAVERJS-UNIT-ETC-177: caver.utils.isValidRole should false with invalid role', ()=>{ + let isValid = caver.utils.isValidRole('invalid') + expect(isValid).to.be.false + + isValid = caver.utils.isValidRole(undefined) + expect(isValid).to.be.false + + isValid = caver.utils.isValidRole({}) + expect(isValid).to.be.false + }) +}) + +describe('caver.utils.isEmptySig', () => { + it('CAVERJS-UNIT-ETC-178: caver.utils.isEmptySig should true with default signatures', ()=>{ + let isDefault = caver.utils.isEmptySig(['0x01', '0x', '0x']) + expect(isDefault).to.be.true + + isDefault = caver.utils.isEmptySig([['0x01', '0x', '0x']]) + expect(isDefault).to.be.true + }) + + it('CAVERJS-UNIT-ETC-179: caver.utils.isEmptySig should false if signatures is not same with default signatures', ()=>{ + let isDefault = caver.utils.isEmptySig([['0x01', '0x', '0x'], ['0x01', '0x', '0x']]) + expect(isDefault).to.be.false + + isDefault = caver.utils.isEmptySig(['0x25', '0xb2a5a15550ec298dc7dddde3774429ed75f864c82caeb5ee24399649ad731be9', '0x29da1014d16f2011b3307f7bbe1035b6e699a4204fc416c763def6cefd976567']) + expect(isDefault).to.be.false + + isDefault = caver.utils.isEmptySig([['0x25', '0xb2a5a15550ec298dc7dddde3774429ed75f864c82caeb5ee24399649ad731be9', '0x29da1014d16f2011b3307f7bbe1035b6e699a4204fc416c763def6cefd976567']]) + expect(isDefault).to.be.false + }) + + it('CAVERJS-UNIT-ETC-180: caver.utils.isEmptySig should throw error with invalid length of signatures', ()=>{ + let expectedError = `Invalid signatures length: 6` + expect(() => caver.utils.isEmptySig(['0x01', '0x', '0x', '0x01', '0x', '0x'])).to.throws(expectedError) + expect(() => caver.utils.isEmptySig([['0x01', '0x', '0x', '0x01', '0x', '0x']])).to.throws(expectedError) + }) +}) \ No newline at end of file diff --git a/test/scenarioTest/accountKeyMultiSig.js b/test/scenarioTest/accountKeyMultiSig.js new file mode 100644 index 00000000..8dbf0d98 --- /dev/null +++ b/test/scenarioTest/accountKeyMultiSig.js @@ -0,0 +1,648 @@ +/* + Copyright 2019 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 . +*/ + +require('it-each')({ testPerIteration: true }) +const { expect } = require('../extendedChai') + +const testRPCURL = require('../testrpc') +const Caver = require('../../index.js') + +let caver +let sender, payer, account +let contractAddress +let legacyKey + +describe('Scenario test with AccountWithAccountKeyMultiSig', () => { + before(() => { + caver = new Caver(testRPCURL) + + let senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 + ? '0x' + process.env.privateKey + : process.env.privateKey + + sender = caver.klay.accounts.wallet.add(senderPrvKey) + }) + + context('1. Prepare for testing', () => { + it('Create test accounts', async () => {// Send KLAY to test account + account = caver.klay.accounts.create() + legacyKey = account.privateKey + let txObject = { + from: sender.address, + to: account.address, + value: caver.utils.toPeb(10, 'KLAY'), + gas: 900000, + } + await caver.klay.sendTransaction(txObject) + + payer = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + txObject = { + from: sender.address, + to: payer.address, + value: caver.utils.toPeb(10, 'KLAY'), + gas: 900000, + } + await caver.klay.sendTransaction(txObject) + + // New private key to update + let keyArray = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + // Create an AccountKeyMultiSig instance that contains one private key string. + let newAccountKeyMultiSig = caver.klay.accounts.createAccountKeyMultiSig(keyArray) + // Get all internal key through the 'keys' property on an instance of AccountKey. + let newPublicKey = [ + caver.klay.accounts.privateKeyToPublicKey(newAccountKeyMultiSig.keys[0]), + caver.klay.accounts.privateKeyToPublicKey(newAccountKeyMultiSig.keys[1]), + caver.klay.accounts.privateKeyToPublicKey(newAccountKeyMultiSig.keys[2]) + ] + + // Create an AccountForUpdate containing about the address of account and key to update + let multiSigOption = { threshold: 2, weight: [1, 1, 1] } + let updator = caver.klay.accounts.createAccountForUpdate(account.address, newAccountKeyMultiSig, multiSigOption) + + // Set AccountForUpdate instance to 'key' + let updateTx = { + type: 'ACCOUNT_UPDATE', + from: account.address, + gas: 90000, + key: updator, + } + + // If the account's accountKey is AccountKeyMultiSig, the privateKey will be 0th of key array, and transactionKey, updateKey and feePayerKey are the same as an array. + // If the account does not exist inside the in-memory wallet, you must pass the privateKey parameter to signTransaction. + let signed = await caver.klay.accounts.signTransaction(updateTx, account.updateKey) + expect(signed.signatures.length).to.equals(1) + let receipt = await caver.klay.sendSignedTransaction(signed) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(4) + + expect(accountKey.key.threshold).to.equals(multiSigOption.threshold) + + expect(accountKey.key.keys[0].weight).to.equals(multiSigOption.weight[0]) + expect(accountKey.key.keys[1].weight).to.equals(multiSigOption.weight[1]) + expect(accountKey.key.keys[2].weight).to.equals(multiSigOption.weight[2]) + expect(accountKey.key.keys[0].weight).to.equals(multiSigOption.weight[0]) + expect(accountKey.key.keys[1].weight).to.equals(multiSigOption.weight[1]) + expect(accountKey.key.keys[2].weight).to.equals(multiSigOption.weight[2]) + + let xyPoint = caver.utils.xyPointFromPublicKey(newPublicKey[0]) + expect(accountKey.key.keys[0].key.x).to.equals(xyPoint[0]) + expect(accountKey.key.keys[0].key.y).to.equals(xyPoint[1]) + xyPoint = caver.utils.xyPointFromPublicKey(newPublicKey[1]) + expect(accountKey.key.keys[1].key.x).to.equals(xyPoint[0]) + expect(accountKey.key.keys[1].key.y).to.equals(xyPoint[1]) + xyPoint = caver.utils.xyPointFromPublicKey(newPublicKey[2]) + expect(accountKey.key.keys[2].key.x).to.equals(xyPoint[0]) + expect(accountKey.key.keys[2].key.y).to.equals(xyPoint[1]) + + // Add account to in-memory wallet + account = caver.klay.accounts.createWithAccountKey(account.address, newAccountKeyMultiSig) + caver.klay.accounts.wallet.add(account) + + // Get account from in-memory wallet + let fromWallet = caver.klay.accounts.wallet.getAccount(account.address) + expect(fromWallet).not.to.undefined + + expect(fromWallet.address).to.equals(account.address) + expect(fromWallet.accountKeyType).to.equals('AccountKeyMultiSig') + + expect(fromWallet.privateKey).to.equals(account.privateKey) + expect(fromWallet.keys.length).to.equals(account.keys.length) + expect(fromWallet.transactionKey.length).to.equals(account.transactionKey.length) + expect(fromWallet.updateKey.length).to.equals(account.updateKey.length) + expect(fromWallet.feePayerKey.length).to.equals(account.feePayerKey.length) + + // Update payer account to AccountKeyMultiSig + multiSigOption = { threshold: 2, weight: [1, 1, 1] } + updator = caver.klay.accounts.createAccountForUpdate(payer.address, newAccountKeyMultiSig, multiSigOption) + updateTx = { + type: 'ACCOUNT_UPDATE', + from: payer.address, + gas: 90000, + key: updator, + } + receipt = await caver.klay.sendTransaction(updateTx) + expect(receipt.status).to.be.true + payer = caver.klay.accounts.wallet.updateAccountKey(payer.address, newAccountKeyMultiSig) + }).timeout(200000) + }) + + context('2. Send VALUE_TRANSFER transaction with AccountWithAccountKeyMultiSig', () => { + it('VALUE_TRANSFER testing', async () => {// Send KLAY to test account + let txObject = { + type: 'VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + } + // If the account exists inside the in-memory wallet, you do not need to pass the privateKey parameter to signTransaction. + // The transactionKey of account will be used + let senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(3) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + } + // If the account exists inside the in-memory wallet, you do not need to pass the privateKey parameter to signTransaction. + // The transactionKey of account will be used + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(3) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address, payer.feePayerKey[0]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + let feePayerSigned2 = await caver.klay.accounts.feePayerSignTransaction(feePayerSigned.rawTransaction, payer.address, payer.feePayerKey[1]) + expect(feePayerSigned2.feePayerSignatures.length).to.equals(2) + let feePayerSigned3 = await caver.klay.accounts.feePayerSignTransaction(feePayerSigned2.rawTransaction, payer.address, payer.feePayerKey[2]) + expect(feePayerSigned3.feePayerSignatures.length).to.equals(3) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned3) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + feeRatio: 50, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(3) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(3) + + let combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, feePayerSigned.rawTransaction]) + expect(combined.signatures.length).to.equals(3) + expect(combined.feePayerSignatures.length).to.equals(3) + + receipt = await caver.klay.sendSignedTransaction(combined) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('3. Send VALUE_TRANSFER_MEMO transaction with AccountWithAccountKeyMultiSig', () => { + it('VALUE_TRANSFER_MEMO testing', async () => {// Send KLAY to test account + let txObject = { + type: 'VALUE_TRANSFER_MEMO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'value transfer memo', + gas: 90000, + } + // Sign transaction with sender + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(3) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_MEMO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'fee delegated value transfer memo', + gas: 900000, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[1]]) + expect(senderSigned.signatures.length).to.equals(2) + txObject.signatures = senderSigned.signatures + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, [payer.feePayerKey[0], payer.feePayerKey[1]]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + let feePayerSigned2 = await caver.klay.accounts.feePayerSignTransaction(feePayerSigned.rawTransaction, payer.address, [payer.feePayerKey[1], payer.feePayerKey[2]]) + expect(feePayerSigned2.feePayerSignatures.length).to.equals(3) + + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned2.feePayerSignatures + + senderSigned = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[2]]) + expect(senderSigned.signatures.length).to.equals(3) + + receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_MEMO_WITH_RATIO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'fee delegated value transfer memo', + gas: 900000, + feeRatio: 10, + } + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(3) + + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + senderSigned = await caver.klay.accounts.signTransaction(feePayerSigned.rawTransaction) + expect(senderSigned.signatures.length).to.equals(3) + + receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('4. Send ACCOUNT_UPDATE transaction with AccountWithAccountKeyMultiSig', () => { + it('ACCOUNT_UPDATE testing', async () => {// Send KLAY to test account + // Update AccountKeyMultiSig -> AccountKeyRoleBased + let newKey = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + let options = { + transactionKey: { threshold: 1, weight: [1, 1] }, + updateKey: { threshold: 6, weight: [1, 2, 3] }, + feePayerKey: { threshold: 1, weight: [1, 1] } + } + updator = caver.klay.accounts.createAccountForUpdate(account.address, newKey, options) + + let txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 900000, + } + + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + + // Update AccountKeyRoleBased -> AccountKeyPublic + newKey = caver.klay.accounts.create().privateKey + let newPublicKey = caver.klay.accounts.privateKeyToPublicKey(newKey) + updator = caver.klay.accounts.createAccountForUpdateWithPublicKey(account.address, newPublicKey) + + txObject = { + type: 'FEE_DELEGATED_ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 900000, + } + + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.updateKey[0]) + expect(senderSigned.signatures.length).to.equals(1) + let senderSigned2 = await caver.klay.accounts.signTransaction(txObject, account.updateKey[1]) + expect(senderSigned2.signatures.length).to.equals(1) + let senderSigned3 = await caver.klay.accounts.signTransaction(txObject, account.updateKey[2]) + expect(senderSigned3.signatures.length).to.equals(1) + + let senderRawTransaction = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, senderSigned2.rawTransaction, senderSigned3.rawTransaction]) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderRawTransaction.rawTransaction, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(3) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(3) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + + // Update AccountKeyPublic -> AccountKeyMultiSig + newKey = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + newPublicKey = [ + caver.klay.accounts.privateKeyToPublicKey(newKey[0]), + caver.klay.accounts.privateKeyToPublicKey(newKey[1]), + caver.klay.accounts.privateKeyToPublicKey(newKey[2]), + caver.klay.accounts.privateKeyToPublicKey(newKey[3]) + ] + options = { threshold: 3, weight: [1, 1, 1, 1] } + updator = caver.klay.accounts.createAccountForUpdateWithPublicKey(account.address, newPublicKey, options) + + txObject = { + type: 'FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO', + from: account.address, + key: updator, + gas: 900000, + feeRatio: 30, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.updateKey) + expect(senderSigned.signatures.length).to.equals(1) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, payer.feePayerKey) + expect(feePayerSigned.feePayerSignatures.length).to.equals(3) + + txObject.signatures = senderSigned.signatures + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + let rawTransaction = await caver.klay.accounts.getRawTransactionWithSignatures(txObject) + + receipt = await caver.klay.sendSignedTransaction(rawTransaction) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(3) + + // Update accountKey in wallet + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + }).timeout(200000) + }) + + context('5. Send SMART_CONTRACT_DEPLOY transaction with AccountWithAccountKeyMultiSig', () => { + it('SMART_CONTRACT_DEPLOY testing', async () => {// Send KLAY to test account + let txObject = { + type: 'SMART_CONTRACT_DEPLOY', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey.slice(0, 3)) + expect(senderSigned.signatures.length).to.equals(3) + + senderSigned = await caver.klay.accounts.signTransaction(senderSigned.rawTransaction, account.transactionKey[3]) + expect(senderSigned.signatures.length).to.equals(4) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(4) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(4) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(3) + + txObject.signatures = senderSigned.signatures + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + receipt = await caver.klay.sendSignedTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(4) + expect(receipt.feePayerSignatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY_WITH_RATIO', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + feeRatio: 30, + } + + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey[0]) + expect(senderSigned.signatures.length).to.equals(1) + + let senderSigned2 = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[1], account.transactionKey[2]]) + expect(senderSigned2.signatures.length).to.equals(2) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(3) + + let combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, senderSigned2.rawTransaction, feePayerSigned.rawTransaction]) + expect(combined.signatures.length).to.equals(3) + expect(combined.feePayerSignatures.length).to.equals(3) + + receipt = await caver.klay.sendSignedTransaction(combined) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(3) + + contractAddress = receipt.contractAddress + }).timeout(200000) + }) + + context('6. Send SMART_CONTRACT_EXECUTION transaction with AccountWithAccountKeyMultiSig', () => { + it('SMART_CONTRACT_EXECUTION testing', async () => {// Send KLAY to test account + let txObject = { + type: 'SMART_CONTRACT_EXECUTION', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 900000, + } + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(4) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 900000, + } + // Sign transaction with sender + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey[0]) + expect(senderSigned.signatures.length).to.equals(1) + + senderSigned = await caver.klay.accounts.signTransaction(senderSigned.rawTransaction, [account.transactionKey[1], account.transactionKey[2]]) + expect(senderSigned.signatures.length).to.equals(3) + + // Set signatures to transaction object + txObject.signatures = senderSigned.signatures + + let senderRaw = await caver.klay.accounts.getRawTransactionWithSignatures(txObject) + + // Send transaction object with signatures through sendSignedTransaction + receipt = await caver.klay.sendTransaction({ + senderRawTransaction: senderRaw.rawTransaction, + feePayer: payer.address + }) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION_WITH_RATIO', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 900000, + feeRatio: 50, + } + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(3) + + senderSigned = await caver.klay.accounts.signTransaction(feePayerSigned.rawTransaction, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(4) + + receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(4) + expect(receipt.feePayerSignatures.length).to.equals(3) + }).timeout(200000) + }) + + context('7. Send CANCEL transaction with AccountWithAccountKeyMultiSig', () => { + it('CANCEL testing', async () => {// Send KLAY to test account + let txObject = { + type: 'CANCEL', + from: account.address, + gas: 90000, + } + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(4) + + txObject = { + type: 'FEE_DELEGATED_CANCEL', + from: account.address, + gas: 900000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[1]]) + expect(senderSigned.signatures.length).to.equals(2) + + let senderSigned2 = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[2]]) + expect(senderSigned2.signatures.length).to.equals(2) + + let senderSigned3 = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[3]]) + expect(senderSigned3.signatures.length).to.equals(2) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, payer.feePayerKey) + expect(feePayerSigned.feePayerSignatures.length).to.equals(3) + + let combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, senderSigned2.rawTransaction, senderSigned3.rawTransaction, feePayerSigned.rawTransaction]) + expect(combined.signatures.length).to.equals(4) + expect(combined.feePayerSignatures.length).to.equals(3) + + receipt = await caver.klay.sendSignedTransaction(combined) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(4) + expect(receipt.feePayerSignatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_CANCEL_WITH_RATIO', + from: account.address, + gas: 900000, + feeRatio: 50, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[1]]) + expect(senderSigned.signatures.length).to.equals(2) + + senderSigned2 = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[2]]) + expect(senderSigned2.signatures.length).to.equals(2) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, [payer.feePayerKey[0], payer.feePayerKey[1]]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + + // Set signatures, feePayer and feePayerSignatures to transaction object + txObject.signatures = senderSigned.signatures.concat(senderSigned2.signatures) + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + let rawTransaction = await caver.klay.accounts.getRawTransactionWithSignatures(txObject) + expect(rawTransaction.signatures.length).to.equals(3) + expect(rawTransaction.feePayerSignatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(rawTransaction) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(2) + }).timeout(200000) + }) + + context('8. Send a transaction to the network with signatures that do not meet the threshold', () => { + it('Insufficient weight signatures testing', async () => {// Send KLAY to test account + let txObject = { + type: 'VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.keys[0]) + expect(senderSigned.signatures.length).to.equals(1) + + const expectedError = 'invalid transaction v, r, s values of the sender' + try { + await caver.klay.sendSignedTransaction(senderSigned) + } catch (e) { expect(e.message).to.include(expectedError) } + }).timeout(200000) + }) + + context('9. Account update with LegacyKey', () => { + it('Account update with legacy key testing', async () => {// Send KLAY to test account + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(account.address) + + const txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 90000, + } + // The updateKey in Account is used when signing. + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(1) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, legacyKey) + expect(account.privateKey).to.equals(legacyKey) + }).timeout(200000) + }) + + context('10. Account update with FailKey', () => { + it('Account update with fail key testing', async () => {// Send KLAY to test account + const updator = caver.klay.accounts.createAccountForUpdateWithFailKey(account.address) + + const txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 90000, + } + // The updateKey in Account is used when signing. + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(3) + }).timeout(200000) + }) +}) \ No newline at end of file diff --git a/test/scenarioTest/accountKeyPublic.js b/test/scenarioTest/accountKeyPublic.js new file mode 100644 index 00000000..365c86cf --- /dev/null +++ b/test/scenarioTest/accountKeyPublic.js @@ -0,0 +1,561 @@ +/* + Copyright 2019 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 . +*/ + +require('it-each')({ testPerIteration: true }) +const { expect } = require('../extendedChai') + +const testRPCURL = require('../testrpc') +const Caver = require('../../index.js') + +let caver +let sender, payer, account +let contractAddress +let legacyKey + +describe('Scenario test with AccountWithAccountKeyPublic', () => { + before(() => { + caver = new Caver(testRPCURL) + + let senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 + ? '0x' + process.env.privateKey + : process.env.privateKey + + sender = caver.klay.accounts.wallet.add(senderPrvKey) + }) + + context('1. Prepare for testing', () => { + it('Create test accounts', async () => {// Send KLAY to test account + // Send KLAY to test account + account = caver.klay.accounts.create() + legacyKey = account.privateKey + let txObject = { + from: sender.address, + to: account.address, + value: caver.utils.toPeb(10, 'KLAY'), + gas: 900000, + } + await caver.klay.sendTransaction(txObject) + + payer = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + txObject = { + from: sender.address, + to: payer.address, + value: caver.utils.toPeb(10, 'KLAY'), + gas: 900000, + } + await caver.klay.sendTransaction(txObject) + + // New private key to update + let newPrivateKey = caver.klay.accounts.create().privateKey + // Create an AccountKeyPublic instance that contains one private key string. + let newAccountKeyPublic = caver.klay.accounts.createAccountKeyPublic(newPrivateKey) + // Get all internal key through the 'keys' property on an instance of AccountKey. + let newPublicKey = caver.klay.accounts.privateKeyToPublicKey(newAccountKeyPublic.keys) + + // Create an AccountForUpdate containing about the address of account and key to update + let updator = caver.klay.accounts.createAccountForUpdate(account.address, newAccountKeyPublic) + + // Set AccountForUpdate instance to 'key' + let updateTx = { + type: 'ACCOUNT_UPDATE', + from: account.address, + gas: 90000, + key: updator, + } + + // If the account's accountKey is AccountKeyPublic, the privateKey, transactionKey, updateKey and feePayerKey are the same. + // If the account does not exist inside the in-memory wallet, you must pass the privateKey parameter to signTransaction. + let signed = await caver.klay.accounts.signTransaction(updateTx, account.updateKey) + let receipt = await caver.klay.sendSignedTransaction(signed) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + let xyPoint = caver.utils.xyPointFromPublicKey(newPublicKey) + expect(accountKey.keyType).to.equals(2) + expect(accountKey.key.x).to.equals(xyPoint[0]) + expect(accountKey.key.y).to.equals(xyPoint[1]) + + // Add account to in-memory wallet + account = caver.klay.accounts.createWithAccountKey(account.address, newAccountKeyPublic) + expect(caver.klay.accounts.isDecoupled(account.keys, account.address)).to.be.true + caver.klay.accounts.wallet.add(account) + + // Get account from in-memory wallet + let fromWallet = caver.klay.accounts.wallet.getAccount(account.address) + expect(fromWallet).not.to.undefined + + expect(fromWallet.address).to.equals(account.address) + expect(fromWallet.accountKeyType).to.equals('AccountKeyPublic') + + expect(fromWallet.privateKey).to.equals(account.privateKey) + expect(fromWallet.keys).to.equals(account.keys) + expect(fromWallet.transactionKey).to.equals(account.transactionKey) + expect(fromWallet.updateKey).to.equals(account.updateKey) + expect(fromWallet.feePayerKey).to.equals(account.feePayerKey) + + // Update payer account to AccountKeyPublic + updator = caver.klay.accounts.createAccountForUpdate(payer.address, newAccountKeyPublic) + updateTx = { + type: 'ACCOUNT_UPDATE', + from: payer.address, + gas: 90000, + key: updator, + } + receipt = await caver.klay.sendTransaction(updateTx) + expect(receipt.status).to.be.true + payer = caver.klay.accounts.wallet.updateAccountKey(payer.address, newAccountKeyPublic) + }).timeout(200000) + }) + + context('2. Send VALUE_TRANSFER transaction with AccountWithAccountKeyPublic', () => { + it('VALUE_TRANSFER testing', async () => {// Send KLAY to test account + let txObject = { + type: 'VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 90000, + } + // If the account exists inside the in-memory wallet, you do not need to pass the privateKey parameter to signTransaction. + // The transactionKey of account will be used + let senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 90000, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 90000, + feeRatio: 50, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + let combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, feePayerSigned.rawTransaction]) + expect(combined.signatures.length).to.equals(1) + expect(combined.feePayerSignatures.length).to.equals(1) + + receipt = await caver.klay.sendSignedTransaction(combined) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + }).timeout(200000) + }) + + context('3. Send VALUE_TRANSFER_MEMO transaction with AccountWithAccountKeyPublic', () => { + it('VALUE_TRANSFER_MEMO testing', async () => {// Send KLAY to test account + let txObject = { + type: 'VALUE_TRANSFER_MEMO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'fee delegated value transfer memo', + gas: 90000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(1) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_MEMO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'fee delegated value transfer memo', + gas: 90000, + } + // Sign transaction with sender (The transactionKey of account will be used) + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + // Sign transaction with fee payer (The feePayerKey of account will be used) + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_MEMO_WITH_RATIO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'fee delegated value transfer memo', + gas: 90000, + feeRatio: 10, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, payer.feePayerKey) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + txObject.signatures = senderSigned.signatures + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + receipt = await caver.klay.sendSignedTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + }).timeout(200000) + }) + + context('4. Send ACCOUNT_UPDATE transaction with AccountWithAccountKeyPublic', () => { + it('ACCOUNT_UPDATE testing', async () => {// Send KLAY to test account + // Update AccountKeyPublic -> AccountKeyMultiSig + let newKey = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + let options = { threshold: 2, weight: [1, 2, 1] } + let updator = caver.klay.accounts.createAccountForUpdate(account.address, newKey, options) + + let txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 900000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(1) + + txObject.signatures = senderSigned.signatures + + let receipt = await caver.klay.sendSignedTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + + // Update accountKey in wallet + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + + // Update AccountKeyMultiSig -> AccountKeyRoleBased + newKey = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + options = { + transactionKey: { threshold: 1, weight: [1, 1] }, + updateKey: { threshold: 6, weight: [1, 2, 3] }, + feePayerKey: { threshold: 1, weight: [1, 1] } + } + updator = caver.klay.accounts.createAccountForUpdate(account.address, newKey, options) + + txObject = { + type: 'FEE_DELEGATED_ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 900000, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(3) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(1) + + // Update accountKey in wallet + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + + // Update AccountKeyRoleBased -> AccountKeyPublic + newKey = caver.klay.accounts.create().privateKey + newPublicKey = caver.klay.accounts.privateKeyToPublicKey(newKey) + updator = caver.klay.accounts.createAccountForUpdateWithPublicKey(account.address, newPublicKey) + + txObject = { + type: 'FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO', + from: account.address, + key: updator, + gas: 900000, + feeRatio: 30, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.updateKey) + expect(senderSigned.signatures.length).to.equals(3) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, payer.feePayerKey) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + txObject.signatures = senderSigned.signatures + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + let rawTransaction = await caver.klay.accounts.getRawTransactionWithSignatures(txObject) + + receipt = await caver.klay.sendSignedTransaction(rawTransaction) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(1) + + // Update accountKey in wallet + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + }).timeout(200000) + }) + + context('5. Send SMART_CONTRACT_DEPLOY transaction with AccountWithAccountKeyPublic', () => { + it('SMART_CONTRACT_DEPLOY testing', async () => {// Send KLAY to test account + let txObject = { + type: 'SMART_CONTRACT_DEPLOY', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(1) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + txObject.signatures = senderSigned.signatures + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + receipt = await caver.klay.sendSignedTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY_WITH_RATIO', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + feeRatio: 20, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(1) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + let combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, feePayerSigned.rawTransaction]) + expect(combined.signatures.length).to.equals(1) + expect(combined.feePayerSignatures.length).to.equals(1) + + receipt = await caver.klay.sendSignedTransaction(combined.rawTransaction) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + contractAddress = receipt.contractAddress + }).timeout(200000) + }) + + context('6. Send SMART_CONTRACT_EXECUTION transaction with AccountWithAccountKeyPublic', () => { + it('SMART_CONTRACT_EXECUTION testing', async () => {// Send KLAY to test account + let txObject = { + type: 'SMART_CONTRACT_EXECUTION', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 90000, + } + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 90000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(1) + + receipt = await caver.klay.sendTransaction({ + senderRawTransaction: senderSigned.rawTransaction, + feePayer: payer.address + }) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION_WITH_RATIO', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 90000, + feeRatio: 50, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + txObject.signatures = senderSigned.signatures + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, payer.feePayerKey) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + }).timeout(200000) + }) + + context('7. Send CANCEL transaction with AccountWithAccountKeyPublic', () => { + it('CANCEL testing', async () => {// Send KLAY to test account + let txObject = { + type: 'CANCEL', + from: account.address, + gas: 90000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(1) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned.rawTransaction) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_CANCEL', + from: account.address, + gas: 90000, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + txObject.signatures = senderSigned.signatures + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + let rawTransaction = await caver.klay.accounts.getRawTransactionWithSignatures(txObject) + + receipt = await caver.klay.sendSignedTransaction(rawTransaction.rawTransaction) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + + txObject = { + type: 'FEE_DELEGATED_CANCEL_WITH_RATIO', + from: account.address, + gas: 90000, + feeRatio: 80, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(1) + + txObject.signatures = senderSigned.signatures + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, payer.feePayerKey) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(1) + }).timeout(200000) + }) + + context('8. Account update with LegacyKey', () => { + it('Account update with legacy key testing', async () => {// Send KLAY to test account + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(account.address) + + const txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 90000, + } + // The updateKey in Account is used when signing. + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(1) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, legacyKey) + expect(account.privateKey).to.equals(legacyKey) + }).timeout(200000) + }) + + context('9. Account update with FailKey', () => { + it('Account update with fail key testing', async () => {// Send KLAY to test account + const updator = caver.klay.accounts.createAccountForUpdateWithFailKey(account.address) + + const txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 90000, + } + // The updateKey in Account is used when signing. + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(3) + }).timeout(200000) + }) +}) \ No newline at end of file diff --git a/test/scenarioTest/accountKeyRoleBased.js b/test/scenarioTest/accountKeyRoleBased.js new file mode 100644 index 00000000..70c02a89 --- /dev/null +++ b/test/scenarioTest/accountKeyRoleBased.js @@ -0,0 +1,739 @@ +/* + Copyright 2019 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 . +*/ + +require('it-each')({ testPerIteration: true }) +const { expect } = require('../extendedChai') + +const testRPCURL = require('../testrpc') +const Caver = require('../../index.js') + +let caver +let sender, payer, account +let contractAddress +let legacyKey + +describe('Scenario test with AccountWithAccountKeyRoleBased', () => { + before(() => { + caver = new Caver(testRPCURL) + + let senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 + ? '0x' + process.env.privateKey + : process.env.privateKey + + sender = caver.klay.accounts.wallet.add(senderPrvKey) + }) + + context('1. Prepare for testing', () => { + it('Create test accounts', async () => { + // Send KLAY to test account + account = caver.klay.accounts.create() + legacyKey = account.privateKey + let txObject = { + from: sender.address, + to: account.address, + value: caver.utils.toPeb(10, 'KLAY'), + gas: 900000, + } + await caver.klay.sendTransaction(txObject) + + payer = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + txObject = { + from: sender.address, + to: payer.address, + value: caver.utils.toPeb(10, 'KLAY'), + gas: 900000, + } + await caver.klay.sendTransaction(txObject) + + // New private key to update + let keyObject = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + + // Create an AccountForUpdate containing about the address of account and key to update + let roleBasedOption = { + transactionKey: { threshold: 2, weight: [1, 1] }, + updateKey: { threshold: 2, weight: [1, 1, 1] }, + feePayerKey: { threshold: 3, weight: [1, 1, 1, 1] } + } + let updator = caver.klay.accounts.createAccountForUpdate(account.address, keyObject, roleBasedOption) + + // Set AccountForUpdate instance to 'key' + let updateTx = { + type: 'ACCOUNT_UPDATE', + from: account.address, + gas: 900000, + key: updator, + } + + // If the account's accountKey is AccountKeyRoleBased, the privateKey privateKey is the default key with the higher priority (transactionKey-> updateKey-> feePayerKey) of the defined role keys. + // transactionKey, updateKey, and feePayerKey each have different keys. + // If the account does not exist inside the in-memory wallet, you must pass the privateKey parameter to signTransaction. + let signed = await caver.klay.accounts.signTransaction(updateTx, account.updateKey) + let receipt = await caver.klay.sendSignedTransaction(signed) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(5) + + // Add account to in-memory wallet + account = caver.klay.accounts.createWithAccountKey(account.address, keyObject) + caver.klay.accounts.wallet.add(account) + + // Get account from in-memory wallet + let fromWallet = caver.klay.accounts.wallet.getAccount(account.address) + expect(fromWallet).not.to.undefined + + expect(fromWallet.address).to.equals(account.address) + expect(fromWallet.accountKeyType).to.equals('AccountKeyRoleBased') + + expect(fromWallet.privateKey).to.equals(account.privateKey) + expect(fromWallet.keys.transactionKey.length).to.equals(2) + expect(fromWallet.keys.updateKey.length).to.equals(3) + expect(fromWallet.keys.feePayerKey.length).to.equals(4) + expect(fromWallet.transactionKey.length).to.equals(2) + expect(fromWallet.updateKey.length).to.equals(3) + expect(fromWallet.feePayerKey.length).to.equals(4) + + keyObject = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + + roleBasedOption = { + transactionKey: { threshold: 3, weight: [1, 1, 1, 1] }, + updateKey: { threshold: 2, weight: [1, 1, 1] }, + feePayerKey: { threshold: 2, weight: [1, 1] }, + } + + updator = caver.klay.accounts.createAccountForUpdate(payer.address, keyObject, roleBasedOption) + updateTx = { + type: 'ACCOUNT_UPDATE', + from: payer.address, + gas: 900000, + key: updator, + } + receipt = await caver.klay.sendTransaction(updateTx) + expect(receipt.status).to.be.true + payer = caver.klay.accounts.wallet.updateAccountKey(payer.address, keyObject) + }).timeout(200000) + }) + + context('2. Send VALUE_TRANSFER transaction with AccountWithAccountKeyRoleBased', () => { + it('VALUE_TRANSFER testing', async () => { + let txObject = { + type: 'VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(2) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(2) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(2) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address, payer.feePayerKey[0]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + let feePayerSigned2 = await caver.klay.accounts.feePayerSignTransaction(feePayerSigned.rawTransaction, payer.address, payer.feePayerKey[1]) + expect(feePayerSigned2.feePayerSignatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned2) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(2) + expect(receipt.feePayerSignatures.length).to.equals(2) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + feeRatio: 50, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(2) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address, payer.feePayerKey[0]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + feePayerSigned2 = await caver.klay.accounts.feePayerSignTransaction(feePayerSigned.rawTransaction, payer.address, payer.feePayerKey[1]) + expect(feePayerSigned2.feePayerSignatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned2) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(2) + expect(receipt.feePayerSignatures.length).to.equals(2) + }).timeout(200000) + }) + + context('3. Send VALUE_TRANSFER_MEMO transaction with AccountWithAccountKeyRoleBased', () => { + it('VALUE_TRANSFER_MEMO testing', async () => { + let txObject = { + type: 'VALUE_TRANSFER_MEMO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'value transfer memo', + gas: 90000, + } + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(2) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_MEMO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'value transfer memo', + gas: 90000, + } + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + + let senderSigned = await caver.klay.accounts.signTransaction(feePayerSigned.rawTransaction, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(2) + expect(receipt.feePayerSignatures.length).to.equals(2) + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER_MEMO_WITH_RATIO', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + data: 'value transfer memo', + gas: 90000, + feeRatio: 10, + } + // Sign transaction with sender + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(2) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, [payer.feePayerKey[0], payer.feePayerKey[1]]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + + txObject.signatures = senderSigned.signatures + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + receipt = await caver.klay.sendSignedTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(2) + expect(receipt.feePayerSignatures.length).to.equals(2) + }).timeout(200000) + }) + + context('4. Send ACCOUNT_UPDATE transaction with AccountWithAccountKeyRoleBased', () => { + it('ACCOUNT_UPDATE testing', async () => { + // Update AccountKeyRoleBased -> AccountKeyPublic + let newKey = caver.klay.accounts.create().privateKey + let newPublicKey = caver.klay.accounts.privateKeyToPublicKey(newKey) + let updator = caver.klay.accounts.createAccountForUpdateWithPublicKey(account.address, newPublicKey) + + let txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 900000, + } + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + + // Update AccountKeyPublic -> AccountKeyMultiSig + newKey = [ + caver.klay.accounts.create().privateKey, + caver.klay.accounts.create().privateKey, + caver.klay.accounts.create().privateKey, + caver.klay.accounts.create().privateKey, + caver.klay.accounts.create().privateKey + ] + let options = { threshold: 4, weight: [1, 1, 1, 1, 1] } + updator = caver.klay.accounts.createAccountForUpdate(account.address, newKey, options) + + txObject = { + type: 'FEE_DELEGATED_ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 900000, + } + + // Sign transaction with sender + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.updateKey) + expect(senderSigned.signatures.length).to.equals(1) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address, [payer.feePayerKey[0]]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + let feePayerSigned2 = await caver.klay.accounts.feePayerSignTransaction(feePayerSigned.rawTransaction, payer.address, payer.feePayerKey) + expect(feePayerSigned2.feePayerSignatures.length).to.equals(2) + + let combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, feePayerSigned.rawTransaction, feePayerSigned2.rawTransaction]) + expect(combined.signatures.length).to.equals(1) + expect(combined.feePayerSignatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(combined) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(1) + expect(receipt.feePayerSignatures.length).to.equals(2) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + + // Update AccountKeyMultiSig -> AccountKeyRoleBased + newKey = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + } + options = { + transactionKey: { threshold: 3, weight: [2, 1, 2] }, + updateKey: { threshold: 6, weight: [1, 2, 3, 4] }, + feePayerKey: { threshold: 1, weight: [1, 1] } + } + updator = caver.klay.accounts.createAccountForUpdate(account.address, newKey, options) + + txObject = { + type: 'FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO', + from: account.address, + key: updator, + gas: 900000, + feeRatio: 30, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.updateKey) + expect(senderSigned.signatures.length).to.equals(5) + + let senderSigned2 = await caver.klay.accounts.signTransaction(txObject, [account.updateKey[0], account.updateKey[1]]) + expect(senderSigned2.signatures.length).to.equals(2) + + combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, senderSigned2.rawTransaction]) + expect(combined.signatures.length).to.equals(5) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(combined.rawTransaction, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + + combined = await caver.klay.accounts.combineSignatures([combined.rawTransaction, feePayerSigned.rawTransaction]) + expect(combined.signatures.length).to.equals(5) + expect(combined.feePayerSignatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(combined) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(5) + expect(receipt.feePayerSignatures.length).to.equals(2) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + + // Update AccountKeyRoleBased -> AccountKeyRoleBased + newKey = { + updateKey: [ + caver.klay.accounts.create().privateKey, + caver.klay.accounts.create().privateKey, + caver.klay.accounts.create().privateKey, + caver.klay.accounts.create().privateKey, + caver.klay.accounts.create().privateKey + ] + } + options = { updateKey: { threshold: 4, weight: [1, 1, 1, 1, 1] } } + updator = caver.klay.accounts.createAccountForUpdate(account.address, newKey, options) + + txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 900000, + } + receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(4) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, newKey) + }).timeout(200000) + }) + + context('5. Send SMART_CONTRACT_DEPLOY transaction with AccountWithAccountKeyRoleBased', () => { + it('SMART_CONTRACT_DEPLOY testing', async () => { + let txObject = { + type: 'SMART_CONTRACT_DEPLOY', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(3) + + senderSigned = await caver.klay.accounts.signTransaction(senderSigned.rawTransaction, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(3) + + let receipt = await caver.klay.sendSignedTransaction(senderSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + } + + senderSigned = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[1]]) + expect(senderSigned.signatures.length).to.equals(2) + + let senderSigned2 = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[1], account.transactionKey[2]]) + expect(senderSigned2.signatures.length).to.equals(2) + + txObject.signatures = senderSigned.signatures.concat(senderSigned2.signatures) + + let rawTransaction = await caver.klay.accounts.getRawTransactionWithSignatures(txObject) + expect(rawTransaction.signatures.length).to.equals(3) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction.rawTransaction, payer.address, payer.feePayerKey) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(2) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY_WITH_RATIO', + from: account.address, + data: '0x60806040526000805534801561001457600080fd5b5060405161016f38038061016f8339810180604052810190808051906020019092919080518201929190505050816000819055505050610116806100596000396000f3006080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60d2565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260d8565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060d06004803603810190808035906020019092919050505060e0565b005b60005481565b600043905090565b80600081905550505600a165627a7a723058206d2bc553736581b6387f9a0410856ca490fcdc7045a8991ad63a1fd71b651c3a00290000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013200000000000000000000000000000000000000000000000000000000000000', + gas: 900000, + value: 0, + feeRatio: 30, + } + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, payer.feePayerKey[0]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(1) + + let feePayerSigned2 = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address, payer.feePayerKey[1]) + expect(feePayerSigned2.feePayerSignatures.length).to.equals(1) + + senderSigned = await caver.klay.accounts.signTransaction(feePayerSigned.rawTransaction, [account.transactionKey[0], account.transactionKey[1]]) + expect(senderSigned.signatures.length).to.equals(2) + + senderSigned2 = await caver.klay.accounts.signTransaction(feePayerSigned2.rawTransaction, [account.transactionKey[1], account.transactionKey[2]]) + expect(senderSigned2.signatures.length).to.equals(2) + + let combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, senderSigned2.rawTransaction]) + expect(combined.signatures.length).to.equals(3) + expect(combined.feePayerSignatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(combined) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(2) + + contractAddress = receipt.contractAddress + }).timeout(200000) + }) + + context('6. Send SMART_CONTRACT_EXECUTION transaction with AccountWithAccountKeyRoleBased', () => { + it('SMART_CONTRACT_EXECUTION testing', async () => { + let txObject = { + type: 'SMART_CONTRACT_EXECUTION', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 900000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(3) + + txObject.signatures = senderSigned.signatures + + let receipt = await caver.klay.sendSignedTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 900000, + } + // Sign transaction with sender + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey[0]) + expect(senderSigned.signatures.length).to.equals(1) + + senderSigned = await caver.klay.accounts.signTransaction(senderSigned.rawTransaction, [account.transactionKey[1], account.transactionKey[2]]) + expect(senderSigned.signatures.length).to.equals(3) + + // Set signatures to transaction object + txObject.signatures = senderSigned.signatures + + let senderRaw = await caver.klay.accounts.getRawTransactionWithSignatures(txObject) + + // Send transaction object with signatures through sendSignedTransaction + receipt = await caver.klay.sendTransaction({ + senderRawTransaction: senderRaw.rawTransaction, + feePayer: payer.address + }) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(2) + + txObject = { + type: 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION_WITH_RATIO', + from: account.address, + to: contractAddress, + data: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000005', + gas: 900000, + feeRatio: 50, + } + + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + expect(senderSigned.signatures.length).to.equals(3) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + + txObject.signatures = senderSigned.signatures + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + receipt = await caver.klay.sendSignedTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(2) + }).timeout(200000) + }) + + context('7. Send CANCEL transaction with AccountWithAccountKeyRoleBased', () => { + it('CANCEL testing', async () => { + let txObject = { + type: 'CANCEL', + from: account.address, + gas: 90000, + } + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + + txObject = { + type: 'FEE_DELEGATED_CANCEL', + from: account.address, + gas: 900000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[2]]) + expect(senderSigned.signatures.length).to.equals(2) + + let senderSigned2 = await caver.klay.accounts.signTransaction(txObject, [account.transactionKey[0], account.transactionKey[1]]) + expect(senderSigned2.signatures.length).to.equals(2) + + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned2.rawTransaction, payer.address, [payer.feePayerKey[0], payer.feePayerKey[1]]) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + + let combined = await caver.klay.accounts.combineSignatures([senderSigned.rawTransaction, senderSigned2.rawTransaction, feePayerSigned.rawTransaction]) + expect(combined.signatures.length).to.equals(3) + expect(combined.feePayerSignatures.length).to.equals(2) + + receipt = await caver.klay.sendSignedTransaction(combined) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(2) + + txObject = { + type: 'FEE_DELEGATED_CANCEL_WITH_RATIO', + from: account.address, + gas: 900000, + feeRatio: 50, + } + senderSigned = await caver.klay.accounts.signTransaction(txObject) + expect(senderSigned.signatures.length).to.equals(3) + + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObject, payer.address) + expect(feePayerSigned.feePayerSignatures.length).to.equals(2) + + txObject.signatures = senderSigned.signatures + txObject.feePayer = payer.address + txObject.feePayerSignatures = feePayerSigned.feePayerSignatures + + let rawTransaction = await caver.klay.accounts.getRawTransactionWithSignatures(txObject) + + receipt = await caver.klay.sendSignedTransaction(rawTransaction.rawTransaction) + expect(receipt.status).to.be.true + expect(receipt.signatures.length).to.equals(3) + expect(receipt.feePayerSignatures.length).to.equals(2) + }).timeout(200000) + }) + + context('8. Send a transaction to the network with invalid role', () => { + it('invalid role signed testing', async () => { + let txObject = { + type: 'VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + } + let senderSigned = await caver.klay.accounts.signTransaction(txObject, account.updateKey) + + let expectedError = 'invalid transaction v, r, s values of the sender' + + try { + await caver.klay.sendSignedTransaction(senderSigned) + } catch (e) { expect(e.message).to.include(expectedError) } + + // insufficient weight + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey[0]) + + try { + await caver.klay.sendSignedTransaction(senderSigned) + } catch (e) { expect(e.message).to.include(expectedError) } + + txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: caver.klay.accounts.createAccountForUpdateWithLegacyKey(account.address), + gas: 900000, + } + + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + + try { + await caver.klay.sendSignedTransaction(senderSigned) + } catch (e) { expect(e.message).to.include(expectedError) } + + // insufficient weight + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.updateKey[0]) + + try { + await caver.klay.sendSignedTransaction(senderSigned) + } catch (e) { expect(e.message).to.include(expectedError) } + + txObject = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: account.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + } + + senderSigned = await caver.klay.accounts.signTransaction(txObject, account.transactionKey) + let feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address, payer.transactionKey) + + expectedError = 'invalid fee payer' + + try { + await caver.klay.sendSignedTransaction(feePayerSigned) + } catch (e) { expect(e.message).to.include(expectedError) } + + // insufficient weight + feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payer.address, payer.feePayerKey[0]) + + try { + await caver.klay.sendSignedTransaction(feePayerSigned) + } catch (e) { expect(e.message).to.include(expectedError) } + + }).timeout(200000) + }) + + context('9. Account update with RoleBased with legacyKey and failKey', () => { + it('Account update with roleBased with legacy key and fail key testing', async () => { + const keyObject = { + transactionKey: 'legacyKey', + feePayerKey: 'failKey' + } + const updator = caver.klay.accounts.createAccountForUpdate(account.address, keyObject) + + const txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 90000, + } + // The updateKey in Account is used when signing. + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(5) + expect(accountKey.key[0].keyType).to.equals(1) + expect(accountKey.key[1].keyType).to.equals(4) + expect(accountKey.key[2].keyType).to.equals(3) + }).timeout(200000) + }) + + context('10. Account update with LegacyKey', () => { + it('Account update with legacy key testing', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(account.address) + + const txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 90000, + } + // The updateKey in Account is used when signing. + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(1) + + account = caver.klay.accounts.wallet.updateAccountKey(account.address, legacyKey) + expect(account.privateKey).to.equals(legacyKey) + }).timeout(200000) + }) + + context('11. Account update with FailKey', () => { + it('Account update with fail key testing', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithFailKey(account.address) + + const txObject = { + type: 'ACCOUNT_UPDATE', + from: account.address, + key: updator, + gas: 90000, + } + // The updateKey in Account is used when signing. + let receipt = await caver.klay.sendTransaction(txObject) + expect(receipt.status).to.be.true + + // Check result of account update + let accountKey = await caver.klay.getAccountKey(account.address) + expect(accountKey.keyType).to.equals(3) + }).timeout(200000) + }) +}) \ No newline at end of file diff --git a/test/sendSignedTransaction.js b/test/sendSignedTransaction.js index bd1a664a..966d86df 100644 --- a/test/sendSignedTransaction.js +++ b/test/sendSignedTransaction.js @@ -22,24 +22,27 @@ const testRPCURL = require('./testrpc') var Caver = require('../index.js') const caver = new Caver(testRPCURL) -var senderPrvKey -var senderAddress +var senderPrvKey, payerPrvKey +var senderAddress, payerAddress var receiver before(() => { - senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 + senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 ? '0x' + process.env.privateKey : process.env.privateKey + payerPrvKey = process.env.privateKey2 && String(process.env.privateKey2).indexOf('0x') === -1 + ? '0x' + process.env.privateKey2 + : process.env.privateKey2 - caver.klay.accounts.wallet.add(senderPrvKey) + const sender = caver.klay.accounts.wallet.add(senderPrvKey) + senderAddress = sender.address + const payer = caver.klay.accounts.wallet.add(payerPrvKey) + payerAddress = payer.address - const sender = caver.klay.accounts.privateKeyToAccount(senderPrvKey) - senderAddress = sender.address - - receiver = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + receiver = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) }) -describe('caver.klay.sendSignedTransaction', (done) => { +describe('CAVERJS-UNIT-TX-581: caver.klay.sendSignedTransaction with valid non fee delegated transaction raw string', () => { it('should send successfully with valid rawTransaction', async () => { const txObj = { from: senderAddress, @@ -52,23 +55,167 @@ describe('caver.klay.sendSignedTransaction', (done) => { const receipt = await caver.klay.sendSignedTransaction(rawTransaction) expect(receipt).not.to.null - expect(receipt.blockHash).not.to.undefined - expect(receipt.blockNumber).not.to.undefined - expect(receipt.contractAddress).not.to.undefined - expect(receipt.from).not.to.undefined - expect(receipt.gas).not.to.undefined - expect(receipt.gasPrice).not.to.undefined - expect(receipt.gasUsed).not.to.undefined - expect(receipt.logs).not.to.undefined - expect(receipt.logsBloom).not.to.undefined - expect(receipt.nonce).not.to.undefined - expect(receipt.signatures).not.to.undefined - expect(receipt.status).equals(true) - expect(receipt.to).not.to.undefined - expect(receipt.transactionHash).not.to.undefined - expect(receipt.transactionIndex).not.to.undefined - expect(receipt.type).not.to.undefined - expect(receipt.typeInt).not.to.undefined - expect(receipt.value).not.to.undefined + + const keys = ['blockHash', 'blockNumber', 'contractAddress', 'from', 'gas', 'gasPrice', 'gasUsed', 'input', 'logs', 'logsBloom', 'nonce', 'senderTxHash', 'signatures', 'status', 'to', 'transactionHash', 'transactionIndex', 'type', 'typeInt', 'value'] + expect(Object.getOwnPropertyNames(receipt)).to.deep.equal(keys) + + expect(receipt.status).to.equals(true) + expect(receipt.senderTxHash).to.equals(receipt.transactionHash) + }).timeout(100000) +}) + +describe('CAVERJS-UNIT-TX-582: caver.klay.sendSignedTransaction with valid fee delegated transaction raw string', () => { + it('should send successfully with valid rawTransaction', async () => { + const txObj = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: senderAddress, + to: receiver.address, + value: 1, + gas: 900000, + } + + const senderSigned = await caver.klay.accounts.signTransaction(txObj) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned.rawTransaction) + + expect(receipt).not.to.null + + const keys = ['blockHash', 'blockNumber', 'contractAddress', 'feePayer', 'feePayerSignatures', 'from', 'gas', 'gasPrice', 'gasUsed', 'logs', 'logsBloom', 'nonce', 'senderTxHash', 'signatures', 'status', 'to', 'transactionHash', 'transactionIndex', 'type', 'typeInt', 'value'] + expect(Object.getOwnPropertyNames(receipt)).to.deep.equal(keys) + + expect(receipt.status).to.equals(true) + expect(receipt.senderTxHash).not.to.equals(receipt.transactionHash) + }).timeout(100000) +}) + +describe('CAVERJS-UNIT-TX-583: caver.klay.sendSignedTransaction with object which has non fee delegated transaction raw string', () => { + it('should send successfully with valid rawTransaction', async () => { + const txObj = { + from: senderAddress, + to: receiver.address, + value: 1, + gas: 900000, + } + + const senderSigned = await caver.klay.accounts.signTransaction(txObj, senderPrvKey) + const receipt = await caver.klay.sendSignedTransaction(senderSigned) + + expect(receipt).not.to.null + + const keys = ['blockHash', 'blockNumber', 'contractAddress', 'from', 'gas', 'gasPrice', 'gasUsed', 'input', 'logs', 'logsBloom', 'nonce', 'senderTxHash', 'signatures', 'status', 'to', 'transactionHash', 'transactionIndex', 'type', 'typeInt', 'value'] + expect(Object.getOwnPropertyNames(receipt)).to.deep.equal(keys) + + expect(receipt.status).to.equals(true) + expect(receipt.senderTxHash).to.equals(receipt.transactionHash) + }).timeout(100000) +}) + +describe('CAVERJS-UNIT-TX-584: caver.klay.sendSignedTransaction with object which has fee delegated transaction raw string', () => { + it('should send successfully with valid rawTransaction', async () => { + const txObj = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: senderAddress, + to: receiver.address, + value: 1, + gas: 900000, + } + + const senderSigned = await caver.klay.accounts.signTransaction(txObj) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + + expect(receipt).not.to.null + + const keys = ['blockHash', 'blockNumber', 'contractAddress', 'feePayer', 'feePayerSignatures', 'from', 'gas', 'gasPrice', 'gasUsed', 'logs', 'logsBloom', 'nonce', 'senderTxHash', 'signatures', 'status', 'to', 'transactionHash', 'transactionIndex', 'type', 'typeInt', 'value'] + expect(Object.getOwnPropertyNames(receipt)).to.deep.equal(keys) + + expect(receipt.status).to.equals(true) + expect(receipt.senderTxHash).not.to.equals(receipt.transactionHash) }).timeout(100000) }) + +describe('CAVERJS-UNIT-TX-585: caver.klay.sendSignedTransaction with transaction object which defines signatures', () => { + it('should send successfully with valid rawTransaction', async () => { + const txObj = { + from: senderAddress, + to: receiver.address, + value: 1, + gas: 900000, + } + + const senderSigned = await caver.klay.accounts.signTransaction(txObj, senderPrvKey) + txObj.signatures = senderSigned.signatures + + const receipt = await caver.klay.sendSignedTransaction(txObj) + + expect(receipt).not.to.null + + const keys = ['blockHash', 'blockNumber', 'contractAddress', 'from', 'gas', 'gasPrice', 'gasUsed', 'input', 'logs', 'logsBloom', 'nonce', 'senderTxHash', 'signatures', 'status', 'to', 'transactionHash', 'transactionIndex', 'type', 'typeInt', 'value'] + expect(Object.getOwnPropertyNames(receipt)).to.deep.equal(keys) + + expect(receipt.status).to.equals(true) + expect(receipt.senderTxHash).to.equals(receipt.transactionHash) + }).timeout(100000) +}) + +describe('CAVERJS-UNIT-TX-586: caver.klay.sendSignedTransaction with transaction object which defines signatures and feePayerSignatrues', () => { + it('should send successfully with valid rawTransaction', async () => { + const txObj = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: senderAddress, + to: receiver.address, + value: 1, + gas: 900000, + } + + const senderSigned = await caver.klay.accounts.signTransaction(txObj) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(txObj, payerAddress) + + txObj.signatures = senderSigned.signatures + txObj.feePayer = payerAddress + txObj.feePayerSignatures = feePayerSigned.feePayerSignatures + + const receipt = await caver.klay.sendSignedTransaction(txObj) + + expect(receipt).not.to.null + + const keys = ['blockHash', 'blockNumber', 'contractAddress', 'feePayer', 'feePayerSignatures', 'from', 'gas', 'gasPrice', 'gasUsed', 'logs', 'logsBloom', 'nonce', 'senderTxHash', 'signatures', 'status', 'to', 'transactionHash', 'transactionIndex', 'type', 'typeInt', 'value'] + expect(Object.getOwnPropertyNames(receipt)).to.deep.equal(keys) + + expect(receipt.status).to.equals(true) + expect(receipt.senderTxHash).not.to.equals(receipt.transactionHash) + }).timeout(100000) +}) + +describe('CAVERJS-UNIT-TX-587: caver.klay.sendSignedTransaction with fee payer transaction object which defines feePayerSignatrues', () => { + it('should send successfully with valid rawTransaction', async () => { + const txObj = { + type: 'FEE_DELEGATED_VALUE_TRANSFER', + from: senderAddress, + to: receiver.address, + value: 1, + gas: 900000, + } + + const senderSigned = await caver.klay.accounts.signTransaction(txObj) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, payerAddress) + + const feePayerTx = { + senderRawTransaction: senderSigned.rawTransaction, + feePayer: payerAddress, + feePayerSignatures: feePayerSigned.feePayerSignatures + } + + const receipt = await caver.klay.sendSignedTransaction(feePayerTx) + + expect(receipt).not.to.null + + const keys = ['blockHash', 'blockNumber', 'contractAddress', 'feePayer', 'feePayerSignatures', 'from', 'gas', 'gasPrice', 'gasUsed', 'logs', 'logsBloom', 'nonce', 'senderTxHash', 'signatures', 'status', 'to', 'transactionHash', 'transactionIndex', 'type', 'typeInt', 'value'] + expect(Object.getOwnPropertyNames(receipt)).to.deep.equal(keys) + + expect(receipt.status).to.equals(true) + expect(receipt.senderTxHash).not.to.equals(receipt.transactionHash) + }).timeout(100000) +}) \ No newline at end of file diff --git a/test/sendTransactionCallback.js b/test/sendTransactionCallback.js new file mode 100644 index 00000000..84afc162 --- /dev/null +++ b/test/sendTransactionCallback.js @@ -0,0 +1,75 @@ +/* + Copyright 2019 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 { expect } = require('chai') +const assert = require('assert') + +var Caver = require('../index.js') +const testRPCURL = require('./testrpc') +const caver = new Caver(testRPCURL) + +var senderPrvKey, senderAddress +var receiver + +before(() => { + senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 + ? '0x' + process.env.privateKey + : process.env.privateKey + + const sender = caver.klay.accounts.wallet.add(senderPrvKey) + senderAddress = sender.address + + receiver = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) +}) + +describe('sendTransaction with callback', () => { + it('CAVERJS-UNIT-TX-574: sendTransaction should only call callback once with the transaction hash', async () => { + const txObj = { + from: senderAddress, + to: receiver.address, + value: 1, + gas: 900000, + } + await caver.klay.sendTransaction(txObj, (error, result) => { + expect(error).to.be.null + expect(typeof result).to.be.equals('string') + }) + }).timeout(100000) + + it('CAVERJS-UNIT-TX-575: sendTransaction should call callback with error when error is occured during signTransaction', async () => { + // When try account update with invalid publicKey, error is occured during signTransaction + let e + const txObj = { + type: 'ACCOUNT_UPDATE', + from: senderAddress, + publicKey : caver.utils.randomHex(63), + gas: 900000, + } + + try { + await caver.klay.sendTransaction(txObj, (error, result) => { + e = error.message + expect(error).not.to.be.null + expect(result).to.be.undefined + }) + assert(false) + } catch(error) { + expect(error.message).to.equals(e) + } + }).timeout(100000) +}) \ No newline at end of file diff --git a/test/setProvider.js b/test/setProvider.js new file mode 100644 index 00000000..ba4ddfed --- /dev/null +++ b/test/setProvider.js @@ -0,0 +1,112 @@ +/* + Copyright 2019 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 Caver = require('../index') +const { expect } = require('./extendedChai') + +const baobabHost = 'https://api.baobab.klaytn.net:8651/' +const cypressHost = 'https://api.cypress.klaytn.net:8651/' +const baobabProvider = new Caver.providers.HttpProvider(baobabHost) +const cypressProvider = new Caver.providers.HttpProvider(cypressHost) + +describe('Test setProvider', () => { + it('CAVERJS-UNIT-ETC-166: If provider is not set, currentProvider must be null.', () => { + const caver = new Caver() + + expect(caver.klay.currentProvider).to.be.null + expect(caver.klay.net.currentProvider).to.be.null + expect(caver.klay.personal.currentProvider).to.be.null + expect(caver.klay.Contract.currentProvider).to.be.null + expect(caver.klay.accounts.currentProvider).to.be.null + }).timeout(10000) + + it('CAVERJS-UNIT-ETC-167: When passing host information as a parameter through a Caver contructor, the provider must be set.', () => { + const caver = new Caver(baobabHost) + + expect(caver.klay.currentProvider).not.to.be.null + expect(caver.klay.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.net.currentProvider).not.to.be.null + expect(caver.klay.net.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.personal.currentProvider).not.to.be.null + expect(caver.klay.personal.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.Contract.currentProvider).not.to.be.null + expect(caver.klay.Contract.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.accounts.currentProvider).not.to.be.null + expect(caver.klay.accounts.currentProvider.host).to.equals(baobabHost) + }).timeout(10000) + + it('CAVERJS-UNIT-ETC-168: When passing provider as a parameter through a Caver contructor, the provider must be set.', () => { + const caver = new Caver(baobabProvider) + + expect(caver.klay.currentProvider).not.to.be.null + expect(caver.klay.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.net.currentProvider).not.to.be.null + expect(caver.klay.net.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.personal.currentProvider).not.to.be.null + expect(caver.klay.personal.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.Contract.currentProvider).not.to.be.null + expect(caver.klay.Contract.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.accounts.currentProvider).not.to.be.null + expect(caver.klay.accounts.currentProvider.host).to.equals(baobabHost) + }).timeout(10000) + + it('CAVERJS-UNIT-ETC-169: When setting a provider with setProvider function, the currentProvider must be set appropriately.', () => { + const caver = new Caver() + caver.klay.setProvider(baobabProvider) + + expect(caver.klay.currentProvider).not.to.be.null + expect(caver.klay.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.net.currentProvider).not.to.be.null + expect(caver.klay.net.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.personal.currentProvider).not.to.be.null + expect(caver.klay.personal.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.Contract.currentProvider).not.to.be.null + expect(caver.klay.Contract.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.accounts.currentProvider).not.to.be.null + expect(caver.klay.accounts.currentProvider.host).to.equals(baobabHost) + }).timeout(10000) + + it('CAVERJS-UNIT-ETC-170: If provider is set already, currentProvider must change when new provider is set with setProvider function.', () => { + const caver = new Caver() + caver.klay.setProvider(baobabProvider) + + expect(caver.klay.currentProvider).not.to.be.null + expect(caver.klay.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.net.currentProvider).not.to.be.null + expect(caver.klay.net.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.personal.currentProvider).not.to.be.null + expect(caver.klay.personal.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.Contract.currentProvider).not.to.be.null + expect(caver.klay.Contract.currentProvider.host).to.equals(baobabHost) + expect(caver.klay.accounts.currentProvider).not.to.be.null + expect(caver.klay.accounts.currentProvider.host).to.equals(baobabHost) + + caver.klay.setProvider(cypressProvider) + + expect(caver.klay.currentProvider).not.to.be.null + expect(caver.klay.currentProvider.host).to.equals(cypressHost) + expect(caver.klay.net.currentProvider).not.to.be.null + expect(caver.klay.net.currentProvider.host).to.equals(cypressHost) + expect(caver.klay.personal.currentProvider).not.to.be.null + expect(caver.klay.personal.currentProvider.host).to.equals(cypressHost) + expect(caver.klay.Contract.currentProvider).not.to.be.null + expect(caver.klay.Contract.currentProvider.host).to.equals(cypressHost) + expect(caver.klay.accounts.currentProvider).not.to.be.null + expect(caver.klay.accounts.currentProvider.host).to.equals(cypressHost) + }).timeout(10000) +}) \ No newline at end of file diff --git a/test/signWithMultiSig.js b/test/signWithMultiSig.js new file mode 100644 index 00000000..b0e4974d --- /dev/null +++ b/test/signWithMultiSig.js @@ -0,0 +1,90 @@ +/* + Copyright 2019 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 { expect } = require('chai') + +const Caver = require('../index.js') +const testRPCURL = require('./testrpc') +const caver = new Caver(testRPCURL) + +let sender +let multiSigAccount, multiSigKeys + +let createMultiSigAccount = async () => { + multiSigAccount = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) + multiSigKeys = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + + const txObject = { + from: sender.address, + to: multiSigAccount.address, + value: caver.utils.toPeb(10, 'KLAY'), + gas: 900000, + } + + await caver.klay.sendTransaction(txObject) + + // account update transaction object + const accountUpdateObject = { + type: 'ACCOUNT_UPDATE', + from: multiSigAccount.address, + multisig: { + threshold: 2, + keys: [ + { weight: 1, publicKey: caver.klay.accounts.privateKeyToPublicKey(multiSigKeys[0]) }, + { weight: 1, publicKey: caver.klay.accounts.privateKeyToPublicKey(multiSigKeys[1]) }, + { weight: 1, publicKey: caver.klay.accounts.privateKeyToPublicKey(multiSigKeys[2]) }, + ], + }, + gas: 900000, + } + + return caver.klay.sendTransaction(accountUpdateObject) +} + +before(function (done) { + this.timeout(200000) + + let senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 + ? '0x' + process.env.privateKey + : process.env.privateKey + + sender = caver.klay.accounts.wallet.add(senderPrvKey) + + createMultiSigAccount().then(() => done()) +}) + +describe('sign transaction with multi sig account key', () => { + it('CAVERJS-UNIT-TX-576 : signTransaction method should sign with private key array correctly', async () => { + const accountKey = await caver.klay.getAccountKey(multiSigAccount.address) + expect(accountKey.keyType).to.equals(4) + expect(accountKey.key.threshold).to.equals(2) + + const txObj = { + type: 'VALUE_TRANSFER', + from: multiSigAccount.address, + to: caver.klay.accounts.create().address, + value: 1, + gas: 900000, + } + const result = await caver.klay.accounts.signTransaction(txObj, multiSigKeys) + + const tx = await caver.klay.sendSignedTransaction(result.rawTransaction) + + expect(tx.signatures.length).to.equals(multiSigKeys.length) + }).timeout(100000) +}) \ No newline at end of file diff --git a/test/transactionType/accountUpdate.js b/test/transactionType/accountUpdate.js index 2d369c29..d365b66b 100644 --- a/test/transactionType/accountUpdate.js +++ b/test/transactionType/accountUpdate.js @@ -1574,4 +1574,234 @@ describe('ACCOUNT_UPDATE transaction', () => { // Throw error from formatter validation expect(()=> caver.klay.sendTransaction(tx)).to.throws('The key parameter to be used for ACCOUNT_UPDATE is duplicated.') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-588: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-588: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // UnnecessaryFeePayerSignatures + it('CAVERJS-UNIT-TX-589: If transaction object has unnecessary feePayerSignatures, signTransaction should throw error', async () => { + const tx = Object.assign({publicKey, feePayerSignatures: [['0x01', '0x', '0x']]}, accountUpdateObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-589: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const tx = Object.assign({publicKey, feePayerSignatures: [['0x01', '0x', '0x']]}, accountUpdateObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Update with key field with AccountKeyPublic. + it('CAVERJS-UNIT-TX-672: If transaction object has key with AccountKeyPublic, update account with AccountKeyPublic', async () => { + const key = caver.klay.accounts.create().privateKey + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const receipt = await caver.klay.sendTransaction(tx) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(2) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyMultiSig. + it('CAVERJS-UNIT-TX-673: If transaction object has key with AccountKeyMultiSig, update account with AccountKeyMultiSig', async () => { + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const options = { threshold: 1, weight: [1, 1] } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key, options) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const receipt = await caver.klay.sendTransaction(tx) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(4) + expect(accountKey.key.threshold).to.equals(options.threshold) + expect(accountKey.key.keys.length).to.equals(key.length) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyRoleBased. + it('CAVERJS-UNIT-TX-674: If transaction object has key with AccountKeyRoleBased, update account with AccountKeyRoleBased', async () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const options = { + transactionKey: { threshold: 2, weight: [1, 1] }, + feePayerKey: { threshold: 2, weight: [1, 1, 1] } + } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key, options) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const receipt = await caver.klay.sendTransaction(tx) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(5) + expect(accountKey.key.length).to.equals(3) + expect(accountKey.key[0].keyType).to.equals(4) + expect(accountKey.key[1].keyType).to.equals(2) + expect(accountKey.key[2].keyType).to.equals(4) + expect(accountKey.key[0].key.threshold).to.equals(options.transactionKey.threshold) + expect(accountKey.key[2].key.threshold).to.equals(options.feePayerKey.threshold) + expect(accountKey.key[0].key.keys.length).to.equals(key.transactionKey.length) + expect(accountKey.key[2].key.keys.length).to.equals(key.feePayerKey.length) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with LegacyKey. + it('CAVERJS-UNIT-TX-675: If transaction object has key with LegacyKey, update account with LegacyKey', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const receipt = await caver.klay.sendTransaction(tx) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(1) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with FailKey. + it('CAVERJS-UNIT-TX-676: If transaction object has key with FailKey, update account with FailKey', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithFailKey(testAccount.address) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const receipt = await caver.klay.sendTransaction(tx) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(3) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyRoleBased with legacyKey and failKey. + it('CAVERJS-UNIT-TX-677: If transaction object has key with AccountKeyRoleBased, update account with AccountKeyRoleBased', async () => { + const key = { + transactionKey: 'legacyKey', + updateKey: 'failKey', + feePayerKey: 'legacyKey' + } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const receipt = await caver.klay.sendTransaction(tx) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(5) + expect(accountKey.key.length).to.equals(3) + expect(accountKey.key[0].keyType).to.equals(1) + expect(accountKey.key[1].keyType).to.equals(3) + expect(accountKey.key[2].keyType).to.equals(1) + + await createTestAccount() + }).timeout(200000) + + // Duplication key check with key field + it('CAVERJS-UNIT-TX-678: If transaction object has key with legacyKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, legacyKey: true}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-679: If transaction object has key with publicKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, publicKey}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-680: If transaction object has key with multisig field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, multisig}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-681: If transaction object has key with roleTransactionKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleTransactionKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-682: If transaction object has key with roleAccountUpdateKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleAccountUpdateKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-683: If transaction object has key with roleFeePayerKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleFeePayerKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-684: If transaction object has key with failKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, failKey: true}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/cancelTransaction.js b/test/transactionType/cancelTransaction.js index 6b2480f5..e2caf6ee 100644 --- a/test/transactionType/cancelTransaction.js +++ b/test/transactionType/cancelTransaction.js @@ -144,7 +144,7 @@ describe('CANCEL transaction', () => { it('CAVERJS-UNIT-TX-516 : If transaction object has unnecessary feePayer field, sendTransaction should throw error', () => { const tx = Object.assign({feePayer : testAccount.address}, cancelObject) - expect(()=> caver.klay.sendTransaction(tx)).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') + expect(()=> caver.klay.sendTransaction(tx)).to.throws('"feePayer" cannot be used with CANCEL transaction') }).timeout(200000) // UnnecessaryFeeRatio @@ -317,4 +317,42 @@ describe('CANCEL transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with CANCEL transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-590: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-590: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, cancelObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // UnnecessaryFeePayerSignatures + it('CAVERJS-UNIT-TX-591: If transaction object has unnecessary feePayerSignatures, signTransaction should throw error', async () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, cancelObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-591: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, cancelObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/contractDeploy.js b/test/transactionType/contractDeploy.js index d3d80a8e..fbe079e3 100644 --- a/test/transactionType/contractDeploy.js +++ b/test/transactionType/contractDeploy.js @@ -157,7 +157,7 @@ describe('SMART_CONTRACT_DEPLOY transaction', () => { const tx = Object.assign({feePayer: testAccount.address}, deployObject) // This error return from formatter. Because in formatter discriminate fee delegation through feePayer and senderRawTransaction - expect(()=> caver.klay.sendTransaction(tx)).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') + expect(()=> caver.klay.sendTransaction(tx)).to.throws('"feePayer" cannot be used with SMART_CONTRACT_DEPLOY transaction') }).timeout(200000) // UnnecessaryFeeRatio @@ -330,4 +330,61 @@ describe('SMART_CONTRACT_DEPLOY transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"to" cannot be used with SMART_CONTRACT_DEPLOY transaction') }).timeout(200000) + + it('CAVERJS-UNIT-TX-577: If data field of transaction is not 0x-hex prefixed, signTransaction should formatting and sign', async() => { + deployObject.data = caver.utils.stripHexPrefix(deployObject.data) + expect(deployObject.data.slice(0, 2) !== '0x').to.be.true + + let { rawTransaction } = await caver.klay.accounts.signTransaction(deployObject, senderPrvKey) + const receipt = await caver.klay.sendSignedTransaction(rawTransaction) + + expect(receipt.status).to.be.true + }).timeout(200000) + + it('CAVERJS-UNIT-TX-578: If data field of transaction is not 0x-hex prefixed, sendTransaction should formatting and sign', async() => { + deployObject.data = caver.utils.stripHexPrefix(deployObject.data) + expect(deployObject.data.slice(0, 2) !== '0x').to.be.true + + const receipt = await caver.klay.sendTransaction(deployObject) + + expect(receipt.status).to.be.true + }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-592: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-592: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, deployObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // UnnecessaryFeePayerSignatures + it('CAVERJS-UNIT-TX-593: If transaction object has unnecessary feePayerSignatures, signTransaction should throw error', async () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, deployObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-593: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, deployObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/contractExecution.js b/test/transactionType/contractExecution.js index e6f1467d..55ee0f89 100644 --- a/test/transactionType/contractExecution.js +++ b/test/transactionType/contractExecution.js @@ -146,7 +146,7 @@ describe('SMART_CONTRACT_EXECUTION transaction', () => { const tx = Object.assign({feePayer: testAccount.address}, executionObject) // This error return from formatter. Because in formatter discriminate fee delegation through feePayer and senderRawTransaction - expect(()=> caver.klay.sendTransaction(tx)).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') + expect(()=> caver.klay.sendTransaction(tx)).to.throws('"feePayer" cannot be used with SMART_CONTRACT_EXECUTION transaction') }).timeout(200000) // UnnecessaryFeeRatio @@ -319,4 +319,64 @@ describe('SMART_CONTRACT_EXECUTION transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with SMART_CONTRACT_EXECUTION transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-594: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-594: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, executionObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // UnnecessaryFeePayerSignatures + it('CAVERJS-UNIT-TX-595: If transaction object has unnecessary feePayerSignatures, signTransaction should throw error', async () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, executionObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-595: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, executionObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-596: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, executionObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-596: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, executionObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedAccountUpdate.js b/test/transactionType/feeDelegatedAccountUpdate.js index 22cb3c3f..ed6d3eff 100644 --- a/test/transactionType/feeDelegatedAccountUpdate.js +++ b/test/transactionType/feeDelegatedAccountUpdate.js @@ -1247,11 +1247,6 @@ describe('FEE_DELEGATED_ACCOUNT_UPDATE transaction', () => { expect(()=>caver.klay.sendTransaction({senderRawTransaction: ret.rawTransaction})).to.throws('The "feePayer" field must be defined for signing with feePayer!') }).timeout(200000) - // Error senderRawTransaction missing (A check on the senderRawTransaction is performed when the feePayer attempts to sign the rawTransaction after sender signed.) - it('CAVERJS-UNIT-TX-334 : If transaction object missing senderRawTransaction, signTransaction should throw error', async () => { - expect(()=>caver.klay.sendTransaction({feePayer: payerAddress})).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') - }).timeout(200000) - // UnnecessaryFeeRatio it('CAVERJS-UNIT-TX-335 : If transaction object has feeRatio, signTransaction should throw error', async () => { const tx = Object.assign({feeRatio: 20, publicKey}, accountUpdateObject) @@ -1393,4 +1388,334 @@ describe('FEE_DELEGATED_ACCOUNT_UPDATE transaction', () => { // Throw error from formatter validation expect(()=> caver.klay.sendTransaction(tx)).to.throws('The key parameter to be used for FEE_DELEGATED_ACCOUNT_UPDATE is duplicated.') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-597: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-597: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-598: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({publicKey, feePayerSignatures}, accountUpdateObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-598: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({publicKey, feePayerSignatures}, accountUpdateObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-599: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, publicKey, feePayerSignatures}, accountUpdateObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-599: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, publicKey, feePayerSignatures}, accountUpdateObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-600: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-600: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-601: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-601: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Update with key field with AccountKeyPublic. + it('CAVERJS-UNIT-TX-685: If transaction object has key with AccountKeyPublic, update account with AccountKeyPublic', async () => { + const key = caver.klay.accounts.create().privateKey + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(2) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyMultiSig. + it('CAVERJS-UNIT-TX-686: If transaction object has key with AccountKeyMultiSig, update account with AccountKeyMultiSig', async () => { + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const options = { threshold: 1, weight: [1, 1] } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key, options) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(4) + expect(accountKey.key.threshold).to.equals(options.threshold) + expect(accountKey.key.keys.length).to.equals(key.length) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyRoleBased. + it('CAVERJS-UNIT-TX-687: If transaction object has key with AccountKeyRoleBased, update account with AccountKeyRoleBased', async () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const options = { + transactionKey: { threshold: 2, weight: [1, 1] }, + feePayerKey: { threshold: 2, weight: [1, 1, 1] } + } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key, options) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(5) + expect(accountKey.key.length).to.equals(3) + expect(accountKey.key[0].keyType).to.equals(4) + expect(accountKey.key[1].keyType).to.equals(2) + expect(accountKey.key[2].keyType).to.equals(4) + expect(accountKey.key[0].key.threshold).to.equals(options.transactionKey.threshold) + expect(accountKey.key[2].key.threshold).to.equals(options.feePayerKey.threshold) + expect(accountKey.key[0].key.keys.length).to.equals(key.transactionKey.length) + expect(accountKey.key[2].key.keys.length).to.equals(key.feePayerKey.length) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with LegacyKey. + it('CAVERJS-UNIT-TX-688: If transaction object has key with LegacyKey, update account with LegacyKey', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(1) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with FailKey. + it('CAVERJS-UNIT-TX-689: If transaction object has key with FailKey, update account with FailKey', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithFailKey(testAccount.address) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(3) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyRoleBased with legacyKey and failKey. + it('CAVERJS-UNIT-TX-690: If transaction object has key with AccountKeyRoleBased, update account with AccountKeyRoleBased', async () => { + const key = { + transactionKey: 'legacyKey', + updateKey: 'failKey', + feePayerKey: 'legacyKey' + } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(5) + expect(accountKey.key.length).to.equals(3) + expect(accountKey.key[0].keyType).to.equals(1) + expect(accountKey.key[1].keyType).to.equals(3) + expect(accountKey.key[2].keyType).to.equals(1) + + await createTestAccount() + }).timeout(200000) + + // Duplication key check with key field + it('CAVERJS-UNIT-TX-691: If transaction object has key with legacyKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, legacyKey: true}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-692: If transaction object has key with publicKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, publicKey}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-693: If transaction object has key with multisig field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, multisig}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-694: If transaction object has key with roleTransactionKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleTransactionKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-695: If transaction object has key with roleAccountUpdateKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleAccountUpdateKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-696: If transaction object has key with roleFeePayerKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleFeePayerKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-697: If transaction object has key with failKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, failKey: true}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedAccountUpdateWithRatio.js b/test/transactionType/feeDelegatedAccountUpdateWithRatio.js index 71f8c2e6..077dc5d9 100644 --- a/test/transactionType/feeDelegatedAccountUpdateWithRatio.js +++ b/test/transactionType/feeDelegatedAccountUpdateWithRatio.js @@ -1251,12 +1251,7 @@ describe('FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO transaction', () => { const ret = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) expect(()=>caver.klay.sendTransaction({senderRawTransaction: ret.rawTransaction})).to.throws('The "feePayer" field must be defined for signing with feePayer!') }).timeout(200000) - - // Error senderRawTransaction missing (A check on the senderRawTransaction is performed when the feePayer attempts to sign the rawTransaction after sender signed.) - it('CAVERJS-UNIT-TX-413 : If transaction object missing senderRawTransaction, signTransaction should throw error', async () => { - expect(()=>caver.klay.sendTransaction({feePayer: payerAddress})).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') - }).timeout(200000) - + // MissingFeeRatio it('CAVERJS-UNIT-TX-414 : If transaction object has feeRatio, signTransaction should throw error', async () => { const tx = Object.assign({publicKey}, accountUpdateObject) @@ -1400,4 +1395,334 @@ describe('FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO transaction', () => { // Throw error from formatter validation expect(()=> caver.klay.sendTransaction(tx)).to.throws('The key parameter to be used for FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO is duplicated.') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-602: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-602: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-603: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({publicKey, feePayerSignatures}, accountUpdateObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-603: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({publicKey, feePayerSignatures}, accountUpdateObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-604: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, publicKey, feePayerSignatures}, accountUpdateObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-604: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, publicKey, feePayerSignatures}, accountUpdateObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-605: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-605: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-606: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-606: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({publicKey}, accountUpdateObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Update with key field with AccountKeyPublic. + it('CAVERJS-UNIT-TX-698: If transaction object has key with AccountKeyPublic, update account with AccountKeyPublic', async () => { + const key = caver.klay.accounts.create().privateKey + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(2) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyMultiSig. + it('CAVERJS-UNIT-TX-699: If transaction object has key with AccountKeyMultiSig, update account with AccountKeyMultiSig', async () => { + const key = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + const options = { threshold: 1, weight: [1, 1] } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key, options) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(4) + expect(accountKey.key.threshold).to.equals(options.threshold) + expect(accountKey.key.keys.length).to.equals(key.length) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyRoleBased. + it('CAVERJS-UNIT-TX-700: If transaction object has key with AccountKeyRoleBased, update account with AccountKeyRoleBased', async () => { + const key = { + transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey], + updateKey: caver.klay.accounts.create().privateKey, + feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey] + } + const options = { + transactionKey: { threshold: 2, weight: [1, 1] }, + feePayerKey: { threshold: 2, weight: [1, 1, 1] } + } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key, options) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(5) + expect(accountKey.key.length).to.equals(3) + expect(accountKey.key[0].keyType).to.equals(4) + expect(accountKey.key[1].keyType).to.equals(2) + expect(accountKey.key[2].keyType).to.equals(4) + expect(accountKey.key[0].key.threshold).to.equals(options.transactionKey.threshold) + expect(accountKey.key[2].key.threshold).to.equals(options.feePayerKey.threshold) + expect(accountKey.key[0].key.keys.length).to.equals(key.transactionKey.length) + expect(accountKey.key[2].key.keys.length).to.equals(key.feePayerKey.length) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with LegacyKey. + it('CAVERJS-UNIT-TX-701: If transaction object has key with LegacyKey, update account with LegacyKey', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(1) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with FailKey. + it('CAVERJS-UNIT-TX-702: If transaction object has key with FailKey, update account with FailKey', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithFailKey(testAccount.address) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(3) + + await createTestAccount() + }).timeout(200000) + + // Update with key field with AccountKeyRoleBased with legacyKey and failKey. + it('CAVERJS-UNIT-TX-703: If transaction object has key with AccountKeyRoleBased, update account with AccountKeyRoleBased', async () => { + const key = { + transactionKey: 'legacyKey', + updateKey: 'failKey', + feePayerKey: 'legacyKey' + } + const updator = caver.klay.accounts.createAccountForUpdate(testAccount.address, key) + + var tx = Object.assign({key: updator}, accountUpdateObject) + + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx) + const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(rawTransaction, payerAddress) + + const receipt = await caver.klay.sendSignedTransaction(feePayerSigned) + expect(receipt.from).to.equals(tx.from) + + const accountKey = await caver.klay.getAccountKey(receipt.from) + expect(accountKey.keyType).to.equals(5) + expect(accountKey.key.length).to.equals(3) + expect(accountKey.key[0].keyType).to.equals(1) + expect(accountKey.key[1].keyType).to.equals(3) + expect(accountKey.key[2].keyType).to.equals(1) + + await createTestAccount() + }).timeout(200000) + + // Duplication key check with key field + it('CAVERJS-UNIT-TX-704: If transaction object has key with legacyKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, legacyKey: true}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-705: If transaction object has key with publicKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, publicKey}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-706: If transaction object has key with multisig field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, multisig}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-707: If transaction object has key with roleTransactionKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleTransactionKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-708: If transaction object has key with roleAccountUpdateKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleAccountUpdateKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-709: If transaction object has key with roleFeePayerKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, roleFeePayerKey: {publicKey}}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-710: If transaction object has key with failKey field, should throw error', async () => { + const updator = caver.klay.accounts.createAccountForUpdateWithLegacyKey(testAccount.address) + + var tx = Object.assign({key: updator, failKey: true}, accountUpdateObject) + + const expectedError = `The key parameter to be used for ${tx.type} is duplicated.` + + expect(()=> caver.klay.signTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedCancelTransaction.js b/test/transactionType/feeDelegatedCancelTransaction.js index 44220203..c4f7ad3b 100644 --- a/test/transactionType/feeDelegatedCancelTransaction.js +++ b/test/transactionType/feeDelegatedCancelTransaction.js @@ -139,11 +139,6 @@ describe('FEE_DELEGATED_CANCEL transaction', () => { const ret = await caver.klay.accounts.signTransaction(tx, senderPrvKey) expect(()=>caver.klay.sendTransaction({senderRawTransaction: ret.rawTransaction})).to.throws('The "feePayer" field must be defined for signing with feePayer!') }).timeout(200000) - - // MissingSenderRawTransaction - it('CAVERJS-UNIT-TX-532 : If transaction object missing senderRawTransaction field, should throw error', async() => { - expect(()=>caver.klay.sendTransaction({feePayer: testAccount.address})).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') - }).timeout(200000) // UnnecessaryFeeRatio it('CAVERJS-UNIT-TX-533 : If transaction object has unnecessary feeRatio field, signTransaction should throw error', async() => { @@ -315,4 +310,126 @@ describe('FEE_DELEGATED_CANCEL transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with FEE_DELEGATED_CANCEL transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-607: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-607: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, cancelObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-608: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, cancelObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-608: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, cancelObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-609: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, cancelObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-609: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, cancelObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-610: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-610: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-611: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-611: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedCancelTransactionWithRatio.js b/test/transactionType/feeDelegatedCancelTransactionWithRatio.js index ebab94ac..21b9fa73 100644 --- a/test/transactionType/feeDelegatedCancelTransactionWithRatio.js +++ b/test/transactionType/feeDelegatedCancelTransactionWithRatio.js @@ -140,11 +140,6 @@ describe('FEE_DELEGATED_CANCEL_WITH_RATIO transaction', () => { const ret = await caver.klay.accounts.signTransaction(tx, senderPrvKey) expect(()=>caver.klay.sendTransaction({senderRawTransaction: ret.rawTransaction})).to.throws('The "feePayer" field must be defined for signing with feePayer!') }).timeout(200000) - - // MissingSenderRawTransaction - it('CAVERJS-UNIT-TX-548 : If transaction object missing senderRawTransaction field, should throw error', async() => { - expect(()=>caver.klay.sendTransaction({feePayer: testAccount.address})).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') - }).timeout(200000) // MissingFeeRatio it('CAVERJS-UNIT-TX-549 : If transaction object missing feeRatio, signTransaction should throw error', async() => { @@ -318,4 +313,124 @@ describe('FEE_DELEGATED_CANCEL_WITH_RATIO transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with FEE_DELEGATED_CANCEL_WITH_RATIO transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-612: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-612: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, cancelObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-613: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, cancelObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-613: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, cancelObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-614: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, cancelObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-614: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, cancelObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-615: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-615: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-616: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-616: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, cancelObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedContractDeploy.js b/test/transactionType/feeDelegatedContractDeploy.js index c0fa253b..8108d1bf 100644 --- a/test/transactionType/feeDelegatedContractDeploy.js +++ b/test/transactionType/feeDelegatedContractDeploy.js @@ -24,21 +24,25 @@ const testRPCURL = require('../testrpc') const Caver = require('../../index.js') let caver -var senderPrvKey -var senderAddress +var senderPrvKey, payerPrvKey +var senderAddress, payerAddress var testAccount before(() => { caver = new Caver(testRPCURL) - if (process.env.privateKey) { + if (process.env.privateKey && process.env.privateKey2) { senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 ? '0x' + process.env.privateKey : process.env.privateKey + payerPrvKey = process.env.privateKey2 && String(process.env.privateKey2).indexOf('0x') === -1 + ? '0x' + process.env.privateKey2 + : process.env.privateKey2 - caver.klay.accounts.wallet.add(senderPrvKey) + const sender = caver.klay.accounts.wallet.add(senderPrvKey) + const payer = caver.klay.accounts.wallet.add(payerPrvKey) - const sender = caver.klay.accounts.privateKeyToAccount(senderPrvKey) senderAddress = sender.address + payerAddress = payer.address } else { const sender = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) senderPrvKey = sender.privateKey @@ -153,11 +157,6 @@ describe('FEE_DELEGATED_SMART_CONTRACT_DEPLOY transaction', () => { expect(()=>caver.klay.sendTransaction({senderRawTransaction: ret.rawTransaction})).to.throws('The "feePayer" field must be defined for signing with feePayer!') }).timeout(200000) - // MissingSenderRawTransaction - it('CAVERJS-UNIT-TX-443 : If transaction object missing senderRawTransaction field, should throw error', async() => { - expect(()=>caver.klay.sendTransaction({feePayer: testAccount.address})).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') - }).timeout(200000) - // UnnecessaryFeeRatio it('CAVERJS-UNIT-TX-444 : If transaction object has unnecessary feeRatio field, signTransaction should throw error', async() => { const tx = Object.assign({feeRatio: 10}, deployObject) @@ -328,4 +327,138 @@ describe('FEE_DELEGATED_SMART_CONTRACT_DEPLOY transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"to" cannot be used with FEE_DELEGATED_SMART_CONTRACT_DEPLOY transaction') }).timeout(200000) + + it('CAVERJS-UNIT-TX-579: If data field of transaction is not 0x-hex prefixed, signTransaction should formatting and sign', async() => { + deployObject.data = caver.utils.stripHexPrefix(deployObject.data) + expect(deployObject.data.slice(0, 2) !== '0x').to.be.true + + let { rawTransaction } = await caver.klay.accounts.signTransaction(deployObject, senderPrvKey) + + const receipt = await caver.klay.sendTransaction({ + senderRawTransaction: rawTransaction, + feePayer: payerAddress + }) + + expect(receipt.status).to.be.true + }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-617: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-617: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, deployObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-618: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, deployObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-618: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, deployObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-619: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, deployObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-619: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, deployObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-620: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-620: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-621: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-621: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedContractDeployWithRatio.js b/test/transactionType/feeDelegatedContractDeployWithRatio.js index 652382fe..7c31af41 100644 --- a/test/transactionType/feeDelegatedContractDeployWithRatio.js +++ b/test/transactionType/feeDelegatedContractDeployWithRatio.js @@ -24,21 +24,25 @@ const testRPCURL = require('../testrpc') const Caver = require('../../index.js') let caver -var senderPrvKey -var senderAddress +var senderPrvKey, payerPrvKey +var senderAddress, payerAddress var testAccount before(() => { caver = new Caver(testRPCURL) - if (process.env.privateKey) { + if (process.env.privateKey && process.env.privateKey2) { senderPrvKey = process.env.privateKey && String(process.env.privateKey).indexOf('0x') === -1 ? '0x' + process.env.privateKey : process.env.privateKey + payerPrvKey = process.env.privateKey2 && String(process.env.privateKey2).indexOf('0x') === -1 + ? '0x' + process.env.privateKey2 + : process.env.privateKey2 - caver.klay.accounts.wallet.add(senderPrvKey) + const sender = caver.klay.accounts.wallet.add(senderPrvKey) + const payer = caver.klay.accounts.wallet.add(payerPrvKey) - const sender = caver.klay.accounts.privateKeyToAccount(senderPrvKey) senderAddress = sender.address + payerAddress = payer.address } else { const sender = caver.klay.accounts.wallet.add(caver.klay.accounts.create()) senderPrvKey = sender.privateKey @@ -154,11 +158,6 @@ describe('FEE_DELEGATED_SMART_CONTRACT_DEPLOY_WITH_RATIO transaction', () => { expect(()=>caver.klay.sendTransaction({senderRawTransaction: ret.rawTransaction})).to.throws('The "feePayer" field must be defined for signing with feePayer!') }).timeout(200000) - // MissingSenderRawTransaction - it('CAVERJS-UNIT-TX-458 : If transaction object missing senderRawTransaction field, should throw error', async() => { - expect(()=>caver.klay.sendTransaction({feePayer: testAccount.address})).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') - }).timeout(200000) - // MissingFeeRatio it('CAVERJS-UNIT-TX-459 : If transaction object missing feeRatio field, signTransaction should throw error', async() => { const tx = Object.assign(deployObject) @@ -331,4 +330,138 @@ describe('FEE_DELEGATED_SMART_CONTRACT_DEPLOY_WITH_RATIO transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"to" cannot be used with FEE_DELEGATED_SMART_CONTRACT_DEPLOY_WITH_RATIO transaction') }).timeout(200000) + + it('CAVERJS-UNIT-TX-580: If data field of transaction is not 0x-hex prefixed, signTransaction should formatting and sign', async() => { + deployObject.data = caver.utils.stripHexPrefix(deployObject.data) + expect(deployObject.data.slice(0, 2) !== '0x').to.be.true + + let { rawTransaction } = await caver.klay.accounts.signTransaction(deployObject, senderPrvKey) + + const receipt = await caver.klay.sendTransaction({ + senderRawTransaction: rawTransaction, + feePayer: payerAddress + }) + + expect(receipt.status).to.be.true + }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-622: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-622: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, deployObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-623: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, deployObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-623: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, deployObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-624: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, deployObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-624: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, deployObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-625: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-625: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-626: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-626: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, deployObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedContractExecution.js b/test/transactionType/feeDelegatedContractExecution.js index 10a26bfb..9e49688d 100644 --- a/test/transactionType/feeDelegatedContractExecution.js +++ b/test/transactionType/feeDelegatedContractExecution.js @@ -140,11 +140,6 @@ describe('FEE_DELEGATED_SMART_CONTRACT_EXECUTION transaction', () => { const ret = await caver.klay.accounts.signTransaction(tx, senderPrvKey) expect(()=>caver.klay.sendTransaction({senderRawTransaction: ret.rawTransaction})).to.throws('The "feePayer" field must be defined for signing with feePayer!') }).timeout(200000) - - // MissingSenderRawTransaction - it('CAVERJS-UNIT-TX-486 : If transaction object missing senderRawTransaction field, should throw error', async() => { - expect(()=>caver.klay.sendTransaction({feePayer: testAccount.address})).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') - }).timeout(200000) // UnnecessaryFeeRatio it('CAVERJS-UNIT-TX-487 : If transaction object has unnecessary feeRatio field, signTransaction should throw error', async() => { @@ -316,4 +311,146 @@ describe('FEE_DELEGATED_SMART_CONTRACT_EXECUTION transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with FEE_DELEGATED_SMART_CONTRACT_EXECUTION transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-627: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-627: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, executionObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-628: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, executionObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-628: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, executionObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-629: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, executionObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-629: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, executionObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-630: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, executionObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-630: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, executionObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-631: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-631: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-632: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-632: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedContractExecutionWithRatio.js b/test/transactionType/feeDelegatedContractExecutionWithRatio.js index b1a1cfce..25eba158 100644 --- a/test/transactionType/feeDelegatedContractExecutionWithRatio.js +++ b/test/transactionType/feeDelegatedContractExecutionWithRatio.js @@ -141,11 +141,6 @@ describe('FEE_DELEGATED_SMART_CONTRACT_EXECUTION_WITH_RATIO transaction', () => const ret = await caver.klay.accounts.signTransaction(tx, senderPrvKey) expect(()=>caver.klay.sendTransaction({senderRawTransaction: ret.rawTransaction})).to.throws('The "feePayer" field must be defined for signing with feePayer!') }).timeout(200000) - - // MissingSenderRawTransaction - it('CAVERJS-UNIT-TX-501 : If transaction object missing senderRawTransaction field, should throw error', async() => { - expect(()=>caver.klay.sendTransaction({feePayer: testAccount.address})).to.throws('The "senderRawTransaction" field must be defined for signing with feePayer!') - }).timeout(200000) // MissingFeeRatio it('CAVERJS-UNIT-TX-502 : If transaction object missing feeRatio, signTransaction should throw error', async() => { @@ -319,4 +314,146 @@ describe('FEE_DELEGATED_SMART_CONTRACT_EXECUTION_WITH_RATIO transaction', () => expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with FEE_DELEGATED_SMART_CONTRACT_EXECUTION_WITH_RATIO transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-633: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-633: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, executionObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-634: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, executionObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-634: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, executionObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-635: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, executionObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-635: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, executionObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-636: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, executionObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-636: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, executionObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-637: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-637: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-638: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-638: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, executionObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedValueTransfer.js b/test/transactionType/feeDelegatedValueTransfer.js index 278eddbd..1772b722 100644 --- a/test/transactionType/feeDelegatedValueTransfer.js +++ b/test/transactionType/feeDelegatedValueTransfer.js @@ -389,4 +389,146 @@ describe('FEE_DELEGATED_VALUE_TRANSFER transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with FEE_DELEGATED_VALUE_TRANSFER transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-639: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-639: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, feeDelegatedValueTransferObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-640: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, feeDelegatedValueTransferObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-640: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, feeDelegatedValueTransferObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-641: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, feeDelegatedValueTransferObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-641: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, feeDelegatedValueTransferObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-642: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, feeDelegatedValueTransferObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-642: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, feeDelegatedValueTransferObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-643: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-643: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-644: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-644: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedValueTransferMemo.js b/test/transactionType/feeDelegatedValueTransferMemo.js index 064a32a4..8d643292 100644 --- a/test/transactionType/feeDelegatedValueTransferMemo.js +++ b/test/transactionType/feeDelegatedValueTransferMemo.js @@ -402,4 +402,147 @@ describe('FEE_DELEGATED_VALUE_TRANSFER_MEMO transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with FEE_DELEGATED_VALUE_TRANSFER_MEMO transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-651: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-651: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-652: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, feeDelegatedValueTransferMemoObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-652: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, feeDelegatedValueTransferMemoObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-653: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, feeDelegatedValueTransferMemoObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-653: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, feeDelegatedValueTransferMemoObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-654: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, feeDelegatedValueTransferMemoObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-654: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, feeDelegatedValueTransferMemoObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-655: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-655: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-656: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-656: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedValueTransferMemoWithRatio.js b/test/transactionType/feeDelegatedValueTransferMemoWithRatio.js index c1b43c15..a9f4a6eb 100644 --- a/test/transactionType/feeDelegatedValueTransferMemoWithRatio.js +++ b/test/transactionType/feeDelegatedValueTransferMemoWithRatio.js @@ -405,4 +405,147 @@ describe('FEE_DELEGATED_VALUE_TRANSFER_MEMO_WITH_RATIO transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with FEE_DELEGATED_VALUE_TRANSFER_MEMO_WITH_RATIO transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-657: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoWithRatioObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-657: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoWithRatioObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-658: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, feeDelegatedValueTransferMemoWithRatioObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-658: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, feeDelegatedValueTransferMemoWithRatioObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-659: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, feeDelegatedValueTransferMemoWithRatioObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-659: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, feeDelegatedValueTransferMemoWithRatioObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-660: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, feeDelegatedValueTransferMemoWithRatioObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-660: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, feeDelegatedValueTransferMemoWithRatioObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-661: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoWithRatioObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-661: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoWithRatioObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-662: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoWithRatioObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-662: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferMemoWithRatioObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/feeDelegatedValueTransferWithRatio.js b/test/transactionType/feeDelegatedValueTransferWithRatio.js index c3606a98..812792c0 100644 --- a/test/transactionType/feeDelegatedValueTransferWithRatio.js +++ b/test/transactionType/feeDelegatedValueTransferWithRatio.js @@ -405,4 +405,147 @@ describe('FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-645: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferWithRatioObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-645: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, feeDelegatedValueTransferWithRatioObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-646: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, feeDelegatedValueTransferWithRatioObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-646: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const tx = Object.assign({feePayerSignatures}, feeDelegatedValueTransferWithRatioObject) + + const expectedError = `"feePayer" is missing: feePayer must be defined with feePayerSignatures.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error with invalid feePayer missing when feePayerSignatures is defined in transaction object + it('CAVERJS-UNIT-TX-647: If transaction object missing feePayer, signTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, feeDelegatedValueTransferWithRatioObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-647: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const feePayerSignatures = [['0x26', '0x984e9d43c496ef39ef2d496c8e1aee695f871e4f6cfae7f205ddda1589ca5c9e', '0x46647d1ce8755cd664f5fb4eba3082dd1a13817488029f3869662986b7b1a5ae']] + const invalidFeePayer = 'feePayer' + const tx = Object.assign({feePayer: invalidFeePayer, feePayerSignatures}, feeDelegatedValueTransferWithRatioObject) + + const expectedError = `Invalid address of fee payer: ${invalidFeePayer}` + + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-648: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, feeDelegatedValueTransferWithRatioObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-648: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, feeDelegatedValueTransferWithRatioObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is not defined with fee payer transaction format + it('CAVERJS-UNIT-TX-649: If transaction object missing feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferWithRatioObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + const expectedError = `Invalid fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-649: If transaction object missing feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferWithRatioObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: '0x', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) + + // Error when feePayer is invalid with fee payer transaction format + it('CAVERJS-UNIT-TX-650: If transaction object has invalid feePayer, signTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferWithRatioObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + const expectedError = `Invalid address of fee payer: ${feePayerTx.feePayer}` + + await expect(caver.klay.accounts.signTransaction(feePayerTx, payerPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-650: If transaction object has invalid feePayer, sendTransaction should throw error', async () => { + const tx = Object.assign({}, feeDelegatedValueTransferWithRatioObject) + const { rawTransaction } = await caver.klay.accounts.signTransaction(tx, testAccount.privateKey) + + const feePayerTx = { + senderRawTransaction: rawTransaction, + feePayer: 'invalid', + } + + // when sendTransaction, get account from wallet before calling signTransaction + const expectedError = `Provided address "${feePayerTx.feePayer}" is invalid, the capitalization checksum test failed.` + + expect(()=> caver.klay.sendTransaction(feePayerTx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/legacyTransaction.js b/test/transactionType/legacyTransaction.js index 54819018..0fc973d8 100644 --- a/test/transactionType/legacyTransaction.js +++ b/test/transactionType/legacyTransaction.js @@ -353,4 +353,64 @@ describe('LEGACY transaction', () => { // Throw error from formatter validation expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with LEGACY transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-663: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, legacyObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-663: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, legacyObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // UnnecessaryFeePayerSignatures + it('CAVERJS-UNIT-TX-664: If transaction object has unnecessary feePayerSignatures, signTransaction should throw error', async () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, legacyObject) + + const expectedError = `"feePayerSignatures" cannot be used with LEGACY transaction` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-664: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, legacyObject) + + const expectedError = `"feePayerSignatures" cannot be used with LEGACY transaction` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-665: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, legacyObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-665: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, legacyObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) diff --git a/test/transactionType/serializationTest.js b/test/transactionType/serializationTest.js index 7c73f7cc..c7a7db9d 100644 --- a/test/transactionType/serializationTest.js +++ b/test/transactionType/serializationTest.js @@ -52,7 +52,7 @@ describe('Legacy: Legacy transaction', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-059: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals('LEGACY') @@ -65,6 +65,10 @@ describe('Legacy: Legacy transaction', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0]).to.equals(txObj.v) + expect(txObj.signatures[1]).to.equals(txObj.r) + expect(txObj.signatures[2]).to.equals(txObj.s) }).timeout(200000) }) @@ -91,7 +95,7 @@ describe('Value transfer: Value Transfer', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-060: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -104,6 +108,10 @@ describe('Value transfer: Value Transfer', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) }).timeout(200000) }) @@ -140,7 +148,7 @@ describe('Value transfer: Fee Delegated Value Transfer', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-055: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -153,10 +161,18 @@ describe('Value transfer: Fee Delegated Value Transfer', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -180,7 +196,7 @@ describe('Value transfer: Fee Delegated Value Transfer With Ratio', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -199,7 +215,7 @@ describe('Value transfer: Fee Delegated Value Transfer With Ratio', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-058: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -213,10 +229,18 @@ describe('Value transfer: Fee Delegated Value Transfer With Ratio', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -244,7 +268,7 @@ describe('Value transfer memo: Value Transfer With Memo', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-061: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -258,6 +282,10 @@ describe('Value transfer memo: Value Transfer With Memo', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) }).timeout(200000) }) @@ -281,7 +309,7 @@ describe('Value transfer memo: Fee Delegated Value Transfer Memo', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -300,7 +328,7 @@ describe('Value transfer memo: Fee Delegated Value Transfer Memo', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-056: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -314,10 +342,18 @@ describe('Value transfer memo: Fee Delegated Value Transfer Memo', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -342,7 +378,7 @@ describe('Value transfer memo with ratio: Fee Delegated Value Transfer Memo With caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -361,7 +397,7 @@ describe('Value transfer memo with ratio: Fee Delegated Value Transfer Memo With }).timeout(200000) it('CAVERJS-UNIT-SER-057: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -376,10 +412,18 @@ describe('Value transfer memo with ratio: Fee Delegated Value Transfer Memo With expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -409,7 +453,7 @@ describe('Account: Account update', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-042: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -421,6 +465,10 @@ describe('Account: Account update', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) }).timeout(200000) }) @@ -442,7 +490,7 @@ describe('Account: Fee Delegated Account Update', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -461,7 +509,7 @@ describe('Account: Fee Delegated Account Update', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-047: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -472,10 +520,18 @@ describe('Account: Fee Delegated Account Update', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) expect(txObj.publicKey).to.equals(caver.utils.compressPublicKey(sender_transaction.publicKey)) }).timeout(200000) }) @@ -500,7 +556,7 @@ describe('Account: Fee Delegated Account Update with ratio', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -519,7 +575,7 @@ describe('Account: Fee Delegated Account Update with ratio', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-048: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -531,10 +587,18 @@ describe('Account: Fee Delegated Account Update with ratio', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) expect(txObj.publicKey).to.equals(caver.utils.compressPublicKey(sender_transaction.publicKey)) }).timeout(200000) }) @@ -563,7 +627,7 @@ describe('Contract: Contract deploy', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-045: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -579,6 +643,10 @@ describe('Contract: Contract deploy', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) }).timeout(200000) }) @@ -601,7 +669,7 @@ describe('Contract: Fee Delegated Contract Deploy', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -621,7 +689,7 @@ describe('Contract: Fee Delegated Contract Deploy', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-051: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -637,10 +705,18 @@ describe('Contract: Fee Delegated Contract Deploy', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -664,7 +740,7 @@ describe('Contract: Fee Delegated Contract Deploy With Ratio', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -683,7 +759,7 @@ describe('Contract: Fee Delegated Contract Deploy With Ratio', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-052: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -700,10 +776,18 @@ describe('Contract: Fee Delegated Contract Deploy With Ratio', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -732,7 +816,7 @@ describe('Contract: Contract execution', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-046: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -746,6 +830,10 @@ describe('Contract: Contract execution', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) }).timeout(200000) }) @@ -769,7 +857,7 @@ describe('Contract: Fee Delegated Contract Execution', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -788,7 +876,7 @@ describe('Contract: Fee Delegated Contract Execution', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-053: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -802,10 +890,18 @@ describe('Contract: Fee Delegated Contract Execution', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -831,7 +927,7 @@ describe('Contract: Fee Delegated Contract Execution With Ratio', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -851,7 +947,7 @@ describe('Contract: Fee Delegated Contract Execution With Ratio', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-054: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -866,10 +962,18 @@ describe('Contract: Fee Delegated Contract Execution With Ratio', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -895,7 +999,7 @@ describe('Cancel: Cancel transaction', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-043: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -906,6 +1010,10 @@ describe('Cancel: Cancel transaction', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) }).timeout(200000) }) @@ -926,7 +1034,7 @@ describe('Cancel: Fee Delegated Cancel Transaction', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -946,7 +1054,7 @@ describe('Cancel: Fee Delegated Cancel Transaction', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-049: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -957,10 +1065,18 @@ describe('Cancel: Fee Delegated Cancel Transaction', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -982,7 +1098,7 @@ describe('Cancel: Fee Delegated Cancel Transaction With Ratio', () => { caver.klay.accounts.wallet.add(privateKey) const { rawTransaction: senderRawTransaction } = await caver.klay.accounts.signTransaction(sender_transaction, privateKey) - const decoded = await caver.klay.decodeTransaction(senderRawTransaction) + const decoded = caver.klay.decodeTransaction(senderRawTransaction) expect(decoded.feePayer).to.equals('0x') expect(decoded.payerV).to.equals('0x01') expect(decoded.payerR).to.equals('0x') @@ -1001,7 +1117,7 @@ describe('Cancel: Fee Delegated Cancel Transaction With Ratio', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-050: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -1013,10 +1129,18 @@ describe('Cancel: Fee Delegated Cancel Transaction With Ratio', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) expect(txObj.feePayer).to.equals(feePayer) expect(txObj.payerV).not.to.be.undefined expect(txObj.payerR).not.to.be.undefined expect(txObj.payerS).not.to.be.undefined + expect(txObj.feePayerSignatures).not.to.be.undefined + expect(txObj.feePayerSignatures[0][0]).to.equals(txObj.payerV) + expect(txObj.feePayerSignatures[0][1]).to.equals(txObj.payerR) + expect(txObj.feePayerSignatures[0][2]).to.equals(txObj.payerS) }).timeout(200000) }) @@ -1045,7 +1169,7 @@ describe('ServiceChain: Chain data anchoring', () => { }).timeout(200000) it('CAVERJS-UNIT-SER-044: Decode raw transaction', async () => { - const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) + const txObj = caver.klay.decodeTransaction(expectedRawTransaction) expect(txObj).not.to.be.undefined expect(txObj.type).to.equals(sender_transaction.type) @@ -1059,6 +1183,10 @@ describe('ServiceChain: Chain data anchoring', () => { expect(txObj.v).not.to.be.undefined expect(txObj.r).not.to.be.undefined expect(txObj.s).not.to.be.undefined + expect(txObj.signatures).not.to.be.undefined + expect(txObj.signatures[0][0]).to.equals(txObj.v) + expect(txObj.signatures[0][1]).to.equals(txObj.r) + expect(txObj.signatures[0][2]).to.equals(txObj.s) }).timeout(200000) }) @@ -1091,7 +1219,7 @@ describe('ServiceChain: Chain data anchoring', () => { // }).timeout(200000) // it('CAVERJS-UNIT-SER-041: Decode raw transaction', async () => { -// const txObj = await caver.klay.decodeTransaction(expectedRawTransaction) +// const txObj = caver.klay.decodeTransaction(expectedRawTransaction) // expect(txObj).not.to.be.undefined // expect(txObj.type).to.equals(sender_transaction.type) diff --git a/test/transactionType/valueTransfer.js b/test/transactionType/valueTransfer.js index 82904ec9..654b23a5 100644 --- a/test/transactionType/valueTransfer.js +++ b/test/transactionType/valueTransfer.js @@ -378,4 +378,64 @@ describe('VALUE_TRANSFER transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with VALUE_TRANSFER transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-666: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, valueTransferObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-666: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, valueTransferObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // UnnecessaryFeePayerSignatures + it('CAVERJS-UNIT-TX-667: If transaction object has unnecessary feePayerSignatures, signTransaction should throw error', async () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, valueTransferObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-667: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, valueTransferObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-668: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, valueTransferObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-668: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, valueTransferObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file diff --git a/test/transactionType/valueTransferWithMemo.js b/test/transactionType/valueTransferWithMemo.js index 4d404bf4..d6ab31c2 100644 --- a/test/transactionType/valueTransferWithMemo.js +++ b/test/transactionType/valueTransferWithMemo.js @@ -378,4 +378,64 @@ describe('VALUE_TRANSFER_MEMO transaction', () => { expect(()=> caver.klay.sendTransaction(tx)).to.throws('"legacyKey" cannot be used with VALUE_TRANSFER_MEMO transaction') }).timeout(200000) + + // Invalid from address + it('CAVERJS-UNIT-TX-669: If transaction object has invalid from, signTransaction should throw error', async () => { + const tx = Object.assign({}, valueTransferMemoObject) + tx.from = 'invalidAddress' + + const expectedError = `Invalid address of from: ${tx.from}` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-669: If transaction object has invalid from, sendTransaction should throw error', () => { + const tx = Object.assign({}, valueTransferMemoObject) + tx.from = 'invalidAddress' + + const expectedError = `Provided address "${tx.from}" is invalid, the capitalization checksum test failed` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // UnnecessaryFeePayerSignatures + it('CAVERJS-UNIT-TX-670: If transaction object has unnecessary feePayerSignatures, signTransaction should throw error', async () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, valueTransferMemoObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + await expect(caver.klay.accounts.signTransaction(tx, testAccount.privateKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-670: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const tx = Object.assign({feePayerSignatures: [['0x01', '0x', '0x']]}, valueTransferMemoObject) + + const expectedError = `"feePayerSignatures" cannot be used with ${tx.type} transaction` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) + + // InvalidTo + it('CAVERJS-UNIT-TX-671: If transaction object has invalid to address, signTransaction should throw error', async () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, valueTransferMemoObject) + tx.to = invalidTo + + const expectedError = `Invalid address of to: ${tx.to}` + + await expect(caver.klay.accounts.signTransaction(tx, senderPrvKey)).to.be.rejectedWith(expectedError) + }).timeout(200000) + + it('CAVERJS-UNIT-TX-671: If transaction object has unnecessary feePayerSignatures, sendTransaction should throw error', () => { + const invalidTo = 'invalid' + const tx = Object.assign({}, valueTransferMemoObject) + tx.to = invalidTo + + const expectedError = `Provided address "${tx.to}" is invalid, the capitalization checksum test failed.` + + // Throw error from formatter validation + expect(()=> caver.klay.sendTransaction(tx)).to.throws(expectedError) + }).timeout(200000) }) \ No newline at end of file