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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
39fdd6c
feat: add last epoch support
weiihann Oct 8, 2024
32ce419
calculate leaf commitment for insert/delete
weiihann Oct 10, 2024
25ba7b9
Add expiry access rules
weiihann Oct 10, 2024
ec74e56
Add ExpiredLeafNode
weiihann Oct 11, 2024
3f23ce0
add StateEpoch to Get
weiihann Oct 11, 2024
de94975
change epoch to timestamps, add encoding test
weiihann Oct 16, 2024
d536897
add basic expired leaf test
weiihann Oct 21, 2024
342e683
add basic operation tests
weiihann Oct 21, 2024
5419a7d
support stateless proof
weiihann Oct 24, 2024
d037673
change access timestamp to state epoch
weiihann Oct 30, 2024
4f15b6c
implement toDot for expired leaf
weiihann Oct 30, 2024
3d0659a
make epoch size internal
weiihann Oct 30, 2024
d59b620
fix bug in leaf commitment
weiihann Oct 30, 2024
cc43672
minor formatting improvements
weiihann Oct 30, 2024
861826a
make state epoch uint16
weiihann Oct 30, 2024
e558f36
remove epoch from GetProofItems
weiihann Oct 30, 2024
ae9ac46
Merge branch 'master' of https://github.com/ethereum/go-verkle into e…
weiihann Nov 19, 2024
9e5c6fa
change epoch to period
weiihann Nov 19, 2024
f631e65
refactor isExpired
weiihann Nov 19, 2024
ed32767
update expiry check
weiihann Dec 18, 2024
d68c4ae
use period consts
weiihann Dec 18, 2024
84e62fb
refresh period upon a read
weiihann Dec 20, 2024
2bb9b2e
compare periods at DeleteAtStem
weiihann Dec 20, 2024
1a73d65
add Revive() to VerkleNode interface
weiihann Dec 20, 2024
64151cb
add Revive()
weiihann Dec 27, 2024
b670596
add lastPeriod to ExpiredLeafNode
weiihann Dec 27, 2024
36036fb
Revert "add lastPeriod to ExpiredLeafNode"
weiihann Dec 27, 2024
1ce49aa
fix period encoding bug
weiihann Dec 27, 2024
c42a5c2
add `skipVerify` in Revive()
weiihann Dec 27, 2024
971c099
remove skipVerify
weiihann Dec 27, 2024
4cee9df
add lastPeriod to ExpiredLeafNode
weiihann Dec 27, 2024
fe3e25c
add isExpired
weiihann Dec 27, 2024
dd87e7f
fix stateless proof
weiihann Jan 3, 2025
2950a74
Remove expiry and add period in stateless proof
weiihann Jan 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions benchs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func benchmarkInsertInExisting() {
for i := 0; i < 5; i++ {
root := verkle.New()
for _, k := range keys {
if err := root.Insert(k, value, nil); err != nil {
if err := root.Insert(k, value, 0, nil); err != nil {
panic(err)
}
}
Expand All @@ -58,7 +58,7 @@ func benchmarkInsertInExisting() {
// Now insert the 10k leaves and measure time
start := time.Now()
for _, k := range toInsertKeys {
if err := root.Insert(k, value, nil); err != nil {
if err := root.Insert(k, value, 0, nil); err != nil {
panic(err)
}
}
Expand Down
1 change: 1 addition & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const (
NodeWidth = 256
NodeBitWidth byte = 8
StemSize = 31
periodSize = 2
)

func equalPaths(key1, key2 []byte) bool {
Expand Down
4 changes: 2 additions & 2 deletions conversion.go
weiihann marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func BatchNewLeafNode(nodesValues []BatchNewLeafNodeData) ([]LeafNode, error) {
}

var leaf *LeafNode
leaf, err := NewLeafNode(nv.Stem, valsslice)
leaf, err := NewLeafNode(nv.Stem, valsslice, 0)
if err != nil {
return err
}
Expand Down Expand Up @@ -192,7 +192,7 @@ func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, resolver N
}
}

if err := node.updateMultipleLeaves(nonPresentValues); err != nil {
if err := node.updateMultipleLeaves(nonPresentValues, 0); err != nil {
return fmt.Errorf("updating leaves: %s", err)
}
continue
Expand Down
12 changes: 9 additions & 3 deletions debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ type (
Stem Stem `json:"stem"`
Values [][]byte `json:"values"`

C [32]byte `json:"commitment"`
C1 [32]byte `json:"c1"`
C2 [32]byte `json:"c2"`
C [32]byte `json:"commitment"`
C1 [32]byte `json:"c1"`
C2 [32]byte `json:"c2"`
Period StatePeriod `json:"period"`
}

ExportableExpiredLeafNode struct {
Stem Stem `json:"stem"`
Commitment [32]byte `json:"commitment"`
}
)
8 changes: 4 additions & 4 deletions debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ func TestJSON(t *testing.T) {
t.Parallel()

root := New()
if err := root.Insert(zeroKeyTest, fourtyKeyTest, nil); err != nil {
if err := root.Insert(zeroKeyTest, fourtyKeyTest, 0, nil); err != nil {
t.Fatal(err)
}
if err := root.Insert(oneKeyTest, zeroKeyTest, nil); err != nil {
if err := root.Insert(oneKeyTest, zeroKeyTest, 0, nil); err != nil {
t.Fatal(err)
}
if err := root.Insert(forkOneKeyTest, zeroKeyTest, nil); err != nil { // Force an internal node in the first layer.
if err := root.Insert(forkOneKeyTest, zeroKeyTest, 0, nil); err != nil { // Force an internal node in the first layer.
t.Fatal(err)
}
if err := root.Insert(fourtyKeyTest, oneKeyTest, nil); err != nil {
if err := root.Insert(fourtyKeyTest, oneKeyTest, 0, nil); err != nil {
t.Fatal(err)
}
root.(*InternalNode).children[152] = HashedNode{}
Expand Down
6 changes: 6 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ var (
errUnknownNodeType = errors.New("unknown node type detected")
errMissingNodeInStateless = errors.New("trying to access a node that is missing from the stateless view")
errIsPOAStub = errors.New("trying to read/write a proof of absence leaf node")
errExpired = errors.New("trying to access an expired leaf node")
errReviveCommitmentMismatch = errors.New("commitment mismatch in revive")
errReviveValuesMismatch = errors.New("values mismatch in revive")
errReviveStemMismatch = errors.New("stem mismatch in revive")
errRevivePeriodMismatch = errors.New("period mismatch in revive")
errRevivePeriodOlderThanNew = errors.New("revive old period is older than the new period")
)

const (
Expand Down
14 changes: 9 additions & 5 deletions empty.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,22 @@ type Empty struct{}

var errDirectInsertIntoEmptyNode = errors.New("an empty node should not be inserted directly into")

func (Empty) Insert([]byte, []byte, NodeResolverFn) error {
func (Empty) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error {
return errDirectInsertIntoEmptyNode
}

func (Empty) Delete([]byte, NodeResolverFn) (bool, error) {
func (Empty) Delete([]byte, StatePeriod, NodeResolverFn) (bool, error) {
return false, errors.New("cant delete an empty node")
}

func (Empty) Get([]byte, NodeResolverFn) ([]byte, error) {
func (Empty) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) {
return nil, nil
}

func (Empty) Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error {
return errors.New("cannot revive an empty node")
}

func (n Empty) Commit() *Point {
return n.Commitment()
}
Expand All @@ -53,8 +57,8 @@ func (Empty) Commitment() *Point {
return &id
}

func (Empty) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) {
return nil, nil, nil, errors.New("trying to produce a commitment for an empty subtree")
func (Empty) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) {
return nil, nil, nil, nil, errors.New("trying to produce a commitment for an empty subtree")
}

func (Empty) Serialize() ([]byte, error) {
Expand Down
8 changes: 4 additions & 4 deletions empty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ func TestEmptyFuncs(t *testing.T) {
t.Parallel()

var e Empty
err := e.Insert(zeroKeyTest, zeroKeyTest, nil)
err := e.Insert(zeroKeyTest, zeroKeyTest, 0, nil)
if err == nil {
t.Fatal("got nil error when inserting into empty")
}
_, err = e.Delete(zeroKeyTest, nil)
_, err = e.Delete(zeroKeyTest, 0, nil)
if err == nil {
t.Fatal("got nil error when deleting from empty")
}
v, err := e.Get(zeroKeyTest, nil)
v, err := e.Get(zeroKeyTest, 0, nil)
if err != nil {
t.Fatal("got non-nil error when getting from empty")
}
Expand All @@ -51,7 +51,7 @@ func TestEmptyFuncs(t *testing.T) {
t.Fatal("commitment and commit mismatch")
}

if _, _, _, err := e.GetProofItems(nil, nil); err == nil {
if _, _, _, _, err := e.GetProofItems(nil, nil); err == nil {
t.Fatal("get proof items should error")
}

Expand Down
43 changes: 32 additions & 11 deletions encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ const (
leafC1CommitmentOffset = leafCommitmentOffset + banderwagon.UncompressedSize
leafC2CommitmentOffset = leafC1CommitmentOffset + banderwagon.UncompressedSize
leafChildrenOffset = leafC2CommitmentOffset + banderwagon.UncompressedSize
leafLastPeriodOffset = leafChildrenOffset + periodSize
leafBasicDataSize = 32
leafSlotSize = 32
leafValueIndexSize = 1
singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize
eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize
singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + periodSize
eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize + periodSize
expiredLeafSize = nodeTypeSize + StemSize + banderwagon.UncompressedSize
)

func bit(bitlist []byte, nr int) bool {
Expand All @@ -75,9 +77,10 @@ var errSerializedPayloadTooShort = errors.New("verkle payload is too short")
// ParseNode deserializes a node into its proper VerkleNode instance.
// The serialized bytes have the format:
// - Internal nodes: <nodeType><bitlist><commitment>
// - Leaf nodes: <nodeType><stem><bitlist><comm><c1comm><c2comm><children...>
// - EoA nodes: <nodeType><stem><comm><c1comm><balance><nonce>
// - single slot node: <nodeType><stem><comm><cncomm><leaf index><slot>
// - Leaf nodes: <nodeType><stem><bitlist><comm><c1comm><c2comm><period><children...>
// - EoA nodes: <nodeType><stem><comm><c1comm><period><balance><nonce>
// - single slot node: <nodeType><stem><comm><cncomm><period><leaf index><slot>
// - Expired leaf nodes: <nodeType><stem><commitment>
func ParseNode(serializedNode []byte, depth byte) (VerkleNode, error) {
// Check that the length of the serialized node is at least the smallest possible serialized node.
if len(serializedNode) < nodeTypeSize+banderwagon.UncompressedSize {
Expand All @@ -93,6 +96,8 @@ func ParseNode(serializedNode []byte, depth byte) (VerkleNode, error) {
return parseEoAccountNode(serializedNode, depth)
case singleSlotType:
return parseSingleSlotNode(serializedNode, depth)
case expiredLeafType:
return parseExpiredLeafNode(serializedNode, depth)
default:
return nil, ErrInvalidNodeEncoding
}
Expand All @@ -111,7 +116,10 @@ func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) {
offset += LeafValueSize
}
}
ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:])
ln := NewLeafNodeWithNoComms(
serialized[leafStemOffset:leafStemOffset+StemSize],
values[:],
StatePeriodFromBytes(serialized[leafLastPeriodOffset:leafLastPeriodOffset+periodSize]))
ln.setDepth(depth)
ln.c1 = new(Point)

Expand All @@ -136,10 +144,11 @@ func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) {

func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) {
var values [NodeWidth][]byte
offset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize
offset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize + periodSize
periodOffset := offset - periodSize
values[0] = serialized[offset : offset+leafBasicDataSize] // basic data
values[1] = EmptyCodeHash[:]
ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:])
ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:], StatePeriodFromBytes(serialized[periodOffset:periodOffset+periodSize]))
ln.setDepth(depth)
ln.c1 = new(Point)
if err := ln.c1.SetBytesUncompressed(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+banderwagon.UncompressedSize], true); err != nil {
Expand All @@ -156,12 +165,13 @@ func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) {
func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) {
var values [NodeWidth][]byte
offset := leafStemOffset
ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:])
periodOffset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize
ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], StatePeriodFromBytes(serialized[periodOffset:periodOffset+periodSize]))
offset += StemSize
cnCommBytes := serialized[offset : offset+banderwagon.UncompressedSize]
offset += banderwagon.UncompressedSize
rootCommBytes := serialized[offset : offset+banderwagon.UncompressedSize]
offset += banderwagon.UncompressedSize
offset += banderwagon.UncompressedSize + periodSize
idx := serialized[offset]
offset += leafValueIndexSize
values[idx] = serialized[offset : offset+leafSlotSize] // copy slot
Expand All @@ -186,6 +196,17 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) {
return ln, nil
}

func parseExpiredLeafNode(serialized []byte, depth byte) (VerkleNode, error) {
l := &ExpiredLeafNode{}
l.setDepth(depth)
l.stem = serialized[leafStemOffset : leafStemOffset+StemSize]
l.commitment = new(Point)
if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize:], true); err != nil {
return nil, fmt.Errorf("setting commitment: %w", err)
}
return l, nil
}

func CreateInternalNode(bitlist []byte, raw []byte, depth byte) (*InternalNode, error) {
// GetTreeConfig caches computation result, hence
// this op has low overhead
Expand All @@ -203,7 +224,7 @@ func CreateInternalNode(bitlist []byte, raw []byte, depth byte) (*InternalNode,
if b&mask[j] != 0 {
node.children[8*i+j] = HashedNode{}
} else {

node.children[8*i+j] = Empty(struct{}{})
}
}
Expand Down
43 changes: 38 additions & 5 deletions encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ func TestLeafStemLength(t *testing.T) {
// Serialize a leaf with no values, but whose stem is 32 bytes. The
// serialization should trim the extra byte.
toolong := make([]byte, 32)
leaf, err := NewLeafNode(toolong, make([][]byte, NodeWidth))
leaf, err := NewLeafNode(toolong, make([][]byte, NodeWidth), 0)
if err != nil {
t.Fatal(err)
}
ser, err := leaf.Serialize()
if err != nil {
t.Fatal(err)
}
if len(ser) != nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize {
if len(ser) != nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+periodSize {
t.Fatalf("invalid serialization when the stem is longer than 31 bytes: %x (%d bytes != %d)", ser, len(ser), nodeTypeSize+StemSize+bitlistSize+2*banderwagon.UncompressedSize)
}
}
Expand All @@ -46,7 +46,7 @@ func TestInvalidNodeEncoding(t *testing.T) {
// Test an invalid node type.
values := make([][]byte, NodeWidth)
values[42] = testValue
ln, err := NewLeafNode(ffx32KeyTest, values)
ln, err := NewLeafNode(ffx32KeyTest, values, 0)
if err != nil {
t.Fatal(err)
}
Expand All @@ -67,7 +67,7 @@ func TestParseNodeEoA(t *testing.T) {
values[2] = fourtyKeyTest[:] // set nonce to 64
values[3] = EmptyCodeHash[:] // set empty code hash
values[4] = zero32[:] // zero-size
ln, err := NewLeafNode(ffx32KeyTest[:31], values)
ln, err := NewLeafNode(ffx32KeyTest[:31], values, 0)
if err != nil {
t.Fatalf("error creating leaf node: %v", err)
}
Expand Down Expand Up @@ -131,10 +131,11 @@ func TestParseNodeEoA(t *testing.T) {
t.Fatalf("invalid commitment, got %x, expected %x", lnd.commitment, ln.commitment)
}
}

func TestParseNodeSingleSlot(t *testing.T) {
values := make([][]byte, 256)
values[153] = EmptyCodeHash
ln, err := NewLeafNode(ffx32KeyTest[:31], values)
ln, err := NewLeafNode(ffx32KeyTest[:31], values, 0)
if err != nil {
t.Fatalf("error creating leaf node: %v", err)
}
Expand Down Expand Up @@ -190,3 +191,35 @@ func TestParseNodeSingleSlot(t *testing.T) {
t.Fatalf("invalid commitment, got %x, expected %x", lnd.commitment, ln.commitment)
}
}

func TestParseExpiredLeaf(t *testing.T) {
cfg := GetConfig()
srs := cfg.conf.SRS

comm := srs[0]
stem := ffx32KeyTest[:StemSize]
el := NewExpiredLeafNode(stem, &comm)

serialized, err := el.Serialize()
if err != nil {
t.Fatalf("error serializing expired leaf node: %v", err)
}

deserialized, err := ParseNode(serialized, 0)
if err != nil {
t.Fatalf("error deserializing expired leaf node: %v", err)
}

el2, ok := deserialized.(*ExpiredLeafNode)
if !ok {
t.Fatalf("expected expired leaf node, got %T", deserialized)
}

if !bytes.Equal(el2.stem, stem) {
t.Fatalf("invalid stem, got %x, expected %x", el2.stem, stem)
}

if !el2.commitment.Equal(&comm) {
t.Fatalf("invalid commitment, got %x, expected %x", el2.commitment, comm)
}
}
Loading