EOSIO / eosjs

General purpose library for the EOSIO blockchain.
http://eosio.github.io/eosjs
MIT License
1.43k stars 463 forks source link

Transaction headers are not preserved when using abiCache to import contract ABI offline #265

Closed dmmd77 closed 6 years ago

dmmd77 commented 6 years ago

Hi,

I am trying to sign a transaction from the cold storage preserving existing transaction headers. The unit test from index.test.js that uses eosjs.transfer works for me, but when I try to use my own contract by bringing it into the ABI cache, the transaction headers in the config do not get applied. Is this the right way to initialize the contract ABI when httpEndpoint is null? Would it be possible to get a unit test for offline signing with existing transaction headers on a custom contract? Thank you!

const offlineSigningConfig = { keyProvider: [key], httpEndpoint: null, transactionHeaders: headers }

const eos = Eos(offlineSigningConfig); await eos.fc.abiCache.abi(accountName, JSON.parse(myAbi)); const myContract = await eos.contract(accountName, { signProvider }); const myAction = await myContract.myAction(myData); // Incorrect transaction headrers in myAction.transaction.transaction

const memo = '' const testtrx = await eos.transfer('few', 'many', '100.0000 SYS', memo) // Correct transaction headrers in testtrx.transaction.transaction

jcalfee commented 6 years ago

transactionHeaders is a little awkward.. Make sure it is a method that looks exactly like this:

transactionHeaders: (expireInSeconds, callback) => {
        callback(null/*error*/, eosTransactionHeader)
      }
dmmd77 commented 6 years ago

Passing in transactionHeaders as a callback did not help. Calling an action on my contract still sets the expiration time relative to when it is called rather than using the provided transactionHeaders. I verified that the transactionHeaders callback is being called for eos.transfer() but not for myContract.myAction().

jcalfee commented 6 years ago

Remove {signProvider} here, that is not an option on a contract:

const myContract = await eos.contract(accountName, { signProvider });

Not sure what is going on. I was able to set static headers and they were used in these cases.

// just like your case above:
eos.contract('eosio.msig', (err, msig) => {
  msig.unapprove('test', 'test', {actor: 'test', permission: 'active'})
})

eos.transaction('eosio.msig', msig => {
  msig.unapprove('test', 'test', {actor: 'test', permission: 'active'})
})

From https://github.com/eosio/eosjs#offline-or-cold-storage-transaction Modified to use eos.fc.abiCache.abi .. Notice the expiration dates are the same:

> (async function() {
... expireInSeconds = 60 * 60 // 1 hour
... 
... eos = Eos(/* {httpEndpoint: 'https://..'} */)
... 
... info = await eos.getInfo({})
... chainDate = new Date(info.head_block_time + 'Z')
... expiration = new Date(chainDate.getTime() + expireInSeconds * 1000)
... expiration = expiration.toISOString().split('.')[0]
... 
... block = await eos.getBlock(info.last_irreversible_block_num)
... 
... transactionHeaders = {
...   expiration,
...   ref_block_num: info.last_irreversible_block_num & 0xFFFF,
...   ref_block_prefix: block.ref_block_prefix
... }
... })()

> transactionHeaders.expiration
'2018-07-22T19:06:12'

> chainId='aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906'
'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906'
> keyProvider='5...'
> msigAbi = JSON.parse(fs.readFileSync(`docker/contracts/eosio.msig/eosio.msig.abi`))

> (async function() {
...   eos = Eos({httpEndpoint: null, chainId, keyProvider, transactionHeaders})
...   eos.fc.abiCache.abi('eosio.msig', msigAbi)
...   msig = await eos.contract('eosio.msig')
...   msigTr = await msig.unapprove('test', 'test', {actor: 'test', permission: 'active'}, {broadcast: false})
... })()

> msigTr.transaction.transaction.expiration
'2018-07-22T19:06:12'
dmmd77 commented 6 years ago

Thanks for your help, I got it all working.