-
Notifications
You must be signed in to change notification settings - Fork 2.6k
MMR Leaf Data: off-by-one error in beefy_next_authority_set #11797
Comments
AFAICT an easy fix is to move Mmr: pallet_mmr before But I would prefer a fix contained in |
So since session is rotated prior to leaf construction currently, |
We haven't really been affected by this off-by-one issue while developing our light client (or we just worked around it). The proposed change may affect how our light client detects authority handovers. But I have a feeling there shouldn't be any impact. If there is we can update our code. Some background on our light client: It receives updates like the following at the start of every beefy session (Assume a session length of 10 blocks). Each update contains the beefy commitment for block N (first block of session) and the MMR leaf for block N-1 (last block of the previous session).
The light client knows it can perform an authority handover when the latest update contains a |
There is no issue for forks in either option (current behavior & suggested change) because the validator set is identical for blocks at height The issue is more about usability in the sense that the BEEFY payload for block (block numbers are 1-indexed whereas leaf indexes are 0-indexed) Marking this as "Some day maybe" since existing clients already successfully handle current behavior. |
actually have encountered this issue and i created this pr as a fix: #10907 |
this is actually something different - by building leaf // contents for leaf index <N-1> added by block <N>
MmrLeaf {
version: <leaf-data-format-version>,
(
parent_num: <N-1>,
parent_hash: <hash_of_<N-1>>,
),
- extra_data: <para_heads_of_<N-1>>,
+ extra_data: <para_heads_of_<N>>,
next_auth_set: <next_auth_set_of<N>>,
} and at this point you also run into trouble with 1-block deep forks writing different leaf contents to offchain db for same The offchain-db thing can be fixed by also doing #11799. @seunlanlege Is the mixed layout of Isn't light-client code simpler if it simply references leaf index |
So you're correct that this makes sense, but only from a leaf index perspective. But light clients won't be tracking leaf indexes, they'll be tracking block heights. And it's a bit wonky from that perspective that a leaf emitted a block Whereas it's easier to reason that a leaf emitted at block Also the leaves in this |
Aha, that makes sense. So to summarize, you're saying for a light client, the best experience to interact with // contents for leaf added by block <N>
MmrLeaf {
version: <leaf-data-format-version>,
(
parent_num: <N-1>,
parent_hash: <hash_of_block<N-1>>,
),
extra_data: <para_heads_at_block<N>>,
next_auth_set: <next_auth_set_at_block<N>>,
} right? |
yes correct. |
I'm happy with this change too. It makes sense for me and would allow us to improve some of the code in our off-chain relayers. As the relationship between block-height, leaf-index, and the desired leaf.paras-root was previously a bit confusing. +1 |
I'm sorry @vgeddes @seunlanlege but this doesn't seem to be possible 😢 see #12446 (comment) Closing for now, please reopen if you have any other ideas. |
We decided we want to do at least this (since #12446 isn't possible): // contents for leaf index <N-1> added by block <N>
MmrLeaf {
version: <leaf-data-format-version>,
(
parent_num: <N-1>,
parent_hash: <hash_of_<N-1>>,
),
extra_data: <para_heads_of_<N-1>>,
- next_auth_set: <next_auth_set_of<N>>,
+ next_auth_set: <next_auth_set_of<N-1>>,
} which could be as easy as: |
I can confirm this works:
So this would make the leaf content consistent wrt. block numbering. However, looking at Adrian's PR #12446 again, the reason for closing it was that offchain worker is not guaranteed to run on every block, but since #12753 moved the offchain storage handling to client-side gadget, there's nothing blocking doing Otherwise, I'll open up PR for |
it could be done at the expense of extra runtime storage - keep non-canon full data in runtime storage instead of offchain storage and "back it up" to offchain on client-side finality, but then you still have problems when finality lags and aggressive pruning is configured; and obviously increased storage costs for the chain. I think the wise path is to take the slightly increased code complexity on light-client side and use
I think this is the right call. @vgeddes @seunlanlege just to confirm there is no difference in light-client "runtime costs" between: MmrLeaf @ block N {
version: <leaf-data-format-version>,
(
parent_num: <N-1>,
parent_hash: <hash_of_<N-1>>,
),
extra_data: <para_heads_of_<N-1>>,
next_auth_set: <next_auth_set_of<N-1>>,
} and MmrLeaf @ block N {
version: <leaf-data-format-version>,
(
parent_num: <N-1>,
parent_hash: <hash_of_<N-1>>,
),
extra_data: <para_heads_of_<N>>, <---- diff here
next_auth_set: <next_auth_set_of<N>>, <---- diff here
} |
Yes correct. The light client only needs to be aware of a single header, rather than 2. |
Should not be a problem for our light client, I think. |
Fixed in paritytech/polkadot#6577 |
A given Leaf with MMR 0-based leaf index
N-1
is constructed and added to MMR during construction of blockN
here.The Leaf data/contents can be seen populated here and it consists of:
While this is not a show-stopper - (light)clients can simply handle this in their code - it introduces cognitive complexity and code logic corner cases.
We could avoid this and make it easier for users by changing
Leaf<N-1>
contents to:The text was updated successfully, but these errors were encountered: