caver-js
Version:
caver-js is a JavaScript API library that allows developers to interact with a Klaytn node
820 lines (650 loc) • 40 kB
JavaScript
/*
Copyright 2020 The caver-js Authors
This file is part of the caver-js library.
The caver-js library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The caver-js library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the caver-js. If not, see <http://www.gnu.org/licenses/>.
*/
const chai = require('chai')
const sinon = require('sinon')
const sinonChai = require('sinon-chai')
const chaiAsPromised = require('chai-as-promised')
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 } = require('../utils')
let caver
let sender
let roleBasedKeyring
const sandbox = sinon.createSandbox()
const input =
'0xa9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a0000000000000000000000000000000000000000000000000000000000003039'
const txWithExpectedValues = {}
before(() => {
caver = new Caver(testRPCURL)
sender = caver.wallet.add(caver.wallet.keyring.generate())
roleBasedKeyring = generateRoleBasedKeyring([3, 3, 3])
txWithExpectedValues.tx = {
from: '0x6b604e77c0fbebb5b2941bcde3ab5eb09d99ad24',
to: '0xe3cd4e1cd287235cc0ea48c9fd02978533f5ec2b',
value: '0x0',
gas: '0xdbba0',
gasPrice: '0x5d21dba00',
input,
chainId: '0x7e3',
signatures: [
[
'0x0fea',
'0x66e1650b5779f152489633f343581c07938f8b2fc92c919d4dd7c7295d0beace',
'0x67b0b79383dbcd42a3aa8ebb1aa4bcb1fc0623ef9e97bc1e9b82d96fe37b5881',
],
],
nonce: '0x3',
}
txWithExpectedValues.rlpEncodingForSigning =
'0xf886b87ff87d30038505d21dba00830dbba094e3cd4e1cd287235cc0ea48c9fd02978533f5ec2b80946b604e77c0fbebb5b2941bcde3ab5eb09d99ad24b844a9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a00000000000000000000000000000000000000000000000000000000000030398207e38080'
txWithExpectedValues.rlpEncodingCommon =
'0xf87d30038505d21dba00830dbba094e3cd4e1cd287235cc0ea48c9fd02978533f5ec2b80946b604e77c0fbebb5b2941bcde3ab5eb09d99ad24b844a9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a0000000000000000000000000000000000000000000000000000000000003039'
txWithExpectedValues.senderTxHash = '0x75119321f4c5e93e486accb33fedc3d714de2610ffdc9655182ac50e4a5f3298'
txWithExpectedValues.transactionHash = '0x75119321f4c5e93e486accb33fedc3d714de2610ffdc9655182ac50e4a5f3298'
txWithExpectedValues.rlpEncoding =
'0x30f8c5038505d21dba00830dbba094e3cd4e1cd287235cc0ea48c9fd02978533f5ec2b80946b604e77c0fbebb5b2941bcde3ab5eb09d99ad24b844a9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a0000000000000000000000000000000000000000000000000000000000003039f847f845820feaa066e1650b5779f152489633f343581c07938f8b2fc92c919d4dd7c7295d0beacea067b0b79383dbcd42a3aa8ebb1aa4bcb1fc0623ef9e97bc1e9b82d96fe37b5881'
})
describe('TxTypeSmartContractExecution', () => {
let transactionObj
let getGasPriceSpy
let getHeaderSpy
let getNonceSpy
let getChainIdSpy
beforeEach(() => {
transactionObj = {
from: sender.address,
to: '0xe3cd4e1cd287235cc0ea48c9fd02978533f5ec2b',
value: 0,
gas: '0x15f90',
input,
}
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 smartContractExecution instance', () => {
it('CAVERJS-UNIT-TRANSACTION-248: If smartContractExecution not define from, return error', () => {
delete transactionObj.from
const expectedError = '"from" is missing'
expect(() => caver.transaction.smartContractExecution.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-249: If smartContractExecution not define to, return error', () => {
delete transactionObj.to
const expectedError = '"to" is missing'
expect(() => caver.transaction.smartContractExecution.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-250: If smartContractExecution not define gas, return error', () => {
delete transactionObj.gas
const expectedError = '"gas" is missing'
expect(() => caver.transaction.smartContractExecution.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-251: If smartContractExecution not define input, return error', () => {
delete transactionObj.input
const expectedError = '"input" is missing'
expect(() => caver.transaction.smartContractExecution.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-252: If smartContractExecution define from property with invalid address, return error', () => {
transactionObj.from = 'invalid'
const expectedError = `Invalid address of from: ${transactionObj.from}`
expect(() => caver.transaction.smartContractExecution.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-253: If smartContractExecution define to property with invalid address, return error', () => {
transactionObj.to = 'invalid'
const expectedError = `Invalid address of to: ${transactionObj.to}`
expect(() => caver.transaction.smartContractExecution.create(transactionObj)).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-254: If smartContractExecution define unnecessary property, return error', () => {
const unnecessaries = [
propertiesForUnnecessary.codeFormat,
propertiesForUnnecessary.humanReadable,
propertiesForUnnecessary.failKey,
propertiesForUnnecessary.feePayer,
propertiesForUnnecessary.feePayerSignatures,
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.TxTypeSmartContractExecution} transaction`
expect(() => caver.transaction.smartContractExecution.create(transactionObj)).to.throw(expectedError)
}
})
})
context('smartContractExecution.getRLPEncoding', () => {
it('CAVERJS-UNIT-TRANSACTION-255: Returns RLP-encoded string', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
expect(tx.getRLPEncoding()).to.equal(txWithExpectedValues.rlpEncoding)
})
it('CAVERJS-UNIT-TRANSACTION-256: getRLPEncoding should throw error when nonce is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._nonce
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-TRANSACTION-257: getRLPEncoding should throw error when gasPrice is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._gasPrice
const expectedError = `gasPrice is undefined. Define gasPrice in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getRLPEncoding()).to.throw(expectedError)
})
})
context('smartContractExecution.sign', () => {
const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550'
let fillTransactionSpy
let createFromPrivateKeySpy
let senderSignSpy
let appendSignaturesSpy
let hasherSpy
let tx
beforeEach(() => {
tx = caver.transaction.smartContractExecution.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-TRANSACTION-259: 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-TRANSACTION-260: 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-TRANSACTION-261: 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-TRANSACTION-262: 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-TRANSACTION-263: 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-TRANSACTION-264: 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-TRANSACTION-265: input: keyring. should throw error when from is different.', async () => {
transactionObj.from = roleBasedKeyring.address
tx = caver.transaction.smartContractExecution.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-TRANSACTION-266: input: rolebased keyring, index out of range. should throw error.', async () => {
transactionObj.from = roleBasedKeyring.address
tx = caver.transaction.smartContractExecution.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('smartContractExecution.sign with multiple keys', () => {
const txHash = '0xe9a11d9ef95fb437f75d07ce768d43e74f158dd54b106e7d3746ce29d545b550'
let fillTransactionSpy
let createFromPrivateKeySpy
let senderSignWithKeysSpy
let appendSignaturesSpy
let hasherSpy
let tx
beforeEach(() => {
tx = caver.transaction.smartContractExecution.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-TRANSACTION-267: 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-TRANSACTION-268: 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-TRANSACTION-269: 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-TRANSACTION-270: 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-TRANSACTION-271: input: keyring. should throw error when from is different.', async () => {
transactionObj.from = roleBasedKeyring.address
tx = caver.transaction.smartContractExecution.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-TRANSACTION-272: 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('smartContractExecution.appendSignatures', () => {
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTION-273: If signatures is empty, appendSignatures append signatures in transaction', () => {
const tx = caver.transaction.smartContractExecution.create(transactionObj)
const sig = [
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
]
tx.appendSignatures(sig)
checkSignature(tx)
})
it('CAVERJS-UNIT-TRANSACTION-274: If signatures is empty, appendSignatures append signatures with two-dimensional signature array', () => {
const tx = caver.transaction.smartContractExecution.create(transactionObj)
const sig = [
[
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
],
]
tx.appendSignatures(sig)
checkSignature(tx)
})
it('CAVERJS-UNIT-TRANSACTION-275: If signatures is not empty, appendSignatures should append signatures', () => {
transactionObj.signatures = [
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
]
const tx = caver.transaction.smartContractExecution.create(transactionObj)
const sig = [
'0x0fea',
'0x7a5011b41cfcb6270af1b5f8aeac8aeabb1edb436f028261b5add564de694700',
'0x23ac51660b8b421bf732ef8148d0d4f19d5e29cb97be6bccb5ae505ebe89eb4a',
]
tx.appendSignatures(sig)
checkSignature(tx, { expectedLength: 2 })
})
it('CAVERJS-UNIT-TRANSACTION-276: appendSignatures should append multiple signatures', () => {
const tx = caver.transaction.smartContractExecution.create(transactionObj)
const sig = [
[
'0x0fea',
'0xbde66cceed35a576010966338b7ded961f2c160c96f928e193b47aaf4480aa07',
'0x546eb193ec138523b7fd34c4f12a1a04d0f74470e8f3bbe91ce0b4ec16e7f0d2',
],
[
'0x0fea',
'0xade9480f584fe481bf070ab758ecc010afa15debc33e1bd75af637d834073a6e',
'0x38160105d78cef4529d765941ad6637d8dcf6bd99310e165fee1c39fff2aa27e',
],
]
tx.appendSignatures(sig)
checkSignature(tx, { expectedLength: 2 })
})
})
context('smartContractExecution.combineSignedRawTransactions', () => {
beforeEach(() => {
transactionObj = {
from: '0x2de587b91c76fb0b92fd607c4fbd5e9a17da799f',
to: '0xe3cd4e1cd287235cc0ea48c9fd02978533f5ec2b',
value: '0x0',
gas: '0xdbba0',
gasPrice: '0x5d21dba00',
input,
chainId: '0x7e3',
nonce: '0x1',
}
})
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTION-277: combineSignedRawTransactions combines single signature and sets signatures in transaction', () => {
const tx = caver.transaction.smartContractExecution.create(transactionObj)
const appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures')
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const rlpEncoded =
'0x30f8c5018505d21dba00830dbba094e3cd4e1cd287235cc0ea48c9fd02978533f5ec2b80942de587b91c76fb0b92fd607c4fbd5e9a17da799fb844a9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a0000000000000000000000000000000000000000000000000000000000003039f847f845820fe9a04a7d00c2680e5bca49a5880e23c5adb40b069af204a55e888f45746a20978e46a007b57a439201d182f4aec5db28d72192468f58f4fe7a1e717f96dd0d1def2d16'
const combined = tx.combineSignedRawTransactions([rlpEncoded])
const expectedSignatures = [
[
'0x0fe9',
'0x4a7d00c2680e5bca49a5880e23c5adb40b069af204a55e888f45746a20978e46',
'0x07b57a439201d182f4aec5db28d72192468f58f4fe7a1e717f96dd0d1def2d16',
],
]
expect(appendSignaturesSpy).to.have.been.calledOnce
expect(getRLPEncodingSpy).to.have.been.calledOnce
expect(combined).to.equal(rlpEncoded)
checkSignature(tx, { expectedSignatures })
})
it('CAVERJS-UNIT-TRANSACTION-278: combineSignedRawTransactions combines multiple signatures and sets signatures in transaction', () => {
transactionObj.signatures = [
[
'0x0fe9',
'0x4a7d00c2680e5bca49a5880e23c5adb40b069af204a55e888f45746a20978e46',
'0x07b57a439201d182f4aec5db28d72192468f58f4fe7a1e717f96dd0d1def2d16',
],
]
const tx = caver.transaction.smartContractExecution.create(transactionObj)
const rlpEncodedStrings = [
'0x30f8c5018505d21dba00830dbba094e3cd4e1cd287235cc0ea48c9fd02978533f5ec2b80942de587b91c76fb0b92fd607c4fbd5e9a17da799fb844a9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a0000000000000000000000000000000000000000000000000000000000003039f847f845820fe9a08494bfd86b0480e33700635d37ab0eb0ce3e6d93b5c51e6eda9fadd179569804a047f601d9fcb8682090165d8d048d6a5e3c5a48377ec9b212be6d7ee72b768bfd',
'0x30f8c5018505d21dba00830dbba094e3cd4e1cd287235cc0ea48c9fd02978533f5ec2b80942de587b91c76fb0b92fd607c4fbd5e9a17da799fb844a9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a0000000000000000000000000000000000000000000000000000000000003039f847f845820fe9a0f642b38cf64cf70c89a0ccd74de13266ea98854078119a4619cad3bb2e6d4530a02307abe779333fe9da8eeebf40fbfeff9f1314ae8467a0119541339dfb65f10a',
]
const appendSignaturesSpy = sandbox.spy(tx, 'appendSignatures')
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const combined = tx.combineSignedRawTransactions(rlpEncodedStrings)
const expectedRLPEncoded =
'0x30f90153018505d21dba00830dbba094e3cd4e1cd287235cc0ea48c9fd02978533f5ec2b80942de587b91c76fb0b92fd607c4fbd5e9a17da799fb844a9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a0000000000000000000000000000000000000000000000000000000000003039f8d5f845820fe9a04a7d00c2680e5bca49a5880e23c5adb40b069af204a55e888f45746a20978e46a007b57a439201d182f4aec5db28d72192468f58f4fe7a1e717f96dd0d1def2d16f845820fe9a08494bfd86b0480e33700635d37ab0eb0ce3e6d93b5c51e6eda9fadd179569804a047f601d9fcb8682090165d8d048d6a5e3c5a48377ec9b212be6d7ee72b768bfdf845820fe9a0f642b38cf64cf70c89a0ccd74de13266ea98854078119a4619cad3bb2e6d4530a02307abe779333fe9da8eeebf40fbfeff9f1314ae8467a0119541339dfb65f10a'
const expectedSignatures = [
[
'0x0fe9',
'0x4a7d00c2680e5bca49a5880e23c5adb40b069af204a55e888f45746a20978e46',
'0x07b57a439201d182f4aec5db28d72192468f58f4fe7a1e717f96dd0d1def2d16',
],
[
'0x0fe9',
'0x8494bfd86b0480e33700635d37ab0eb0ce3e6d93b5c51e6eda9fadd179569804',
'0x47f601d9fcb8682090165d8d048d6a5e3c5a48377ec9b212be6d7ee72b768bfd',
],
[
'0x0fe9',
'0xf642b38cf64cf70c89a0ccd74de13266ea98854078119a4619cad3bb2e6d4530',
'0x2307abe779333fe9da8eeebf40fbfeff9f1314ae8467a0119541339dfb65f10a',
],
]
expect(appendSignaturesSpy).to.have.been.callCount(rlpEncodedStrings.length)
expect(getRLPEncodingSpy).to.have.been.calledOnce
expect(combined).to.equal(expectedRLPEncoded)
checkSignature(tx, { expectedSignatures })
})
it('CAVERJS-UNIT-TRANSACTION-279: If decode transaction has different values, combineSignedRawTransactions should throw error', () => {
const tx = caver.transaction.smartContractExecution.create(transactionObj)
tx.value = 10000
const rlpEncoded =
'0x30f8c5018505d21dba00830dbba094e3cd4e1cd287235cc0ea48c9fd02978533f5ec2b80942de587b91c76fb0b92fd607c4fbd5e9a17da799fb844a9059cbb0000000000000000000000008a4c9c443bb0645df646a2d5bb55def0ed1e885a0000000000000000000000000000000000000000000000000000000000003039f847f845820fe9a08494bfd86b0480e33700635d37ab0eb0ce3e6d93b5c51e6eda9fadd179569804a047f601d9fcb8682090165d8d048d6a5e3c5a48377ec9b212be6d7ee72b768bfd'
const expectedError = `Transactions containing different information cannot be combined.`
expect(() => tx.combineSignedRawTransactions([rlpEncoded])).to.throw(expectedError)
})
})
context('smartContractExecution.getRawTransaction', () => {
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTION-280: getRawTransaction should call getRLPEncoding function', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const expected = txWithExpectedValues.rlpEncoding
const rawTransaction = tx.getRawTransaction()
expect(getRLPEncodingSpy).to.have.been.calledOnce
expect(rawTransaction).to.equal(expected)
})
})
context('smartContractExecution.getTransactionHash', () => {
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTION-281: getTransactionHash should call getRLPEncoding function and return hash of RLPEncoding', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const expected = txWithExpectedValues.transactionHash
const txHash = tx.getTransactionHash()
expect(getRLPEncodingSpy).to.have.been.calledOnce
expect(txHash).to.equal(expected)
expect(caver.utils.isValidHashStrict(txHash)).to.be.true
})
it('CAVERJS-UNIT-TRANSACTION-282: getTransactionHash should throw error when nonce is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._nonce
const expectedError = `nonce is undefined. Define nonce in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getTransactionHash()).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-283: getTransactionHash should throw error when gasPrice is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._gasPrice
const expectedError = `gasPrice is undefined. Define gasPrice in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getTransactionHash()).to.throw(expectedError)
})
})
context('smartContractExecution.getSenderTxHash', () => {
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTION-285: getSenderTxHash should call getRLPEncoding function and return hash of RLPEncoding', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
const getRLPEncodingSpy = sandbox.spy(tx, 'getRLPEncoding')
const expected = txWithExpectedValues.senderTxHash
const senderTxHash = tx.getSenderTxHash()
expect(getRLPEncodingSpy).to.have.been.calledOnce
expect(senderTxHash).to.equal(expected)
expect(caver.utils.isValidHashStrict(senderTxHash)).to.be.true
})
it('CAVERJS-UNIT-TRANSACTION-286: getSenderTxHash should throw error when nonce is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._nonce
const expectedError = `nonce is undefined. Define nonce in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getSenderTxHash()).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-287: getSenderTxHash should throw error when gasPrice is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._gasPrice
const expectedError = `gasPrice is undefined. Define gasPrice in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getSenderTxHash()).to.throw(expectedError)
})
})
context('smartContractExecution.getRLPEncodingForSignature', () => {
afterEach(() => {
sandbox.restore()
})
it('CAVERJS-UNIT-TRANSACTION-289: getRLPEncodingForSignature should return RLP-encoded transaction string for signing', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
const commonRLPForSigningSpy = sandbox.spy(tx, 'getCommonRLPEncodingForSignature')
const expected = txWithExpectedValues.rlpEncodingForSigning
const rlpEncodingForSign = tx.getRLPEncodingForSignature()
expect(rlpEncodingForSign).to.equal(expected)
expect(commonRLPForSigningSpy).to.have.been.calledOnce
})
it('CAVERJS-UNIT-TRANSACTION-290: getRLPEncodingForSignature should throw error when nonce is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._nonce
const expectedError = `nonce is undefined. Define nonce in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getRLPEncodingForSignature()).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-291: getRLPEncodingForSignature should throw error when gasPrice is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._gasPrice
const expectedError = `gasPrice is undefined. Define gasPrice in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getRLPEncodingForSignature()).to.throw(expectedError)
})
it('CAVERJS-UNIT-TRANSACTION-292: getRLPEncodingForSignature should throw error when chainId is undefined', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._chainId
const expectedError = `chainId is undefined. Define chainId in transaction or use 'transaction.fillTransaction' to fill values.`
expect(() => tx.getRLPEncodingForSignature()).to.throw(expectedError)
})
})
context('smartContractExecution.getCommonRLPEncodingForSignature', () => {
it('CAVERJS-UNIT-TRANSACTION-293: getRLPEncodingForSignature should return RLP-encoded transaction string for signing', () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
const commonRLPForSign = tx.getCommonRLPEncodingForSignature()
expect(commonRLPForSign).to.equal(txWithExpectedValues.rlpEncodingCommon)
})
})
context('smartContractExecution.fillTransaction', () => {
it('CAVERJS-UNIT-TRANSACTION-294: fillTransaction should call klay_getGasPrice to fill gasPrice when gasPrice is undefined', async () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._gasPrice
await tx.fillTransaction()
expect(getGasPriceSpy).to.have.been.calledOnce
expect(getHeaderSpy).not.to.have.been.calledOnce
expect(getNonceSpy).not.to.have.been.calledOnce
expect(getChainIdSpy).not.to.have.been.calledOnce
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTION-295: fillTransaction should call klay_getTransactionCount to fill nonce when nonce is undefined', async () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._nonce
await tx.fillTransaction()
expect(getGasPriceSpy).not.to.have.been.calledOnce
expect(getHeaderSpy).not.to.have.been.calledOnce
expect(getNonceSpy).to.have.been.calledOnce
expect(getChainIdSpy).not.to.have.been.calledOnce
}).timeout(200000)
it('CAVERJS-UNIT-TRANSACTION-296: fillTransaction should call klay_getChainid to fill chainId when chainId is undefined', async () => {
const tx = caver.transaction.smartContractExecution.create(txWithExpectedValues.tx)
delete tx._chainId
await tx.fillTransaction()
expect(getGasPriceSpy).not.to.have.been.calledOnce
expect(getHeaderSpy).not.to.have.been.calledOnce
expect(getNonceSpy).not.to.have.been.calledOnce
expect(getChainIdSpy).to.have.been.calledOnce
}).timeout(200000)
})
context('smartContractExecution.recoverPublicKeys', () => {
const expectedPublicKeyArray = [
'0x8bb6aaeb2d96d024754d3b50babf116cece68977acbe8ba6a66f14d5217c60d96af020a0568661e7c72e753e80efe084a3aed9f9ac87bf44d09ce67aad3d4e01',
'0xc7751c794337a93e4db041fb5401c2c816cf0a099d8fd4b1f3f555aab5dfead2417521bb0c03d8637f350df15ef6a6cb3cdb806bd9d10bc71982dd03ff5d9ddd',
'0x3919091ba17c106dd034af508cfe00b963d173dffab2c7702890e25a96d107ca1bb4f148ee1984751e57d2435468558193ce84ab9a7731b842e9672e40dc0f22',
]
it('CAVERJS-UNIT-TRANSACTION-426: should return public key string recovered from signatures in SmartContractExecution', async () => {
const tx = caver.transaction.smartContractExecution.create({
from: '0xf21460730845e3652aa3cc9bc13b345e4f53984a',
to: '0x59177716c34ac6e49e295a0e78e33522f14d61ee',
value: '0x1',
chainId: '0x7e3',
gasPrice: '0x5d21dba00',
nonce: '0x0',
gas: '0x2faf080',
input:
'0xd95aced7000000000000000000000000640a4c021cb5889fa1d37378f04a36ad452862240000000000000000000000000000000000000000000000000000000000000001',
signatures: [
[
'0x0fe9',
'0xd544476d9d0cadad0f5d6aea6f487d56299166af7b0c372459674a6f05ffcdd7',
'0x77842050ad1b259b3ba53165784ec38ac0bfc5e1c4efb7a5cc0524cfc5e62ef2',
],
[
'0x0fe9',
'0x10eae9aac3bb49e5ff86e69cf06ae8694f94660317adf8f43f906ad52072a5f6',
'0x212130f92da5e832ac94565f245930b58aeed65775339b4aaae091971741c6e5',
],
[
'0x0fe9',
'0xc63d6be7f19c43d529d90dfe317db3cc5bee27f54c9141ae86e771b06756f528',
'0x3bf1621074fdad4c37c7575a76bde0e937d8e72db914df87080d35763dea4567',
],
],
})
const publicKeys = tx.recoverPublicKeys()
expect(publicKeys.length).to.equal(expectedPublicKeyArray.length)
for (let i = 0; i < publicKeys.length; i++) {
expect(publicKeys[i].toLowerCase()).to.equal(expectedPublicKeyArray[i].toLowerCase())
}
}).timeout(200000)
})
})