Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Bug in @trufflesuite/web3-provider-engine 14.0.6 (dependency of @truffle/hdwallet-provider 1.0.36 where getBlockByNumber function of the blockTracker event listener sometimes returns a null block object and crashes applications with error TypeError: Cannot read property 'number' of null #3130

Closed
1 task done
ltfschoen opened this issue Jun 29, 2020 · 11 comments · Fixed by trufflesuite/provider-engine#6 or #3135

Comments

@ltfschoen
Copy link


Issue

Truffle's fork of the Metamask web3-provider-engine doesn't include changes such as adding a _getBlockByNumberWithRetry and catering for when the block tracker event listener occasionally returns a block object that has a null value. As a result, when you use the dependency "@truffle/hdwallet-provider": "^1.0.36" (which depends on "@trufflesuite/web3-provider-engine" "14.0.6") in your project, it will eventually crash.

Steps to Reproduce

It requires patience to reproduce because you have to wait until _getBlockByNumber eventually returns a null value, which could take over an hour.

To reproduce, clone my faucet project https://github.com/DataHighway-DHX/faucet, set it up by following the instructions in the README, which include installing Node.js 14.4.0 and Yarn.

Then modify the dependencies to add some console.logs for debugging by editing DataHighway-DHX/faucet-mining/node_modules/@trufflesuite/web3-provider-engine/index.js, and modifying the code by adding the section shown below between // MODIFICATIONS START HERE and // MODIFICATIONS END HERE:

  self._blockTracker.on('latest', (blockNumber) => {
    // get block body
    self._getBlockByNumber(blockNumber, (err, block) => {
      if (err) {
        this.emit('error', err)
        return
      }
      // MODIFICATIONS START HERE
      if (!block) {
        console.error('NO BLOCK: ', typeof block);
      }
      console.log('BLOCK: ', block);
      // MODIFICATIONS END HERE
      const bufferBlock = toBufferBlock(block)

Then run the code:

yarn global add nodemon;
DEBUG=app yarn dev;

And watch the terminal logs until the output becomes

[0] BLOCK: {
[0]   difficulty: '0x9bdeaf63',
[0]   extraData: '0x74657374',
[0]   gasLimit: '0x7a121d',
[0]   gasUsed: '0x20c1c',
[0]   hash: '0x593679a19b9cc02e52d819d9256d2c5e9a131cdecda98ef7dac144b240397720',
[0]   logsBloom: '0x00000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000',
[0]   miner: '0xbbf5029fd710d227630c8b7d338051b8e76d50b3',
[0]   mixHash: '0x97232baa933ebee13b4e14447ea8114bb8fc5652ee5dfc35caca3a4afb0f4ebd',
[0]   nonce: '0x0c736d40080dda7b',
[0]   number: '0x7d02c5',
[0]   parentHash: '0xbfc6dfaed1488da8edd72b18e9f770c0911b3a9f5edce5294e306a474824eb97',
[0]   receiptsRoot: '0xf279f211e14db07e36961789f6ba968f94b7b2413b3df218ca98effeeaab29a2',
[0]   sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
[0]   size: '0x36b',
[0]   stateRoot: '0x042ad505ab14e8dfb0bcba694418ea1b1a535ae4cbab3d04e69f0226e7bef091',
[0]   timestamp: '0x5ef9cb84',
[0]   totalDifficulty: '0x6cdaa9a19c28f4',
[0]   transactions: [
[0]     '0x86a58956c34fe668f0a9b3031b424939eb014112e351f7bd09140e62ab667ddf',
[0]     '0x8d37315790bf1ec21a27bf2acbac464035d18ec838e768235dd588d40956622b'
[0]   ],
[0]   transactionsRoot: '0x3bc040f4ab29394a3000efb4f88b6d6933b760d3da7e0bcd08d90b7b81ddf510',
[0]   uncles: []
[0] }
[0] NO BLOCK: object
[0] BLOCK: null
[0] 
[0] /Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/@trufflesuite/web3-provider-engine/index.js:223
[0]     number:           ethUtil.toBuffer(jsonBlock.number),
[0]                                                  ^
[0] TypeError: Cannot read property 'number' of null
[0]     at toBufferBlock (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/@trufflesuite/web3-provider-engine/index.js:223:50)
[0]     at /Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/@trufflesuite/web3-provider-engine/index.js:64:27
[0]     at /Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/@trufflesuite/web3-provider-engine/index.js:140:12
[0]     at /Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/@trufflesuite/web3-provider-engine/index.js:203:9
[0]     at /Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/async/internal/once.js:12:16
[0]     at replenish (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/async/internal/eachOfLimit.js:61:25)
[0]     at /Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/async/internal/eachOfLimit.js:71:9
[0]     at eachLimit (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/async/eachLimit.js:43:36)
[0]     at /Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/async/internal/doLimit.js:9:16
[0]     at end (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/@trufflesuite/web3-provider-engine/index.js:180:5)
[0]     at /Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/@trufflesuite/web3-provider-engine/subproviders/provider.js:19:5
[0]     at XMLHttpRequest.request.onreadystatechange (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/web3-providers-http/src/index.js:96:13)
[0]     at XMLHttpRequestEventTarget.dispatchEvent (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/xhr2-cookies/xml-http-request-event-target.ts:44:13)
[0]     at XMLHttpRequest._setReadyState (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/xhr2-cookies/xml-http-request.ts:219:8)
[0]     at XMLHttpRequest._onHttpResponseEnd (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/xhr2-cookies/xml-http-request.ts:345:8)
[0]     at IncomingMessage.<anonymous> (/Users/ls/code/src/DataHighway-com/faucet-mining/node_modules/xhr2-cookies/xml-http-request.ts:311:39)
[0] [nodemon] app crashed - waiting for file changes before starting...

Then you've got evidence of where and why it crashed.

Expected Behavior

I expected it not to crash. But now that I know that it crashes because the block object sometimes returns a null object, I know that to fix it we just need to make the same changes that MetaMask's web3-provider-engine did. They previously fixed that bug by exiting early from that getBlockByNumber function here https://github.com/MetaMask/web3-provider-engine/blob/master/index.js#L57, and more recently they just replaced the _getBlockByNumber function with a _getBlockByNumberWithRetry function in this commit MetaMask/web3-provider-engine@caaf92b

Actual Results

Refer to actual results in the above "Steps to Reproduce" section

Environment

  • Operating System: macOS Mojave 10.14.6
  • Ethereum client: Infura
  • Truffle version (truffle version): N/A ("@truffle/hdwallet-provider": "^1.0.36")
  • node version (node --version): v14.4.0
  • npm version (npm --version): 6.14.5
@CruzMolina
Copy link
Contributor

Nice sleuthing @ltfschoen ! Glad to hear this was a known issue MM resolved in a later version of web3-provider-engine.

@CruzMolina
Copy link
Contributor

Until this goes out in our official weekly release, the fix for this is out at @truffle/hdwallet-provider@next.

Please give it a shot and let us know if this particular error has been resolved! 🙏

@boolafish
Copy link

just to confirm this works :)
Our deployment start to re-work after using @truffle/hdwallet-provider@next

boolafish added a commit to omgnetwork/plasma-contracts that referenced this issue Jul 1, 2020
* fix: npm install && npm audit fix

Seems like there is some dependency trouble. Try blindly solving it with npm audit fix.

* chore: CI node version to 10.20

* chore: lock truffle version

* test: tmp diable CI cache

* chore: add debug on http request

* chore: revive cache...seems unrelated

* artifect logs

* chore: bump truffle version

* debug: is it HDwallet issue?

* debug: back to function hd wallet provider

* test: does deploy still work?

* debug: use cache var to delay wallet provider initiation

* test

* revert truffle-config.js

* provider back to a function

* chore: use @truffle/hdwallet-provider@next

Testing out dependency change: trufflesuite/truffle#3130 (comment)

* chore: re-enable master branch filter on rinkeby deploy

* docs: remove unrelevant comment

Co-authored-by: Kevin Sullivan <4653170+kevsul@users.noreply.github.com>
@PatrickAlphaC
Copy link

Is there something I can do to work around this? I tried to update my package.json file to use a different version of "@truffle/hdwallet-provider": "^1.0.36".

Is there something I can do while this is down?

@PatrickAlphaC
Copy link

Oh I saw you can do "@truffle/hdwallet-provider": "next", and then run npm install Thank you!

@PatrickAlphaC
Copy link

PatrickAlphaC commented Jul 1, 2020

Ah now my script is just hanging instead of breaking. I'm using npx truffle exec scripts/fund-contract.js --network live in my chainlink truffle box.

It's freezing at const token = await LinkToken.at(tokenAddress); The tokenAddress seems to be correct when I console.log it for ropsten. The LinkToken is from const { LinkToken } = require("@chainlink/contracts/truffle/v0.4/LinkToken");

Not sure if that is enough information. Sounds like we may have to update the script or the box

@CruzMolina
Copy link
Contributor

CruzMolina commented Jul 1, 2020

Hey @PatrickAlphaC , does the script consistently fail on that line? I assume you're referring to the official box here. I'm wondering if the issue you're seeing is related to #3133. Will take a look and try to reproduce!

@CruzMolina
Copy link
Contributor

CruzMolina commented Jul 1, 2020

@PatrickAlphaC following up, the issue you're seeing isn't #3133.

Looking at the script I believe you're referring to here...

You'll need to set the provider on LinkToken before calling .at.
For example, say you'd like to run the script on ropsten....

const MyContract = artifacts.require('MyContract')
const { LinkToken } = require('@chainlink/contracts/truffle/v0.4/LinkToken')

module.exports = async callback => {
  const mc = await MyContract.deployed()
  const tokenAddress = await mc.getChainlinkToken()
  LinkToken.setProvider(process.env.RPC_URL) // <-------------------- ropsten infura api url
  const token = await LinkToken.at(tokenAddress)
  console.log('Funding contract:', mc.address)
  const tx = await token.transfer(mc.address, payment)
  callback(tx.tx)
}

We also expose web3 with HDWalletProvider loaded in the truffle exec context, so instead of RPC_URL, you can pass web3.currentProvider.

The other hiccup is that you'll need to specify the sender in the token.transfer call.

ie.

const tx = await token.transfer(mc.address, payment, {
  from: web3.currentProvider.addresses[0],
});

In migration & testing contexts, truffle does some magic to require and provision contracts with the provider already set along with default params (happens underneath artifacts.require).

And finally... because the script is attempting to interact with contracts already live on the network, I don't believe you'll be able to successfully transfer tokens unless the loaded account/sender already has tokens assigned to it on the live contract.

@PatrickAlphaC
Copy link

@CruzMolina thank you so much for this detailed response. With your suggestions it now hangs at the part that you said wouldn't work:

const tx = await token.transfer(mc.address, payment, {
  from: web3.currentProvider.addresses[0],
});

I guess I don't understand why this isn't working.

because the script is attempting to interact with contracts already live on the network, I don't believe you'll be able to successfully transfer tokens unless the loaded account/sender already has tokens assigned to it on the live contract.

@CruzMolina
Copy link
Contributor

@PatrickAlphaC You should be able to see the rpc error bubble up by wrapping the exec script in a try/catch

const MyContract = artifacts.require("MyContract");
const { LinkToken } = require("@chainlink/contracts/truffle/v0.4/LinkToken");

module.exports = async (callback) => {
  try {
    const mc = await MyContract.deployed();
    const tokenAddress = await mc.getChainlinkToken();
    LinkToken.setProvider(process.env.RPC_URL);
    const token = await LinkToken.at(tokenAddress);
    console.log("Funding contract:", mc.address);
    const tx = await token.transfer(mc.address, payment, {
      from: web3.currentProvider.addresses[0],
    });
    callback(tx.tx);
  } catch (error) {
    callback(error);
  }
};

@PatrickAlphaC
Copy link

Very helpful, thank you!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
4 participants