Skip to content

sync -- change proof request can return range proof #1772

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

Merged
merged 41 commits into from
Aug 16, 2023

Conversation

danlaine
Copy link

@danlaine danlaine commented Jul 27, 2023

Why this should be merged

Right now, if a server has insufficient history to generate a change proof, it responds with a change proof that has a flag signifying this. It'd be better if instead, it sent a range proof for the trie at the end root. This change does that.

How this works

  • Remove HadRootsInHistory field from ChangeProof
  • Consolidate ErrRootIDNotPresent and ErrStartRootNotFound into ErrInsufficientHistory
  • Add type ChangeOrRangeProof
  • Change client.GetChangeProof to return ChangeOrRangeProof
  • Change Manager.getAndApplyChangeProof to handle range proof

How this was tested

New UT.

@danlaine danlaine self-assigned this Jul 27, 2023
Comment on lines 842 to 918
name: "no roots in history and non-empty key-values",
proof: &ChangeProof{
HadRootsInHistory: false,
KeyChanges: []KeyChange{{Key: []byte{1}, Value: Some([]byte{1})}},
},
start: []byte{0},
end: nil, // Also tests start can be after end if end is nil
expectedErr: ErrDataInMissingRootProof,
},
{
name: "no roots in history and non-empty deleted keys",
proof: &ChangeProof{
HadRootsInHistory: false,
KeyChanges: []KeyChange{{Key: []byte{1}}},
},
start: nil,
end: nil,
expectedErr: ErrDataInMissingRootProof,
},
{
name: "no roots in history and non-empty start proof",
proof: &ChangeProof{
HadRootsInHistory: false,
StartProof: []ProofNode{{}},
},
start: nil,
end: nil,
expectedErr: ErrDataInMissingRootProof,
},
{
name: "no roots in history and non-empty end proof",
proof: &ChangeProof{
HadRootsInHistory: false,
EndProof: []ProofNode{{}},
},
start: nil,
end: nil,
expectedErr: ErrDataInMissingRootProof,
},
{
name: "no roots in history; empty",
proof: &ChangeProof{
HadRootsInHistory: false,
},
start: nil,
end: nil,
expectedErr: nil,
},
{
name: "root in history; empty",
proof: &ChangeProof{
HadRootsInHistory: true,
},
Copy link
Author

Choose a reason for hiding this comment

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

Removed tests where HadRootsInHistory is false because change proofs can no longer indicate that the server didn't have enough history to serve the change proof (a range proof is returned instead.)

type mockClient struct {
db DB
}
func newCallthroughSyncClient(ctrl *gomock.Controller, db merkledb.MerkleDB) *MockClient {
Copy link
Author

Choose a reason for hiding this comment

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

Replaced mockClient with usage of the gomock mock since I don't think we need 2 mock implementations.

Comment on lines 166 to 175
startRoot, err := ids.ToID(req.StartRootHash)
if err != nil {
return err
}

endRoot, err := ids.ToID(req.EndRootHash)
if err != nil {
return err
}

Copy link
Author

@danlaine danlaine Jul 31, 2023

Choose a reason for hiding this comment

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

We don't need to parse the start/end root in every loop iteration

changeProof, err := s.db.GetChangeProof(ctx, startRoot, endRoot, req.StartKey, req.EndKey, int(keyLimit))
if err != nil {
// handle expected errors so clients cannot cause servers to spam warning logs.
if errors.Is(err, merkledb.ErrRootIDNotPresent) || errors.Is(err, merkledb.ErrStartRootNotFound) {
s.log.Debug(
Copy link
Author

Choose a reason for hiding this comment

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

The old log was midleading. Just because we had insufficient history doesn't mean the request was invalid.

return err
}

// TODO handle this fatal error
Copy link
Author

Choose a reason for hiding this comment

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

This was previously unhandled too

if err != nil {
// handle expected errors so clients cannot cause servers to spam warning logs.
if errors.Is(err, merkledb.ErrRootIDNotPresent) {
s.log.Debug(
Copy link
Author

Choose a reason for hiding this comment

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

The old log was misleading -- insufficient history doesn't mean invalid request

)
return nil // dropping request
if errors.Is(err, merkledb.ErrInsufficientHistory) {
return nil, nil // drop request
Copy link
Author

Choose a reason for hiding this comment

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

I could add a log here but it seems kind of annoying. I'd also be open to checking for ErrInsufficientHistory in the caller.

@danlaine danlaine marked this pull request as draft August 11, 2023 15:03
@danlaine danlaine force-pushed the sync-handle-insufficient-history branch from c9fba3b to 30ab6ea Compare August 11, 2023 18:00
@danlaine danlaine marked this pull request as ready for review August 11, 2023 20:50
Comment on lines 101 to 107
return nil, fmt.Errorf("%w: end root %s not found", ErrInsufficientHistory, endRoot)
}

// [startRootChanges] is the last appearance of [startRoot]
startRootChanges, ok := th.lastChanges[startRoot]
if !ok {
return nil, ErrStartRootNotFound
return nil, fmt.Errorf("%w: start root %s not found", ErrInsufficientHistory, startRoot)
Copy link
Contributor

@StephenButtolph StephenButtolph Aug 15, 2023

Choose a reason for hiding this comment

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

It seems weird not to treat a missing endRoot differently to a missing startRoot... If we hit a missing endRoot I wouldn't expect the range proof to work either right?

Copy link
Author

Choose a reason for hiding this comment

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

What do you mean? We also return ErrInsufficientHistory if the startRoot isn't found

Copy link
Contributor

@StephenButtolph StephenButtolph Aug 15, 2023

Choose a reason for hiding this comment

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

Why would we attempt to generate a range proof if we know we don't have the state to generate it?

(My original comment was missing a "not" oops)

Copy link
Author

Choose a reason for hiding this comment

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

Still not sure I follow. This is the point where we figure out whether we have the state to generate the range proof.

Copy link
Author

Choose a reason for hiding this comment

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

Put a TODO to return ErrNoEndRoot (or similar named error) once we require go 1.20 and then we can check for that error and not try to get a range proof if we know we have insufficient history

@danlaine danlaine merged commit ac9138b into dev Aug 16, 2023
@danlaine danlaine deleted the sync-handle-insufficient-history branch August 16, 2023 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
No open projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants