Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zoneconcierge/epoching: proof that a header is in an epoch #248

Merged
merged 9 commits into from
Dec 16, 2022

Conversation

SebastianElvis
Copy link
Member

@SebastianElvis SebastianElvis commented Dec 15, 2022

Fixes BM-302

This PR implements ProofHeaderInEpoch, the proof that a header is in an epoch. The proof is constructed by a Merkle proof that the header's AppHash is committed to the Merkle root of all AppHashs in the epoch. Along the way, this PR implements:

  • KVStore that saves AppHash at every height.
  • calculating Merkle root of all AppHashs in an epoch, and stores the Merkle root to the epoch metadata
  • prover and verifier that an AppHash is committed to the Merkle root
  • prover and verifier for ProofHeaderInEpoch
  • A fuzz test for Merkle proofs

In addition, since this PR modifies many Protobuf schemas, this PR performed make proto-swagger-gen to regenerate Swagger yml files.

Copy link
Contributor

@gitferry gitferry left a comment

Choose a reason for hiding this comment

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

Wonderful! Minor comments:

Comment on lines 80 to 83
epoch := k.GetEpoch(ctx)
if !epoch.IsLastBlock(ctx) {
panic("RecordLastBlockHeader can only be invoked at the last block of an epoch")
return sdkerrors.Wrapf(types.ErrInvalidHeight, "RecordLastBlockHeader can only be invoked at the last block of an epoch")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

As the name implies, the caller should ensure that it is this is the last block of the epoch. So maybe we don't need to check it here again. We can add a note about calling this function tho.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm yeah such a contract can do the job as well, but I feel checking it here again can be a better precaution for misuses. Now constructing the full epoch metadata becomes quite cryptic with the following steps:

  • Upon the first block of epoch i, create the epoch metadata with empty LastBlockheader, AppHashRoot, and SealerHeader.
  • Upon the last block of epoch i, fill LastBlockHeader and AppHashRoot
  • Upon the 2nd block of epoch i+1, fill SealerHeader for epoch i

So I feel it might be better to impose as many restrictions to these functions as possible.

Comment on lines 76 to 82
if err := k.RecordLastBlockHeader(ctx); err != nil {
panic(err)
}
// record the Merkle root of all AppHashs in this epoch
if err := k.RecordAppHashRoot(ctx); err != nil {
panic(err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that the epoch info is stored twice in the two functions. Is it possible to combine the two into one like RecordEpochInfo?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep that's a good idea. Merged them to RecordLastHeaderAndAppHashRoot so that we only write to DB once 👍

if err != nil {
return nil, err
}
if height < epoch.FirstBlockHeight || uint64(epoch.LastBlockHeader.Height) < height {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if height < epoch.FirstBlockHeight || uint64(epoch.LastBlockHeader.Height) < height {
if height < epoch.FirstBlockHeight || height > uint64(epoch.LastBlockHeader.Height) {

Same thing but seems more intuitive. To be cleaner, maybe we can add a method to Epoch like WithinBoundary(height uint64) bool

Copy link
Member Author

Choose a reason for hiding this comment

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

The WithinBoundary is really a good idea! Done 👍


unwrappedProof, err := merkle.ProofFromProto(proof)
if err != nil {
return err
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return err
return fmt.Errorf("failed to unwrap proof: %w", err)

Just to be more informative

@SebastianElvis SebastianElvis merged commit 828d2da into dev Dec 16, 2022
@SebastianElvis SebastianElvis deleted the zoneconcierge-apphash-store branch December 16, 2022 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants