UNPKG

caver-js

Version:

caver-js is a JavaScript API library that allows developers to interact with a Klaytn node

983 lines (812 loc) 342 kB
/* 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 <http://www.gnu.org/licenses/>. */ const BN = require('bn.js') const Hash = require('eth-lib/lib/hash') const testRPCURL = require('../testrpc') const { expect, assert } = require('../extendedChai') const setting = require('./setting') const Caver = require('../../index') const MessagePrefix = '\x19Klaytn Signed Message:\n' let caver beforeEach(() => { caver = new Caver(testRPCURL) }) 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 } 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 isAccount(data, { keys, address } = {}) { // account object keys const objectKeys = [ 'address', 'accountKey', 'privateKey', 'signTransaction', 'feePayerSignTransaction', 'sign', 'encrypt', 'getKlaytnWalletKey', ] expect(Object.getOwnPropertyNames(data)).to.deep.equal(objectKeys) expect(caver.utils.isAddress(data.address)).to.equal(true) if (keys !== undefined) { compareAccountKey(data.accountKey, keys) } if (address !== undefined) { expect(data.address.toLowerCase()).to.equal(address.toLowerCase()) } } function checkHashMessage(hashed, originMessage) { const enveloped = MessagePrefix + originMessage.length + originMessage const originHashed = caver.utils.sha3(enveloped) expect(hashed).to.equal(originHashed) } function isKeystore(data, { address }, version = 4) { const objectKeys = ['version', 'id', 'address'] if (version > 3) { objectKeys.push('keyring') } else { objectKeys.push('crypto') } expect(Object.getOwnPropertyNames(data)).to.deep.equal(objectKeys) expect(data.version).to.equals(version) expect(caver.utils.isAddress(data.address)).to.equal(true) const prefixTrimmed = data.address.replace(/^(0x)*/i, '') expect(prefixTrimmed).to.match(new RegExp(`^${address.slice(2)}$`, 'i')) } function isWallet(data, { accounts } = {}) { // check if function exists const fns = ['add', 'remove', 'clear'] fns.forEach(fn => { expect(fn in data).to.equal(true) }) expect(data.defaultKeyName).to.equal('caverjs_wallet') if (accounts && accounts.length > 0) { expect(data.length).to.equal(accounts.length) for (let i = 0; i < data.length; i++) { let accountObj = caver.klay.accounts.createWithAccountKey(data[i].address, data[i].accountKey) isAccount(accountObj, { keys: accounts[i].keys, address: accounts[i].address }) accountObj = caver.klay.accounts.createWithAccountKey(data[i].address, data[accountObj.address].accountKey) isAccount(accountObj, { keys: accounts[i].keys, address: accounts[i].address }) } } } describe('caver.klay.accounts.create', () => { context('CAVERJS-UNIT-WALLET-021 : input: no parameter', () => { it('should return valid account', () => { const result = caver.klay.accounts.create() return isAccount(result) }) }) context('CAVERJS-UNIT-WALLET-022 : input: entropy', () => { it('should return valid account', () => { const entropy = caver.utils.randomHex(32) const result = caver.klay.accounts.create(entropy) return isAccount(result) }) }) }) describe('caver.klay.accounts.privateKeyToAccount', () => { context('input: valid privatekey', () => { it('should return valid account', () => { const privateKey = caver.utils.randomHex(32) const result = caver.klay.accounts.privateKeyToAccount(privateKey) return isAccount(result) }) }) context('input: invalid privatekey', () => { it('should throw an error', () => { const invalidPrivateKey = caver.utils.randomHex(31) const errorMessage = 'Invalid private key' expect(() => caver.klay.accounts.privateKeyToAccount(invalidPrivateKey)).to.throw(errorMessage) }) }) }) describe('caver.klay.accounts.signTransaction', () => { let txObj let vtTx let account let feePayer let 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', () => { it('should return signature and rawTransaction', async () => { const result = await caver.klay.accounts.signTransaction(txObj, account.privateKey) 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) }) }) context('CAVERJS-UNIT-WALLET-026 : input: tx, privateKey, without nonce', () => { it('should return signature and rawTransaction', async () => { const tx = { ...txObj } delete tx.nonce const result = await caver.klay.accounts.signTransaction(tx, account.privateKey) 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) }) }) context('CAVERJS-UNIT-WALLET-027 : input: tx, privateKey, without gasPrice', () => { it('should return signature and rawTransaction', async () => { const tx = { ...txObj } delete tx.gasPrice const result = await caver.klay.accounts.signTransaction(tx, account.privateKey) 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) }) }) context('CAVERJS-UNIT-WALLET-028 : input: tx, privateKey, without chainId', () => { it('should return signature and rawTransaction', async () => { const tx = { ...txObj } delete tx.chainId const result = await caver.klay.accounts.signTransaction(tx, account.privateKey) 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) }) }) context('CAVERJS-UNIT-WALLET-024 : input: tx:invalid address, privateKey', () => { it('should throw an error', async () => { const invalid = { ...txObj } delete invalid.to delete invalid.data const errorMessage = 'contract creation without any data provided' await expect(caver.klay.accounts.signTransaction(invalid, account.privateKey)).to.be.rejectedWith(errorMessage) }) }) context('CAVERJS-UNIT-WALLET-024 : input: tx:invalid value, privateKey', async () => { it('should throw an error', async () => { const invalid = { ...txObj } invalid.value = '0xzzzz' const errorMessage = `Given input "${invalid.value}" is not a number.` await expect(caver.klay.accounts.signTransaction(invalid, account.privateKey)).to.be.rejectedWith(errorMessage) }) }) context('CAVERJS-UNIT-WALLET-025 : input: tx, privateKey:invalid', () => { it('should throw an error', async () => { const invalidPrivateKey = caver.utils.randomHex(31) // 31bytes const errorMessage = 'Invalid private key' await expect(caver.klay.accounts.signTransaction(txObj, invalidPrivateKey)).to.be.rejectedWith(errorMessage) }) }) context('CAVERJS-UNIT-WALLET-023 : input: tx, privateKey, callback', () => { it('should return signature and rawTransaction', done => { caver.klay.accounts.signTransaction(txObj, account.privateKey, (_, result) => { 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) done() }) }) }) context('CAVERJS-UNIT-WALLET-102 : input: tx, privateKey, callback', () => { it('should return valid senderTxHash', async () => { const feeDelegatedTx = { type: 'FEE_DELEGATED_VALUE_TRANSFER', from: '0x76d1cc1cdb081de8627cab2c074f02ebc7bce0d0', to: '0xd05c5926b0a2f31aadcc9a9cbd3868a50104d834', value: '0x1', gas: '0xdbba0', chainId: '0x7e3', gasPrice: '0x5d21dba00', nonce: '0x9a', } const result = await caver.klay.accounts.signTransaction( feeDelegatedTx, '1881a973628dba6ab07b6b47c8f3fb50d8e7cbf71fef3b4739155619a3c126fa' ) expect(result.senderTxHash).to.equal('0x1b7c0f2fc7548056e90d9690e8c397acf99eb38e622ac91ee22c2085065f8a55') }) }) 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 ) const tx = { from: decoupledAccount.address, nonce: '0x0', to: setting.toAddress, gas: setting.gas, gasPrice: setting.gasPrice, value: '0x1', chainId: 2019, } const errorMessage = 'A legacy transaction must be with a legacy account key' await expect(caver.klay.accounts.signTransaction(tx, decoupledAccount.privateKey)).to.be.rejectedWith(errorMessage) }) }) 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) }) 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) }) }) 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 keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) expect(typeof result.signatures[0]).to.equals('string') expect(caver.klay.accounts.recoverTransaction(result.rawTransaction)).to.equal(account.address) }) it('should sign to transaction parameter with private key in wallet with callback', async () => { let isCalled = false const signed = await caver.klay.accounts.signTransaction(txObj, () => (isCalled = true)) const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(signed)).to.deep.equal(keys) expect(typeof signed.signatures[0]).to.equals('string') expect(caver.klay.accounts.recoverTransaction(signed.rawTransaction)).to.equal(account.address) expect(isCalled).to.be.true }) it('should sign to transaction parameter with private key parameter', async () => { const result = await caver.klay.accounts.signTransaction(vtTx, account.privateKey) const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) expect(Array.isArray(result.signatures[0])).to.be.true expect(result.signatures.length).to.equals(1) }) 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) const keys = ['messageHash', 'v', 'r', 's', 'rawTransaction', 'txHash', 'senderTxHash', 'signatures'] expect(Object.getOwnPropertyNames(result)).to.deep.equal(keys) 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-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-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, } const errorMessage = `Invalid fee payer: ${feePayerTransaction.feePayer}` await expect(caver.klay.accounts.signTransaction(feePayerTransaction)).to.be.rejectedWith(errorMessage) }) }) 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-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) expect(result.signatures.length).to.equals(1) expect(result.feePayerSignatures).to.be.undefined }) }) 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-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) expect(result.signatures).to.be.undefined expect(result.feePayerSignatures.length).to.equals(1) }) }) 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-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) }) }) 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-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) }) }) context('CAVERJS-UNIT-WALLET-390: sign transaction with signTransaction function in Account instance', () => { it('should be signed with transactionKey', async () => { const keyObject = { transactionKey: [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) vtTx.from = roleBasedAddress const result = await roleBasedAccount.signTransaction(vtTx) expect(result.signatures.length).to.equals(2) expect(result.feePayerSignatures).to.be.undefined }) }) context('CAVERJS-UNIT-WALLET-391: sign transaction with signTransaction function in Account instance', () => { it('should be signed with updateKey', async () => { const keyObject = { updateKey: [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) const updateTx = { type: 'ACCOUNT_UPDATE', from: roleBasedAccount.address, gas: 100000, publicKey: '0x4255497c08282874319715c4f2752ffc205591cfe8c10d285bfa673273b47db74b35980432bc3e36a62e356b9a29e2d0a1650c8f350a1ef121c460473236a873', } const result = await roleBasedAccount.signTransaction(updateTx) expect(result.signatures.length).to.equals(2) expect(result.feePayerSignatures).to.be.undefined }) }) context('CAVERJS-UNIT-WALLET-392: sign transaction with signTransaction function in Account instance', () => { it('should be signed with feePayerKey', async () => { const keyObject = { feePayerKey: [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) const rawTransaction = '0x09f842819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6c4c301808080c4c3018080' const feePayerTx = { senderRawTransaction: rawTransaction, feePayer: roleBasedAccount.address } const result = await roleBasedAccount.signTransaction(feePayerTx) expect(result.feePayerSignatures.length).to.equals(2) }) }) }) describe('caver.klay.accounts.feePayerSignTransaction', () => { let txObj let sender let feePayer const withoutSig = '0x09f842819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6c4c301808080c4c3018080' const withSenderSig = '0x09f90114819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6f8d5f845820feaa06d2b6c9530a9f311a3ea42b2fc474ce0decefc65a88510161a392eef029714b8a0360fff97c9818dabb7d25ce4dc1afb4a5cca00409d79c0e3f76e151dec542701f845820feaa0fb24ef24dd6d10a9410417c56a5a8b09575d0611251f6f03b9199b4004cee087a02a1b040cc80942deda8523c2bf24364b3b2ea1a6d33165fde35d86a1c247ddfef845820fe9a0ae0d77d98aec5880efc7bd943fb58ea691e3023975e757a720586d3781284d9aa077072cfa045f872a1e33840e28ed2704f8a6d77f5077171b6b45e3ec7a671ddf80c4c3018080' const withFeePayerSig = '0x09f90128819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6c4c3018080944a804669b2637b18d46e62109ed8edc0dc8526c7f8d5f845824e43a003df110e3d328d75ac8b05ff29e3b00b65c4402bc0f2556590077e3ffd699f85a0395252d8b2bf6a5b1b997d41694bb84b6e30bc846263b6fc55a023a66ef68630f845824e44a08eb3eb4414fe1b5f0f1baaa0192a9ee018b6132b8fc965918318bdd7087acb42a0211741eae45dae25659894ada38c0c5b03483337148182d2951e7386cb2c2ab8f845824e44a0691eaea2dead54efce368395f2394a9cbc7b3d68effd5c5b3ba9bee7b57dfa59a00b7cbe6b8ebcf013a197f6ee81e3c3e180cf62c940fd8f9282d3f6814d710c9d' const withBothSig = '0x09f901fa819a8505d21dba00830dbba094d2553e0508d481892aa1b481b3ac29aba5c7fb4d01946f9a8851feca74f6694a12d11c9684f0b5c1d3b6f8d5f845820feaa06d2b6c9530a9f311a3ea42b2fc474ce0decefc65a88510161a392eef029714b8a0360fff97c9818dabb7d25ce4dc1afb4a5cca00409d79c0e3f76e151dec542701f845820feaa0fb24ef24dd6d10a9410417c56a5a8b09575d0611251f6f03b9199b4004cee087a02a1b040cc80942deda8523c2bf24364b3b2ea1a6d33165fde35d86a1c247ddfef845820fe9a0ae0d77d98aec5880efc7bd943fb58ea691e3023975e757a720586d3781284d9aa077072cfa045f872a1e33840e28ed2704f8a6d77f5077171b6b45e3ec7a671ddf944a804669b2637b18d46e62109ed8edc0dc8526c7f8d5f845824e43a0e0cd799758d93f3ac9ff1fd5055bff9e7c7e664599f5615c5016c88b7c8edea5a00353e206c246a10a5ac4388924e8eb42fedbbbfb674efa8441f0b0c4957cf05df845824e43a04c5c84dcace452a5bde411d7888d116f0a993a579b11a79cc9ed7fa6e9adb421a023d33c71fced04801643d4d58c9fbb184625cbfaa8dab5a8e25ec5e84d25452af845824e44a0051fad2c19ee4936721b5985ebdf354d069f3e9e3d3c832751caf20f69202c20a03f340e42613e6868cff9b3312fa0f671523b340d469d7d69f6573724bd6f6047' beforeEach(() => { const senderRoleBasedKey = { transactionKey: [ caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, ], } const 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) }) }).timeout(10000) 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