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

Proof of absence and presence for the same path with nil proof of presence value #414

Merged
merged 5 commits into from
Nov 3, 2023

Conversation

jsign
Copy link
Collaborator

@jsign jsign commented Nov 2, 2023

This is a proposed fix to #413.

At the end of the day, it's almost aligned with the ultra-original Python implementation, which probably did this for the same reason I found #413 problematic.

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
proof_ipa.go Outdated Show resolved Hide resolved
proof_ipa.go Outdated Show resolved Hide resolved
proof_ipa.go Outdated Show resolved Hide resolved
proof_ipa.go Show resolved Hide resolved
tree.go Show resolved Hide resolved
tree_test.go Outdated Show resolved Hide resolved
proof_ipa.go Show resolved Hide resolved
proof_ipa.go Outdated Show resolved Hide resolved
proof_test.go Show resolved Hide resolved
proof_test.go Outdated Show resolved Hide resolved
Copy link
Member

@gballet gballet left a comment

Choose a reason for hiding this comment

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

LGTM. It's true that it makes things a bit simpler.

proof_test.go Outdated Show resolved Hide resolved
proof_test.go Outdated Show resolved Hide resolved
tree.go Show resolved Hide resolved
proof_test.go Show resolved Hide resolved
proof_ipa.go Show resolved Hide resolved
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
@@ -398,8 +398,9 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { //
stems = append(stems, k[:31])
}
}
stemIndex := 0
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We don't need this anymore, things got simplified.

Comment on lines +401 to +403
if len(stems) != len(proof.ExtStatus) {
return nil, fmt.Errorf("invalid number of stems and extension statuses: %d != %d", len(stems), len(proof.ExtStatus))
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As a preliminary check, now check if there's a 1:1 match between stems and extension statuses.

Comment on lines +416 to +424
// We build a cache of paths that have a presence extension status.
pathsWithExtPresent := map[string]struct{}{}
i := 0
for _, es := range proof.ExtStatus {
depth := es >> 3
path := stems[stemIndex][:depth]
if es&3 == extStatusPresent {
pathsWithExtPresent[string(stems[i][:es>>3])] = struct{}{}
}
i++
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nothing new here compared to last review.

Comment on lines +445 to 449
for j := range proof.Keys { // TODO: DoS risk, use map or binary search.
if bytes.HasPrefix(proof.Keys[j], stems[i]) && proof.PreValues[j] != nil {
return nil, fmt.Errorf("proof of absence (other) stem %x has a value", si.stem)
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

First, we check that all keys for this stem have nil values.
This must be the case since all are absent. If that's not true, wrong proof.

Comment on lines +451 to +457
// For this absent path, we must first check if this path contains a proof of presence.
// If that is the case, we don't have to do anything since the corresponding leaf will be
// constructed by that extension status (already processed or to be processed).
// In other case, we should get the stem from the list of proof of absence stems.
if _, ok := pathsWithExtPresent[string(path)]; ok {
continue
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Second, if we know there is a proof of presence for this path; we don't do anything since that proof of presence will create this path.

Comment on lines +466 to +467
si.stem = poas[0]
poas = poas[1:]
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Forth, we're the first (or only) proof of absence (without proof of presences) for this path. Create it.

Comment on lines 469 to -475
si.values = map[byte][]byte{}
for i, k := range proof.Keys { // TODO: DoS risk, use map or binary search.
if bytes.Equal(k[:len(path)], stemPath) && proof.PreValues[i] != nil {
si.values[k[31]] = proof.PreValues[i]
si.stem = stems[i]
for j, k := range proof.Keys { // TODO: DoS risk, use map or binary search.
if bytes.Equal(k[:31], si.stem) {
si.values[k[31]] = proof.PreValues[j]
si.has_c1 = si.has_c1 || (k[31] < 128)
si.has_c2 = si.has_c2 || (k[31] >= 128)
// This key has values, its stem is the one that
// is present.
if si.stem == nil {
si.stem = k[:31]
continue
}
// Any other key with values must have the same
// same previously detected stem. If that isn't the case,
// the proof is invalid.
if !bytes.Equal(si.stem, k[:31]) {
return nil, fmt.Errorf("multiple keys with values found for stem %x", k[:31])
}
}
}
// For a proof of presence, we must always have detected a stem.
// If that isn't the case, the proof is invalid.
if si.stem == nil {
return nil, fmt.Errorf("no stem found for path %x", path)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All this got quite simplified since:

  • si.stem is stem[i] directly due to the current 1:1 correspondence
  • Look for keys, and simply add them into stemInfo (we don't care if their values are nil or not-nil. just add the values and mark c1/c2 appropriately).

Comment on lines -479 to -489

// Skip over all the stems that share the same path
// to the extension tree. This happens e.g. if two
// stems have the same path, but one is a proof of
// absence and the other one is present.
stemIndex++
for ; stemIndex < len(stems); stemIndex++ {
if !bytes.Equal(stems[stemIndex][:depth], path) {
break
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks to the 1:1 correspondence, all this is unnecessary since we don't have to "infer" the current stem by "tracking" some separate index from keys.

Comment on lines +1475 to 1481
// If this is the first extension status added for this path,
// add the proof of absence stem (only once). If later we detect a proof of
// presence, we'll clear the list since that proof of presence
// will be enough to provide the stem.
if len(esses) == 0 {
esses = append(esses, extStatusAbsentOther|(n.depth<<3))
poass = append(poass, n.stem)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What comment says.

Comment on lines +1482 to +1488
// Add an extension status absent other for this stem.
// Note we keep a cache to avoid adding the same stem twice (or more) if
// there're multiple keys with the same stem.
if _, ok := addedAbsentStems[string(key[:StemSize])]; !ok {
esses = append(esses, extStatusAbsentOther|(n.depth<<3))
addedAbsentStems[string(key[:StemSize])] = struct{}{}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We use the addedAbsentStems to avoid adding duplicate extension statuses for the same stem. This can happen since this external loop is iterating keys, and not "grouped stems".

@jsign jsign requested a review from gballet November 3, 2023 14:49
@jsign jsign marked this pull request as ready for review November 3, 2023 14:49
Copy link
Member

@gballet gballet 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 tackling this, it was long overdue. Before we merge, have you ensured that it could sync kaustinen and verify every proof?

@jsign
Copy link
Collaborator Author

jsign commented Nov 3, 2023

Thanks for tackling this, it was long overdue. Before we merge, have you ensured that it could sync kaustinen and verify every proof?

I'll merge all back to #412, and I'll test Kaustinen with that branch before merging to main.

@jsign jsign merged commit bd5b319 into jsign-proof-tests Nov 3, 2023
2 checks passed
jsign added a commit that referenced this pull request Nov 3, 2023
* proof: add proof of absence border case tests

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* Proof of absence and presence for the same path with nil proof of presence value (#414)

* keep the extension statuses of absent stems

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* more improvements and tests

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* fix & extra test case

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* make steams and extStatuses be 1:1

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* cleanup

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

---------

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

---------

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
gballet pushed a commit that referenced this pull request Nov 6, 2023
* proof: verify proof of absence steam is sorted

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* proof: more length checks

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* more tree from proof checks

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* add checks in CreatePath

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* lints

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* fix comments

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* Proof of absence border case test (#413)

* proof: add proof of absence border case tests

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* Proof of absence and presence for the same path with nil proof of presence value (#414)

* keep the extension statuses of absent stems

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* more improvements and tests

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* fix & extra test case

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* make steams and extStatuses be 1:1

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* cleanup

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

---------

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

---------

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* lint

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* add extra border case test

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* fix bug

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* further fixes

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* fix

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

* cleanup

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>

---------

Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
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