Skip to content

feat: Change proof commit and find next key#1646

Open
bernard-avalabs wants to merge 10 commits intobernard/change-proof-api-v3-part3from
bernard/change-proof-api-v3-part4
Open

feat: Change proof commit and find next key#1646
bernard-avalabs wants to merge 10 commits intobernard/change-proof-api-v3-part3from
bernard/change-proof-api-v3-part4

Conversation

@bernard-avalabs
Copy link
Contributor

Why this should be merged

Builds on #1641. This is likely the last in the current series of Change Proof PRs. It implements fwd_db_verify_and_commit_change_proof and fwd_change_proof_find_next_key.

How this works

Mostly follows the implementation from range proofs with some additional checks that are specific to change proofs. The find next key approach is the same basic one used in range proofs and should later be replaced.

How this was tested

Added end-to-end tests for committing a change proof, applying multiple rounds of change proofs with different start keys determined by calling find next key, and testing change proofs with deletes instead of just puts.

@bernard-avalabs bernard-avalabs self-assigned this Feb 2, 2026
}

// VerifyChangeProof verifies the provided change [proof] proves the changes
// VerifyRangeProof verifies the provided change [proof] proves the changes
Copy link
Contributor

@alarso16 alarso16 Jan 30, 2026

Choose a reason for hiding this comment

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

Haha good catch, but will you fix the entire line?

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'll rewrite the comment. This was something that I was planning to come back to after catching the error, but I must have forgotten.

keys, vals, batch := kvForTest(100)
root1, err := db1.Update(batch[:50])
r.NoError(err)
root2, err := db2.Update(batch[:50])
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
root2, err := db2.Update(batch[:50])
_, err := db2.Update(batch[:50])

Shouldn't these match? Just adds what seems like more mental overhead then necessary. Then you only need root1, root2, gotRoot

r.NoError(err)

// Create a change proof from db1.
change_proof, err := db1.ChangeProof(root1, root1_updated, nothing(), nothing(), changeProofLenUnbounded)
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
change_proof, err := db1.ChangeProof(root1, root1_updated, nothing(), nothing(), changeProofLenUnbounded)
changeProof, err := db1.ChangeProof(root1, root1_updated, nothing(), nothing(), changeProofLenUnbounded)

camelCase is conventional in Go

r.NotNil(nextRange)
startKey := nextRange.StartKey()
r.NotEmpty(startKey)
startKey = append([]byte{}, startKey...) // copy to new slice to avoid use-after-free
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
startKey = append([]byte{}, startKey...) // copy to new slice to avoid use-after-free

I believe startKey is copied when it's returned from the API, so this is unnecessary

}
startKey = maybe{
hasValue: true,
value: append([]byte{}, nextRange.StartKey()...), // copy to new slice to avoid use-after-free
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
value: append([]byte{}, nextRange.StartKey()...), // copy to new slice to avoid use-after-free
value: StartKey(),


// Create and commit multiple change proofs to update db2 to match db1.
startKey := nothing()
for {
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems prone to busy loop forever if there's ever a bug. Do we know the exact amount of proofs this should take? or can we add some debugging with t.Logf? Just think this test will be very hard to debug

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 will add a loop limit.

}
}

func TestMultiRoundChangeProofWithDeletes(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you combine the tests above? It's very conventional for functional in Go to have a test table like this:

tests := []struct {
    name string,
    params any, // put everything necessary in other fields
}

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        r := require.New(t) // optional, but make sure you use the new `*testing.T`
        actualTest(tt.Params) // or write the test here
    })
}

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 didn't know that this is Go convention. I'll make this change.

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.

3 participants