Incorrect number of transactions in eth_getBlockByNumber

Hi,

We are using go-ethereum client to get full block with eth_getBlockByNumber (and with full transaction objects) every time web socket notifies the app there is a new one (which means the app fetches it very quickly after it was announced by the node). However we bumped into a situation where the Infura node returned an incorrect number of transactions in the block:

{
    insertId: "cfmm163xwm2hd7kkp"
    jsonPayload: {
        block_height: 10367267   
        count: 216   
        level: "info"   
        msg: "processBlockHeight"   
    }
    severity: "INFO"  
    timestamp: "2020-06-30T13:05:11.159724245Z"  
}

Above, you can see that the response of eth_getBlockByNumber has only 216 transactions. However later call to the same block returned the correct number:

{
 insertId: "cfmm163xwm2hd7ubu"  
 
jsonPayload: {
  block_height: 10367267   
  count: 256   
  level: "info"   
  msg: "processBlockHeight"   
 }
 
 severity: "INFO"  
 timestamp: "2020-06-30T14:51:33.691945150Z"  
}

Etherscan: https://etherscan.io/block/10367267.
Also there were no fork on the tip because the first block hash received at this height was the final hash: 0xe3b590ad7509c30066231400e13f0cd2a595777af40d148a93c50124efd299a5.

I believe this was caused by a bug in a node. Can you please investigate this?

Hi @yellowred,

It looks like there was a reorg at that time; if you send an eth_getBlockByHash request for that original blockHash you will see the original block with the 216 transactions. It can be risky to rely on blockNumber alone because a re-org can lead to a different block being represented by a the same blockNumber.

Thank you for the reply Leiya!

Can you please provide more details on what was the original block hash and how did you get it?

From our logs, this is what was pushed to us from the web socket subscription:

{
 insertId: "cfmm163xwm2hd7kkg"  
 
jsonPayload: {
  block_hash: "0xe3b590ad7509c30066231400e13f0cd2a595777af40d148a93c50124efd299a5"   
  block_height: "10367267"   
  level: "info"   
  msg: "NewHead"   
  proc: "subscribeToHeads"   
 }
 
 severity: "INFO"  
 timestamp: "2020-06-30T13:05:00.223810168Z"  
}

After the app receives that it executes getBlockByHeight and because we had been pushed with the final hash I make a conclusion that there were no reorg, hence querying that block height 11 seconds after we were pushed with a notification about it should not cause any issues.

On top of that isn’t Infura supposed to send a notification for every new block mined even for the same height? There is no indication in the logs of the app that this happened.

So it would be ideal to have evidence that a reorg happened before we moving to implement a solution.

There were several competing blocks at that height, one of which had hash 0xdc3dac4a4a106e1f3c9f3d842b7e765499985e2987b1a1029f3083489d6c8269 and became an uncle:

https://etherscan.io/uncle/0xdc3dac4a4a106e1f3c9f3d842b7e765499985e2987b1a1029f3083489d6c8269

If you execute {"id":1, "jsonrpc": "2.0", "method": "eth_getBlockByHash", "params": ["0xdc3dac4a4a106e1f3c9f3d842b7e765499985e2987b1a1029f3083489d6c8269", false]} you’ll get back a block with 216 transactions.

My suggestion would be to use eth_getBlockByHash rather than eth_getBlockByNumber in response to the subscription and use the published hash instead of a block height.

The Ethereum spec for NewHead subscriptions is very light, but generally does not guarantee that every competing block is announced. I agree that in this particular case the results should have been more consistent and am looking into the possible cause.

That said, I’m curious what is your use case for processing every block as they come in? The reason I mention this is that often multiple miners will present candidate blocks, and it isn’t until multiple competing chains grow off those blocks that a canonical chain is formed, so even if all of Infura’s subsystems agree on what the current “latest” block is this does not mean that the entire Ethereum network shares that view. Based on your use case we might be able to suggest an alternative approach.

Thanks,
Ryan

Hi Ryan,

Our app uses blocks subscription to trigger native transactions fetch as well as logs fetch for the new height. Native transactions are fetched by getBlockByHeight and credited to the customers. Logs are analyzed for value transfer and then, again, credited to the customers. There is a delay between a new block received by the subscriptions and the fetch processes are started (around 10-20 secs). One of the solutions would be to fetch logs and blocks that are 3 levels below the latest. But maybe there is a better one?

1 Like

Fetching blocks/logs 3 levels below latest seems like a good compromise. However, while rare, reorgs that deep can still happen. So, when processing new blocks you should still double check the “integrity” of the chain. For example:

  • Let’s say that NewHeads tells you about block #2004.
  • Then you query eth_getBlockByNumber on block 2001 (2004 minus 3)
  • Before processing that block, check that it’s parentHash equals that hash field of the last block 2000 you processed.
  • If it doesn’t, then there’s been a reorg deeper than 3 blocks, and you probably need to handle that in some way (the exact nature of how you handle it depends on the nature of your app).

I don’t have statistics on how often reorgs of different depths occur, but if you look at the reorg information on etherscan (https://etherscan.io/blocks_forked?p=1) you can see that the vast majority of reorgs are depth 1.