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

Add EIP7736 support #457

Draft
wants to merge 34 commits into
base: master
Choose a base branch
from
Draft

Conversation

weiihann
Copy link
Contributor

Primary changes:

  • Add new StateEpoch type
  • Modify Get, Insert and Delete to include StateEpoch. If trying to access expired leaf node, returns error.
  • Additional lastEpoch field in LeafNode, included in serialization and node commitment calculation
  • Add new ExpiryLeafNode struct

@gballet gballet requested review from gballet and jsign October 14, 2024 07:17
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.

Thank you for this PR. This is a good start, although it requires a lot of polishing, and probably a couple spec discussions as well. I left a few comments for you to consider, I can have another pass when you've had a look.

benchs/main.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

it would be nice to have another benchmark that checks how much time it takes to expire the data. For example, a benchmark that inserts a few "old" leaves in the tree, then a lot more "newer" leaves, and then see how much it takes to delete all leaves.

We are not looking into bulk-expiring the tree, I think it would be insane, but to estimate what it would take to expire a few leaves per block, in case we detect that they are still present in the tree in case they are expired. Think of a "garbage collect on access" type of approach.

conversion.go Outdated Show resolved Hide resolved
conversion.go Outdated Show resolved Hide resolved
expired_leaf.go Outdated Show resolved Hide resolved
expired_leaf.go Outdated Show resolved Hide resolved
state_epoch.go Outdated Show resolved Hide resolved
state_epoch.go Outdated Show resolved Hide resolved
state_epoch.go Outdated Show resolved Hide resolved
proof_ipa.go Outdated Show resolved Hide resolved
@weiihann
Copy link
Contributor Author

Here's a general explanation of how expiry works with stateless proofs:

There are 4 scenarios for a given stem:

  1. Both prestate and poststate not expired
  • Works as normal
  1. Both prestate and poststate expired
  • Works as normal, there shouldn't be any state diffs because they are expired
  1. Prestate not expired, poststate expired
  • Works as normal, this indicates that there are no state updates from prestate to poststate, so state diffs do not exist
  1. Prestate expired, poststate not expired
  • This indicates that a resurrection has occurred. How to verify:
    • We added a new extension status type called extStatusExpired to indicate if a given stem has expired.
    • If an extension status of a stem is extStatusExpired and any of the postvalue in the stem given in the proof is not absent, then we know that there's a resurrection.

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.

Left more change requests

conversion.go Outdated Show resolved Hide resolved
conversion.go Outdated Show resolved Hide resolved
tree_test.go Outdated Show resolved Hide resolved
tree_test.go Outdated Show resolved Hide resolved
tree_test.go Outdated Show resolved Hide resolved
proof_ipa.go Outdated
@@ -94,6 +94,7 @@ type SuffixStateDiffs []SuffixStateDiff
type StemStateDiff struct {
Stem [StemSize]byte `json:"stem"`
SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"`
Resurrected bool `json:"resurrected"`
Copy link
Member

Choose a reason for hiding this comment

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

This is going to be taking more space. I would do a expiredStem field like we have poaStems. But no need to take action on this right now, as long as it works it's fine. This is a later-stage optimization.

encoding.go Outdated Show resolved Hide resolved
tree.go Outdated Show resolved Hide resolved
tree.go Outdated Show resolved Hide resolved
tree.go Outdated Show resolved Hide resolved
@weiihann weiihann force-pushed the eip7736/master branch 2 times, most recently from 546eedd to 8a00f80 Compare October 30, 2024 03:36
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.

  • This PR must be updated to refresh the period upon a read
  • Also, I don't think that the tests are actually checking that an expiration has been proven. but maybe I misundertsood.
  • It also seems to me that inserting a sibling to an expired node isn't tested.

epoch.go Outdated Show resolved Hide resolved
epoch.go Outdated
"encoding/binary"
)

type StatePeriod uint16
Copy link
Member

Choose a reason for hiding this comment

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

I think I already asked that, but why uint16? On some archs with strong alignment constaints, that could cause a lot of slowdowns, and, however small that slowdown is, I don't know why we would pay that cost?

Copy link
Member

Choose a reason for hiding this comment

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

note that if this is because we don't want to take too much space with the leaf encoding, we can always store it as a uint16 (or even a uint8, that's 128 years before we need to upgrade!)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As per our convo, this is a premature optimization and this comment should be discarded. We can look into it in the future.

conversion.go Show resolved Hide resolved
tree.go Outdated Show resolved Hide resolved
tree.go Outdated Show resolved Hide resolved
proof_test.go Outdated Show resolved Hide resolved
proof_test.go Outdated Show resolved Hide resolved
proof_ipa.go Outdated Show resolved Hide resolved
proof_ipa.go Outdated Show resolved Hide resolved
expired_tree_test.go Show resolved Hide resolved
@weiihann
Copy link
Contributor Author

weiihann commented Jan 5, 2025

Change Log (5/1/2025):

  • Add Revive() to VerkleNode interface
  • Proof now includes the periods of each stem in pre-state tree. How stateless proof works with expiry:
    • Given a pre-state tree with expired stem, we first perform Revive()
    • This particular Revive() operation should not refresh the period of the leaf node to prevent changing commitment
    • We now have a pre-state tree with expired stems resolved
    • Generate proof. For each stem, the period is included in the proof
    • In the verification process, the pre-state tree can be reconstructed from the proof, and the period of each stem is resolved correctly
  • Minor code quality improvements and bug fixes

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.

It's getting into shape! I'm not sure that i understand the point of skipUpdate: why shouldn't the counter/commitment be updated upon revival? We can think of a use case where someone resurects an account for someone else, and doesn't directly write/read from that account.

return errMissingNodeInStateless
case Empty:
// TODO(weiihann): double confirm this
return errors.New("cannot revive an empty node")
Copy link
Member

Choose a reason for hiding this comment

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

If you go through a tree and the node that you are given indicates that the commitment is non-zero, you should definitely error. But if the commitment is 0, there's a case to be made that the tx should not fail... although, if it's simpler to handle, then let's just error.


return nil
case *LeafNode:
return child.Revive(stem, values, oldPeriod, curPeriod, skipUpdate, resolver)
Copy link
Member

Choose a reason for hiding this comment

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

hmmmm, since no invididual leaf can be expired, I think this should be an error, or at least ignored.

Copy link
Member

Choose a reason for hiding this comment

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

I get it now, the field might not have been pruned by the collector. Add a comment here, so that I don't ask myself that question each time, please.


// Serialize encodes the node to RLP.
Serialize() ([]byte, error)

// Copy a node and its children
Copy() VerkleNode

Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error
Copy link
Member

Choose a reason for hiding this comment

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

could you please document what the boolean does?

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