Given a block hash that could be at any height, it's seemingly inefficient to traverse from the tip to the height and then compare if the block hash matches to see whether it's in the canonical chain. We need to simply drop it if the given block hash is not in the canonical chain in our business logic. Is there an efficient way in Substrate to do it without any assumptions(e.g., finality)? If no, with what prerequisites can we achieve it? what's the recommended approach for this goal?

  • 1
    Are you starting off a block hash? Or are you just trying to follow the progress of the chain? If its the latter, you can subscribe to the finalized block heads via RPC, which will give you the hashes of every block that is definitely included in the canonical chain. Commented Apr 6, 2023 at 16:55
  • I'm not following the chain, it's a general question actually. Basically, you recorded some block hashes at a certain height before and want to perform some actions on this height later, due to the forks, there are potentially multiple block hashes at this height, you want to find one that is on the canonical chain to proceed and ignore the others.
    – xlc
    Commented Apr 7, 2023 at 0:07
  • Are you using grandpa? Or other finality mechanism?
    – Nuke
    Commented Apr 8, 2023 at 3:42
  • No, we are not using grandpa but using a K-deep confirmation. I know the finality helps find the canonicality to some extent. This question is more about the canonical chain in general, not the finalized chain, more specifically, check whether the block of a given block block is on the same branch with the current tip block.
    – xlc
    Commented Apr 9, 2023 at 1:04

2 Answers 2


To check whether a block hash H is on the canonical chain in Substate:

  1. Query the block number of this block hash using client.number(H).
  2. Query the block hash of the queried block number above using client.hash(N), if the quried block hash matches H, then H is on the canonical chain.
fn is_on_the_canonical_chain(client, block_hash: Hash) -> bool {
    let block_number = client.number(block_hash);
    client.hash(block_number) == block_hash

I wasn't super sure about this way as I had some unclearness about client.hash(number), but after looking into the code, I'm pretty clear that client.hash(number) returns the canonical block hash given the block number.(In case you want to dive into the code 1, 2, whenever the tip changes, the number to hash mappings will be updated to the new tip along the way).


I think we can check it this way:

  1. query the block_number corresponding to the block hash and if you get reply with a valid( say b) block_number then its a sign that the network is keeping that block and is a part of the canonical chain.

  2. just to be sure that canonical chain is finalized query the block_number of the latest finalized block(say latest_finalised_block_number) on chain. And if the b < latest_finalised_block_number then the the block b is in finalized chain.

  • 1. It is incorrect this way because a block being tracked does not mean it's part of the canonical chain. A fork block also gives you a valid block_number. Say, there are 3 blocks at height 100, 100a, 100b, 100c, 100b is on the canonical chain, but 100a, 100b, 100c all returns 100 when you quey the block number using the corresponding block hash.
    – xlc
    Commented Apr 9, 2023 at 0:50
  • 2. I agree relying on the finality is a way since finality implies canonicality github.com/subspace/substrate/blob/…, but this question is focusing on checking canonical chain.
    – xlc
    Commented Apr 9, 2023 at 1:00
  • reg point 1. i think node would discard the blocks of non-canonical chain as soon thats detected keeps only the canonical chain in the local store. So from the point of view of node being queried we ll get the block number in reply only if the node believe its in canonical chain .. theoretically.. i hv not tested the code
    – K Gunjan
    Commented Apr 10, 2023 at 6:59
  • The blocks on the non-canonical chain won't be pruned until they are too old. If I read the code correctly, they will be pruned after 4096 blocks regardless. After diving into the code, now I think I have my answer to this question.
    – xlc
    Commented Apr 10, 2023 at 16:04
  • make sense .. thanks for putting this up here
    – K Gunjan
    Commented Apr 15, 2023 at 10:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.