UNPKG

caver-js

Version:

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

894 lines (713 loc) 83.8 kB
/* Copyright 2020 The caver-js Authors This file is part of the caver-js library. The caver-js library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The caver-js library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the caver-js. If not, see <http://www.gnu.org/licenses/>. */ const chai = require('chai') const sinon = require('sinon') const sinonChai = require('sinon-chai') const chaiAsPromised = require('chai-as-promised') const RLP = require('eth-lib/lib/rlp') chai.use(chaiAsPromised) chai.use(sinonChai) const expect = chai.expect const { propertiesForUnnecessary } = require('../utils') const testRPCURL = require('../../testrpc') const Caver = require('../../../index') const Keyring = require('../../../packages/caver-wallet/src/keyring/keyringFactory') const SingleKeyring = require('../../../packages/caver-wallet/src/keyring/singleKeyring') const TransactionHasher = require('../../../packages/caver-transaction/src/transactionHasher/transactionHasher') const { generateRoleBasedKeyring, checkSignature, checkFeePayerSignature } = require('../utils') let caver let sender let roleBasedKeyring const txWithExpectedValues = {} const sandbox = sinon.createSandbox() const input = '0x608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f0029' before(() => { caver = new Caver(testRPCURL) sender = caver.wallet.add(caver.wallet.keyring.generate()) roleBasedKeyring = generateRoleBasedKeyring([3, 3, 3]) txWithExpectedValues.tx = { from: '0x8061145252c8f2b4f110aed096435ae6ed7d5a95', to: '0x', value: '0x0', input, gas: '0xdbba0', gasPrice: '0x5d21dba00', chainId: 2019, nonce: '0x0', codeFormat: 'EVM', humanReadable: false, signatures: [ [ '0x0fe9', '0x7abfd0f0cfb9a9c38c6e3e1a4eeb15f43aeb4b4f6dee7c3f37c07e417af89d9b', '0x3f1e54a512c906d2e57a611b25ce4739d12928e199c3e89792b82f577f0da9ad', ], ], feePayer: '0x2c8eb96e7060ab864d94e91ab16f214dc6647628', feePayerSignatures: [ [ '0x0fe9', '0x192e3b6457f13c6ef557bd11074702d5062dd463473c483278c57f651d5b712b', '0x3ff8638b7cc7ed86c793cb5ffe0e8a064fc94946c3aab624bb7704c62e81ec2d', ], ], } txWithExpectedValues.rlpEncodingForSigning = '0xf90231b90229f9022629808505d21dba00830dbba08080948061145252c8f2b4f110aed096435ae6ed7d5a95b901fe608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f002980808207e38080' txWithExpectedValues.rlpEncodingForFeePayerSigning = '0xf90246b90229f9022629808505d21dba00830dbba08080948061145252c8f2b4f110aed096435ae6ed7d5a95b901fe608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f00298080942c8eb96e7060ab864d94e91ab16f214dc66476288207e38080' txWithExpectedValues.senderTxHash = '0xe189c8ead022fcddb97a8489bb1ee6362368579c65da7404db6b4e704b037ed7' txWithExpectedValues.transactionHash = '0x8dc83759a9c9b226493cb9f7b81a33e0b6b4643f2e82a02fbac784fbe53f9cd9' txWithExpectedValues.rlpEncoding = '0x29f902cc808505d21dba00830dbba08080948061145252c8f2b4f110aed096435ae6ed7d5a95b901fe608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f00298080f847f845820fe9a07abfd0f0cfb9a9c38c6e3e1a4eeb15f43aeb4b4f6dee7c3f37c07e417af89d9ba03f1e54a512c906d2e57a611b25ce4739d12928e199c3e89792b82f577f0da9ad942c8eb96e7060ab864d94e91ab16f214dc6647628f847f845820fe9a0192e3b6457f13c6ef557bd11074702d5062dd463473c483278c57f651d5b712ba03ff8638b7cc7ed86c793cb5ffe0e8a064fc94946c3aab624bb7704c62e81ec2d' }) describe('TxTypeFeeDelegatedSmartContractDeploy', () => { let transactionObj let getGasPriceSpy let getHeaderSpy let getNonceSpy let getChainIdSpy beforeEach(() => { transactionObj = { from: sender.address, input, gas: '0x15f90', } getGasPriceSpy = sandbox.stub(caver.transaction.klaytnCall, 'getGasPrice') getGasPriceSpy.returns('0x5d21dba00') getHeaderSpy = sandbox.stub(caver.transaction.klaytnCall, 'getHeaderByNumber') getHeaderSpy.returns({ baseFeePerGas: '0x5d21dba00' }) getNonceSpy = sandbox.stub(caver.transaction.klaytnCall, 'getTransactionCount') getNonceSpy.returns('0x3a') getChainIdSpy = sandbox.stub(caver.transaction.klaytnCall, 'getChainId') getChainIdSpy.returns('0x7e3') }) afterEach(() => { sandbox.restore() }) context('create feeDelegatedSmartContractDeploy instance', () => { it('CAVERJS-UNIT-TRANSACTIONFD-222: If feeDelegatedSmartContractDeploy not define from, return error', () => { delete transactionObj.from const expectedError = '"from" is missing' expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-224: If feeDelegatedSmartContractDeploy not define gas, return error', () => { delete transactionObj.gas const expectedError = '"gas" is missing' expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-225: If feeDelegatedSmartContractDeploy not define input, return error', () => { delete transactionObj.input const expectedError = '"input" is missing' expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-226: If feeDelegatedSmartContractDeploy define from property with invalid address, return error', () => { transactionObj.from = 'invalid' const expectedError = `Invalid address of from: ${transactionObj.from}` expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-227: If feeDelegatedSmartContractDeploy define feePayer property with invalid address, return error', () => { transactionObj.feePayer = 'invalid' const expectedError = `Invalid address of fee payer: ${transactionObj.feePayer}` expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-228: If feeDelegatedSmartContractDeploy define codeFormat property with invalid codeFormat, return error', () => { transactionObj.codeFormat = 'nonEVM' const expectedError = `The codeFormat(${transactionObj.codeFormat}) is invalid.` expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-229: If feeDelegatedSmartContractDeploy define humanReadable property with true, return error', () => { transactionObj.humanReadable = true const expectedError = `HumanReadableAddress is not supported yet.` expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-230: If feeDelegatedSmartContractDeploy define feePayerSignatures property without feePayer, return error', () => { transactionObj.feePayerSignatures = [ [ '0x26', '0xf45cf8d7f88c08e6b6ec0b3b562f34ca94283e4689021987abb6b0772ddfd80a', '0x298fe2c5aeabb6a518f4cbb5ff39631a5d88be505d3923374f65fdcf63c2955b', ], ] const expectedError = '"feePayer" is missing: feePayer must be defined with feePayerSignatures.' expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-231: If feeDelegatedSmartContractDeploy define unnecessary property, return error', () => { const unnecessaries = [ propertiesForUnnecessary.to, propertiesForUnnecessary.failKey, propertiesForUnnecessary.feeRatio, propertiesForUnnecessary.account, propertiesForUnnecessary.key, propertiesForUnnecessary.legacyKey, propertiesForUnnecessary.publicKey, propertiesForUnnecessary.failKey, propertiesForUnnecessary.multisig, propertiesForUnnecessary.roleTransactionKey, propertiesForUnnecessary.roleAccountUpdateKey, propertiesForUnnecessary.roleFeePayerKey, propertiesForUnnecessary.accessList, propertiesForUnnecessary.maxPriorityFeePerGas, propertiesForUnnecessary.maxFeePerGas, ] for (let i = 0; i < unnecessaries.length; i++) { if (i > 0) delete transactionObj[unnecessaries[i - 1].name] transactionObj[unnecessaries[i].name] = unnecessaries[i].value const expectedError = `"${unnecessaries[i].name}" cannot be used with ${caver.transaction.type.TxTypeFeeDelegatedSmartContractDeploy} transaction` expect(() => caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj)).to.throw(expectedError) } }) }) context('feeDelegatedSmartContractDeploy.getRLPEncoding', () => { it('CAVERJS-UNIT-TRANSACTIONFD-232: Returns RLP-encoded string', () => { const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(txWithExpectedValues.tx) expect(tx.getRLPEncoding()).to.equal(txWithExpectedValues.rlpEncoding) }) it('CAVERJS-UNIT-TRANSACTIONFD-233: getRLPEncoding should throw error when nonce is undefined', () => { transactionObj.chainId = 2019 transactionObj.gasPrice = '0x5d21dba00' const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const expectedError = `nonce is undefined. Define nonce in transaction or use 'transaction.fillTransaction' to fill values.` expect(() => tx.getRLPEncoding()).to.throw(expectedError) }) it('CAVERJS-UNIT-TRANSACTIONFD-234: getRLPEncoding should throw error when gasPrice is undefined', () => { transactionObj.chainId = 2019 transactionObj.nonce = '0x3a' const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const expectedError = `gasPrice is undefined. Define gasPrice in transaction or use 'transaction.fillTransaction' to fill values.` expect(() => tx.getRLPEncoding()).to.throw(expectedError) }) }) context('feeDelegatedSmartContractDeploy.sign', () => { const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' let fillTransactionSpy let createFromPrivateKeySpy let senderSignSpy let appendSignaturesSpy let hasherSpy let tx beforeEach(() => { tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) fillTransactionSpy = sandbox.spy(tx, 'fillTransaction') createFromPrivateKeySpy = sandbox.spy(Keyring, 'createFromPrivateKey') senderSignSpy = sandbox.spy(sender, 'sign') appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures') hasherSpy = sandbox.stub(TransactionHasher, 'getHashForSignature') hasherSpy.returns(txHash) }) afterEach(() => { sandbox.restore() }) function checkFunctionCall(customHasher = false) { expect(fillTransactionSpy).to.have.been.calledOnce expect(appendSignaturesSpy).to.have.been.calledOnce if (!customHasher) expect(hasherSpy).to.have.been.calledWith(tx) } it('CAVERJS-UNIT-TRANSACTIONFD-236: input: keyring. should sign transaction.', async () => { await tx.sign(sender) checkFunctionCall() checkSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignSpy).to.have.been.calledWith(txHash, '0x7e3', 0, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-237: input: private key string. should sign transaction.', async () => { const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign') await tx.sign(sender.key.privateKey) checkFunctionCall() checkSignature(tx) expect(createFromPrivateKeySpy).to.have.been.calledOnce expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 0, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-238: input: KlaytnWalletKey. should sign transaction.', async () => { const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign') await tx.sign(sender.getKlaytnWalletKey()) checkFunctionCall() checkSignature(tx) expect(createFromPrivateKeySpy).to.have.been.calledOnce expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 0, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-239: input: keyring, index. should sign transaction with specific index.', async () => { const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign') tx.from = roleBasedKeyring.address await tx.sign(roleBasedKeyring, 1) checkFunctionCall() checkSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(roleBasedSignSpy).to.have.been.calledWith(txHash, '0x7e3', 0, 1) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-240: input: keyring, custom hasher. should use custom hasher.', async () => { const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae' const customHasher = () => hashForCustomHasher await tx.sign(sender, customHasher) checkFunctionCall(true) checkSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 0, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-241: input: keyring, index, custom hasher. should use custom hasher when sign transaction.', async () => { const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae' const customHasher = () => hashForCustomHasher const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign') tx.from = roleBasedKeyring.address await tx.sign(roleBasedKeyring, 1, customHasher) checkFunctionCall(true) checkSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(roleBasedSignSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 0, 1) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-242: input: keyring. should throw error when from is different.', async () => { transactionObj.from = roleBasedKeyring.address tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const expectedError = `The from address of the transaction is different with the address of the keyring to use.` await expect(tx.sign(sender)).to.be.rejectedWith(expectedError) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-243: input: rolebased keyring, index out of range. should throw error.', async () => { transactionObj.from = roleBasedKeyring.address tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const expectedError = `Invalid index(10): index must be less than the length of keys(${roleBasedKeyring.keys[0].length}).` await expect(tx.sign(roleBasedKeyring, 10)).to.be.rejectedWith(expectedError) }).timeout(200000) }) context('feeDelegatedSmartContractDeploy.signAsFeePayer', () => { const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' let fillTransactionSpy let createFromPrivateKeySpy let senderSignSpy let appendSignaturesSpy let hasherSpy let tx beforeEach(() => { tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) tx.feePayer = sender.address fillTransactionSpy = sandbox.spy(tx, 'fillTransaction') createFromPrivateKeySpy = sandbox.spy(Keyring, 'createFromPrivateKey') senderSignSpy = sandbox.spy(sender, 'sign') appendSignaturesSpy = sandbox.spy(tx, 'appendFeePayerSignatures') hasherSpy = sandbox.stub(TransactionHasher, 'getHashForFeePayerSignature') hasherSpy.returns(txHash) }) afterEach(() => { sandbox.restore() }) function checkFunctionCall(customHasher = false) { expect(fillTransactionSpy).to.have.been.calledOnce expect(appendSignaturesSpy).to.have.been.calledOnce if (!customHasher) expect(hasherSpy).to.have.been.calledWith(tx) } it('CAVERJS-UNIT-TRANSACTIONFD-244: input: keyring. If feePayer is not defined, should be set with keyring address.', async () => { tx.feePayer = '0x' await tx.signAsFeePayer(sender) expect(tx.feePayer.toLowerCase()).to.equal(sender.address.toLowerCase()) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignSpy).to.have.been.calledWith(txHash, '0x7e3', 2, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-245: input: keyring. should sign transaction.', async () => { await tx.signAsFeePayer(sender) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignSpy).to.have.been.calledWith(txHash, '0x7e3', 2, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-246: input: private key string. should sign transaction.', async () => { const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign') await tx.signAsFeePayer(sender.key.privateKey) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).to.have.been.calledOnce expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 2, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-247: input: KlaytnWalletKey. should sign transaction.', async () => { const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign') await tx.signAsFeePayer(sender.getKlaytnWalletKey()) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).to.have.been.calledOnce expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 2, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-248: input: keyring, index. should sign transaction with specific index.', async () => { const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign') tx.feePayer = roleBasedKeyring.address await tx.signAsFeePayer(roleBasedKeyring, 1) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(roleBasedSignSpy).to.have.been.calledWith(txHash, '0x7e3', 2, 1) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-249: input: keyring, custom hasher. should use custom hasher.', async () => { const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae' const customHasher = () => hashForCustomHasher const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign') tx.feePayer = roleBasedKeyring.address await tx.signAsFeePayer(roleBasedKeyring, customHasher) checkFunctionCall(true) checkFeePayerSignature(tx, { expectedLength: roleBasedKeyring.keys[2].length }) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(roleBasedSignSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 2, undefined) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-250: input: keyring, index, custom hasher. should use custom hasher when sign transaction.', async () => { const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae' const customHasher = () => hashForCustomHasher const roleBasedSignSpy = sandbox.spy(roleBasedKeyring, 'sign') tx.feePayer = roleBasedKeyring.address await tx.signAsFeePayer(roleBasedKeyring, 1, customHasher) checkFunctionCall(true) checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(roleBasedSignSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 2, 1) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-251: input: keyring. should throw error when feePayer is different.', async () => { tx.feePayer = roleBasedKeyring.address const expectedError = `The feePayer address of the transaction is different with the address of the keyring to use.` await expect(tx.signAsFeePayer(sender)).to.be.rejectedWith(expectedError) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-252: input: rolebased keyring, index out of range. should throw error.', async () => { transactionObj.from = roleBasedKeyring.address tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const expectedError = `Invalid index(10): index must be less than the length of keys(${roleBasedKeyring.keys[0].length}).` await expect(tx.signAsFeePayer(roleBasedKeyring, 10)).to.be.rejectedWith(expectedError) }).timeout(200000) }) context('feeDelegatedSmartContractDeploy.sign with multiple keys', () => { const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' let fillTransactionSpy let createFromPrivateKeySpy let senderSignWithKeysSpy let appendSignaturesSpy let hasherSpy let tx beforeEach(() => { tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) fillTransactionSpy = sandbox.spy(tx, 'fillTransaction') createFromPrivateKeySpy = sandbox.spy(Keyring, 'createFromPrivateKey') senderSignWithKeysSpy = sandbox.spy(sender, 'sign') appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures') hasherSpy = sandbox.stub(TransactionHasher, 'getHashForSignature') hasherSpy.returns(txHash) }) afterEach(() => { sandbox.restore() }) function checkFunctionCall(customHasher = false) { expect(fillTransactionSpy).to.have.been.calledOnce expect(appendSignaturesSpy).to.have.been.calledOnce if (!customHasher) expect(hasherSpy).to.have.been.calledWith(tx) } it('CAVERJS-UNIT-TRANSACTIONFD-253: input: keyring. should sign transaction.', async () => { await tx.sign(sender) checkFunctionCall() checkSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 0) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-254: input: private key string. should sign transaction.', async () => { const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign') await tx.sign(sender.key.privateKey) checkFunctionCall() checkSignature(tx) expect(createFromPrivateKeySpy).to.have.been.calledOnce expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 0) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-255: input: KlaytnWalletKey. should sign transaction.', async () => { const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign') await tx.sign(sender.getKlaytnWalletKey()) checkFunctionCall() checkSignature(tx) expect(createFromPrivateKeySpy).to.have.been.calledOnce expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 0) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-256: input: keyring, custom hasher. should use custom hasher when sign transaction.', async () => { const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae' const customHasher = () => hashForCustomHasher await tx.sign(sender, customHasher) checkFunctionCall(true) checkSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignWithKeysSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 0) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-257: input: keyring. should throw error when from is different.', async () => { transactionObj.from = roleBasedKeyring.address tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const expectedError = `The from address of the transaction is different with the address of the keyring to use.` await expect(tx.sign(sender)).to.be.rejectedWith(expectedError) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-258: input: roleBased keyring. should sign with multiple keys and append signatures', async () => { const roleBasedSignWithKeysSpy = sandbox.spy(roleBasedKeyring, 'sign') tx.from = roleBasedKeyring.address await tx.sign(roleBasedKeyring) checkFunctionCall(true) checkSignature(tx, { expectedLength: roleBasedKeyring.keys[0].length }) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(roleBasedSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 0) }).timeout(200000) }) context('feeDelegatedSmartContractDeploy.signAsFeePayer', () => { const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550' let fillTransactionSpy let createFromPrivateKeySpy let senderSignWithKeysSpy let appendSignaturesSpy let hasherSpy let tx beforeEach(() => { tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) fillTransactionSpy = sandbox.spy(tx, 'fillTransaction') createFromPrivateKeySpy = sandbox.spy(Keyring, 'createFromPrivateKey') senderSignWithKeysSpy = sandbox.spy(sender, 'sign') appendSignaturesSpy = sandbox.spy(tx, 'appendFeePayerSignatures') hasherSpy = sandbox.stub(TransactionHasher, 'getHashForFeePayerSignature') hasherSpy.returns(txHash) }) afterEach(() => { sandbox.restore() }) function checkFunctionCall(customHasher = false) { expect(fillTransactionSpy).to.have.been.calledOnce expect(appendSignaturesSpy).to.have.been.calledOnce if (!customHasher) expect(hasherSpy).to.have.been.calledWith(tx) } it('CAVERJS-UNIT-TRANSACTIONFD-259: input: keyring. If feePayer is not defined, should be set with keyring address.', async () => { tx.feePayer = '0x' await tx.signAsFeePayer(sender) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 2) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-260: input: keyring. should sign transaction.', async () => { await tx.signAsFeePayer(sender) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 2) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-261: input: private key string. should sign transaction.', async () => { const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign') await tx.signAsFeePayer(sender.key.privateKey) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).to.have.been.calledOnce expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 2) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-262: input: KlaytnWalletKey. should sign transaction.', async () => { const signProtoSpy = sandbox.spy(SingleKeyring.prototype, 'sign') await tx.signAsFeePayer(sender.getKlaytnWalletKey()) checkFunctionCall() checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).to.have.been.calledOnce expect(signProtoSpy).to.have.been.calledWith(txHash, '0x7e3', 2) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-263: input: keyring, custom hasher. should use custom hasher when sign transaction.', async () => { const hashForCustomHasher = '0x9e4b4835f6ea5ce55bd1037fe92040dd070af6154aefc30d32c65364a1123cae' const customHasher = () => hashForCustomHasher await tx.signAsFeePayer(sender, customHasher) checkFunctionCall(true) checkFeePayerSignature(tx) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(senderSignWithKeysSpy).to.have.been.calledWith(hashForCustomHasher, '0x7e3', 2) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-264: input: keyring. should throw error when feePayer is different.', async () => { tx.feePayer = roleBasedKeyring.address const expectedError = `The feePayer address of the transaction is different with the address of the keyring to use.` await expect(tx.signAsFeePayer(sender)).to.be.rejectedWith(expectedError) }).timeout(200000) it('CAVERJS-UNIT-TRANSACTIONFD-265: input: roleBased keyring. should sign with multiple keys and append signatures', async () => { const roleBasedSignWithKeysSpy = sandbox.spy(roleBasedKeyring, 'sign') tx.feePayer = roleBasedKeyring.address await tx.signAsFeePayer(roleBasedKeyring) checkFunctionCall(true) checkFeePayerSignature(tx, { expectedLength: roleBasedKeyring.keys[2].length }) expect(createFromPrivateKeySpy).not.to.have.been.calledOnce expect(roleBasedSignWithKeysSpy).to.have.been.calledWith(txHash, '0x7e3', 2) }).timeout(200000) }) context('feeDelegatedSmartContractDeploy.appendSignatures', () => { afterEach(() => { sandbox.restore() }) it('CAVERJS-UNIT-TRANSACTIONFD-266: If signatures is empty, appendSignatures append signatures in transaction', () => { const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const sig = [ '0x0fea', '0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e', '0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e', ] tx.appendSignatures(sig) checkSignature(tx) }) it('CAVERJS-UNIT-TRANSACTIONFD-267: If signatures is empty, appendSignatures append signatures with two-dimensional signature array', () => { const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const sig = [ [ '0x0fea', '0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e', '0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e', ], ] tx.appendSignatures(sig) checkSignature(tx) }) it('CAVERJS-UNIT-TRANSACTIONFD-268: If signatures is not empty, appendSignatures should append signatures', () => { transactionObj.signatures = [ '0x0fea', '0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e', '0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e', ] const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const sig = [ '0x0fea', '0x7a5011b41cfcb6270af1b5f8aeac8aeabb1edb436f028261b5add564de694700', '0x23ac51660b8b421bf732ef8148d0d4f19d5e29cb97be6bccb5ae505ebe89eb4a', ] tx.appendSignatures(sig) checkSignature(tx, { expectedLength: 2 }) }) it('CAVERJS-UNIT-TRANSACTIONFD-269: appendSignatures should append multiple signatures', () => { const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const sig = [ [ '0x0fea', '0xbde66cceed35a576010966338b7ded961f2c160c96f928e193b47aaf4480aa07', '0x546eb193ec138523b7fd34c4f12a1a04d0f74470e8f3bbe91ce0b4ec16e7f0d2', ], [ '0x0fea', '0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e', '0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e', ], ] tx.appendSignatures(sig) checkSignature(tx, { expectedLength: 2 }) }) }) context('feeDelegatedSmartContractDeploy.appendFeePayerSignatures', () => { beforeEach(() => { transactionObj.feePayer = '0x90b3e9a3770481345a7f17f22f16d020bccfd33e' }) afterEach(() => { sandbox.restore() }) it('CAVERJS-UNIT-TRANSACTIONFD-270: If feePayerSignatures is empty, appendFeePayerSignatures append feePayerSignatures in transaction', () => { const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const sig = [ '0x0fea', '0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e', '0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e', ] tx.appendFeePayerSignatures(sig) checkFeePayerSignature(tx) }) it('CAVERJS-UNIT-TRANSACTIONFD-271: If feePayerSignatures is empty, appendFeePayerSignatures append feePayerSignatures with two-dimensional signature array', () => { const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const sig = [ [ '0x0fea', '0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e', '0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e', ], ] tx.appendFeePayerSignatures(sig) checkFeePayerSignature(tx) }) it('CAVERJS-UNIT-TRANSACTIONFD-272: If feePayerSignatures is not empty, appendFeePayerSignatures should append feePayerSignatures', () => { transactionObj.feePayerSignatures = [ '0x0fea', '0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e', '0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e', ] const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const sig = [ '0x0fea', '0x7a5011b41cfcb6270af1b5f8aeac8aeabb1edb436f028261b5add564de694700', '0x23ac51660b8b421bf732ef8148d0d4f19d5e29cb97be6bccb5ae505ebe89eb4a', ] tx.appendFeePayerSignatures(sig) checkFeePayerSignature(tx, { expectedLength: 2 }) }) it('CAVERJS-UNIT-TRANSACTIONFD-273: appendFeePayerSignatures should append multiple feePayerSignatures', () => { const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const sig = [ [ '0x0fea', '0xbde66cceed35a576010966338b7ded961f2c160c96f928e193b47aaf4480aa07', '0x546eb193ec138523b7fd34c4f12a1a04d0f74470e8f3bbe91ce0b4ec16e7f0d2', ], [ '0x0fea', '0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e', '0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e', ], ] tx.appendFeePayerSignatures(sig) checkFeePayerSignature(tx, { expectedLength: 2 }) }) }) context('feeDelegatedSmartContractDeploy.combineSignedRawTransactions', () => { beforeEach(() => { transactionObj = { from: '0x54dc8905caf698250cebfcbde49f037b52d55f61', to: '0x', value: '0x0', input, gas: '0xdbba0', humanReadable: false, codeFormat: 'EVM', nonce: '0x1', gasPrice: '0x5d21dba00', chainId: '0x7e3', } }) afterEach(() => { sandbox.restore() }) it('CAVERJS-UNIT-TRANSACTIONFD-274: combineSignedRawTransactions combines single signature and sets signatures in transaction', () => { const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures') const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding') const rlpEncoded = '0x29f90288018505d21dba00830dbba080809454dc8905caf698250cebfcbde49f037b52d55f61b901fe608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f00298080f847f845820fe9a0627b73b8636a2d98f5f51bc30381631055127c3aa13f6f5d470c94ace4d10780a010632196cf8e128de3f99f5e13d41c254dfe3edcc17eea84a49e287cf5b28bda940000000000000000000000000000000000000000c4c3018080' const combined = tx.combineSignedRawTransactions([rlpEncoded]) const expectedSignatures = [ [ '0x0fe9', '0x627b73b8636a2d98f5f51bc30381631055127c3aa13f6f5d470c94ace4d10780', '0x10632196cf8e128de3f99f5e13d41c254dfe3edcc17eea84a49e287cf5b28bda', ], ] expect(appendSignaturesSpy).to.have.been.calledOnce expect(getRLPEncodingSpy).to.have.been.calledOnce expect(combined).to.equal(rlpEncoded) checkSignature(tx, { expectedSignatures }) }) it('CAVERJS-UNIT-TRANSACTIONFD-275: combineSignedRawTransactions combines multiple signatures and sets signatures in transaction', () => { transactionObj.signatures = [ [ '0x0fe9', '0x627b73b8636a2d98f5f51bc30381631055127c3aa13f6f5d470c94ace4d10780', '0x10632196cf8e128de3f99f5e13d41c254dfe3edcc17eea84a49e287cf5b28bda', ], ] const tx = caver.transaction.feeDelegatedSmartContractDeploy.create(transactionObj) const rlpEncodedStrings = [ '0x29f90274018505d21dba00830dbba080809454dc8905caf698250cebfcbde49f037b52d55f61b901fe608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f00298080f847f845820fe9a0c941f8f173e5a5c22216f3f0fdfa4da602356398e24ceee99beb2a2a9c2bfafca00f231de0075bd109708513416a3896fa076130cf6fd891cb1a7abd1835a352be80c4c3018080', '0x29f90274018505d21dba00830dbba080809454dc8905caf698250cebfcbde49f037b52d55f61b901fe608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f00298080f847f845820fe9a073bdd7375228ab9598ab5be10a4ffe1c44211de675295cf07bfce726eef2b764a018923a5455d601c52532280259d820ec2ae5cda3ec57095d8df2a872192a5ae980c4c3018080', ] const appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures') const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding') const combined = tx.combineSignedRawTransactions(rlpEncodedStrings) const expectedRLPEncoded = '0x29f90316018505d21dba00830dbba080809454dc8905caf698250cebfcbde49f037b52d55f61b901fe608060405234801561001057600080fd5b506101de806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a39d8ef81146100805780636353586b146100a757806370a08231146100ca578063fd6b7ef8146100f8575b3360009081526001602052604081208054349081019091558154019055005b34801561008c57600080fd5b5061009561010d565b60408051918252519081900360200190f35b6100c873ffffffffffffffffffffffffffffffffffffffff60043516610113565b005b3480156100d657600080fd5b5061009573ffffffffffffffffffffffffffffffffffffffff60043516610147565b34801561010457600080fd5b506100c8610159565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604081208054349081019091558154019055565b60016020526000908152604090205481565b336000908152600160205260408120805490829055908111156101af57604051339082156108fc029083906000818181858888f193505050501561019c576101af565b3360009081526001602052604090208190555b505600a165627a7a72305820627ca46bb09478a015762806cc00c431230501118c7c26c30ac58c4e09e51c4f00298080f8d5f8458