Skip to content

Parachains finality pallet#1068

Merged
svyatonik merged 18 commits intomasterfrom
parachains-finality
May 12, 2022
Merged

Parachains finality pallet#1068
svyatonik merged 18 commits intomasterfrom
parachains-finality

Conversation

@svyatonik
Copy link
Copy Markdown
Contributor

I've been considering generic pallet that'll e able to solve both parachains finality + #1002 (comment) . That's because both pallets are simple receiving storage proof => parsing proof => optionally check that the proof is duplicate => doing some actions, based on this proof. But the generic palle will be too generic - imo it is better to have parachains finality logic separated from some generic code.

This is very WIP, although current code already gives general idea of what it'll look like. Main questions that still needs to be solved is keeping old parachain headers (aka heads) as we do with relay chain headers + add similar pruning.

@svyatonik svyatonik added B-Substrate <> Substrate P-Runtime PR-audit-needed A PR has to be audited before going live. labels Jul 22, 2021
@svyatonik svyatonik marked this pull request as draft July 22, 2021 12:53
@svyatonik svyatonik marked this pull request as ready for review July 23, 2021 11:08
for parachain in parachains {
let parachain_head = match Pallet::<T, I>::read_parachain_head(&storage, parachain) {
Some(parachain_head) => parachain_head,
None => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it should be supported - parachain may be offboarded and its head may be removed. So there should be 3 possible outcomes from read_parachain_head: Err(_), Ok(Some(_)) and Ok(None). Right now the first (which is relayer error) is replaced with Ok(None), which is handled incorrectly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to deal with it in follow-up PRs, as it requires some more thinking + there are already two PRs on top of that one.

use sp_std::vec::Vec;

/// Parachain id.
pub type ParaId = u32;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All parachain pallets from Polkadot are using polkadot_parachain::primitives::Id to store parachain id. The difference is that it is compact-encoded u32 => it should be fixed

pub type ParaId = u32;

/// Parachain head, which is (at least in Cumulus) a SCALE-encoded parachain header.
pub type ParaHead = Vec<u8>;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parachains pallet from Polkadot is using polkadot_parachain::primitives::HeadData to store parachain heads. The difference is that they're encoding parachain header twice, i.e. it is prefixed with encoded bytevec length => it should be fixed.

@svyatonik
Copy link
Copy Markdown
Contributor Author

@AurevoirXavier @fewensa If you're really keen on this parachain pallets+relay+bridge, then it'll be really helpful if you could add your review to these three dependent PRs - this one, #1199 and #1218 . Please ask any questions directly in PRs (better option) or DM me on riot (you've been already chatting with me there).

The most important design question: there are two options to use parachains finality - one is a separate pallet+relay and another one is simply adding parachain head proof to existing proofs. The latter looks simpler - i.e. right now proof of messages looks like (bridged_header_hash, message_storage_proof, lane, range_of_nonces). We could then simply add a (parachain_id, parachain_head_storage_proof) there and everything should work. But in this case we won't be able to use results of previous parachain_head_storage_proof verification when/if another messages proof is submitted. We may add some cache, but it'll result exactly in a separate pallet. Another thing is that this separate pallet is just another layer on top of our bridge-grandpa (and following bridge-beefy) pallet. Both are sharing (almost) the same API and we're not mixing all possible cases in a single pallet - e.g. we may combine relay<>parachain, relay<>relay and parachain<>parachain bridge by using different set of isolated pallets, while only changing the "glue" code.

@hackfisher
Copy link
Copy Markdown

But in this case we won't be able to use results of previous parachain_head_storage_proof verification when/if another messages proof is submitted.

I don't get this part, do you mean a new parachain_head_storage_proof could be submitted before some message proof get included using relative older parachain header?

If that's the case, bridge grandpa pallet may also have similar issue, and onchain cache looks simple and straight forward to me.

@svyatonik
Copy link
Copy Markdown
Contributor Author

But in this case we won't be able to use results of previous parachain_head_storage_proof verification when/if another messages proof is submitted.

I don't get this part, do you mean a new parachain_head_storage_proof could be submitted before some message proof get included using relative older parachain header?

If that's the case, bridge grandpa pallet may also have similar issue, and onchain cache looks simple and straight forward to me.

GRANDPA pallet works exactly like this cache :) Let me explain in details. So message proof = (bridged_header_hash, message_storage_proof, lane, range_of_nonces). To verify message_storage_proof, you'll need to know a storage trie root, which is a part of bridged_header_hash header. And you need to be sure that this header is finalized.

So imagine you don't have a bridge GRANDPA pallet in the runtime (let's omit validator set changes for simplicity). Then the messages pallet may still be used, but the message proof changes to (bridged_header, bridged_header_grandpa_justification, message_storage_proof, lane, range_of_nonces). So it includes finality proof now. And if you need to prove several same-header messages using several transactions, it would mean that you'll need to submit and verify justification multiple times.

That's where GRANDPA pallet helps and where it works like cache - once finality proof is verified, you may just provide header hash in your message proof. and when this proof is verified, you may just read the header from the cache (runtime storage), without reverifying finality proof again.

Now imagine you're bridging with parachain and you don't have both GRANDPA pallet and parachains finality pallet. Then the message proof would be (bridged_relay_header, bridged_relay_header_grandpa_justification, bridged_parachain_header, bridged_parachain_header_storage_proof, message_storage_proof, lane, range_of_nonces). And you would need to verify 3 proofs every time you're reusing the same parachain header in the message proof. So what this separate parachains finality pallet does, is removing the bridged_parachain_header, bridged_parachain_header_storage_proof from this tuple, instead providing a trusted cached map of bridged_parachain_header_hash => bridged_parachain_header. So it also works like cache, but with a parallel API, like bridge GRANDPA pallet does.

Let me know if it makes sense to you.

Copy link
Copy Markdown
Member

@eskimor eskimor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good as far as I can tell.

// intersection of `parachains-interesting-to-us` and `parachains`

// TODO: if some parachain is no more interesting to us, we should start pruning its
// heads
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually have the rule that each TODO should reference some ticket.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, thanks - I'll open issues for that

@svyatonik svyatonik merged commit 5a2f9fe into master May 12, 2022
@svyatonik svyatonik deleted the parachains-finality branch May 12, 2022 10:20
Copy link
Copy Markdown
Collaborator

@acatangiu acatangiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just nits and typos 👍

Copy link
Copy Markdown
Collaborator

@serban300 serban300 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to ramp-up as a bit on how parachains work and I did multiple iterations on this code. However I still have limited experience with this. But for me, the design looks good. I can't find any issue with it. I just left 2 nits.


/// Best parachain heads.
#[pallet::storage]
pub type BestParaHeads<T: Config<I>, I: 'static = ()> =
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Personally I would change this name to something like ParaState or ParaMetaData since it contains more than just the BestParaHeads.

Comment on lines +48 to +55
pub struct BestParaHead {
/// Number of relay block where this head has been updated.
pub at_relay_block_number: RelayBlockNumber,
/// Hash of parachain head.
pub head_hash: ParaHash,
/// Current ring buffer position for this parachain.
pub next_imported_hash_position: u32,
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I would change this structure to something like:

pub struct ParaState {
	pub best_head_hash: BestParaHeadHash,
	pub next_imported_hash_position: u32,
}

in order to reuse the BestParaHeadHash structure instead of having 2 structures that contain at_relay_block_number and head_hash.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a good idea imo

@serban300 serban300 mentioned this pull request Sep 2, 2022
svyatonik pushed a commit that referenced this pull request Sep 5, 2022
* BestParaHead small changes

Signed-off-by: Serban Iorga <serban@parity.io>

* Renamings

Signed-off-by: Serban Iorga <serban@parity.io>

* Use ParaInfo in parachains loop

Signed-off-by: Serban Iorga <serban@parity.io>

* Define StorageMapKeyProvider

Signed-off-by: Serban Iorga <serban@parity.io>

* CR fixes

Signed-off-by: Serban Iorga <serban@parity.io>
serban300 pushed a commit to serban300/parity-bridges-common that referenced this pull request Mar 27, 2024
* parachains finality

* parachains pallet test

* demo of how to configure GRANDPA pallet instance

* allow instances in parachains pallet

* spellcheck

* TODO + fix

* fmt

* removed invalid storage_keys file

* change all hashers to Blake2_128Concat

* use Twox64Concat for insertion position

* fix build

* fix compilation

* change ParaId and ParaHead types

* TODOs -> TODOs with issues refs
serban300 added a commit to serban300/parity-bridges-common that referenced this pull request Mar 27, 2024
* BestParaHead small changes

Signed-off-by: Serban Iorga <serban@parity.io>

* Renamings

Signed-off-by: Serban Iorga <serban@parity.io>

* Use ParaInfo in parachains loop

Signed-off-by: Serban Iorga <serban@parity.io>

* Define StorageMapKeyProvider

Signed-off-by: Serban Iorga <serban@parity.io>

* CR fixes

Signed-off-by: Serban Iorga <serban@parity.io>
serban300 pushed a commit to serban300/parity-bridges-common that referenced this pull request Apr 8, 2024
* parachains finality

* parachains pallet test

* demo of how to configure GRANDPA pallet instance

* allow instances in parachains pallet

* spellcheck

* TODO + fix

* fmt

* removed invalid storage_keys file

* change all hashers to Blake2_128Concat

* use Twox64Concat for insertion position

* fix build

* fix compilation

* change ParaId and ParaHead types

* TODOs -> TODOs with issues refs
serban300 added a commit to serban300/parity-bridges-common that referenced this pull request Apr 8, 2024
* BestParaHead small changes

Signed-off-by: Serban Iorga <serban@parity.io>

* Renamings

Signed-off-by: Serban Iorga <serban@parity.io>

* Use ParaInfo in parachains loop

Signed-off-by: Serban Iorga <serban@parity.io>

* Define StorageMapKeyProvider

Signed-off-by: Serban Iorga <serban@parity.io>

* CR fixes

Signed-off-by: Serban Iorga <serban@parity.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P-Runtime PR-audit-needed A PR has to be audited before going live.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants