diff --git a/benchs/main.go b/benchs/main.go index 529cbec6..19eae14f 100644 --- a/benchs/main.go +++ b/benchs/main.go @@ -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) } } @@ -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) } } diff --git a/config.go b/config.go index 6d92a429..57d8fbee 100644 --- a/config.go +++ b/config.go @@ -35,6 +35,7 @@ const ( NodeWidth = 256 NodeBitWidth byte = 8 StemSize = 31 + periodSize = 2 ) func equalPaths(key1, key2 []byte) bool { diff --git a/conversion.go b/conversion.go index 31c54827..dcffed3f 100644 --- a/conversion.go +++ b/conversion.go @@ -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 } @@ -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 diff --git a/debug.go b/debug.go index 055e200a..4c854d00 100644 --- a/debug.go +++ b/debug.go @@ -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"` } ) diff --git a/debug_test.go b/debug_test.go index 3658e998..fb2e2e61 100644 --- a/debug_test.go +++ b/debug_test.go @@ -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{} diff --git a/doc.go b/doc.go index 258ec2c1..6aa2d553 100644 --- a/doc.go +++ b/doc.go @@ -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 ( diff --git a/empty.go b/empty.go index 2115c9db..342baa03 100644 --- a/empty.go +++ b/empty.go @@ -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() } @@ -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) { diff --git a/empty_test.go b/empty_test.go index e5889f82..c4a2eef2 100644 --- a/empty_test.go +++ b/empty_test.go @@ -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") } @@ -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") } diff --git a/encoding.go b/encoding.go index c0c8c6be..7ac8800f 100644 --- a/encoding.go +++ b/encoding.go @@ -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 { @@ -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: -// - Leaf nodes: -// - EoA nodes: -// - single slot node: +// - Leaf nodes: +// - EoA nodes: +// - single slot node: +// - Expired leaf nodes: 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 { @@ -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 } @@ -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) @@ -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 { @@ -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 @@ -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 @@ -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{}{}) } } diff --git a/encoding_test.go b/encoding_test.go index c7eb53d6..0c7cec99 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -22,7 +22,7 @@ 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) } @@ -30,7 +30,7 @@ func TestLeafStemLength(t *testing.T) { 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) } } @@ -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) } @@ -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) } @@ -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) } @@ -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) + } +} diff --git a/expired_leaf.go b/expired_leaf.go new file mode 100644 index 00000000..7dfbda03 --- /dev/null +++ b/expired_leaf.go @@ -0,0 +1,112 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to + +package verkle + +import ( + "fmt" + "errors" +) + +type ExpiredLeafNode struct { + stem Stem + commitment *Point + depth byte // used for proof only, not commitment calculation +} + +func NewExpiredLeafNode(stem Stem, commitment *Point) *ExpiredLeafNode { + return &ExpiredLeafNode{stem: stem, commitment: commitment} +} + +func (n *ExpiredLeafNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { + return errExpired +} + +func (n *ExpiredLeafNode) Delete([]byte, StatePeriod, NodeResolverFn) (bool, error) { + return false, errExpired +} + +func (n *ExpiredLeafNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { + return nil, errExpired +} + +func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error { + return errors.New("cannot revive an expired leaf node directly") +} + +func (n *ExpiredLeafNode) Commit() *Point { + if n.commitment == nil { + panic("nil commitment") + } + return n.commitment +} + +func (n *ExpiredLeafNode) Commitment() *Point { + return n.commitment +} + +func (n *ExpiredLeafNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) { + return nil, nil, nil, nil, errors.New("cannot get proof items from expired leaf node") +} + +func (n *ExpiredLeafNode) Serialize() ([]byte, error) { + cBytes := n.commitment.BytesUncompressedTrusted() + + var buf [expiredLeafSize]byte + result := buf[:] + result[0] = expiredLeafType + copy(result[leafStemOffset:], n.stem[:StemSize]) + copy(result[leafStemOffset+StemSize:], cBytes[:]) + + return result, nil +} + +func (n *ExpiredLeafNode) Copy() VerkleNode { + l := &ExpiredLeafNode{} + l.stem = make(Stem, len(n.stem)) + l.depth = n.depth + copy(l.stem, n.stem) + if n.commitment != nil { + l.commitment = new(Point).Set(n.commitment) + } + + return l +} + +func (n *ExpiredLeafNode) toDot(parent, path string) string { + var hash Fr + n.Commitment().MapToScalarField(&hash) + return fmt.Sprintf("expired%s [label=\"EL: %x\nC: %x\nStem: %x\"]\n%s -> leaf%s\n", path, hash.Bytes(), n.commitment.Bytes(), n.stem, parent, path) +} + +func (n *ExpiredLeafNode) setDepth(d byte) { + n.depth = d +} + +func (n *ExpiredLeafNode) Hash() *Fr { + var hash Fr + n.commitment.MapToScalarField(&hash) + return &hash +} diff --git a/expired_leaf_test.go b/expired_leaf_test.go new file mode 100644 index 00000000..527dac9a --- /dev/null +++ b/expired_leaf_test.go @@ -0,0 +1,37 @@ +package verkle + +import ( + "errors" + "testing" +) + +func TestExpiredLeafBasic(t *testing.T) { + t.Parallel() + + cfg := GetConfig() + srs := cfg.conf.SRS + comm := srs[0] + leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], &comm) + + err := leaf.Insert(zeroKeyTest, zeroKeyTest, period2, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected period expired error when inserting, got %v", err) + } + + _, err = leaf.Delete(zeroKeyTest, period2, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected period expired error when deleting, got %v", err) + } + + v, err := leaf.Get(zeroKeyTest, period2, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected period expired error when getting, got %v", err) + } + if v != nil { + t.Fatal("expected nil value when getting") + } + + if !leaf.Commitment().Equal(leaf.Commit()) { + t.Fatal("expected commitment and commit to be equal") + } +} diff --git a/expired_tree_test.go b/expired_tree_test.go new file mode 100644 index 00000000..46e7fb08 --- /dev/null +++ b/expired_tree_test.go @@ -0,0 +1,371 @@ +package verkle + +import ( + "bytes" + "errors" + "testing" +) + +func TestInsertSameLeafNoExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + if err := root.Insert(oneKeyTest, testValue, period1, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + leaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + if !bytes.Equal(leaf.values[zeroKeyTest[StemSize]], testValue) { + t.Fatalf("expected value %x, got %x", testValue, leaf.values[zeroKeyTest[StemSize]]) + } + + if !bytes.Equal(leaf.values[oneKeyTest[StemSize]], testValue) { + t.Fatalf("expected value %x, got %x", testValue, leaf.values[oneKeyTest[StemSize]]) + } + + if leaf.period != period1 { + t.Fatalf("expected last accessed to be 1, got %d", leaf.period) + } +} + +func TestInsertSameLeafExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + err := root.Insert(oneKeyTest, testValue, period2, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected period expired error when inserting, got %v", err) + } + + leaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + if !bytes.Equal(leaf.values[zeroKeyTest[StemSize]], testValue) { + t.Fatalf("expected value %x, got %x", testValue, leaf.values[zeroKeyTest[StemSize]]) + } + + if leaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.period) + } +} + +func TestInsertDiffLeaf(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + if err := root.Insert(ffx32KeyTest, testValue, period2, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + leaf0, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + leaff, ok := root.(*InternalNode).children[255].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[255]) + } + + if !bytes.Equal(leaf0.values[zeroKeyTest[StemSize]], testValue) { + t.Fatalf("expected value %x, got %x", testValue, leaf0.values[zeroKeyTest[StemSize]]) + } + + if !bytes.Equal(leaff.values[ffx32KeyTest[StemSize]], testValue) { + t.Fatalf("expected value %x, got %x", testValue, leaff.values[ffx32KeyTest[StemSize]]) + } + + if leaf0.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf0.period) + } + + if leaff.period != period2 { + t.Fatalf("expected last accessed to be 2, got %d", leaff.period) + } +} + +func TestInsertExpiredLeafSibling(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + leaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + root.(*InternalNode).children[0] = NewExpiredLeafNode(leaf.stem, leaf.commitment) + + if err := root.Insert(forkOneKeyTest, testValue, period2, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + c1 := root.Commit() + + // Reconstruct a new tree with the same key-values but without the expired leaf node + root2 := New() + if err := root2.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + if err := root2.Insert(forkOneKeyTest, testValue, period2, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + c2 := root2.Commit() + + // The two trees should have the same commitment + if !c1.Equal(c2) { + t.Fatalf("expected commitment to be %x, got %x", c1, c2) + } +} + +func TestGetNoExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + val, err := root.Get(zeroKeyTest, period1, nil) + if err != nil { + t.Fatalf("error getting: %v", err) + } + + leaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + if !bytes.Equal(val, testValue) { + t.Fatalf("expected value %x, got %x", testValue, val) + } + + if leaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.period) + } +} + +func TestGetExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + val, err := root.Get(zeroKeyTest, period2, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected period expired error when getting, got %v", err) + } + + if val != nil { + t.Fatalf("expected value to be nil, got %x", val) + } + + leaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + if leaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.period) + } +} + +func TestDelLeafNoExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + if _, err := root.Delete(zeroKeyTest, period1, nil); err != nil { + t.Fatalf("error deleting: %v", err) + } + + _, ok := root.(*InternalNode).children[0].(Empty) + if !ok { + t.Fatalf("expected empty node, got %T", root.(*InternalNode).children[0]) + } +} + +func TestDelLeafExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + _, err := root.Delete(zeroKeyTest, period2, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected period expired error when deleting, got %v", err) + } + + leaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected empty node, got %T", root.(*InternalNode).children[0]) + } + + if leaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.period) + } +} + +func TestUpdatePeriod(t *testing.T) { + values := make([][]byte, NodeWidth) + values[0] = testValue + leaf1, err := NewLeafNode(zeroKeyTest, values, period1) + if err != nil { + t.Fatalf("error creating leaf node: %v", err) + } + + leaf1.updatePeriod(period2) + + leaf2, err := NewLeafNode(zeroKeyTest, values, period2) + if err != nil { + t.Fatalf("error creating leaf node: %v", err) + } + + if !leaf1.Commitment().Equal(leaf2.Commitment()) { + t.Fatalf("expected commitment to be %x, got %x", leaf1.Commitment(), leaf2.Commitment()) + } +} + +func TestReviveExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + leaf := root.(*InternalNode).children[0].(*LeafNode) + expiredLeaf := NewExpiredLeafNode(leaf.stem, new(Point).Set(leaf.commitment)) + expiredLeaf.setDepth(1) + root.(*InternalNode).children[0] = expiredLeaf + + if err := root.Revive(leaf.stem, leaf.values, leaf.period, period2, false, nil); err != nil { + t.Fatalf("error reviving: %v", err) + } + comm1 := root.Commit() + + rLeaf := root.(*InternalNode).children[0].(*LeafNode) + if rLeaf.period != period2 { + t.Fatalf("expected last accessed to be 2, got %d", rLeaf.period) + } + + // Create a new root and insert the same key-value with the post-revive period + root2 := New() + if err := root2.Insert(zeroKeyTest, testValue, period2, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + comm2 := root2.Commit() + + if !comm1.Equal(comm2) { + t.Fatalf("expected commitment to be %x, got %x", comm1, comm2) + } +} + +func TestReviveNoExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + leaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + comm := root.Commit() + + if err := root.Revive(leaf.stem, leaf.values, leaf.period, period0, false, nil); err != nil { + t.Fatalf("error reviving: %v", err) + } + + rLeaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + if rLeaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", rLeaf.period) + } + + rComm := root.Commit() + if !rComm.Equal(comm) { + t.Fatalf("expected commitment to be %x, got %x", comm, rComm) + } +} + +func TestRootCommitExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + leaf, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + var init Point + init.Set(root.Commit()) + + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + root.(*InternalNode).children[0] = expiredLeaf + + comm := root.Commit() + if !comm.Equal(&init) { + t.Fatalf("expected commitment to be %x, got %x", &init, comm) + } +} + +func TestRootCommitDiffEpoch(t *testing.T) { + t.Parallel() + + root1 := New() + if err := root1.Insert(zeroKeyTest, testValue, period0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + comm1 := root1.Commit() + + root2 := New() + if err := root2.Insert(zeroKeyTest, testValue, period2, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + comm2 := root2.Commit() + + if comm1.Equal(comm2) { + t.Fatalf("expected different commitments, got %x", comm1) + } +} \ No newline at end of file diff --git a/hashednode.go b/hashednode.go index bd53c9dc..4ed7b062 100644 --- a/hashednode.go +++ b/hashednode.go @@ -32,18 +32,22 @@ import ( type HashedNode struct{} -func (HashedNode) Insert([]byte, []byte, NodeResolverFn) error { +func (HashedNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { return errInsertIntoHash } -func (HashedNode) Delete([]byte, NodeResolverFn) (bool, error) { +func (HashedNode) Delete([]byte, StatePeriod, NodeResolverFn) (bool, error) { return false, errors.New("cant delete a hashed node in-place") } -func (HashedNode) Get([]byte, NodeResolverFn) ([]byte, error) { +func (HashedNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, errors.New("can not read from a hash node") } +func (HashedNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error { + return errors.New("cannot revive a hashed node") +} + func (HashedNode) Commit() *Point { // TODO: we should reconsider what to do with the VerkleNode interface and how // HashedNode fits into the picture, since Commit(), Commitment() and Hash() @@ -58,8 +62,8 @@ func (HashedNode) Commitment() *Point { panic("can not get commitment of a hash node") } -func (HashedNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { - return nil, nil, nil, errors.New("can not get the full path, and there is no proof of absence") +func (HashedNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) { + return nil, nil, nil, nil, errors.New("can not get the full path, and there is no proof of absence") } func (HashedNode) Serialize() ([]byte, error) { diff --git a/hashednode_test.go b/hashednode_test.go index c2312246..c4ccece6 100644 --- a/hashednode_test.go +++ b/hashednode_test.go @@ -31,22 +31,22 @@ func TestHashedNodeFuncs(t *testing.T) { t.Parallel() e := HashedNode{} - err := e.Insert(zeroKeyTest, zeroKeyTest, nil) + err := e.Insert(zeroKeyTest, zeroKeyTest, 0, nil) if err != errInsertIntoHash { t.Fatal("got nil error when inserting into a hashed node") } - _, err = e.Delete(zeroKeyTest, nil) + _, err = e.Delete(zeroKeyTest, 0, nil) if err == nil { t.Fatal("got nil error when deleting from a hashed node") } - v, err := e.Get(zeroKeyTest, nil) + v, err := e.Get(zeroKeyTest, 0, nil) if err == nil { t.Fatal("got nil error when getting from a hashed node") } if v != nil { t.Fatal("non-nil get from a hashed node") } - if _, _, _, err := e.GetProofItems(nil, nil); err == nil { + if _, _, _, _, err := e.GetProofItems(nil, nil); err == nil { t.Fatal("got nil error when getting proof items from a hashed node") } if _, err := e.Serialize(); err != errSerializeHashedNode { diff --git a/period.go b/period.go new file mode 100644 index 00000000..35edec5d --- /dev/null +++ b/period.go @@ -0,0 +1,22 @@ +package verkle + +import ( + "encoding/binary" +) + +type StatePeriod uint16 + +const ( + NumActiveEpochs = 2 + period0 = StatePeriod(0) + period1 = StatePeriod(1) + period2 = StatePeriod(2) +) + +func IsExpired(prev, cur StatePeriod) bool { + return cur > prev && cur - prev >= NumActiveEpochs +} + +func StatePeriodFromBytes(b []byte) StatePeriod { + return StatePeriod(binary.BigEndian.Uint16(b)) +} diff --git a/proof_ipa.go b/proof_ipa.go index 15d4e8a8..69c444d6 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -113,6 +113,7 @@ type Proof struct { Keys [][]byte PreValues [][]byte PostValues [][]byte + PrePeriods []StatePeriod } type SuffixStateDiff struct { @@ -126,6 +127,7 @@ type SuffixStateDiffs []SuffixStateDiff type StemStateDiff struct { Stem [StemSize]byte `json:"stem"` SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"` + PrePeriod StatePeriod `json:"prePeriod,omitempty"` } type StateDiff []StemStateDiff @@ -135,6 +137,7 @@ func (sd StateDiff) Copy() StateDiff { for i := range sd { copy(ret[i].Stem[:], sd[i].Stem[:]) ret[i].SuffixDiffs = make([]SuffixStateDiff, len(sd[i].SuffixDiffs)) + ret[i].PrePeriod = sd[i].PrePeriod for j := range sd[i].SuffixDiffs { ret[i].SuffixDiffs[j].Suffix = sd[i].SuffixDiffs[j].Suffix if sd[i].SuffixDiffs[j].CurrentValue != nil { @@ -184,7 +187,7 @@ func (sd StateDiff) Equal(other StateDiff) error { return nil } -func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) { sort.Sort(keylist(keys)) return root.GetProofItems(keylist(keys), resolver) } @@ -192,16 +195,16 @@ func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeRe // getProofElementsFromTree factors the logic that is used both in the proving and verification methods. It takes a pre-state // tree and an optional post-state tree, extracts the proof data from them and returns all the items required to build/verify // a proof. -func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, error) { +func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, postPeriod StatePeriod, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, []StatePeriod, error) { // go-ipa won't accept no key as an input, catch this corner case // and return an empty result. if len(keys) == 0 { - return nil, nil, nil, nil, errors.New("no key provided for proof") + return nil, nil, nil, nil, nil, errors.New("no key provided for proof") } - pe, es, poas, err := GetCommitmentsForMultiproof(preroot, keys, resolver) + pe, es, poas, ps, err := GetCommitmentsForMultiproof(preroot, keys, resolver) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting pre-state proof data: %w", err) + return nil, nil, nil, nil, nil, fmt.Errorf("error getting pre-state proof data: %w", err) } // if a post-state tree is present, merge its proof elements with @@ -211,9 +214,9 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, resol // keys were sorted already in the above GetcommitmentsForMultiproof. // Set the post values, if they are untouched, leave them `nil` for i := range keys { - val, err := postroot.Get(keys[i], resolver) + val, err := postroot.Get(keys[i], postPeriod, resolver) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting post-state value for key %x: %w", keys[i], err) + return nil, nil, nil, nil, nil, fmt.Errorf("error getting post-state value for key %x: %w", keys[i], err) } if !bytes.Equal(pe.Vals[i], val) { postvals[i] = val @@ -223,11 +226,12 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, resol // [0:3]: proof elements of the pre-state trie for serialization, // 3: values to be inserted in the post-state trie for serialization - return pe, es, poas, postvals, nil + // 4: pre-state periods of each stem + return pe, es, poas, postvals, ps, nil } -func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { - pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, resolver) +func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, postPeriod StatePeriod, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { + pe, es, poas, postvals, ps, err := getProofElementsFromTree(preroot, postroot, keys, postPeriod, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("get commitments for multiproof: %s", err) } @@ -264,13 +268,14 @@ func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, resolver Keys: keys, PreValues: pe.Vals, PostValues: postvals, + PrePeriods: ps, } return proof, pe.Cis, pe.Zis, pe.Yis, nil } // verifyVerkleProofWithPreState takes a proof and a trusted tree root and verifies that the proof is valid. func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode) error { - pe, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, nil) + pe, _, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, period0, nil) if err != nil { return fmt.Errorf("error getting proof elements: %w", err) } @@ -316,6 +321,7 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { var stemdiff *StemStateDiff var statediff StateDiff + for i, key := range proof.Keys { stem := KeyToStem(key) if stemdiff == nil || !bytes.Equal(stemdiff.Stem[:], stem) { @@ -323,6 +329,7 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { stemdiff = &statediff[len(statediff)-1] copy(stemdiff.Stem[:], stem) } + stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{Suffix: key[StemSize]}) newsd := &stemdiff.SuffixDiffs[len(stemdiff.SuffixDiffs)-1] @@ -375,6 +382,7 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { extStatus []byte commitments []*Point multipoint ipa.MultiProof + prePeriods []StatePeriod ) poaStems = make([]Stem, len(vp.OtherStems)) @@ -430,6 +438,8 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { postvalues = append(postvalues, nil) } } + + prePeriods = append(prePeriods, stemdiff.PrePeriod) } proof := Proof{ @@ -440,6 +450,7 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { keys, prevalues, postvalues, + prePeriods, } return &proof, nil } @@ -450,6 +461,7 @@ type stemInfo struct { has_c1, has_c2 bool values map[byte][]byte stem []byte + period StatePeriod } // PreStateTreeFromProof builds a stateless prestate tree from the proof. @@ -475,6 +487,7 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // paths [][]byte err error poas = proof.PoaStems + ps = proof.PrePeriods ) // The proof of absence stems must be sorted. If that isn't the case, the proof is invalid. @@ -541,9 +554,12 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // if bytes.Equal(KeyToStem(k), si.stem) { si.values[k[StemSize]] = proof.PreValues[j] si.has_c1 = si.has_c1 || (k[StemSize] < 128) + si.has_c2 = si.has_c2 || (k[StemSize] >= 128) } } + si.period = ps[0] + ps = ps[1:] default: return nil, fmt.Errorf("invalid extension status: %d", si.stemType) } @@ -585,7 +601,7 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // // PostStateTreeFromProof uses the pre-state trie and the list of updated values // to produce the stateless post-state trie. -func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff) (VerkleNode, error) { +func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postPeriod StatePeriod) (VerkleNode, error) { postroot := preroot.Copy() for _, stemstatediff := range statediff { @@ -606,7 +622,7 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff) (Verkle if overwrites { var stem [StemSize]byte copy(stem[:StemSize], stemstatediff.Stem[:]) - if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, nil); err != nil { + if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postPeriod, true, nil); err != nil { return nil, fmt.Errorf("error overwriting value in post state: %w", err) } } @@ -623,8 +639,7 @@ func (x bytesSlice) Less(i, j int) bool { return bytes.Compare(x[i], x[j]) < 0 } func (x bytesSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] } // Verify is the API function that verifies a verkle proofs as found in a block/execution payload. -func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statediff StateDiff) error { - +func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statediff StateDiff, postPeriod StatePeriod) error { proof, err := DeserializeProof(vp, statediff) if err != nil { return fmt.Errorf("verkle proof deserialization error: %w", err) @@ -646,7 +661,7 @@ func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statedif copy(key[:31], stemdiff.Stem[:]) key[31] = suffixdiff.Suffix - val, err := pretree.Get(key[:], nil) + val, err := pretree.Get(key[:], stemdiff.PrePeriod, nil) if err != nil { return fmt.Errorf("could not find key %x in tree rebuilt from proof: %w", key, err) } @@ -666,7 +681,7 @@ func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statedif // But all this can be avoided with a even faster way. The EVM block execution can // keep track of the written keys, and compare that list with this post-values list. // This can avoid regenerating the post-tree which is somewhat expensive. - posttree, err := PostStateTreeFromStateDiff(pretree, statediff) + posttree, err := PostStateTreeFromStateDiff(pretree, statediff, postPeriod) if err != nil { return fmt.Errorf("error rebuilding the post-tree from proof: %w", err) } diff --git a/proof_json.go b/proof_json.go index 7828697e..76e7ebb7 100644 --- a/proof_json.go +++ b/proof_json.go @@ -186,12 +186,14 @@ func (vp *VerkleProof) UnmarshalJSON(data []byte) error { type stemStateDiffMarshaller struct { Stem string `json:"stem"` SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"` + PrePeriod StatePeriod `json:"prePeriod"` } func (ssd StemStateDiff) MarshalJSON() ([]byte, error) { return json.Marshal(&stemStateDiffMarshaller{ Stem: HexToPrefixedString(ssd.Stem[:]), SuffixDiffs: ssd.SuffixDiffs, + PrePeriod: ssd.PrePeriod, }) } @@ -207,6 +209,7 @@ func (ssd *StemStateDiff) UnmarshalJSON(data []byte) error { } *ssd = StemStateDiff{ SuffixDiffs: aux.SuffixDiffs, + PrePeriod: aux.PrePeriod, } copy(ssd.Stem[:], stem) return nil diff --git a/proof_test.go b/proof_test.go index 5ee7680f..58a1d2ba 100644 --- a/proof_test.go +++ b/proof_test.go @@ -42,7 +42,7 @@ func TestProofEmptyTree(t *testing.T) { root := New() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { t.Fatalf("could not verify verkle proof: %s", ToDot(root)) @@ -53,18 +53,18 @@ func TestProofVerifyTwoLeaves(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatal(err) } - if err := root.Insert(oneKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(oneKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - if err := root.Insert(ffx32KeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(ffx32KeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -85,13 +85,13 @@ func TestProofVerifyMultipleLeaves(t *testing.T) { t.Fatalf("could not read random bytes: %v", err) } keys[i] = key - if err := root.Insert(key, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -112,15 +112,15 @@ func TestMultiProofVerifyMultipleLeaves(t *testing.T) { t.Fatalf("could not read random bytes: %v", err) } keys[i] = key - if err := root.Insert(key, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], period0, nil) - pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) + pe, _, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) if err != nil { t.Fatal(err) } @@ -141,7 +141,7 @@ func TestMultiProofVerifyMultipleLeavesWithAbsentStem(t *testing.T) { for i := 0; i < leafCount; i++ { key := make([]byte, 32) key[2] = byte(i) - if err := root.Insert(key, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } if i%2 == 0 { @@ -158,9 +158,9 @@ func TestMultiProofVerifyMultipleLeavesWithAbsentStem(t *testing.T) { absent[3] = 1 // and the stem differs keys = append(keys, absent) - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) - pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, nil) + pe, _, isabsent, _, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { t.Fatal(err) } @@ -183,18 +183,18 @@ func TestMultiProofVerifyMultipleLeavesCommitmentRedundancy(t *testing.T) { keys := make([][]byte, 2) root := New() keys[0] = zeroKeyTest - if err := root.Insert(keys[0], fourtyKeyTest, nil); err != nil { + if err := root.Insert(keys[0], fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } keys[1] = oneKeyTest - if err := root.Insert(keys[1], fourtyKeyTest, nil); err != nil { + if err := root.Insert(keys[1], fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) - pe, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) + pe, _, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { t.Fatal(err) } @@ -208,15 +208,15 @@ func TestProofOfAbsenceInternalVerify(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - if err := root.Insert(oneKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(oneKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -228,29 +228,30 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - if err := root.Insert(ffx32KeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(ffx32KeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify verkle proof") } } + func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - if err := root.Insert(ffx32KeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(ffx32KeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() @@ -260,7 +261,7 @@ func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { return ret }() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -272,7 +273,7 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } @@ -282,7 +283,7 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { }() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -299,7 +300,7 @@ func BenchmarkProofCalculation(b *testing.B) { b.Fatal(err) } keys[i] = key - if err := root.Insert(key, zeroKeyTest, nil); err != nil { + if err := root.Insert(key, zeroKeyTest, period0, nil); err != nil { b.Fatal(err) } } @@ -308,7 +309,7 @@ func BenchmarkProofCalculation(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - if _, _, _, _, err := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, nil); err != nil { + if _, _, _, _, err := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, nil); err != nil { b.Fatal(err) } } @@ -323,13 +324,13 @@ func BenchmarkProofVerification(b *testing.B) { b.Fatal(err) } keys[i] = key - if err := root.Insert(key, zeroKeyTest, nil); err != nil { + if err := root.Insert(key, zeroKeyTest, period0, nil); err != nil { b.Fatal(err) } } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, nil) b.ResetTimer() b.ReportAllocs() @@ -355,12 +356,12 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { t.Fatalf("could not read random bytes: %v", err) } keys[i] = key - if err := root.Insert(key, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -386,7 +387,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { key := make([]byte, 32) key[2] = byte(i) keys[i] = key - if err := root.Insert(key, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -399,7 +400,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -427,7 +428,7 @@ func TestProofDeserialize(t *testing.T) { key := make([]byte, 32) key[2] = byte(i) keys[i] = key - if err := root.Insert(key, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -440,7 +441,7 @@ func TestProofDeserialize(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -453,7 +454,7 @@ func TestProofDeserialize(t *testing.T) { } _ = deserialized - pe, _, _, err := root.GetProofItems(keylist{absentkey[:]}, nil) + pe, _, _, _, err := root.GetProofItems(keylist{absentkey[:]}, nil) if err != nil { t.Fatal(err) } @@ -470,7 +471,7 @@ func TestProofOfAbsenceEdgeCase(t *testing.T) { root.Commit() ret, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030303") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -484,14 +485,14 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { // but does look the same for most of its length. root := New() key, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030000") - if err := root.Insert(key, testValue, nil); err != nil { + if err := root.Insert(key, testValue, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } rootC := root.Commit() ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030301") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -506,7 +507,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { t.Fatalf("error deserializing %v", err) } - got, err := deserialized.Get(ret1, nil) + got, err := deserialized.Get(ret1, period0, nil) if err != nil { t.Fatalf("error while trying to read missing value: %v", err) } @@ -518,7 +519,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { // proven for absence, but needs to be inserted in the proof-of-absence stem. // It differs from the poa stem here: 🠃 ret3, _ := hex.DecodeString("0303030304030303030303030303030303030303030303030303030303030300") - err = deserialized.Insert(ret3, testValue, nil) + err = deserialized.Insert(ret3, testValue, period0, nil) if err != nil { t.Fatalf("error inserting value in proof-of-asbsence stem: %v", err) } @@ -546,14 +547,14 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) { root := New() key, _ := hex.DecodeString("0403030303030303030303030303030303030303030303030303030303030000") - if err := root.Insert(key, testValue, nil); err != nil { + if err := root.Insert(key, testValue, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030200") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -624,9 +625,10 @@ func TestStemStateDiffJSONMarshalUn(t *testing.T) { 0xDD, 0xEE, 0xFF, 0x00, }, }}, + PrePeriod: StatePeriod(100), } - expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}]}` + expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}],"prePeriod":100}` actualJSON, err := json.Marshal(ssd) if err != nil { t.Errorf("error marshalling SuffixStateDiff to JSON: %v", err) @@ -842,7 +844,7 @@ func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, pr root := New() for k, v := range insertKVs { - if err := root.Insert([]byte(k), v, nil); err != nil { + if err := root.Insert([]byte(k), v, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -862,7 +864,7 @@ func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, pr proveKVs[string(key)] = value } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -997,7 +999,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 root := New() for i := range data.keys { - if err := root.Insert(data.keys[i], data.values[i], nil); err != nil { + if err := root.Insert(data.keys[i], data.values[i], period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -1005,13 +1007,13 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 postroot := root.Copy() for i := range data.updatekeys { - if err := postroot.Insert(data.updatekeys[i], data.updatevalues[i], nil); err != nil { + if err := postroot.Insert(data.updatekeys[i], data.updatevalues[i], period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } postroot.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, period0, nil) keys: for i := range proof.Keys { @@ -1059,7 +1061,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 t.Fatalf("error recreating pre tree: %v", err) } - dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff) + dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff, 0) if err != nil { t.Fatalf("error recreating post tree: %v", err) } @@ -1075,20 +1077,90 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 } } +// TODO(weiihann): add more test cases similar to TestProofVerificationWithPostState +func TestProofVerificationPreStateExpiredPostStateRevived(t *testing.T) { + t.Parallel() + + prePeriod := period0 + postPeriod := period2 + + preRoot := New() + if err := preRoot.Insert(zeroKeyTest, zeroKeyTest, prePeriod, nil); err != nil { + t.Fatalf("could not insert key: %v", err) + } + preReviveRoot := new(Point).Set(preRoot.Commit()) + + leaf := preRoot.(*InternalNode).children[0].(*LeafNode) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf.setDepth(1) + preRoot.(*InternalNode).children[0] = expiredLeaf + + if err := preRoot.Revive(leaf.stem, leaf.values, prePeriod, postPeriod, true, nil); err != nil { + t.Fatalf("error reviving leaf node: %v", err) + } + + postRoot := New() + if err := postRoot.Insert(zeroKeyTest, fourtyKeyTest, postPeriod, nil); err != nil { + t.Fatalf("could not insert key: %v", err) + } + + proof, _, _, _, err := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, postPeriod, nil) + if err != nil { + t.Fatalf("error making proof: %v", err) + } + + p, diff, err := SerializeProof(proof) + if err != nil { + t.Fatalf("error serializing proof: %v", err) + } + + dproof, err := DeserializeProof(p, diff) + if err != nil { + t.Fatalf("error deserializing proof: %v", err) + } + + if err = verifyVerkleProofWithPreState(dproof, preRoot); err != nil { + t.Fatalf("could not verify verkle proof: %v", err) + } + + dpreroot, err := PreStateTreeFromProof(dproof, preReviveRoot) + if err != nil { + t.Fatalf("error recreating pre tree: %v", err) + } + + dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff, postPeriod) + if err != nil { + t.Fatalf("error recreating post tree: %v", err) + } + + if err = verifyVerkleProofWithPreState(dproof, dpreroot); err != nil { + t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) + } + + got, err := dpostroot.Get(zeroKeyTest, postPeriod, nil) + if err != nil { + t.Fatalf("error getting key: %v", err) + } + + if !bytes.Equal(got, fourtyKeyTest) { + t.Fatalf("value mismatch for key %x: %x != %x", zeroKeyTest, got, fourtyKeyTest) + } +} + func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { t.Parallel() // Create a tree with only one key. root := New() presentKey, _ := hex.DecodeString("4000000000000000000000000000000000000000000000000000000000000000") - if err := root.Insert(presentKey, zeroKeyTest, nil); err != nil { + if err := root.Insert(presentKey, zeroKeyTest, period0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } root.Commit() // Create a proof with a key with the same first byte, but different second byte (i.e: absent). absentKey, _ := hex.DecodeString("4010000000000000000000000000000000000000000000000000000000000000") - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, keylist{absentKey}, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, keylist{absentKey}, period0, nil) if err != nil { t.Fatal(err) } @@ -1113,7 +1185,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { } // From the rebuilt tree, validate the proof. - pe, _, _, err := GetCommitmentsForMultiproof(droot, keylist{absentKey}, nil) + pe, _, _, _, err := GetCommitmentsForMultiproof(droot, keylist{absentKey}, nil) if err != nil { t.Fatal(err) } @@ -1131,7 +1203,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { var key [32]byte copy(key[:], presentKey) key[StemSize] = byte(i) - if _, err := droot.Get(key[:], nil); err != errIsPOAStub { + if _, err := droot.Get(key[:], period0, nil); err != errIsPOAStub { t.Fatalf("expected ErrPOALeafValue, got %v", err) } } @@ -1142,7 +1214,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { var key [32]byte copy(key[:], presentKey) key[StemSize] = byte(i) - if err := droot.Insert(key[:], zeroKeyTest, nil); err != errIsPOAStub { + if err := droot.Insert(key[:], zeroKeyTest, period0, nil); err != errIsPOAStub { t.Fatalf("expected ErrPOALeafValue, got %v", err) } } @@ -1157,10 +1229,10 @@ func TestDoubleProofOfAbsence(t *testing.T) { key11, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000001") key12, _ := hex.DecodeString("0003000000000000000000000000000000000000000000000000000000000001") - if err := root.Insert(key11, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key11, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - if err := root.Insert(key12, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key12, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } @@ -1168,7 +1240,7 @@ func TestDoubleProofOfAbsence(t *testing.T) { // in that leaf node. i.e: two proof of absence in the same leaf node with no proof of presence. key2, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000100") key3, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000200") - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1208,16 +1280,16 @@ func TestProveAbsenceInEmptyHalf(t *testing.T) { key1, _ := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000FF") - if err := root.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key1, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - if err := root.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key1, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } key2, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000100") key3, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { diff --git a/tree.go b/tree.go index b62e732c..db1a7d1e 100644 --- a/tree.go +++ b/tree.go @@ -27,6 +27,7 @@ package verkle import ( "bytes" + "encoding/binary" "encoding/hex" "encoding/json" "errors" @@ -67,13 +68,13 @@ func KeyToStem(key []byte) Stem { type VerkleNode interface { // Insert or Update value into the tree - Insert([]byte, []byte, NodeResolverFn) error + Insert([]byte, []byte, StatePeriod, NodeResolverFn) error // Delete a leaf with the given key - Delete([]byte, NodeResolverFn) (bool, error) + Delete([]byte, StatePeriod, NodeResolverFn) (bool, error) // Get value at a given key - Get([]byte, NodeResolverFn) ([]byte, error) + Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) // Commit computes the commitment of the node. The // result (the curve point) is cached. @@ -90,7 +91,7 @@ type VerkleNode interface { // returns them breadth-first. On top of that, it returns // one "extension status" per stem, and an alternate stem // if the key is missing but another stem has been found. - GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) + GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) // Serialize encodes the node to RLP. Serialize() ([]byte, error) @@ -98,6 +99,8 @@ type VerkleNode interface { // Copy a node and its children Copy() VerkleNode + Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error + // toDot returns a string representing this subtree in DOT language toDot(string, string) string @@ -166,10 +169,11 @@ func (pe *ProofElements) Merge(other *ProofElements) { const ( // These types will distinguish internal // and leaf nodes when decoding from RLP. - internalType byte = 1 - leafType byte = 2 - eoAccountType byte = 3 - singleSlotType byte = 4 + internalType byte = 1 + leafType byte = 2 + eoAccountType byte = 3 + singleSlotType byte = 4 + expiredLeafType byte = 5 ) type ( @@ -200,6 +204,8 @@ type ( // for a steam that isn't present in the tree. This flag is only // true in the context of a stateless tree. isPOAStub bool + + period StatePeriod // the last accessed period } ) @@ -224,6 +230,12 @@ func (n *InternalNode) toExportable() *ExportableInternalNode { Values: child.values, C: child.commitment.Bytes(), C1: child.c1.Bytes(), + Period: child.period, + } + case *ExpiredLeafNode: + exportable.Children[i] = &ExportableExpiredLeafNode{ + Stem: child.stem, + Commitment: child.commitment.Bytes(), } default: panic("unexportable type") @@ -266,7 +278,7 @@ func NewStatelessInternal(depth byte, comm *Point) VerkleNode { } // New creates a new leaf node -func NewLeafNode(stem Stem, values [][]byte) (*LeafNode, error) { +func NewLeafNode(stem Stem, values [][]byte, period StatePeriod) (*LeafNode, error) { cfg := GetConfig() // C1. @@ -309,26 +321,30 @@ func NewLeafNode(stem Stem, values [][]byte) (*LeafNode, error) { return nil, fmt.Errorf("batch mapping to scalar fields: %s", err) } + poly[4].SetUint64(uint64(period)) + return &LeafNode{ // depth will be 0, but the commitment calculation // does not need it, and so it won't be free. values: values, stem: stem, - commitment: cfg.CommitToPoly(poly[:], NodeWidth-4), + commitment: cfg.CommitToPoly(poly[:], NodeWidth-5), c1: c1, c2: c2, + period: period, }, nil } // NewLeafNodeWithNoComms create a leaf node but does not compute its // commitments. The created node's commitments are intended to be // initialized with `SetTrustedBytes` in a deserialization context. -func NewLeafNodeWithNoComms(stem Stem, values [][]byte) *LeafNode { +func NewLeafNodeWithNoComms(stem Stem, values [][]byte, period StatePeriod) *LeafNode { return &LeafNode{ // depth will be 0, but the commitment calculation // does not need it, and so it won't be free. values: values, stem: stem, + period: period, } } @@ -358,13 +374,13 @@ func (n *InternalNode) cowChild(index byte) { } } -func (n *InternalNode) Insert(key []byte, value []byte, resolver NodeResolverFn) error { +func (n *InternalNode) Insert(key []byte, value []byte, curPeriod StatePeriod, resolver NodeResolverFn) error { values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.InsertValuesAtStem(KeyToStem(key), values, resolver) + return n.InsertValuesAtStem(KeyToStem(key), values, curPeriod, false,resolver) } -func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, resolver NodeResolverFn) error { +func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod StatePeriod, skipPeriodCheck bool, resolver NodeResolverFn) error { nChild := offset2key(stem, n.depth) // index of the child pointed by the next byte in the key switch child := n.children[nChild].(type) { @@ -373,7 +389,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, resolver N case Empty: n.cowChild(nChild) var err error - n.children[nChild], err = NewLeafNode(stem, values) + n.children[nChild], err = NewLeafNode(stem, values, curPeriod) if err != nil { return err } @@ -394,7 +410,32 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, resolver N n.cowChild(nChild) // recurse to handle the case of a LeafNode child that // splits. - return n.InsertValuesAtStem(stem, values, resolver) + return n.InsertValuesAtStem(stem, values, curPeriod, skipPeriodCheck, resolver) + case *ExpiredLeafNode: + // If the stem is the same as the expired leaf node, we can't insert + if equalPaths(child.stem, stem) { + return errExpired + } + // Otherwise, insert a branch node + nextWordInExistingKey := offset2key(child.stem, n.depth+1) + newBranch := newInternalNode(n.depth + 1).(*InternalNode) + newBranch.cowChild(nextWordInExistingKey) + n.children[nChild] = newBranch + newBranch.children[nextWordInExistingKey] = child + child.depth += 1 + + nextWordInInsertedKey := offset2key(stem, n.depth+1) + if nextWordInInsertedKey == nextWordInExistingKey { + return newBranch.InsertValuesAtStem(stem, values, curPeriod, skipPeriodCheck, resolver) + } + + leaf, err := NewLeafNode(stem, values, curPeriod) + if err != nil { + return err + } + leaf.setDepth(n.depth + 2) + newBranch.cowChild(nextWordInInsertedKey) + newBranch.children[nextWordInInsertedKey] = leaf case *LeafNode: if equalPaths(child.stem, stem) { // We can't insert any values into a POA leaf node. @@ -402,7 +443,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, resolver N return errIsPOAStub } n.cowChild(nChild) - return child.insertMultiple(stem, values) + return child.insertMultiple(stem, values, curPeriod, skipPeriodCheck) } n.cowChild(nChild) @@ -418,12 +459,12 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, resolver N nextWordInInsertedKey := offset2key(stem, n.depth+1) if nextWordInInsertedKey == nextWordInExistingKey { - return newBranch.InsertValuesAtStem(stem, values, resolver) + return newBranch.InsertValuesAtStem(stem, values, curPeriod, skipPeriodCheck, resolver) } // Next word differs, so this was the last level. // Insert it directly into its final slot. - leaf, err := NewLeafNode(stem, values) + leaf, err := NewLeafNode(stem, values, curPeriod) if err != nil { return err } @@ -432,7 +473,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, resolver N newBranch.children[nextWordInInsertedKey] = leaf case *InternalNode: n.cowChild(nChild) - return child.InsertValuesAtStem(stem, values, resolver) + return child.InsertValuesAtStem(stem, values, curPeriod, skipPeriodCheck, resolver) default: // It should be an UknownNode. return errUnknownNodeType } @@ -440,6 +481,60 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, resolver N return nil } +func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, skipUpdate bool,resolver NodeResolverFn) error { + if oldPeriod > curPeriod { + return errRevivePeriodOlderThanNew + } + + nChild := offset2key(stem, n.depth) + + switch child := n.children[nChild].(type) { + case UnknownNode: + return errMissingNodeInStateless + case Empty: + // TODO(weiihann): double confirm this + return errors.New("cannot revive an empty node") + case HashedNode: + if resolver == nil { + return errInsertIntoHash + } + serialized, err := resolver(stem[:n.depth+1]) + if err != nil { + return fmt.Errorf("verkle tree: error resolving node %x at depth %d: %w", stem, n.depth, err) + } + resolved, err := ParseNode(serialized, n.depth+1) + if err != nil { + return fmt.Errorf("verkle tree: error parsing resolved node %x: %w", stem, err) + } + n.children[nChild] = resolved + n.cowChild(nChild) + return n.Revive(stem, values, oldPeriod, curPeriod, skipUpdate, resolver) + case *ExpiredLeafNode: + leaf, err := NewLeafNode(stem, values, oldPeriod) + if err != nil { + return err + } + + // Verify commitment matches + if !child.Commitment().Equal(leaf.Commitment()) { + return errReviveCommitmentMismatch + } + + leaf.setDepth(n.depth + 1) + if !skipUpdate { + leaf.updatePeriod(curPeriod) + } + n.children[nChild] = leaf + n.cowChild(nChild) + + return nil + case *LeafNode: + return child.Revive(stem, values, oldPeriod, curPeriod, skipUpdate, resolver) + } + + return nil +} + // CreatePath inserts a given stem in the tree, placing it as // described by stemInfo. Its third parameters is the list of // commitments that have not been assigned a node. It returns @@ -487,6 +582,7 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point commitment: comms[0], stem: stemInfo.stem, values: values, + period: stemInfo.period, depth: n.depth + 1, } n.children[path[0]] = newchild @@ -525,7 +621,7 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point comms = comms[1:] case *InternalNode: // nothing else to do - case *LeafNode: + case *LeafNode, *ExpiredLeafNode: return comms, fmt.Errorf("error rebuilding the tree from a proof: stem %x leads to an already-existing leaf node at depth %x", stemInfo.stem, n.depth) default: return comms, fmt.Errorf("error rebuilding the tree from a proof: stem %x leads to an unsupported node type %v", stemInfo.stem, child) @@ -543,7 +639,7 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point // GetValuesAtStem returns the all NodeWidth values of the stem. // The returned slice is internal to the tree, so it *must* be considered readonly // for callers. -func (n *InternalNode) GetValuesAtStem(stem Stem, resolver NodeResolverFn) ([][]byte, error) { +func (n *InternalNode) GetValuesAtStem(stem Stem, curPeriod StatePeriod, resolver NodeResolverFn) ([][]byte, error) { nchild := offset2key(stem, n.depth) // index of the child pointed by the next byte in the key switch child := n.children[nchild].(type) { case UnknownNode: @@ -565,8 +661,14 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, resolver NodeResolverFn) ([][] n.children[nchild] = resolved // recurse to handle the case of a LeafNode child that // splits. - return n.GetValuesAtStem(stem, resolver) + return n.GetValuesAtStem(stem, curPeriod, resolver) + case *ExpiredLeafNode: + return nil, errExpired case *LeafNode: + if child.isExpired(curPeriod) { + return nil, errExpired + } + if equalPaths(child.stem, stem) { // We can't return the values since it's a POA leaf node, so we know nothing // about its values. @@ -577,13 +679,13 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, resolver NodeResolverFn) ([][] } return nil, nil case *InternalNode: - return child.GetValuesAtStem(stem, resolver) + return child.GetValuesAtStem(stem, curPeriod, resolver) default: return nil, errUnknownNodeType } } -func (n *InternalNode) Delete(key []byte, resolver NodeResolverFn) (bool, error) { +func (n *InternalNode) Delete(key []byte, curPeriod StatePeriod, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { case Empty: @@ -602,10 +704,12 @@ func (n *InternalNode) Delete(key []byte, resolver NodeResolverFn) (bool, error) return false, err } n.children[nChild] = c - return n.Delete(key, resolver) + return n.Delete(key, curPeriod, resolver) + case *ExpiredLeafNode: + return false, errExpired default: n.cowChild(nChild) - del, err := child.Delete(key, resolver) + del, err := child.Delete(key, curPeriod, resolver) if err != nil { return false, err } @@ -634,7 +738,7 @@ func (n *InternalNode) Delete(key []byte, resolver NodeResolverFn) (bool, error) // DeleteAtStem delete a full stem. Unlike Delete, it will error out if the stem that is to // be deleted does not exist in the tree, because it's meant to be used by rollback code, // that should only delete things that exist. -func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, error) { +func (n *InternalNode) DeleteAtStem(key []byte, curPeriod StatePeriod, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { case Empty: @@ -653,12 +757,18 @@ func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, return false, err } n.children[nChild] = c - return n.DeleteAtStem(key, resolver) + return n.DeleteAtStem(key, curPeriod, resolver) + case *ExpiredLeafNode: + return false, errExpired case *LeafNode: if !bytes.Equal(child.stem, key[:31]) { return false, errDeleteMissing } + if child.isExpired(curPeriod) { + return false, errExpired + } + n.cowChild(nChild) n.children[nChild] = Empty{} @@ -674,7 +784,7 @@ func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, return true, nil case *InternalNode: n.cowChild(nChild) - del, err := child.DeleteAtStem(key, resolver) + del, err := child.DeleteAtStem(key, curPeriod, resolver) if err != nil { return false, err } @@ -763,11 +873,11 @@ func (n *InternalNode) FlushAtDepth(depth uint8, flush NodeFlushFn) { } } -func (n *InternalNode) Get(key []byte, resolver NodeResolverFn) ([]byte, error) { +func (n *InternalNode) Get(key []byte, curPeriod StatePeriod, resolver NodeResolverFn) ([]byte, error) { if len(key) != StemSize+1 { return nil, fmt.Errorf("invalid key length, expected %d, got %d", StemSize+1, len(key)) } - stemValues, err := n.GetValuesAtStem(KeyToStem(key), resolver) + stemValues, err := n.GetValuesAtStem(KeyToStem(key), curPeriod, resolver) if err != nil { return nil, err } @@ -846,7 +956,6 @@ func (n *InternalNode) Commit() *Point { // TODO: make Commit() return an error panic(err) } - }() } wg.Wait() @@ -938,7 +1047,7 @@ func groupKeys(keys keylist, depth byte) []keylist { return groups } -func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) { var ( groups = groupKeys(keys, n.depth) pe = &ProofElements{ @@ -951,6 +1060,7 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr esses []byte = nil // list of extension statuses poass []Stem // list of proof-of-absence stems + ps []StatePeriod ) // fill in the polynomial for this node @@ -966,15 +1076,15 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr copy(childpath[:n.depth+1], keys[0][:n.depth]) childpath[n.depth] = byte(i) if resolver == nil { - return nil, nil, nil, fmt.Errorf("no resolver for path %x", childpath) + return nil, nil, nil, nil, fmt.Errorf("no resolver for path %x", childpath) } serialized, err := resolver(childpath) if err != nil { - return nil, nil, nil, fmt.Errorf("error resolving for path %x: %w", childpath, err) + return nil, nil, nil, nil, fmt.Errorf("error resolving for path %x: %w", childpath, err) } c, err = ParseNode(serialized, n.depth+1) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } n.children[i] = c } else { @@ -987,7 +1097,7 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr } } if err := banderwagon.BatchMapToScalarField(fiPtrs[:], points[:]); err != nil { - return nil, nil, nil, fmt.Errorf("batch mapping to scalar fields: %s", err) + return nil, nil, nil, nil, fmt.Errorf("batch mapping to scalar fields: %s", err) } for _, group := range groups { @@ -1009,15 +1119,13 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr for _, group := range groups { childIdx := offset2key(group[0], n.depth) - if _, isunknown := n.children[childIdx].(UnknownNode); isunknown { + switch n.children[childIdx].(type) { + case UnknownNode: // TODO: add a test case to cover this scenario. - return nil, nil, nil, errMissingNodeInStateless - } - - // Special case of a proof of absence: no children - // commitment, as the value is 0. - _, isempty := n.children[childIdx].(Empty) - if isempty { + return nil, nil, nil, nil,errMissingNodeInStateless + case Empty: + // Special case of a proof of absence: no children + // commitment, as the value is 0. addedStems := map[string]struct{}{} for i := 0; i < len(group); i++ { stemStr := string(KeyToStem(group[i])) @@ -1035,17 +1143,18 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr continue } - pec, es, other, err := n.children[childIdx].GetProofItems(group, resolver) + pec, es, other, otherPs, err := n.children[childIdx].GetProofItems(group, resolver) if err != nil { // TODO: add a test case to cover this scenario. - return nil, nil, nil, err + return nil, nil, nil, nil, err } pe.Merge(pec) poass = append(poass, other...) esses = append(esses, es...) + ps = append(ps, otherPs...) } - return pe, esses, poass, nil + return pe, esses, poass, ps, nil } // Serialize returns the serialized form of the internal node. @@ -1146,7 +1255,7 @@ func (n *InternalNode) touchCoW(index byte) { n.cowChild(index) } -func (n *LeafNode) Insert(key []byte, value []byte, _ NodeResolverFn) error { +func (n *LeafNode) Insert(key []byte, value []byte, curPeriod StatePeriod, _ NodeResolverFn) error { if n.isPOAStub { return errIsPOAStub } @@ -1155,25 +1264,37 @@ func (n *LeafNode) Insert(key []byte, value []byte, _ NodeResolverFn) error { return fmt.Errorf("invalid key size: %d", len(key)) } + if n.isExpired(curPeriod) { + return errExpired + } + stem := KeyToStem(key) if !bytes.Equal(stem, n.stem) { return fmt.Errorf("stems don't match: %x != %x", stem, n.stem) } values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.insertMultiple(stem, values) + return n.insertMultiple(stem, values, curPeriod, false) } -func (n *LeafNode) insertMultiple(stem Stem, values [][]byte) error { +func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curPeriod StatePeriod, skipPeriodCheck bool) error { // Sanity check: ensure the stems are the same. if !equalPaths(stem, n.stem) { return errInsertIntoOtherStem } - return n.updateMultipleLeaves(values) + if !skipPeriodCheck && n.isExpired(curPeriod) { + return errExpired + } + + if err := n.updateMultipleLeaves(values, curPeriod); err != nil { + return err + } + + return nil } -func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr) { +func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curPeriod StatePeriod) { // Calculate the Fr-delta. var deltaC Fr deltaC.Sub(&newC, &oldC) @@ -1182,6 +1303,10 @@ func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr) { var poly [NodeWidth]Fr poly[cxIndex] = deltaC + if n.period < curPeriod { + poly[4].SetUint64(uint64(curPeriod - n.period)) + } + // Add delta to the current commitment. n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) } @@ -1221,7 +1346,7 @@ func (n *LeafNode) updateCn(index byte, value []byte, c *Point) error { return nil } -func (n *LeafNode) updateLeaf(index byte, value []byte) error { +func (n *LeafNode) updateLeaf(index byte, value []byte, curPeriod StatePeriod) error { // Update the corresponding C1 or C2 commitment. var c *Point var oldC Point @@ -1244,13 +1369,14 @@ func (n *LeafNode) updateLeaf(index byte, value []byte) error { // If index is in the first NodeWidth/2 elements, we need to update C1. Otherwise, C2. cxIndex := 2 + int(index)/(NodeWidth/2) // [1, stem, -> C1, C2 <-] - n.updateC(cxIndex, frs[0], frs[1]) - + n.updateC(cxIndex, frs[0], frs[1], curPeriod) + + n.period = curPeriod n.values[index] = value return nil } -func (n *LeafNode) updateMultipleLeaves(values [][]byte) error { // skipcq: GO-R1005 +func (n *LeafNode) updateMultipleLeaves(values [][]byte, curPeriod StatePeriod) error { // skipcq: GO-R1005 var oldC1, oldC2 *Point // We iterate the values, and we update the C1 and/or C2 commitments depending on the index. @@ -1296,18 +1422,21 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte) error { // skipcq: GO-R if err := banderwagon.BatchMapToScalarField([]*Fr{&frs[0], &frs[1], &frs[2], &frs[3]}, []*Point{n.c1, oldC1, n.c2, oldC2}); err != nil { return fmt.Errorf("batch mapping to scalar fields: %s", err) } - n.updateC(c1Idx, frs[0], frs[1]) - n.updateC(c2Idx, frs[2], frs[3]) + n.updateC(c1Idx, frs[0], frs[1], curPeriod) + n.updateC(c2Idx, frs[2], frs[3], curPeriod) + n.period = curPeriod } else if oldC1 != nil { // Case 2. (C1 touched) if err := banderwagon.BatchMapToScalarField([]*Fr{&frs[0], &frs[1]}, []*Point{n.c1, oldC1}); err != nil { return fmt.Errorf("batch mapping to scalar fields: %s", err) } - n.updateC(c1Idx, frs[0], frs[1]) + n.updateC(c1Idx, frs[0], frs[1], curPeriod) + n.period = curPeriod } else if oldC2 != nil { // Case 2. (C2 touched) if err := banderwagon.BatchMapToScalarField([]*Fr{&frs[0], &frs[1]}, []*Point{n.c2, oldC2}); err != nil { return fmt.Errorf("batch mapping to scalar fields: %s", err) } - n.updateC(c2Idx, frs[0], frs[1]) + n.updateC(c2Idx, frs[0], frs[1], curPeriod) + n.period = curPeriod } return nil @@ -1315,12 +1444,16 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte) error { // skipcq: GO-R // Delete deletes a value from the leaf, return `true` as a second // return value, if the parent should entirely delete the child. -func (n *LeafNode) Delete(k []byte, _ NodeResolverFn) (bool, error) { +func (n *LeafNode) Delete(k []byte, curPeriod StatePeriod, _ NodeResolverFn) (bool, error) { // Sanity check: ensure the key header is the same: if !equalPaths(k, n.stem) { return false, nil } + if n.isExpired(curPeriod) { + return false, errExpired + } + // Erase the value it used to contain original := n.values[k[StemSize]] // save original value n.values[k[StemSize]] = nil @@ -1380,8 +1513,14 @@ func (n *LeafNode) Delete(k []byte, _ NodeResolverFn) (bool, error) { // is more important than var poly [4]Fr cn.MapToScalarField(&poly[subtreeindex]) + n.commitment.Sub(n.commitment, cfg.CommitToPoly(poly[:], 0)) + // TODO(weiihann): can this be done together with the previous? + if n.period != curPeriod { + n.updatePeriod(curPeriod) + } + // Clear the corresponding commitment if k[StemSize] < 128 { n.c1 = nil @@ -1403,14 +1542,18 @@ func (n *LeafNode) Delete(k []byte, _ NodeResolverFn) (bool, error) { // the method, as it needs the original // value to compute the commitment diffs. n.values[k[StemSize]] = original - return false, n.updateLeaf(k[StemSize], nil) + return false, n.updateLeaf(k[StemSize], nil, curPeriod) } -func (n *LeafNode) Get(k []byte, _ NodeResolverFn) ([]byte, error) { +func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byte, error) { if n.isPOAStub { return nil, errIsPOAStub } + if n.isExpired(curPeriod) { + return nil, errExpired + } + if !equalPaths(k, n.stem) { // If keys differ, return nil in order to // signal that the key isn't present in the @@ -1418,10 +1561,45 @@ func (n *LeafNode) Get(k []byte, _ NodeResolverFn) ([]byte, error) { // the behavior of Geth's SecureTrie. return nil, nil } + + n.updatePeriod(curPeriod) // value can be nil, as expected by geth return n.values[k[StemSize]], nil } +func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, skipUpdate bool, resolver NodeResolverFn) error { + if oldPeriod > curPeriod { + return errRevivePeriodOlderThanNew + } + + // No-op if already in current period + if n.period == curPeriod { + return nil + } + + // Verify period, stem and values match + if n.period != oldPeriod { + return errRevivePeriodMismatch + } + if !bytes.Equal(n.stem, stem) { + return errReviveStemMismatch + } + if len(n.values) != len(values) { + return errReviveValuesMismatch + } + for i := range values { + if !bytes.Equal(n.values[i], values[i]) { + return errReviveValuesMismatch + } + } + + if !skipUpdate { + n.updatePeriod(curPeriod) + } + + return nil +} + func (n *LeafNode) Hash() *Fr { // TODO cache this in a subsequent PR, not done here // to reduce complexity. @@ -1442,6 +1620,24 @@ func (n *LeafNode) Commit() *Point { return n.commitment } +// updatePeriod updates the last period of the leaf node and updates the commitment +func (n *LeafNode) updatePeriod(curPeriod StatePeriod) { + // no-op if the current period is the same + if curPeriod <= n.period { + return + } + + var poly [5]Fr + poly[4].SetUint64(uint64(curPeriod - n.period)) + n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) + + n.period = curPeriod +} + +func (n *LeafNode) isExpired(cur StatePeriod) bool { + return IsExpired(n.period, cur) +} + // fillSuffixTreePoly takes one of the two suffix tree and // builds the associated polynomial, to be used to compute // the corresponding C{1,2} commitment. @@ -1490,7 +1686,7 @@ func leafToComms(poly []Fr, val []byte) error { return nil } -func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements, []byte, []Stem, error) { // skipcq: GO-R1005 +func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) { // skipcq: GO-R1005 var ( poly [NodeWidth]Fr // top-level polynomial pe = &ProofElements{ @@ -1503,13 +1699,14 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements } esses []byte = nil // list of extension statuses - poass []Stem // list of proof-of-absence stems + poass []Stem // list of proof-of-absence + ps []StatePeriod ) - // Initialize the top-level polynomial with 1 + stem + C1 + C2 + // Initialize the top-level polynomial with 1 + stem + C1 + C2 + period poly[0].SetUint64(1) if err := StemFromLEBytes(&poly[1], n.stem); err != nil { - return nil, nil, nil, fmt.Errorf("error serializing stem '%x': %w", n.stem, err) + return nil, nil, nil, nil, fmt.Errorf("error serializing stem '%x': %w", n.stem, err) } // First pass: add top-level elements first @@ -1531,14 +1728,14 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements // Also, we _need_ them independently of hasC1 or hasC2 since the prover needs `Fis`. if !n.isPOAStub { if err := banderwagon.BatchMapToScalarField([]*Fr{&poly[2], &poly[3]}, []*Point{n.c1, n.c2}); err != nil { - return nil, nil, nil, fmt.Errorf("batch mapping to scalar fields: %s", err) + return nil, nil, nil, nil, fmt.Errorf("batch mapping to scalar fields: %s", err) } } else if hasC1 || hasC2 || n.c1 != nil || n.c2 != nil { // This LeafNode is a proof of absence stub. It must be true that // both c1 and c2 are nil, and that hasC1 and hasC2 are false. // Let's just check that to be sure, since the code below can't use // poly[2] or poly[3]. - return nil, nil, nil, fmt.Errorf("invalid proof of absence stub") + return nil, nil, nil, nil,fmt.Errorf("invalid proof of absence stub") } if hasC1 { @@ -1554,12 +1751,21 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements pe.Fis = append(pe.Fis, poly[:]) } + poly[4].SetUint64(uint64(n.period)) + + pe.Cis = append(pe.Cis, n.commitment) + pe.Zis = append(pe.Zis, 4) + pe.Yis = append(pe.Yis, &poly[4]) + pe.Fis = append(pe.Fis, poly[:]) + addedStems := map[string]struct{}{} // Second pass: add the cn-level elements for _, key := range keys { pe.ByPath[string(key[:n.depth])] = n.commitment + stemStr := string(KeyToStem(key)) + // Proof of absence: case of a differing stem. if !equalPaths(n.stem, key) { // If this is the first extension status added for this path, @@ -1572,7 +1778,6 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements // 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. - stemStr := string(KeyToStem(key)) if _, ok := addedStems[stemStr]; !ok { esses = append(esses, extStatusAbsentOther|(n.depth<<3)) addedStems[stemStr] = struct{}{} @@ -1598,12 +1803,12 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements ) if suffix >= 128 { if _, err = fillSuffixTreePoly(suffPoly[:], n.values[128:]); err != nil { - return nil, nil, nil, fmt.Errorf("filling suffix tree poly: %w", err) + return nil, nil, nil, nil,fmt.Errorf("filling suffix tree poly: %w", err) } scomm = n.c2 } else { if _, err = fillSuffixTreePoly(suffPoly[:], n.values[:128]); err != nil { - return nil, nil, nil, fmt.Errorf("filling suffix tree poly: %w", err) + return nil, nil, nil, nil,fmt.Errorf("filling suffix tree poly: %w", err) } scomm = n.c1 } @@ -1628,7 +1833,6 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements pe.Fis = append(pe.Fis, suffPoly[:], suffPoly[:]) pe.Vals = append(pe.Vals, n.values[key[StemSize]]) - stemStr := string(KeyToStem(key)) if _, ok := addedStems[stemStr]; !ok { esses = append(esses, extStatusPresent|(n.depth<<3)) addedStems[stemStr] = struct{}{} @@ -1638,11 +1842,11 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements pe.ByPath[slotPath] = scomm } - return pe, esses, poass, nil + return pe, esses, poass, ps, nil } // Serialize serializes a LeafNode. -// The format is: +// The format is: func (n *LeafNode) Serialize() ([]byte, error) { cBytes := banderwagon.BatchToBytesUncompressed(n.commitment, n.c1, n.c2) return n.serializeLeafWithUncompressedCommitments(cBytes[0], cBytes[1], cBytes[2]), nil @@ -1751,6 +1955,8 @@ func (n *InternalNode) BatchSerialize() ([]SerializedNode, error) { serializedPointsIdxs[n] = len(pointsToCompress) - 1 case *LeafNode: pointsToCompress = append(pointsToCompress, n.commitment, n.c1, n.c2) + case *ExpiredLeafNode: + pointsToCompress = append(pointsToCompress, n.commitment) } } @@ -1776,6 +1982,19 @@ func (n *InternalNode) BatchSerialize() ([]SerializedNode, error) { } ret = append(ret, sn) idx++ + case *ExpiredLeafNode: + serialized, err := n.Serialize() + if err != nil { + return nil, err + } + sn := SerializedNode{ + Node: n, + Path: paths[i], + CommitmentBytes: serializedPoints[idx], + SerializedBytes: serialized, + } + ret = append(ret, sn) + idx++ case *LeafNode: cBytes := serializedPoints[idx] c1Bytes := serializedPoints[idx+1] @@ -1802,6 +2021,9 @@ func (n *InternalNode) collectNonHashedNodes(list []VerkleNode, paths [][]byte, case *LeafNode: list = append(list, childNode) paths = append(paths, childNode.stem[:len(path)+1]) + case *ExpiredLeafNode: + list = append(list, childNode) + paths = append(paths, childNode.stem[:len(path)+1]) case *InternalNode: childpath := make([]byte, len(path)+1) copy(childpath, path) @@ -1875,6 +2097,8 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte + period := make([]byte, periodSize) + binary.BigEndian.PutUint16(period, uint16(n.period)) switch { case count == 1: var buf [singleSlotLeafSize]byte @@ -1887,8 +2111,9 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafStemOffset+StemSize:], c2Bytes[:]) } copy(result[leafStemOffset+StemSize+banderwagon.UncompressedSize:], cBytes[:]) - result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize] = byte(lastIdx) - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+leafValueIndexSize:], n.values[lastIdx][:]) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], period) + result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+periodSize] = byte(lastIdx) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+periodSize+leafValueIndexSize:], n.values[lastIdx][:]) case isEoA: var buf [eoaLeafSize]byte result = buf[:] @@ -1896,15 +2121,17 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafStemOffset:], n.stem[:StemSize]) copy(result[leafStemOffset+StemSize:], c1Bytes[:]) copy(result[leafStemOffset+StemSize+banderwagon.UncompressedSize:], cBytes[:]) - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], n.values[0]) // copy basic data + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], period) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+periodSize:], n.values[0]) // copy basic data default: - result = make([]byte, nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+len(children)) + result = make([]byte, nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+periodSize+len(children)) result[0] = leafType copy(result[leafStemOffset:], n.stem[:StemSize]) copy(result[leafBitlistOffset:], bitlist[:]) copy(result[leafCommitmentOffset:], cBytes[:]) copy(result[leafC1CommitmentOffset:], c1Bytes[:]) copy(result[leafC2CommitmentOffset:], c2Bytes[:]) + copy(result[leafLastPeriodOffset:], period) copy(result[leafChildrenOffset:], children) } diff --git a/tree_ipa_test.go b/tree_ipa_test.go index fbaf5fa4..4aca4b83 100644 --- a/tree_ipa_test.go +++ b/tree_ipa_test.go @@ -85,7 +85,7 @@ func TestInsertKey0Value0(t *testing.T) { srs = cfg.conf.SRS ) - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("insert failed: %s", err) } comm := root.Commit() @@ -118,7 +118,7 @@ func TestInsertKey1Value1(t *testing.T) { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, } - if err := root.Insert(key, key, nil); err != nil { + if err := root.Insert(key, key, 0, nil); err != nil { t.Fatalf("insert failed: %s", err) } comm := root.Commit() @@ -156,10 +156,10 @@ func TestInsertSameStemTwoLeaves(t *testing.T) { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 128, } - if err := root.Insert(key_a, key_a, nil); err != nil { + if err := root.Insert(key_a, key_a, 0, nil); err != nil { t.Fatalf("insert failed: %s", err) } - if err := root.Insert(key_b, key_b, nil); err != nil { + if err := root.Insert(key_b, key_b, 0, nil); err != nil { t.Fatalf("insert failed: %s", err) } comm := root.Commit() @@ -211,10 +211,10 @@ func TestInsertKey1Val1Key2Val2(t *testing.T) { srs = cfg.conf.SRS ) key_b, _ := hex.DecodeString("0101010101010101010101010101010101010101010101010101010101010101") - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("insert failed: %s", err) } - if err := root.Insert(key_b, key_b, nil); err != nil { + if err := root.Insert(key_b, key_b, 0, nil); err != nil { t.Fatalf("insert failed: %s", err) } comm := root.Commit() diff --git a/tree_test.go b/tree_test.go index 0aace884..7d2fd87a 100644 --- a/tree_test.go +++ b/tree_test.go @@ -61,7 +61,7 @@ func TestInsertIntoRoot(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } @@ -79,10 +79,10 @@ func TestInsertTwoLeaves(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - if err := root.Insert(ffx32KeyTest, testValue, nil); err != nil { + if err := root.Insert(ffx32KeyTest, testValue, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } @@ -109,10 +109,10 @@ func TestInsertTwoLeavesLastLevel(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - if err := root.Insert(oneKeyTest, testValue, nil); err != nil { + if err := root.Insert(oneKeyTest, testValue, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } @@ -133,14 +133,14 @@ func TestGetTwoLeaves(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - if err := root.Insert(ffx32KeyTest, testValue, nil); err != nil { + if err := root.Insert(ffx32KeyTest, testValue, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - val, err := root.Get(zeroKeyTest, nil) + val, err := root.Get(zeroKeyTest, 0, nil) if err != nil { t.Fatal(err) } @@ -149,7 +149,7 @@ func TestGetTwoLeaves(t *testing.T) { t.Fatalf("got a different value from the tree than expected %x != %x", val, testValue) } - val, err = root.Get(oneKeyTest, nil) + val, err = root.Get(oneKeyTest, 0, nil) if err != nil { t.Fatalf("wrong error type, expected %v, got %v", nil, err) } @@ -187,7 +187,7 @@ func TestFlush1kLeaves(t *testing.T) { go func() { root := New() for _, k := range keys { - if err := root.Insert(k, fourtyKeyTest, nil); err != nil { + if err := root.Insert(k, fourtyKeyTest, 0, nil); err != nil { panic(err) } } @@ -221,17 +221,17 @@ func TestCopy(t *testing.T) { key2, _ := hex.DecodeString("0107000000000000000000000000000000000000000000000000000000000000") key3, _ := hex.DecodeString("0405000000000000000000000000000000000000000000000000000000000000") tree := New() - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key2, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key2, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } tree.Commit() copied := tree.Copy() - if err := tree.Insert(key3, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key3, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } @@ -239,7 +239,7 @@ func TestCopy(t *testing.T) { t.Fatal("inserting the copy into the original tree updated the copy's commitment") } - if err := copied.Insert(key3, fourtyKeyTest, nil); err != nil { + if err := copied.Insert(key3, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the copy failed: %v", err) } @@ -256,13 +256,13 @@ func TestCachedCommitment(t *testing.T) { key3, _ := hex.DecodeString("0405000000000000000000000000000000000000000000000000000000000000") key4, _ := hex.DecodeString("0407000000000000000000000000000000000000000000000000000000000000") tree := New() - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key2, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key2, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key3, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key3, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } oldRoot := tree.Commit().Bytes() @@ -272,7 +272,7 @@ func TestCachedCommitment(t *testing.T) { t.Error("root has not cached commitment") } - if err := tree.Insert(key4, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key4, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key4 failed: %v", err) } tree.Commit() @@ -297,25 +297,25 @@ func TestDelLeaf(t *testing.T) { // skipcq: GO-R1005 key2, _ := hex.DecodeString("0107000000000000000000000000000000000000000000000000000000000000") key3, _ := hex.DecodeString("0405000000000000000000000000000000000000000000000000000000000000") tree := New() - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key1p, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1p, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key1pp, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1pp, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key2, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key2, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } var init Point init.Set(tree.Commit()) - if err := tree.Insert(key3, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key3, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if _, err := tree.Delete(key3, nil); err != nil { + if _, err := tree.Delete(key3, 0, nil); err != nil { t.Error(err) } @@ -327,7 +327,7 @@ func TestDelLeaf(t *testing.T) { // skipcq: GO-R1005 t.Errorf("deleting leaf resulted in unexpected tree %x %x", init.Bytes(), postHash.Bytes()) } - res, err := tree.Get(key3, nil) + res, err := tree.Get(key3, 0, nil) if err != nil { t.Error(err) } @@ -335,10 +335,10 @@ func TestDelLeaf(t *testing.T) { // skipcq: GO-R1005 t.Error("leaf hasnt been deleted") } - if _, err := tree.Delete(key1pp, nil); err != nil { + if _, err := tree.Delete(key1pp, 0, nil); err != nil { t.Fatal(err) } - res, err = tree.Get(key1pp, nil) + res, err = tree.Get(key1pp, 0, nil) if err != nil { t.Error(err) } @@ -346,10 +346,10 @@ func TestDelLeaf(t *testing.T) { // skipcq: GO-R1005 t.Error("leaf hasnt been deleted") } - if _, err := tree.Delete(key1p, nil); err != nil { + if _, err := tree.Delete(key1p, 0, nil); err != nil { t.Fatal(err) } - res, err = tree.Get(key1p, nil) + res, err = tree.Get(key1p, 0, nil) if err != nil { t.Error(err) } @@ -366,33 +366,33 @@ func TestDeleteAtStem(t *testing.T) { key1pp, _ := hex.DecodeString("0105000000000000000000000000000000000000000000000000000000000081") // Other Cn group as key1 key2, _ := hex.DecodeString("0107000000000000000000000000000000000000000000000000000000000000") tree := New() - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key1p, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1p, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key1pp, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1pp, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := tree.Insert(key2, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key2, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } var init Point init.Set(tree.Commit()) - if _, err := tree.(*InternalNode).DeleteAtStem(key1[:31], nil); err != err { + if _, err := tree.(*InternalNode).DeleteAtStem(key1[:31], period0, nil); err != err { t.Error(err) } - res, err := tree.Get(key1, nil) + res, err := tree.Get(key1, 0, nil) if err != nil { t.Error(err) } if len(res) > 0 { t.Error("leaf hasnt been deleted") } - res, err = tree.Get(key1pp, nil) + res, err = tree.Get(key1pp, 0, nil) if err != nil { t.Error(err) } @@ -400,7 +400,7 @@ func TestDeleteAtStem(t *testing.T) { t.Error("leaf hasnt been deleted") } - if _, err := tree.(*InternalNode).DeleteAtStem(zeroKeyTest[:31], nil); err != errDeleteMissing { + if _, err := tree.(*InternalNode).DeleteAtStem(zeroKeyTest[:31], period0, nil); err != errDeleteMissing { t.Fatal(err) } } @@ -412,13 +412,13 @@ func TestDeleteNonExistent(t *testing.T) { key2, _ := hex.DecodeString("0107000000000000000000000000000000000000000000000000000000000000") key3, _ := hex.DecodeString("0405000000000000000000000000000000000000000000000000000000000000") tree := New() - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key1 failed: %v", err) } - if err := tree.Insert(key2, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key2, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key2 failed: %v", err) } - if _, err := tree.Delete(key3, nil); err != nil { + if _, err := tree.Delete(key3, 0, nil); err != nil { t.Error("should not fail when deleting a non-existent key") } } @@ -432,29 +432,29 @@ func TestDeletePrune(t *testing.T) { // skipcq: GO-R1005 key4, _ := hex.DecodeString("0407000000000000000000000000000000000000000000000000000000000000") key5, _ := hex.DecodeString("04070000000000000000000000000000000000000000000000000000000000FF") tree := New() - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key1 failed: %v", err) } - if err := tree.Insert(key2, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key2, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key2 failed: %v", err) } var hashPostKey2, hashPostKey4, completeTreeHash Point hashPostKey2.Set(tree.Commit()) - if err := tree.Insert(key3, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key3, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key3 failed: %v", err) } - if err := tree.Insert(key4, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key4, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key4 failed: %v", err) } hashPostKey4.Set(tree.Commit()) - if err := tree.Insert(key5, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key5, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key5 failed: %v", err) } completeTreeHash.Set(tree.Commit()) // hash when the tree has received all its keys // Delete key5. - if _, err := tree.Delete(key5, nil); err != nil { + if _, err := tree.Delete(key5, 0, nil); err != nil { t.Error(err) } postHash := tree.Commit() @@ -467,7 +467,7 @@ func TestDeletePrune(t *testing.T) { // skipcq: GO-R1005 if !hashPostKey4.Equal(postHash) { t.Error("deleting leaf #5 resulted in unexpected tree") } - res, err := tree.Get(key5, nil) + res, err := tree.Get(key5, 0, nil) if err != nil { t.Error(err) } @@ -476,10 +476,10 @@ func TestDeletePrune(t *testing.T) { // skipcq: GO-R1005 } // Delete key4 and key3. - if _, err := tree.Delete(key4, nil); err != nil { + if _, err := tree.Delete(key4, 0, nil); err != nil { t.Error(err) } - if _, err := tree.Delete(key3, nil); err != nil { + if _, err := tree.Delete(key3, 0, nil); err != nil { t.Error(err) } postHash = tree.Commit() @@ -487,7 +487,7 @@ func TestDeletePrune(t *testing.T) { // skipcq: GO-R1005 if !hashPostKey2.Equal(postHash) { t.Error("deleting leaf #3 resulted in unexpected tree") } - res, err = tree.Get(key3, nil) + res, err = tree.Get(key3, 0, nil) if err != nil { t.Error(err) } @@ -507,18 +507,18 @@ func TestDeleteHash(t *testing.T) { key2, _ := hex.DecodeString("0107000000000000000000000000000000000000000000000000000000000000") key3, _ := hex.DecodeString("0405000000000000000000000000000000000000000000000000000000000000") tree := New() - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key1 failed: %v", err) } - if err := tree.Insert(key2, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key2, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key2 failed: %v", err) } - if err := tree.Insert(key3, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key3, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key3 failed: %v", err) } tree.(*InternalNode).FlushAtDepth(0, func(path []byte, vn VerkleNode) {}) tree.Commit() - if _, err := tree.Delete(key2, nil); err != errDeleteHash { + if _, err := tree.Delete(key2, 0, nil); err != errDeleteHash { t.Fatalf("did not report the correct error while deleting from a hash: %v", err) } } @@ -530,15 +530,15 @@ func TestDeleteUnequalPath(t *testing.T) { key2, _ := hex.DecodeString("0107000000000000000000000000000000000000000000000000000000000000") key3, _ := hex.DecodeString("0405000000000000000000000000000000000000000000000000000000000000") tree := New() - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key1 failed: %v", err) } - if err := tree.Insert(key3, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key3, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key3 failed: %v", err) } tree.Commit() - if _, err := tree.Delete(key2, nil); err != nil { + if _, err := tree.Delete(key2, 0, nil); err != nil { t.Fatalf("errored during the deletion of non-existing key, err =%v", err) } } @@ -555,20 +555,20 @@ func TestDeleteResolve(t *testing.T) { saveNode := func(path []byte, node VerkleNode) { savedNodes[string(path)] = node } - if err := tree.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key1, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key1 failed: %v", err) } - if err := tree.Insert(key2, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key2, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key2 failed: %v", err) } - if err := tree.Insert(key3, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(key3, fourtyKeyTest, 0, nil); err != nil { t.Fatalf("inserting into key3 failed: %v", err) } tree.(*InternalNode).FlushAtDepth(0, saveNode) tree.Commit() var called bool - _, err := tree.Delete(key2, func(path []byte) ([]byte, error) { + _, err := tree.Delete(key2, 0, func(path []byte) ([]byte, error) { called = true if node, ok := savedNodes[string(path)]; ok { @@ -590,7 +590,7 @@ func TestConcurrentTrees(t *testing.T) { t.Parallel() tree := New() - err := tree.Insert(zeroKeyTest, fourtyKeyTest, nil) + err := tree.Insert(zeroKeyTest, fourtyKeyTest, 0, nil) if err != nil { t.Fatal(err) } @@ -600,7 +600,7 @@ func TestConcurrentTrees(t *testing.T) { ch := make(chan *Point) builder := func() { tree := New() - if err := tree.Insert(zeroKeyTest, fourtyKeyTest, nil); err != nil { + if err := tree.Insert(zeroKeyTest, fourtyKeyTest, 0, nil); err != nil { panic(err) } ch <- tree.Commit() @@ -618,6 +618,28 @@ func TestConcurrentTrees(t *testing.T) { } } +func TestLeafUpdatePeriod(t *testing.T) { + t.Parallel() + + values := make([][]byte, NodeWidth) + values[0] = []byte{1} + leaf1, err := NewLeafNode(zeroKeyTest, values, period2) + if err != nil { + t.Fatal(err) + } + comm1 := leaf1.Commit() + + leaf2, err := NewLeafNode(zeroKeyTest, values, period0) + if err != nil { + t.Fatal(err) + } + leaf2.updatePeriod(period2) + comm2 := leaf2.Commit() + if !comm1.Equal(comm2) { + t.Error("commitment not updated") + } +} + func BenchmarkCommitLeaves(b *testing.B) { benchmarkCommitNLeaves(b, 1000) benchmarkCommitNLeaves(b, 10000) @@ -637,7 +659,7 @@ func BenchmarkCommitFullNode(b *testing.B) { for i := 0; i < b.N; i++ { root := New() for _, k := range keys { - if err := root.Insert(k, fourtyKeyTest, nil); err != nil { + if err := root.Insert(k, fourtyKeyTest, 0, nil); err != nil { b.Fatal(err) } } @@ -679,7 +701,7 @@ func benchmarkCommitNLeaves(b *testing.B, n int) { for i := 0; i < b.N; i++ { root := New() for _, el := range kvs { - if err := root.Insert(el.k, el.v, nil); err != nil { + if err := root.Insert(el.k, el.v, 0, nil); err != nil { b.Error(err) } } @@ -700,7 +722,7 @@ func BenchmarkModifyLeaves(b *testing.B) { b.Fatalf("failed to generate random key: %v", err) } keys[i] = key - if err := root.Insert(key, val, nil); err != nil { + if err := root.Insert(key, val, 0, nil); err != nil { b.Fatalf("inserting into key1 failed: %v", err) } } @@ -714,7 +736,7 @@ func BenchmarkModifyLeaves(b *testing.B) { binary.BigEndian.PutUint32(val, uint32(i)) for j := 0; j < toEdit; j++ { k := keys[mRand.IntN(n)] - if err := root.Insert(k, val, nil); err != nil { + if err := root.Insert(k, val, 0, nil); err != nil { b.Error(err) } } @@ -744,10 +766,10 @@ func TestNodeSerde(t *testing.T) { t.Parallel() tree := New() - if err := tree.Insert(zeroKeyTest, testValue, nil); err != nil { + if err := tree.Insert(zeroKeyTest, testValue, 0, nil); err != nil { t.Fatalf("inserting into key1 failed: %v", err) } - if err := tree.Insert(fourtyKeyTest, testValue, nil); err != nil { + if err := tree.Insert(fourtyKeyTest, testValue, 0, nil); err != nil { t.Fatalf("inserting into key2 failed: %v", err) } origComm := tree.Commit().BytesUncompressedTrusted() @@ -873,28 +895,28 @@ func TestGetResolveFromHash(t *testing.T) { serialized = append(serialized, s...) } root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := root.Insert(fourtyKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(fourtyKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } root.(*InternalNode).FlushAtDepth(0, flush) - if err := root.Insert(oneKeyTest, zeroKeyTest, nil); err != errInsertIntoHash { + if err := root.Insert(oneKeyTest, zeroKeyTest, 0, nil); err != errInsertIntoHash { t.Fatal(err) } - data, err := root.Get(zeroKeyTest, nil) + data, err := root.Get(zeroKeyTest, 0, nil) if !errors.Is(err, errReadFromInvalid) || len(data) != 0 { t.Fatal(err) } - data, err = root.Get(zeroKeyTest, failingGetter) + data, err = root.Get(zeroKeyTest, 0, failingGetter) if !errors.Is(err, dummyError) || len(data) != 0 { t.Fatal(err) } - data, err = root.Get(zeroKeyTest, getter) + data, err = root.Get(zeroKeyTest, 0, getter) if err != nil { t.Fatalf("error resolving hash: %v", err) } @@ -930,26 +952,26 @@ func TestInsertIntoHashedNode(t *testing.T) { t.SkipNow() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } root.(*InternalNode).FlushAtDepth(0, func(_ []byte, n VerkleNode) {}) - if err := root.Insert(fourtyKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(fourtyKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != errInsertIntoHash { + if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != errInsertIntoHash { t.Fatalf("incorrect error type: %v", err) } resolver := func(h []byte) ([]byte, error) { values := make([][]byte, NodeWidth) values[0] = zeroKeyTest - node, _ := NewLeafNode(KeyToStem(zeroKeyTest), values) + node, _ := NewLeafNode(KeyToStem(zeroKeyTest), values, 0) return node.Serialize() } - if err := root.Copy().Insert(zeroKeyTest, zeroKeyTest, resolver); err != nil { + if err := root.Copy().Insert(zeroKeyTest, zeroKeyTest, 0, resolver); err != nil { t.Fatalf("error in node resolution: %v", err) } @@ -958,12 +980,12 @@ func TestInsertIntoHashedNode(t *testing.T) { invalidRLPResolver := func(h []byte) ([]byte, error) { values := make([][]byte, NodeWidth) values[0] = zeroKeyTest - node, _ := NewLeafNode(KeyToStem(zeroKeyTest), values) + node, _ := NewLeafNode(KeyToStem(zeroKeyTest), values, 0) rlp, _ := node.Serialize() return rlp[:len(rlp)-10], nil } - if err := root.Copy().Insert(zeroKeyTest, zeroKeyTest, invalidRLPResolver); !errors.Is(err, errSerializedPayloadTooShort) { + if err := root.Copy().Insert(zeroKeyTest, zeroKeyTest, 0, invalidRLPResolver); !errors.Is(err, errSerializedPayloadTooShort) { t.Fatalf("error detecting a decoding error after resolution: %v", err) } @@ -972,23 +994,23 @@ func TestInsertIntoHashedNode(t *testing.T) { erroringResolver := func(h []byte) ([]byte, error) { return nil, randomResolverError } - if err := root.Copy().Insert(zeroKeyTest, zeroKeyTest, erroringResolver); !errors.Is(err, randomResolverError) { + if err := root.Copy().Insert(zeroKeyTest, zeroKeyTest, 0, erroringResolver); !errors.Is(err, randomResolverError) { t.Fatalf("error detecting a resolution error: %v", err) } } func TestToDot(t *testing.T) { root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } // TODO fix the issue with FlushAtDepth so that we can also try to verify the display of hashed nodes // root.(*InternalNode).FlushAtDepth(0, func(_ []byte, n VerkleNode) {}) // Hash the leaf to ensure HashedNodes display correctly - if err := root.Insert(fourtyKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(fourtyKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } fourtytwoKeyTest, _ := hex.DecodeString("4020000000000000000000000000000000000000000000000000000000000000") - if err := root.Insert(fourtytwoKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(fourtytwoKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } @@ -1021,11 +1043,11 @@ func TestEmptyCommitment(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } root.Commit() - pe, _, _, err := root.GetProofItems(keylist{ffx32KeyTest}, nil) + pe, _, _, _, err := root.GetProofItems(keylist{ffx32KeyTest}, nil) if err != nil { t.Fatal(err) } @@ -1083,7 +1105,7 @@ func TestGetProofItemsNoPoaIfStemPresent(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(ffx32KeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(ffx32KeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } @@ -1092,7 +1114,7 @@ func TestGetProofItemsNoPoaIfStemPresent(t *testing.T) { key1, _ := hex.DecodeString("ffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") key2, _ := hex.DecodeString("ffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - _, esses, poas, err := root.GetProofItems(keylist{key1, key2, ffx32KeyTest}, nil) + _, esses, poas, _, err := root.GetProofItems(keylist{key1, key2, ffx32KeyTest}, nil) if err != nil { t.Fatal(err) } @@ -1128,7 +1150,7 @@ func TestWithRustCompatibility(t *testing.T) { root := New() for i, key := range testAccountKeys { - err := root.Insert(key, testAccountValues[i], nil) + err := root.Insert(key, testAccountValues[i], 0, nil) if err != nil { t.Fatalf("error inserting: %v", err) } @@ -1150,7 +1172,7 @@ func TestInsertStem(t *testing.T) { values[5] = zeroKeyTest values[192] = fourtyKeyTest - if err := root1.(*InternalNode).InsertValuesAtStem(KeyToStem(fourtyKeyTest), values, nil); err != nil { + if err := root1.(*InternalNode).InsertValuesAtStem(KeyToStem(fourtyKeyTest), values, 0, false, nil); err != nil { t.Fatalf("error inserting: %s", err) } r1c := root1.Commit() @@ -1160,10 +1182,10 @@ func TestInsertStem(t *testing.T) { copy(key192[:], KeyToStem(fourtyKeyTest)) key5[StemSize] = 5 key192[StemSize] = 192 - if err := root2.Insert(key5[:], zeroKeyTest, nil); err != nil { + if err := root2.Insert(key5[:], zeroKeyTest, 0, nil); err != nil { t.Fatalf("error inserting: %s", err) } - if err := root2.Insert(key192[:], fourtyKeyTest, nil); err != nil { + if err := root2.Insert(key192[:], fourtyKeyTest, 0, nil); err != nil { t.Fatalf("error inserting: %s", err) } r2c := root2.Commit() @@ -1179,7 +1201,7 @@ func TestInsertStemTouchingBothHalves(t *testing.T) { root := New() // Insert keys such that both C1 and C2 have values. - if err := root.Insert(zeroKeyTest, testValue, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } zeroKeyTest2 := append([]byte{}, zeroKeyTest...) @@ -1198,7 +1220,7 @@ func TestInsertStemTouchingBothHalves(t *testing.T) { newValues := make([][]byte, NodeWidth) newValues[1] = testValue newValues[NodeWidth-2] = testValue - if err := root.(*InternalNode).InsertValuesAtStem(KeyToStem(zeroKeyTest), newValues, nil); err != nil { + if err := root.(*InternalNode).InsertValuesAtStem(KeyToStem(zeroKeyTest), newValues, 0, false, nil); err != nil { t.Fatalf("error inserting stem: %v", err) } root.Commit() @@ -1215,7 +1237,7 @@ func TestInsertResolveSplitLeaf(t *testing.T) { // Insert a unique leaf and flush it root := New() - if err := root.Insert(zeroKeyTest, ffx32KeyTest, nil); err != nil { + if err := root.Insert(zeroKeyTest, ffx32KeyTest, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } root.(*InternalNode).Flush(func(_ []byte, node VerkleNode) { @@ -1237,7 +1259,7 @@ func TestInsertResolveSplitLeaf(t *testing.T) { // Now insert another leaf, with a resolver function key, _ := hex.DecodeString("0000100000000000000000000000000000000000000000000000000000000000") - if err := root.Insert(key, ffx32KeyTest, func(path []byte) ([]byte, error) { + if err := root.Insert(key, ffx32KeyTest, 0, func(path []byte) ([]byte, error) { if len(path) != int(leaf.depth) { return nil, fmt.Errorf("invalid path length: %d != %d", len(path), leaf.depth) } @@ -1331,7 +1353,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { } v, _ := hex.DecodeString(s) - if err := tree.Insert(keys[i], v, nil); err != nil { + if err := tree.Insert(keys[i], v, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } @@ -1341,13 +1363,13 @@ func TestRustBanderwagonBlock48(t *testing.T) { // Insert the code chunk that isn't part of the proof missingKey, _ := hex.DecodeString("744f493648c83c5ede1726a0cfbe36d3830fd5b64a820b79ca77fe159335268a") missingVal, _ := hex.DecodeString("133b991f93d230604b1b8daaef64766264736f6c634300080700330000000000") - if err := tree.Insert(missingKey, missingVal, nil); err != nil { + if err := tree.Insert(missingKey, missingVal, 0, nil); err != nil { t.Fatalf("error inserting: %v", err) } r := tree.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) @@ -1368,7 +1390,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { if err != nil { t.Fatal(err) } - pe, _, _, err := droot.GetProofItems(keys, nil) + pe, _, _, _, err := droot.GetProofItems(keylist(keys), nil) if err != nil { t.Fatal(err) } @@ -1403,7 +1425,7 @@ func BenchmarkEmptyHashCodeCachedPoint(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _, _ = NewLeafNode(zeroKeyTest, values) + _, _ = NewLeafNode(zeroKeyTest, values, 0) } }) } @@ -1421,7 +1443,7 @@ func TestEmptyHashCodeCachedPoint(t *testing.T) { } values := make([][]byte, NodeWidth) values[CodeHashVectorPosition] = emptyHashCode - ln, _ := NewLeafNode(zeroKeyTest, values) + ln, _ := NewLeafNode(zeroKeyTest, values, 0) // Compare the result (which used the cached point) with the expected result which was // calculated by a previous version of the library that didn't use a cached point. @@ -1459,7 +1481,7 @@ func TestBatchMigratedKeyValues(t *testing.T) { now := time.Now() for _, kv := range randomKeyValues { - if err := tree.Insert(kv.key, kv.value, nil); err != nil { + if err := tree.Insert(kv.key, kv.value, 0, nil); err != nil { t.Fatalf("failed to insert key: %v", err) } } @@ -1529,7 +1551,7 @@ func TestBatchMigratedKeyValues(t *testing.T) { func genRandomTree(rand *mRandV1.Rand, keyValueCount int) VerkleNode { tree := New() for _, kv := range genRandomKeyValues(rand, keyValueCount) { - if err := tree.Insert(kv.key, kv.value, nil); err != nil { + if err := tree.Insert(kv.key, kv.value, 0, nil); err != nil { panic(fmt.Sprintf("failed to insert key: %v", err)) } } @@ -1562,7 +1584,7 @@ func BenchmarkBatchLeavesInsert(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { b.StopTimer() - rand := mRandV1.New(mRandV1.NewSource(42)) //skipcq: GSC-G404 + rand := mRandV1.New(mRandV1.NewSource(42)) // skipcq: GSC-G404 tree := genRandomTree(rand, treeInitialKeyValCount) randomKeyValues := genRandomKeyValues(rand, migrationKeyValueCount) b.StartTimer() @@ -1607,7 +1629,7 @@ func TestManipulateChildren(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(ffx32KeyTest, testValue, nil); err != nil { + if err := root.Insert(ffx32KeyTest, testValue, 0, nil); err != nil { t.Fatalf("failed to insert key: %v", err) } @@ -1642,13 +1664,13 @@ func TestLeafNodeInsert(t *testing.T) { values := make([][]byte, NodeWidth) valIdx := 42 values[valIdx] = testValue - ln, err := NewLeafNode(KeyToStem(keyTest), values) + ln, err := NewLeafNode(KeyToStem(keyTest), values, 0) if err != nil { t.Fatalf("failed to create leaf node: %v", err) } // Check we get the value correctly via Get(...). - getValue, err := ln.Get(append(KeyToStem(keyTest), byte(valIdx)), nil) + getValue, err := ln.Get(append(KeyToStem(keyTest), byte(valIdx)), 0, nil) if err != nil { t.Fatalf("failed to get leaf node key/value: %v", err) } @@ -1672,7 +1694,7 @@ func TestLeafNodeInsert(t *testing.T) { ffx32KeyTest2 := append([]byte{}, keyTest...) ffx32KeyTest2[StemSize] = 11 newValue := []byte("22222222222222222222222222222222") - if err := ln.Insert(ffx32KeyTest2, newValue, nil); err != nil { + if err := ln.Insert(ffx32KeyTest2, newValue, 0, nil); err != nil { t.Fatalf("failed to insert leaf node key/value: %v", err) } if !bytes.Equal(ln.values[valIdx], testValue) { @@ -1683,7 +1705,7 @@ func TestLeafNodeInsert(t *testing.T) { } // Check wrong *key* length. - if err := ln.Insert(KeyToStem(ffx32KeyTest2), newValue, nil); err == nil { + if err := ln.Insert(KeyToStem(ffx32KeyTest2), newValue, 0, nil); err == nil { t.Fatalf("key with size 31 should not be accepted, keys must have length StemSize+1") } @@ -1691,7 +1713,7 @@ func TestLeafNodeInsert(t *testing.T) { ffx32KeyTest3 := append([]byte{}, keyTest...) ffx32KeyTest3[StemSize] = 11 ffx32KeyTest3[StemSize-5] = 99 - if err := ln.Insert(ffx32KeyTest3, newValue, nil); err == nil { + if err := ln.Insert(ffx32KeyTest3, newValue, 0, nil); err == nil { t.Fatalf("inserting a key with a different stem should fail") } @@ -1798,18 +1820,18 @@ func runRandTest(rt randTest) error { for i, step := range rt { switch step.op { case opInsert: - if err := root.Insert(step.key, step.value, nil); err != nil { + if err := root.Insert(step.key, step.value, 0, nil); err != nil { rt[i].err = err } keys = append(keys, step.key) values[string(step.key)] = string(step.value) case opDelete: - if _, err := root.Delete(step.key, nil); err != nil { + if _, err := root.Delete(step.key, 0, nil); err != nil { rt[i].err = err } delete(values, string(step.key)) case opGet: - v, err := root.Get(step.key, nil) + v, err := root.Get(step.key, 0, nil) want := values[string(step.key)] if string(v) != want { rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x, err %v", step.key, v, want, err) @@ -1819,7 +1841,7 @@ func runRandTest(rt randTest) error { continue } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { rt[i].err = fmt.Errorf("could not verify verkle proof: %s, err %v", ToDot(root), err) } @@ -1862,21 +1884,21 @@ func TestRandomExtracted(t *testing.T) { root := New() - if err := root.Insert(k1490, val_k1490_0, nil); err != nil { + if err := root.Insert(k1490, val_k1490_0, 0, nil); err != nil { t.Fatalf("error inserting key: %v", err) } - if err := root.Insert(k1413, val_k1413_0, nil); err != nil { + if err := root.Insert(k1413, val_k1413_0, 0, nil); err != nil { t.Fatalf("error inserting key: %v", err) } - if _, err := root.Delete(k1413, nil); err != nil { + if _, err := root.Delete(k1413, 0, nil); err != nil { t.Fatalf("error deleting key: %v", err) } - val, err := root.Get(k1490, nil) + val, err := root.Get(k1490, 0, nil) if err != nil { t.Fatalf("error getting key: %v", err) } if !bytes.Equal(val, val_k1490_0) { t.Fatalf("got %x, expected %x", val, val_k1490_0) } -} +} \ No newline at end of file diff --git a/unknown.go b/unknown.go index 28d28666..0c42f261 100644 --- a/unknown.go +++ b/unknown.go @@ -29,18 +29,22 @@ import "errors" type UnknownNode struct{} -func (UnknownNode) Insert([]byte, []byte, NodeResolverFn) error { +func (UnknownNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { return errMissingNodeInStateless } -func (UnknownNode) Delete([]byte, NodeResolverFn) (bool, error) { +func (UnknownNode) Delete([]byte, StatePeriod, NodeResolverFn) (bool, error) { return false, errors.New("cant delete in a subtree missing form a stateless view") } -func (UnknownNode) Get([]byte, NodeResolverFn) ([]byte, error) { +func (UnknownNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } +func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error { + return errors.New("cannot revive an unknown node") +} + func (n UnknownNode) Commit() *Point { return n.Commitment() } @@ -51,8 +55,8 @@ func (UnknownNode) Commitment() *Point { return &id } -func (UnknownNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { - return nil, nil, nil, errors.New("can't generate proof items for unknown node") +func (UnknownNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) { + return nil, nil, nil, nil, errors.New("can't generate proof items for unknown node") } func (UnknownNode) Serialize() ([]byte, error) { diff --git a/unknown_test.go b/unknown_test.go index 8b34c9ad..9e219128 100644 --- a/unknown_test.go +++ b/unknown_test.go @@ -7,13 +7,13 @@ func TestUnknownFuncs(t *testing.T) { un := UnknownNode{} - if err := un.Insert(nil, nil, nil); err != errMissingNodeInStateless { + if err := un.Insert(nil, nil, 0, nil); err != errMissingNodeInStateless { t.Errorf("got %v, want %v", err, errMissingNodeInStateless) } - if _, err := un.Delete(nil, nil); err == nil { + if _, err := un.Delete(nil, 0, nil); err == nil { t.Errorf("got nil error when deleting from a hashed node") } - if _, err := un.Get(nil, nil); err != nil { + if _, err := un.Get(nil, 0, nil); err != nil { t.Errorf("got %v, want nil", err) } var identity Point @@ -24,7 +24,7 @@ func TestUnknownFuncs(t *testing.T) { if comm := un.Commitment(); !comm.Equal(&identity) { t.Errorf("got %v, want identity", comm) } - if _, _, _, err := un.GetProofItems(nil, nil); err == nil { + if _, _, _, _, err := un.GetProofItems(nil, nil); err == nil { t.Errorf("got nil error when getting proof items from a hashed node") } if _, err := un.Serialize(); err == nil {