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

feat: Add new query for the last checkpoint with a given status #253

Merged
merged 5 commits into from
Dec 21, 2022

Conversation

gitferry
Copy link
Contributor

This PR adds a new query for the last checkpoint with a given status. This API will be used by the verifier as well as the ZoneConcierge module. This PR also removed some unused APIs.

@gitferry gitferry force-pushed the checkpointing/add-last-checkpoint-api branch from bc1ee3a to c4ebd57 Compare December 21, 2022 04:33
Copy link
Member

@SebastianElvis SebastianElvis left a comment

Choose a reason for hiding this comment

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

Thanks for the API, which can be very helpful! I left some comments about the security of this API.

@@ -72,26 +66,6 @@ message QueryRawCheckpointListResponse {
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

Copy link
Member

Choose a reason for hiding this comment

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

Why are we removing RecentRawCheckpointList API?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we need this API as we already have RawCheckpointList?

// GetLastCheckpointedEpoch returns the last epoch number that associates with a checkpoint
func (k Keeper) GetLastCheckpointedEpoch(ctx sdk.Context) (uint64, error) {
// minus 1 is because the current epoch is not finished
tipEpoch := k.GetEpoch(ctx).EpochNumber - 1
Copy link
Member

Choose a reason for hiding this comment

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

IIUC this function returns the last epoch that has an associated raw checkpoint. Is it possible that the second latest epoch may not have a raw checkpoint yet? For example, when the latest epoch only has 1-2 blocks, the second latest epoch hasn't been sealed yet. It also takes some blocks to receive sufficient BLS sigs to get a valid BLS multisig.

Since this function is only used in LastCheckpointWithStatus, maybe we can consider a slightly different approach to avoid the above issue. In LastCheckpointWithStatus we still iterate backwards from the latest epoch to the zero epoch. In each iteration, check if the epoch has an associated raw checkpoint. If not, continue to the next iteration. If yes, then check if the raw checkpoint is in the given status. If yes, return, otherwise continue to the next iteration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IUC this function returns the last epoch that has an associated raw checkpoint. Is it possible that the second latest epoch may not have a raw checkpoint yet? For example, when the latest epoch only has 1-2 blocks, the second latest epoch hasn't been sealed yet. It also takes some blocks to receive sufficient BLS sigs to get a valid BLS multisig.

This is not possible because the second epoch would at least have a status of Accumulating. Minus 1 is to ensure a checkpoint is created. I don't think we care about whether the checkpoint is Sealed or not.

Copy link
Member

Choose a reason for hiding this comment

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

I see. Thanks for the explanation! Is it possible for this API to return error when the requested status is Sealed or Accumulating?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course! I thought this API would be more general, covering all statuses. Why do we want to reject Sealed or Accumulating requests?

func (k Keeper) GetLastCheckpointedEpoch(ctx sdk.Context) (uint64, error) {
// minus 1 is because the current epoch is not finished
tipEpoch := k.GetEpoch(ctx).EpochNumber - 1
if tipEpoch < 0 { //nolint:staticcheck // uint64 doesn't go below zero but we want to let people know that's an invalid request.
Copy link
Member

Choose a reason for hiding this comment

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

As suggested by the linter, maybe we only need to check if tipEpoch == 0 or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We just need to ensure that the tipEpoch is not below 0. Wouldn't it be weird if we have the following?

if targetEpoch == 0 { //nolint:staticcheck // uint64 doesn't go below zero
	targetEpoch = 0
}

Copy link
Member

Choose a reason for hiding this comment

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

Or can we return error directly if k.GetEpoch(ctx).EpochNumber is 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep. Good idea. Thanks!

if err != nil {
return nil, fmt.Errorf("failed to get the raw checkpoint at epoch %v: %w", e, err)
}
if ckpt.Status == req.Status {
Copy link
Member

Choose a reason for hiding this comment

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

There might be a possibility that no existing checkpoint is in the requested status. For example, assuming all epochs are either finalised or submitted, but not confirmed. If there is a query for the last confirmed epoch, then the function will iterate all the way down to epoch 0 and eventually return an error.

In order to avoid this scenario, maybe we need an extra if statement here. If ckpt.Status is more "mature" than req.Status, then this means none of the existing epoch is in req.Status, and should return an error or something.
Or, upon such scenario, we return ckpt directly here. This also makes sense since for example, a finalised epoch is also confirmed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a great point. Thanks! Will make the change

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

@SebastianElvis SebastianElvis left a comment

Choose a reason for hiding this comment

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

Thanks for addressing the comments, lgtm! 👍

Comment on lines +140 to +142
if curEpoch <= 0 {
return 0, fmt.Errorf("current epoch should be more than 0")
}
Copy link
Member

Choose a reason for hiding this comment

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

With this check L145-147 can be safely removed, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep. Thanks!

@@ -104,6 +104,10 @@ func (cm *RawCheckpointWithMeta) Accumulate(
return true, nil
}

func (cm *RawCheckpointWithMeta) IsMoreMatureThanStatus(status CheckpointStatus) bool {
Copy link
Member

Choose a reason for hiding this comment

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

👍

@gitferry gitferry merged commit 909f644 into dev Dec 21, 2022
@gitferry gitferry deleted the checkpointing/add-last-checkpoint-api branch December 21, 2022 08:32
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