Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Verify Grandpa proofs from within runtime #4167

Conversation

HCastano
Copy link
Contributor

I'm looking to get some feedback on this. For the bridge module, we need to be able to verify the validity of Grandpa proofs. The problem is however, that there is no way to do this from within the runtime as the current Grandpa proof code requires std. What I've done here is taken some of the code from the Substrate finality-grandpa crate and modified it to make it work in a no_std environment. The two main changes were:

  1. Switch HashMap and HashSet to BTreeMap and BTreeSet
  2. Move from using AuthorityPair for signature verification, to using AuthoritySignature + RuntimeAppPublic

Ideally this code wouldn't be duplicated in two places, but right now I'm just trying to make this work in the context of the bridge module.

Before merging this PR I'd like to have the submit_finalized_headers() function actually using the new code to verify Grandpa proofs.

HCastano and others added 18 commits November 11, 2019 21:51
…tytech#3874)

* Make tests work after the changes introduced in paritytech#3793

* Remove unneccessary import
…ytech#3915)

* Make StorageProofChecker happy

* Update some tests

* Check given validator set against set found in storage

* Use Finality Grandpa's Authority Id and Weight

* Add better error handling

* Use error type from decl_error! macro
* Create module for checking ancestry proofs

* Use Vec of Headers instead of a HashMap

* Move the ancestry verification into the lib.rs file

* Change the proof format to exclude `child` and `ancestor` headers

* Add a testing function for building header chains

* Rename AncestorNotFound error to InvalidAncestryProof

* Use ancestor hash instead of header when verifying ancestry

* Clean up some stuff missed in the merge
@HCastano HCastano added the A3-in_progress Pull request is in progress. No review needed at this stage. label Nov 21, 2019
@parity-cla-bot

This comment has been minimized.

@HCastano
Copy link
Contributor Author

As @svyatonik pointed out to me, not all the functions are needed by the bridge module (e.g from_commit(). While this is true, I think they'd be useful for creating unit tests.

/// This is meant to be stored in the db and passed around the network to other
/// nodes, and are used by syncing nodes to prove authority set handoffs.
#[derive(Encode, Decode)]
pub struct GrandpaJustification<Block: BlockT> {
Copy link
Contributor

@rphmeier rphmeier Nov 21, 2019

Choose a reason for hiding this comment

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

I feel like the contents of this module should be in the palette/grandpa crate, since it's generally useful.

Copy link
Contributor

Choose a reason for hiding this comment

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

IMO it should go into primitives/finality-grandpa and then it gets used by both client and pallet code (and also by the bridge pallet).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm going to wait until I rebase past the-big-reorg before moving the code into primitives/finality-grandpa.

Copy link
Contributor

@andresilva andresilva left a comment

Choose a reason for hiding this comment

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

I think migrating to BTrees is sensible, we just did the same thing on the finality-grandpa crate.

if as_public.verify(&encoded_raw, signature) {
Ok(())
} else {
// debug!(target: "afg", "Bad signature on message from {:?}", id);
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably want to keep this for the client code that uses it once this is refactored to re-use the same code.

srml/bridge/src/justification.rs Outdated Show resolved Hide resolved
srml/bridge/src/lib.rs Outdated Show resolved Hide resolved
@HCastano
Copy link
Contributor Author

HCastano commented Dec 4, 2019

There are a couple of things that I want to do in later PRs:

  1. Move the justification code into finality-grandpa/primitives
  2. Properly handle authority set changes

I think it's fine to get this PR in without these two things. Once this is merged I can then go ahead and rebase the whole bridge branch past the-big-reorg which will make my life easier when dealing with (1). As for (2), I need to do a bit more digging before handling it.

@HCastano HCastano marked this pull request as ready for review December 4, 2019 14:06
@HCastano HCastano added A0-please_review Pull request needs code review. and removed A3-in_progress Pull request is in progress. No review needed at this stage. labels Dec 4, 2019
@HCastano HCastano requested a review from jimpo December 4, 2019 14:06
Copy link
Contributor

@jimpo jimpo left a comment

Choose a reason for hiding this comment

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

Just did a very cursory review as you need to drop some disallowed dependencies which show up in a bunch of places.

srml/bridge/Cargo.toml Outdated Show resolved Hide resolved
@@ -8,9 +8,15 @@ edition = "2018"

[dependencies]
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false }
client = { package = "substrate-client", path = "../../core/client" }
fg = { package = "substrate-finality-grandpa", path = "../../core/finality-grandpa/" }
Copy link
Contributor

Choose a reason for hiding this comment

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

You can't import this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm gonna wait on #3868 before getting rid of it

srml/bridge/Cargo.toml Outdated Show resolved Hide resolved
srml/bridge/Cargo.toml Outdated Show resolved Hide resolved
srml/bridge/Cargo.toml Outdated Show resolved Hide resolved
srml/bridge/src/justification.rs Outdated Show resolved Hide resolved
srml/bridge/src/justification.rs Outdated Show resolved Hide resolved
srml/bridge/src/justification.rs Show resolved Hide resolved
srml/bridge/src/lib.rs Show resolved Hide resolved
@HCastano
Copy link
Contributor Author

HCastano commented Dec 9, 2019

I think it's going to be a little annoying to get rid of the client and core/finality-grandpa dependencies. It looks like I'm going to have to move a few things into finality-grandpa/primitives, but it's not totally clear to me how just yet.

@HCastano
Copy link
Contributor Author

I think some of the #[cfg(test)] stuff looks a little gross, but we should be able to get rid of it once we move the code into primitives/finality-grandpa


use codec::{Encode, Decode};
use core::cmp::{Ord, Ordering};
// TODO: Since I can't use types from `core/finality-grandpa`, wait until #3868
Copy link
Contributor

Choose a reason for hiding this comment

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

Put this TODO in the Cargo.toml too, which is more important.

/// targets should be the same as the commit target, since honest voters don't
/// vote past authority set change blocks.
///
/// This is meant to be stored in the db and passed around the network to other
Copy link
Contributor

Choose a reason for hiding this comment

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

This part of the comment seems wrong (stored in the db and networked).

/// Create a GRANDPA justification from the given commit. This method
/// assumes the commit is valid and well-formed.
#[cfg(test)]
pub(crate) fn from_commit<B, E, RA>(
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Just make it pub, not pub(crate). The module is private.

let mut votes_ancestries_hashes = BTreeSet::new();
let mut votes_ancestries = Vec::new();

let error = || {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would just inline this.


/// Decode a GRANDPA justification and validate the commit and the votes'
/// ancestry proofs finalize the given block.
pub(crate) fn decode_and_verify_finalizes(
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: pub

}

#[cfg_attr(test, derive(Debug))]
pub(crate) enum JustificationError {
Copy link
Contributor

Choose a reason for hiding this comment

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

You can get rid of this. Just use the normal error type. Any function that returns Result<(), JustificationError> where the error is always BadJustification (like from_commit and verify) can return an error type of ().


impl<Block: BlockT> AncestryChain<Block> {
fn new(ancestry: &[Block::Header]) -> AncestryChain<Block> {
let ancestry: BTreeMap<_, _> = ancestry
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this type hint shouldn't be necessary.

/// This is useful when validating commits, using the given set of headers to
/// verify a valid ancestry route to the target commit block.
struct AncestryChain<Block: BlockT> {
ancestry: BTreeMap<BlockHashKey<Block>, Block::Header>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps not necessary, but this could be a BTreeMap<BlockHashKey<Block>, &'a Block::Header> (map to header refs to avoid cloning).

Error::InvalidAncestryProof
);
}

// Currently stealing this from `core/finality-grandpa/src/test.rs`
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you expect it to be replaced? Otherwise, don't say "Currently".

use trie::{MemoryDB, Trie, trie_types::TrieDB};

use crate::Error;

pub(crate) type StorageProof = Vec<Vec<u8>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

pub

@HCastano
Copy link
Contributor Author

I talked with Jim about some of the options for moving this forward. Since there's a lot of code that came from core/finality-grandpa we could either

  1. Merge this code as is and refactor it later
  2. Put this on ice, and make a new PR once grandpa: report equivocations #3868 is merged

We think that the best way forward is (2) as it'll allow us to have a cleaner implementation that doesn't duplicate Grandpa code.

@HCastano
Copy link
Contributor Author

Closing in favour of paritytech/parity-bridges-common#19.

@HCastano HCastano closed this Feb 20, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants