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

Cleaner GRANDPA RPC API for proving finality #7339

Merged
38 commits merged into from
Jan 21, 2021

Conversation

octol
Copy link
Contributor

@octol octol commented Oct 16, 2020

Implements #7115 as in the description, with the exception that the RPC call takes block numbers instead of block hashes

polkadot companion: paritytech/polkadot#2100

TODO:

  • Store block numbers in aux_schema
  • Rewrite to store block numbers as part of AuthoritySet instead
  • Manually implement Decode for AuthoritySet for compatibility
  • Flesh out all WIPs and handle all unwraps
  • Clear out finality_proof.rs now that the light client isn't using it anymore
  • Fix: prove finality with the last block of the set fails
  • Tests
  • Handle the case when the stored indices is incomplete (missing beginning)
  • What are the implications of forced changes?
  • Should we replace FinalityProofFragment with something more tailored for this use case?

@octol octol added A3-in_progress Pull request is in progress. No review needed at this stage. B3-apinoteworthy C1-low PR touches the given topic and has a low impact on builders. labels Oct 16, 2020
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.

Looks to be going in the right direction :)

pub(crate) fn get_set_id(&self, number: N) -> (u64, N) {
let set_id = self
.authority_set_changes
.binary_search(&number)
Copy link
Contributor

Choose a reason for hiding this comment

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

I know I suggested not storing the set_id and instead implicitly figuring out by the position in the array. The only disadvantage of this approach is that it breaks for nodes that didn't sync from scratch with this code. I guess it might be better to store the set_id explicitly. Additionally in the future we might start syncing from a specific checkpoint (rather from genesis always) or we may warp sync, in both cases we won't import all blocks.

// Tracks authority set changes. We store the block numbers for the last block of each authority
// set.
#[derive(Debug, Encode, Decode)]
pub(crate) struct AuthoritySetChanges<N> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe this can go inside AuthoritySet? We will always be operating on both at the same time, i.e. when we finalize something from AuthoritySet is also when we update this.

@octol octol linked an issue Nov 11, 2020 that may be closed by this pull request
@octol octol 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 Nov 26, 2020
@octol octol marked this pull request as ready for review November 26, 2020 23:17
@octol
Copy link
Contributor Author

octol commented Nov 26, 2020

Think this is probably ready for another round of feedback now, and finished enough to be upgraded from draft to PR

Most things have been reworked according to feedback from previous versions, as well as #7546 landing in master gives us a lot more freedom in changing finality_proofs.rs

Copy link
Contributor

@wheresaddie wheresaddie left a comment

Choose a reason for hiding this comment

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

lgtm ty

@andresilva
Copy link
Contributor

bot merge

@ghost
Copy link

ghost commented Jan 12, 2021

Trying merge.

@ghost
Copy link

ghost commented Jan 12, 2021

Merge failed: "At least 2 approving reviews are required by reviewers with write access."

@andresilva andresilva requested a review from bkchr January 12, 2021 22:27
Copy link
Member

@bkchr bkchr left a comment

Choose a reason for hiding this comment

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

LGTM as far as I understand it :P

Comment on lines 216 to 221
let err = format!(
"AuthoritySetChanges does not cover the requested block #{}. \
Maybe the subscription API is more appropriate.",
block,
);
trace!(target: "afg", "{}", &err);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let err = format!(
"AuthoritySetChanges does not cover the requested block #{}. \
Maybe the subscription API is more appropriate.",
block,
);
trace!(target: "afg", "{}", &err);
trace!(
target: "afg",
"AuthoritySetChanges does not cover the requested block #{}. \
Maybe the subscription API is more appropriate.",
block,
);

trace!(
target: "afg",
"Encountered new authorities when collecting unknown headers. \
Returning empty proof"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Returning empty proof"
Returning empty proof",

Comment on lines 181 to 186
fn decode<I: Input>(value: &mut I) -> Result<Self, parity_scale_codec::Error> {
let legacy = LegacyAuthoritySet::decode(value)?;
let authority_set_changes = match <AuthoritySetChanges<N>>::decode(value) {
Ok(v) => v,
Err(_) => AuthoritySetChanges::empty(),
};
Copy link
Member

Choose a reason for hiding this comment

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

Where is this decode being used @andresilva ?

If someone uses this where there is still data in I, this could lead to decoding shit :P

Copy link
Contributor

Choose a reason for hiding this comment

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

Currently it's only used through this https://github.com/paritytech/substrate/blob/master/client/finality-grandpa/src/aux_schema.rs#L110, so I don't think it is ever decoded as part of a bigger structure (i.e. there is no more data to decode). It would be nice that we don't have a footgun here though, do you have any suggestion?

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should offer some migration? So, that we only apply this once? But I assume we don't have any way to store a version in aux?

Copy link
Member

Choose a reason for hiding this comment

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

Besides that, I don't have better ideas.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was already not remembering the aux_schema code for GRANDPA. We indeed do have a version there and we have code there that handles migrations of GRANDPA data. So yeah, we should definitely handle this the same way.

@@ -631,6 +689,45 @@ impl<H, N: Add<Output=N> + Clone> PendingChange<H, N> {
}
}

// Tracks historical authority set changes. We store the block numbers for the first block of each
// authority set, once they have been finalalized.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// authority set, once they have been finalalized.
// authority set, once they have been finalized.

// To make sure we have the right set we need to check that the one before it also exists.
if idx > 0 {
let (prev_set_id, _) = self.0[idx - 1usize];
if set_id != prev_set_id + 1u64 {
Copy link
Member

Choose a reason for hiding this comment

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

How can that fail? Wouldn't that mean that the entire object is wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah pretty much, if this happens the object is missing authority set changes and something must have gone wrong applying changes. It's my understanding that this can't happen, but I'm not certain enough of that to omit this check ... @andresilva what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

We wanted to fail gracefully for nodes that will migrate to this and therefore won't have all set ids stored here (only when they sync from scratch), the only check that's really needed for this is the first one idx < self.0.len(). These checks don't hurt but they can be removed since they would imply that this data structure skipped set ids spuriously which is wrong.

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.

@octol LegacyAuthoritySet should be moved to aux_schema.rs and renamed to V2AuthoritySet. The CURRENT_VERSION const there should be incremented and a method migration_from_version2 should be added (and then called from load_persistent). This method will read all remaining GRANDPA structures as they are but decode the authority set as V2AuthoritySet and then return the regular AuthoritySet (with AuthoritySetChanges::empty()).

Sorry for not suggesting this earlier but I didn't remember that we already had migration code for auxiliary GRANDPA data.

@octol
Copy link
Contributor Author

octol commented Jan 20, 2021

@octol LegacyAuthoritySet should be moved to aux_schema.rs and renamed to V2AuthoritySet. The CURRENT_VERSION const there should be incremented and a method migration_from_version2 should be added (and then called from load_persistent). This method will read all remaining GRANDPA structures as they are but decode the authority set as V2AuthoritySet and then return the regular AuthoritySet (with AuthoritySetChanges::empty()).

Oh thanks, yeah that will make it much better!

@octol
Copy link
Contributor Author

octol commented Jan 21, 2021

Pushed a new commit that implements the migration logic as suggested by @andresilva

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.

LGTM, thanks!

@andresilva
Copy link
Contributor

There were conflicts in finality_proof.rs because of #7711. Merged master and fixed them.

@andresilva
Copy link
Contributor

bot merge

@ghost
Copy link

ghost commented Jan 21, 2021

Waiting for commit status.

@ghost
Copy link

ghost commented Jan 21, 2021

Checks failed; merge aborted.

@octol
Copy link
Contributor Author

octol commented Jan 21, 2021

bot merge

@ghost
Copy link

ghost commented Jan 21, 2021

Waiting for commit status.

@ghost ghost merged commit bd5c9a6 into paritytech:master Jan 21, 2021
@octol octol deleted the jon/cleaner-finality-rpc-api branch January 21, 2021 22:12
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. C1-low PR touches the given topic and has a low impact on builders.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

grandpa: cleaner RPC API for proving finality
5 participants