-
Notifications
You must be signed in to change notification settings - Fork 67
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
Conversation
Signed-off-by: Ignacio Hagopian <[email protected]>
Signed-off-by: Ignacio Hagopian <[email protected]>
There was a problem hiding this 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.
6c426c6
to
33c0992
Compare
33c0992
to
a58c6fc
Compare
Signed-off-by: Ignacio Hagopian <[email protected]>
a58c6fc
to
fa7eeeb
Compare
Signed-off-by: Ignacio Hagopian <[email protected]>
Signed-off-by: Ignacio Hagopian <[email protected]>
@@ -398,8 +398,9 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // | |||
stems = append(stems, k[:31]) | |||
} | |||
} | |||
stemIndex := 0 |
There was a problem hiding this comment.
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.
if len(stems) != len(proof.ExtStatus) { | ||
return nil, fmt.Errorf("invalid number of stems and extension statuses: %d != %d", len(stems), len(proof.ExtStatus)) | ||
} |
There was a problem hiding this comment.
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.
// 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++ | ||
} |
There was a problem hiding this comment.
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.
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) | ||
} | ||
} |
There was a problem hiding this comment.
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.
// 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 | ||
} |
There was a problem hiding this comment.
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.
si.stem = poas[0] | ||
poas = poas[1:] |
There was a problem hiding this comment.
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.
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) | ||
} |
There was a problem hiding this comment.
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).
|
||
// 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 | ||
} | ||
} |
There was a problem hiding this comment.
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.
// 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) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What comment says.
// 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{}{} | ||
} |
There was a problem hiding this comment.
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".
There was a problem hiding this 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?
I'll merge all back to #412, and I'll test Kaustinen with that branch before merging to main. |
* proof: add proof of absence border case tests Signed-off-by: Ignacio Hagopian <[email protected]> * 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 <[email protected]> * more improvements and tests Signed-off-by: Ignacio Hagopian <[email protected]> * fix & extra test case Signed-off-by: Ignacio Hagopian <[email protected]> * make steams and extStatuses be 1:1 Signed-off-by: Ignacio Hagopian <[email protected]> * cleanup Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]>
* proof: verify proof of absence steam is sorted Signed-off-by: Ignacio Hagopian <[email protected]> * proof: more length checks Signed-off-by: Ignacio Hagopian <[email protected]> * more tree from proof checks Signed-off-by: Ignacio Hagopian <[email protected]> * add checks in CreatePath Signed-off-by: Ignacio Hagopian <[email protected]> * lints Signed-off-by: Ignacio Hagopian <[email protected]> * fix comments Signed-off-by: Ignacio Hagopian <[email protected]> * Proof of absence border case test (#413) * proof: add proof of absence border case tests Signed-off-by: Ignacio Hagopian <[email protected]> * 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 <[email protected]> * more improvements and tests Signed-off-by: Ignacio Hagopian <[email protected]> * fix & extra test case Signed-off-by: Ignacio Hagopian <[email protected]> * make steams and extStatuses be 1:1 Signed-off-by: Ignacio Hagopian <[email protected]> * cleanup Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]> * lint Signed-off-by: Ignacio Hagopian <[email protected]> * add extra border case test Signed-off-by: Ignacio Hagopian <[email protected]> * fix bug Signed-off-by: Ignacio Hagopian <[email protected]> * further fixes Signed-off-by: Ignacio Hagopian <[email protected]> * fix Signed-off-by: Ignacio Hagopian <[email protected]> * cleanup Signed-off-by: Ignacio Hagopian <[email protected]> --------- Signed-off-by: Ignacio Hagopian <[email protected]>
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.