How to detect previous block is orphaned

Is there a way to know that a previously received block has been orphaned?
I can check if the previous block is valid by receiving the last block and checking the hash of the previous block, but can I know that the previously received block is orphaned through the API provided by Infura other than this way?
If there is no API provided, can you tell me how to recognize an orphaned block when trying to save the block in general?

1 Like

@Atan Thanks for reaching out and welcome! The way you were describing is how one would go to check if a block was orphaned or not.

You would take the block and call eth_getblockbyhash , if the block is not in the main chain it will return null. You can read more about it here with examples included: https://docs.infura.io/infura/networks/ethereum/json-rpc-methods/eth_getblockbyhash

Kind regards,
Alex | Infura Support

1 Like

Thanks for your kind reply. But you gave me a slightly different answer than I wanted to ask.
What I am curious about is whether there is a function to notify that a previously received block has been orphaned if it continues to receive a block by subscribing and stores the block internally. If I subscribe to receive and store blocks, and the previous block is orphaned, I do not know it and continue to subscribe to and receive the latest block. This is because there may be a problem.
In other words, I am subscribing to a block and I would like to know if there is a function to notify that a previously received block is orphaned through an event.
If there is no such function, please explain how I can know that the previously saved block has been orpahned while I am subscribing to the latest block and saving it. Thanks

1 Like

@Atan

I discussed this question with the team and there is no RPC method built in to do this. Here is a code example that waits a specific number of blocks and verifies that the transaction receipt is valid. If a fork happens then the receipt check should fail and return with errors.

 // @method awaitBlockConsensus
 // @param web3s[0] is the node you submitted the transaction to,  the other web3s 
 //    are for cross verification, because you shouldn't trust one node.
 // @param txhash is the transaction hash from when you submitted the transaction
 // @param blockCount is the number of blocks to wait for.
 // @param timout in seconds 
 // @param callback - callback(error, transaction_receipt) 
 //
 exports.awaitBlockConsensus = function(web3s, txhash, blockCount, timeout, callback) {
   var txWeb3 = web3s[0];
   var startBlock = Number.MAX_SAFE_INTEGER;
   var interval;
   var stateEnum = { start: 1, mined: 2, awaited: 3, confirmed: 4, unconfirmed: 5 };
   var savedTxInfo;
   var attempts = 0;

   var pollState = stateEnum.start;

   var poll = function() {
     if (pollState === stateEnum.start) {
       txWeb3.eth.getTransaction(txhash, function(e, txInfo) {
         if (e || txInfo == null) {
           return; // XXX silently drop errors
         }
         if (txInfo.blockHash != null) {
           startBlock = txInfo.blockNumber;
           savedTxInfo = txInfo;
           console.log("mined");
           pollState = stateEnum.mined;
         }
       });
     }
     else if (pollState == stateEnum.mined) {
         txWeb3.eth.getBlockNumber(function (e, blockNum) {
           if (e) {
             return; // XXX silently drop errors
           }
           console.log("blockNum: ", blockNum);
           if (blockNum >= (blockCount + startBlock)) {
             pollState = stateEnum.awaited;
           }
         });
     }
    else if (pollState == stateEnum.awaited) {
         txWeb3.eth.getTransactionReceipt(txhash, function(e, receipt) {
           if (e || receipt == null) {
             return; // XXX silently drop errors.  TBD callback error?
           }
           // confirm we didn't run out of gas
           // XXX this is where we should be checking a plurality of nodes.  TBD
           clearInterval(interval);
           if (receipt.gasUsed >= savedTxInfo.gas) {
             pollState = stateEnum.unconfirmed;
             callback(new Error("we ran out of gas, not confirmed!"), null);
           } else {
             pollState = stateEnum.confirmed;
             callback(null, receipt);
           }
       });
     } else {
       throw(new Error("We should never get here, illegal state: " + pollState));
     }

     // note assuming poll interval is 1 second
     attempts++;
     if (attempts > timeout) {
       clearInterval(interval);
       pollState = stateEnum.unconfirmed;
       callback(new Error("Timed out, not confirmed"), null);
     }
   };

   interval = setInterval(poll, 1000);
   poll();
 };

Let me know if this helps!
Thank you

1 Like