I'm writing a tool to build a fork-off chain. But the new chain can not get finalized.

I've skipped the Babe/Grandpa/Session's storage.

I also check the chain state, that no historical storage under the Grandpa.

But when I call the RPC, round_state. It shows all the old validators didn't vote. Interesting, where did the old validator data come from?

And I found these things:

const VERSION_KEY: &[u8] = b"grandpa_schema_version";
const SET_STATE_KEY: &[u8] = b"grandpa_completed_round";
const CONCLUDED_ROUNDS: &[u8] = b"grandpa_concluded_rounds";
const AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters";
const BEST_JUSTIFICATION: &[u8] = b"grandpa_best_justification";

Are these stored in runtime storage? If yes, what are the keys to them?

If I grab the storage base on the metadata pallet prefix. The finalization works. But if I grab all the keys and filter the Babe/Grandpa/Session prefixes, then it won't. So, I believe there are some 'well-known' keys storing the Grandpa cache.

1 Answer 1


The data for the current state of GRANDPA is stored outside the runtime in local storage. It's important to take into account that even though GRANDPA gets its authorities from the chain and its purpose is to finalize said chain, the protocol itself runs off-chain. The keys in your post are indeed the keys in local storage where the GRANDPA data is stored, and I think for your use-case just wiping those entries should work (as if it was genesis state). You should be able to use this backend API to remove these keys.

Edit: In the meantime I looked at the GRANDPA pallet code and remembered something, the GRANDPA authorities are stored under a special runtime storage key (https://github.com/paritytech/substrate/blob/master/primitives/finality-grandpa/src/lib.rs#L60-L62). As you can see here: https://github.com/paritytech/substrate/blob/master/frame/grandpa/src/lib.rs#L412-L420. The reason this was done in the past was that we needed a stable key in order to be able to generate storage proofs for the GRANDPA authorities (which were useful for light clients). I believe if you also scrape this key when you export state then that should solve your problem.

  • I use state_getStoragePaged recursively fetch all data. And put them ({ k: v }) into a JSON file. How do I remove those keys? Is there a way to get the keys' value? Then I could index/locate them in the JSON.
    – AurevoirXavier
    Commented Sep 5, 2022 at 14:20
  • These keys are not stored in regular state storage since they are not part of the runtime state. You'll need to write some code that uses the API I linked above to remove the data from the local storage database. Alternatively you could: dump the state as you're doing, and then import it into a blank/new database. This should make sure that no existing "off-chain" GRANDPA state exists on the local database.
    – André
    Commented Sep 5, 2022 at 14:36
  • But the interesting thing is I dumpped all the data (skip Babe/Grandpa/Session). And insert them into a brand new genesis for (k, v) in dump { genesis[k] = v; }. Then ./node --chain genesis.json. Babe/Grandpa/Session is empty, that's good. But when I call grandpa_roundState. What?! The old validator set shows up.
    – AurevoirXavier
    Commented Sep 5, 2022 at 14:49
  • Can you do the same thing using ./node --chain genesis.json --tmp and check if the problem is still there? If it's not then the issue is that you are reusing a database which contains the old local GRANDPA data (i.e. not runtime state data).
    – André
    Commented Sep 5, 2022 at 14:55
  • Ah, sorry. The state_getStoragePaged one is fine. The ./node export-state > dump.json one has this issue.
    – AurevoirXavier
    Commented Sep 5, 2022 at 15:17

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.