-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Fix the runtime-version choice. #11209
Fix the runtime-version choice. #11209
Conversation
Basically this is Solution #3 from the issue. I am not sure I understand this comment:
If I call the runtime on some non-pruned block, why are we guaranteed to have the runtime from the previous block available and not pruned? |
There is no guarantee for that at all. |
I think I misinterpreted the comment. I read "last one" as "best block", but it is the opposite, the first available non-pruned block. Got it 👍 . So we would accept that for these blocks you just can not call. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some comments. In general there are two things that we need to think about the current approach:
- We will need to make the distinction between block construction and general runtime calls, to make sure we take the
:code
from the correct block - We might need to make some changes to pruning, of the top of my head I was thinking that we might want to make sure we keep around the state of the parent of the last finalized block (currently we'll also prune on finality regardless of window iirc)
Solution 4 from the original will also require that we do the first point above (i.e. distinguish between block execution and other calls), since we need to pick the code from different storage entries, but it would not require any changes to pruning.
fn state_with_runtime_code( | ||
&self, | ||
block_id: BlockId<Block>, | ||
) -> sp_blockchain::Result<<B as backend::Backend<Block>>::State> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed earlier I think here we should fetch the header first (either by block number or block hash, depending on the block id that's passed), and then we fetch the parent hash from it. The assumption we make is that the caller of this method will make the right decision on whether to use a block number or a hash.
let state_data = self.backend.state_at(*at)?; | ||
let state_code = self.state_with_runtime_code(*at)?; | ||
|
||
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state_code); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This executor will be used in both the context of general runtime calls as well as block construction (correct me if I'm wrong here @bkchr). We will need to somehow make that distinction because in the case of block construction we don't want to use the logic of picking the code from the parent, otherwise we'd end up in a situation where: we want to build block N, therefore we use the state from N-1 and the code from N-2. In the case of block construction we basically want to keep the logic as is, i.e. to create block N we already use the code from block N-1 (since N doesn't even exist in the first place).
DisclaimerI might have slightly oversimplified the things, but nonetheless the following is how I understand it. @skunert , @wigy-opensource-developer , please feel free to correct me if I am criminally wrong. PrefaceWhen a block is computed — it yields:
Once a block is canonicalised, the StateDb's "Pruning RefWindow" is notified. That as I understand it gives us "sort of guarantee" (a bit oxymoronic, hence the quotes) that
The corner case here is when the
Option # 3There is an existing API So in order to we would need to execute the version of the runtime available just before that block was computed, i.e. What states can be queried?In essence all states to which both of the following is true: That is (for all
|
Disclaimer, I have written this before @RGafiyatullin and @andresilva commented, so this may contains duplicate information. Just posting it for posterity. I'm personally not convinced that using the wasm blob from the parent block is the best way to move forward (yes I know that I proposed it initially). When we do it that way we will need to change the pruning to keep one more block in a hidden way inside the unpruned blocks to always have the parent wasm blob around. If we go with proposal 4, we would also need to annotate block production/import to switch to the new wasm file. |
Correct me if I am mistaken, but we cannot stay completely backward compatible for each of the following cases:
I really like the simplicity of this solution opposed to the really overengineered idea I had that also would allow multi-block migrations. If the |
substrate/client/service/src/client/call_executor.rs Lines 130 to 133 in 8ce5095
Also, @RGafiyatullin, I think André tried to get you navigate here using the previous block hash instead of just decreasing the block number. That implementation would be fork-aware. |
These are basically all kind of interactions with the runtime ;) We will need to stay backwards compatible anyway, because otherwise we can not import all the old blocks. |
My changes are based upon the assumption that the following is true: pub trait Backend<Block: BlockT>: AuxStore + Send + Sync {
// >8 >8 >8 >8 >8 >8 >8 >8 >8 >8 >8 >8
/// Returns state backend with post-state of given block.
fn state_at(&self, block: BlockId<Block>) -> sp_blockchain::Result<Self::State>; Which gave me the following:
Sorry, I do not get how that is true. |
In that if block |
@bkchr please disregard the above... I traced a little bit more, I misunderstood what the input argument |
I found a critical mistake in the very base of the reasoning that stands behind that PR-draft, therefore I will close it. I will draw several doodles reflecting my up-to-date vision of the problem and post it in paritytech/polkadot-sdk#64 . |
Adresses: paritytech/polkadot-sdk#64
When called in context of a block, the runtime-version, defined before that block's computation, is used; with a little exception for the cases when the context is the genesis-block. (example for such a call)
When running in a constrained pruning mode (i.e. when a limited quantity of state-db snapshots is available) such an invocation will be available for all the non-pruned blocks except for the last one.