From 39fdd6cdd063e88718da5ebadf3eaf6a22857bc1 Mon Sep 17 00:00:00 2001 From: weiihann Date: Tue, 8 Oct 2024 22:46:45 +0800 Subject: [PATCH 01/33] feat: add last epoch support --- benchs/main.go | 4 +- config.go | 1 + conversion.go | 21 ++--- debug.go | 7 +- debug_test.go | 8 +- doc.go | 2 + empty.go | 4 +- empty_test.go | 4 +- encoding.go | 27 +++--- encoding_test.go | 11 +-- hashednode.go | 4 +- hashednode_test.go | 4 +- proof_ipa.go | 6 +- proof_test.go | 65 +++++++------- state_epoch.go | 27 ++++++ tree.go | 81 ++++++++++------- tree_ipa_test.go | 12 +-- tree_test.go | 220 ++++++++++++++++++++++----------------------- unknown.go | 4 +- unknown_test.go | 4 +- 20 files changed, 286 insertions(+), 230 deletions(-) create mode 100644 state_epoch.go 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..6661f97a 100644 --- a/config.go +++ b/config.go @@ -35,6 +35,7 @@ const ( NodeWidth = 256 NodeBitWidth byte = 8 StemSize = 31 + EpochSize = 8 ) func equalPaths(key1, key2 []byte) bool { diff --git a/conversion.go b/conversion.go index 31c54827..3eb78507 100644 --- a/conversion.go +++ b/conversion.go @@ -13,13 +13,14 @@ import ( // BatchNewLeafNodeData is a struct that contains the data needed to create a new leaf node. type BatchNewLeafNodeData struct { - Stem Stem - Values map[byte][]byte + Stem Stem + Values map[byte][]byte + LastEpoch StateEpoch } // BatchNewLeafNode creates a new leaf node from the given data. It optimizes LeafNode creation // by batching expensive cryptography operations. It returns the LeafNodes sorted by stem. -func BatchNewLeafNode(nodesValues []BatchNewLeafNodeData) ([]LeafNode, error) { +func BatchNewLeafNode(nodesValues []BatchNewLeafNodeData, curEpoch StateEpoch) ([]LeafNode, error) { cfg := GetConfig() ret := make([]LeafNode, len(nodesValues)) @@ -45,7 +46,7 @@ func BatchNewLeafNode(nodesValues []BatchNewLeafNodeData) ([]LeafNode, error) { } var leaf *LeafNode - leaf, err := NewLeafNode(nv.Stem, valsslice) + leaf, err := NewLeafNode(nv.Stem, valsslice, nv.LastEpoch) if err != nil { return err } @@ -97,14 +98,14 @@ func firstDiffByteIdx(stem1 []byte, stem2 []byte) int { panic("stems are equal") } -func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, resolver NodeResolverFn) error { +func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curEpoch StateEpoch, resolver NodeResolverFn) error { sort.Slice(leaves, func(i, j int) bool { return bytes.Compare(leaves[i].stem, leaves[j].stem) < 0 }) // We first mark all children of the subtreess that we'll update in parallel, // so the subtree updating doesn't produce a concurrent access to n.cowChild(...). - var lastChildrenIdx = -1 + lastChildrenIdx := -1 for i := range leaves { if int(leaves[i].stem[0]) != lastChildrenIdx { lastChildrenIdx = int(leaves[i].stem[0]) @@ -132,13 +133,13 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, resolver NodeReso start := currStemFirstByte end := i group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[start:end], resolver) + return n.insertMigratedLeavesSubtree(leaves[start:end], curEpoch, resolver) }) currStemFirstByte = i } } group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], resolver) + return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], curEpoch, resolver) }) if err := group.Wait(); err != nil { return fmt.Errorf("inserting migrated leaves: %w", err) @@ -147,7 +148,7 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, resolver NodeReso return nil } -func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, resolver NodeResolverFn) error { // skipcq: GO-R1005 +func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curEpoch StateEpoch, resolver NodeResolverFn) error { // skipcq: GO-R1005 for i := range leaves { ln := leaves[i] parent := n @@ -192,7 +193,7 @@ func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, resolver N } } - if err := node.updateMultipleLeaves(nonPresentValues); err != nil { + if err := node.updateMultipleLeaves(nonPresentValues, curEpoch); err != nil { return fmt.Errorf("updating leaves: %s", err) } continue diff --git a/debug.go b/debug.go index 055e200a..e8ae7302 100644 --- a/debug.go +++ b/debug.go @@ -39,8 +39,9 @@ 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"` + LastEpoch StateEpoch `json:"last_epoch"` } ) 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..5274d525 100644 --- a/doc.go +++ b/doc.go @@ -38,6 +38,8 @@ 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") + errExpiredLeafNode = errors.New("trying to access an expired leaf node") + errExpiredNodeNotFound = errors.New("cannot find expired node when reviving") ) const ( diff --git a/empty.go b/empty.go index 2115c9db..a54bb55b 100644 --- a/empty.go +++ b/empty.go @@ -31,11 +31,11 @@ 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, StateEpoch, NodeResolverFn) error { return errDirectInsertIntoEmptyNode } -func (Empty) Delete([]byte, NodeResolverFn) (bool, error) { +func (Empty) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { return false, errors.New("cant delete an empty node") } diff --git a/empty_test.go b/empty_test.go index e5889f82..d48a1f33 100644 --- a/empty_test.go +++ b/empty_test.go @@ -31,11 +31,11 @@ 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") } diff --git a/encoding.go b/encoding.go index d5e27d17..ed394141 100644 --- a/encoding.go +++ b/encoding.go @@ -56,12 +56,13 @@ const ( leafC1CommitmentOffset = leafCommitmentOffset + banderwagon.UncompressedSize leafC2CommitmentOffset = leafC1CommitmentOffset + banderwagon.UncompressedSize leafChildrenOffset = leafC2CommitmentOffset + banderwagon.UncompressedSize + leafLastEpochOffset = leafChildrenOffset + EpochSize leafBalanceSize = 32 leafNonceSize = 8 leafSlotSize = 32 leafValueIndexSize = 1 - singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize - eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBalanceSize + leafNonceSize + singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + EpochSize + eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBalanceSize + leafNonceSize + EpochSize ) func bit(bitlist []byte, nr int) bool { @@ -76,9 +77,9 @@ 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: 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 { @@ -112,7 +113,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[:], + StateEpochFromBytes(serialized[leafLastEpochOffset:leafLastEpochOffset+EpochSize])) ln.setDepth(depth) ln.c1 = new(Point) @@ -137,7 +141,8 @@ 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 + EpochSize + epochOffset := offset - EpochSize values[0] = zero32[:] // 0 version values[1] = serialized[offset : offset+leafBalanceSize] // balance var nonce [32]byte @@ -146,7 +151,7 @@ func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) { values[2] = nonce[:] // nonce values[3] = EmptyCodeHash[:] values[4] = zero32[:] // 0 code size - ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:]) + ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+EpochSize])) ln.setDepth(depth) ln.c1 = new(Point) if err := ln.c1.SetBytesUncompressed(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+banderwagon.UncompressedSize], true); err != nil { @@ -163,12 +168,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[:]) + epochOffset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize + ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+EpochSize])) offset += StemSize cnCommBytes := serialized[offset : offset+banderwagon.UncompressedSize] offset += banderwagon.UncompressedSize rootCommBytes := serialized[offset : offset+banderwagon.UncompressedSize] - offset += banderwagon.UncompressedSize + offset += banderwagon.UncompressedSize + EpochSize idx := serialized[offset] offset += leafValueIndexSize values[idx] = serialized[offset : offset+leafSlotSize] // copy slot @@ -210,7 +216,6 @@ 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..98144819 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+EpochSize { 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) } diff --git a/hashednode.go b/hashednode.go index bd53c9dc..69884741 100644 --- a/hashednode.go +++ b/hashednode.go @@ -32,11 +32,11 @@ import ( type HashedNode struct{} -func (HashedNode) Insert([]byte, []byte, NodeResolverFn) error { +func (HashedNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { return errInsertIntoHash } -func (HashedNode) Delete([]byte, NodeResolverFn) (bool, error) { +func (HashedNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { return false, errors.New("cant delete a hashed node in-place") } diff --git a/hashednode_test.go b/hashednode_test.go index c2312246..bc5a3974 100644 --- a/hashednode_test.go +++ b/hashednode_test.go @@ -31,11 +31,11 @@ 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") } diff --git a/proof_ipa.go b/proof_ipa.go index 15d4e8a8..7d44dd89 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -326,7 +326,7 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{Suffix: key[StemSize]}) newsd := &stemdiff.SuffixDiffs[len(stemdiff.SuffixDiffs)-1] - var valueLen = len(proof.PreValues[i]) + valueLen := len(proof.PreValues[i]) switch valueLen { case 0: // null value @@ -606,7 +606,8 @@ 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 { + // TODO(weiihann): double check the epoch + if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, 0, nil); err != nil { return nil, fmt.Errorf("error overwriting value in post state: %w", err) } } @@ -624,7 +625,6 @@ 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 { - proof, err := DeserializeProof(vp, statediff) if err != nil { return fmt.Errorf("verkle proof deserialization error: %w", err) diff --git a/proof_test.go b/proof_test.go index 5ee7680f..80203f1b 100644 --- a/proof_test.go +++ b/proof_test.go @@ -53,13 +53,13 @@ func TestProofVerifyTwoLeaves(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.Fatal(err) } - if err := root.Insert(oneKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(oneKeyTest, zeroKeyTest, 0, 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() @@ -85,7 +85,7 @@ 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -112,7 +112,7 @@ 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } if i%2 == 0 { @@ -183,11 +183,11 @@ 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, 0, 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() @@ -208,10 +208,10 @@ func TestProofOfAbsenceInternalVerify(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("could not insert key: %v", err) } - if err := root.Insert(oneKeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(oneKeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() @@ -228,10 +228,10 @@ func TestProofOfAbsenceLeafVerify(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("could not insert key: %v", err) } - if err := root.Insert(ffx32KeyTest, zeroKeyTest, nil); err != nil { + if err := root.Insert(ffx32KeyTest, zeroKeyTest, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() @@ -243,14 +243,15 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { 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, 0, 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() @@ -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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } @@ -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, 0, nil); err != nil { b.Fatal(err) } } @@ -323,7 +324,7 @@ 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, 0, nil); err != nil { b.Fatal(err) } } @@ -355,7 +356,7 @@ 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -484,7 +485,7 @@ 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } rootC := root.Commit() @@ -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, 0, nil) if err != nil { t.Fatalf("error inserting value in proof-of-asbsence stem: %v", err) } @@ -546,7 +547,7 @@ 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() @@ -842,7 +843,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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -997,7 +998,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], 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -1005,7 +1006,7 @@ 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], 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -1081,7 +1082,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { // 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, 0, nil); err != nil { t.Fatalf("inserting into the original failed: %v", err) } root.Commit() @@ -1142,7 +1143,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, 0, nil); err != errIsPOAStub { t.Fatalf("expected ErrPOALeafValue, got %v", err) } } @@ -1157,10 +1158,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, 0, 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } @@ -1208,10 +1209,10 @@ func TestProveAbsenceInEmptyHalf(t *testing.T) { key1, _ := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000FF") - if err := root.Insert(key1, fourtyKeyTest, nil); err != nil { + if err := root.Insert(key1, fourtyKeyTest, 0, 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, 0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } diff --git a/state_epoch.go b/state_epoch.go new file mode 100644 index 00000000..69a1a594 --- /dev/null +++ b/state_epoch.go @@ -0,0 +1,27 @@ +package verkle + +import ( + "encoding/binary" +) + +type StateEpoch uint64 + +const ( + NumActiveEpochs = 2 +) + +func EpochExpired(prev StateEpoch, cur StateEpoch) bool { + return cur-prev >= NumActiveEpochs +} + +// Convert epoch to bytes +func (e StateEpoch) Bytes() []byte { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(e)) + return b +} + +// Get the state epoch from bytes +func StateEpochFromBytes(b []byte) StateEpoch { + return StateEpoch(binary.BigEndian.Uint64(b)) +} diff --git a/tree.go b/tree.go index 38c2128a..78b101c2 100644 --- a/tree.go +++ b/tree.go @@ -67,10 +67,10 @@ func KeyToStem(key []byte) Stem { type VerkleNode interface { // Insert or Update value into the tree - Insert([]byte, []byte, NodeResolverFn) error + Insert([]byte, []byte, StateEpoch, NodeResolverFn) error // Delete a leaf with the given key - Delete([]byte, NodeResolverFn) (bool, error) + Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) // Get value at a given key Get([]byte, NodeResolverFn) ([]byte, error) @@ -200,6 +200,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 + + lastEpoch StateEpoch } ) @@ -266,7 +268,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, lastEpoch StateEpoch) (*LeafNode, error) { cfg := GetConfig() // C1. @@ -309,26 +311,31 @@ func NewLeafNode(stem Stem, values [][]byte) (*LeafNode, error) { return nil, fmt.Errorf("batch mapping to scalar fields: %s", err) } + // state epoch + poly[4].SetUint64(uint64(lastEpoch)) + 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, + lastEpoch: lastEpoch, }, 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, lastEpoch StateEpoch) *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, + values: values, + stem: stem, + lastEpoch: lastEpoch, } } @@ -358,13 +365,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, curEpoch StateEpoch, resolver NodeResolverFn) error { values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.InsertValuesAtStem(KeyToStem(key), values, resolver) + return n.InsertValuesAtStem(KeyToStem(key), values, curEpoch, resolver) } -func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, resolver NodeResolverFn) error { +func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch StateEpoch, 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 +380,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, curEpoch) if err != nil { return err } @@ -394,7 +401,7 @@ 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, curEpoch, resolver) case *LeafNode: if equalPaths(child.stem, stem) { // We can't insert any values into a POA leaf node. @@ -402,7 +409,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, curEpoch) } n.cowChild(nChild) @@ -418,12 +425,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, curEpoch, 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, curEpoch) if err != nil { return err } @@ -432,7 +439,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, curEpoch, resolver) default: // It should be an UknownNode. return errUnknownNodeType } @@ -583,7 +590,7 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, resolver NodeResolverFn) ([][] } } -func (n *InternalNode) Delete(key []byte, resolver NodeResolverFn) (bool, error) { +func (n *InternalNode) Delete(key []byte, curEpoch StateEpoch, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { case Empty: @@ -602,10 +609,10 @@ 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, curEpoch, resolver) default: n.cowChild(nChild) - del, err := child.Delete(key, resolver) + del, err := child.Delete(key, curEpoch, resolver) if err != nil { return false, err } @@ -846,7 +853,6 @@ func (n *InternalNode) Commit() *Point { // TODO: make Commit() return an error panic(err) } - }() } wg.Wait() @@ -1146,7 +1152,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, curEpoch StateEpoch, _ NodeResolverFn) error { if n.isPOAStub { return errIsPOAStub } @@ -1161,16 +1167,20 @@ func (n *LeafNode) Insert(key []byte, value []byte, _ NodeResolverFn) error { } values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.insertMultiple(stem, values) + return n.insertMultiple(stem, values, curEpoch) } -func (n *LeafNode) insertMultiple(stem Stem, values [][]byte) error { +func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curEpoch StateEpoch) error { // Sanity check: ensure the stems are the same. if !equalPaths(stem, n.stem) { return errInsertIntoOtherStem } - return n.updateMultipleLeaves(values) + if err := n.updateMultipleLeaves(values, curEpoch); err != nil { + return err + } + + return nil } func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr) { @@ -1250,7 +1260,7 @@ func (n *LeafNode) updateLeaf(index byte, value []byte) error { return nil } -func (n *LeafNode) updateMultipleLeaves(values [][]byte) error { // skipcq: GO-R1005 +func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) 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. @@ -1298,16 +1308,19 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte) error { // skipcq: GO-R } n.updateC(c1Idx, frs[0], frs[1]) n.updateC(c2Idx, frs[2], frs[3]) + n.lastEpoch = curEpoch } 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.lastEpoch = curEpoch } 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.lastEpoch = curEpoch } return nil @@ -1315,7 +1328,7 @@ 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, curEpoch StateEpoch, _ NodeResolverFn) (bool, error) { // Sanity check: ensure the key header is the same: if !equalPaths(k, n.stem) { return false, nil @@ -1642,7 +1655,7 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements } // 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 @@ -1888,6 +1901,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte + lastEpoch := n.lastEpoch.Bytes() switch { case count == 1: var buf [singleSlotLeafSize]byte @@ -1896,8 +1910,9 @@ 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[:]) - 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:], lastEpoch) + result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize] = byte(lastIdx) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize+leafValueIndexSize:], n.values[lastIdx][:]) case isEoA: var buf [eoaLeafSize]byte result = buf[:] @@ -1905,16 +1920,18 @@ 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[1]) // copy balance - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+leafBalanceSize:], n.values[2][:leafNonceSize]) // copy nonce + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastEpoch) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize:], n.values[1]) // copy balance + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize+leafBalanceSize:], n.values[2][:leafNonceSize]) // copy nonce default: - result = make([]byte, nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+len(children)) + result = make([]byte, nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+EpochSize+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[leafLastEpochOffset:], lastEpoch) 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..5304061c 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,10 +133,10 @@ 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) } @@ -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) } @@ -335,7 +335,7 @@ 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) @@ -346,7 +346,7 @@ 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) @@ -366,16 +366,16 @@ 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 @@ -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() @@ -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() @@ -500,25 +500,25 @@ func TestDeletePrune(t *testing.T) { // skipcq: GO-R1005 // their hashed values. It then tries to delete the hashed values, which should // fail. func TestDeleteHash(t *testing.T) { - //TODO: fix this test when we take a final decision about FlushAtDepth API. + // TODO: fix this test when we take a final decision about FlushAtDepth API. t.SkipNow() key1, _ := hex.DecodeString("0105000000000000000000000000000000000000000000000000000000000000") 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,21 +530,21 @@ 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) } } func TestDeleteResolve(t *testing.T) { - //TODO: fix this test when we take a final decision about FlushAtDepth API. + // TODO: fix this test when we take a final decision about FlushAtDepth API. t.SkipNow() key1, _ := hex.DecodeString("0105000000000000000000000000000000000000000000000000000000000000") @@ -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() @@ -637,7 +637,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 +679,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 +700,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 +714,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 +744,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() @@ -851,7 +851,7 @@ func isLeafEqual(a, b *LeafNode) bool { } func TestGetResolveFromHash(t *testing.T) { - //TODO: fix this test when we take a final decision about FlushAtDepth API. + // TODO: fix this test when we take a final decision about FlushAtDepth API. t.SkipNow() var count uint @@ -873,14 +873,14 @@ 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) } @@ -926,30 +926,30 @@ func TestGetKey(t *testing.T) { } func TestInsertIntoHashedNode(t *testing.T) { - //TODO: fix this test when we take a final decision about FlushAtDepth API. + // TODO: fix this test when we take a final decision about FlushAtDepth API. 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 +958,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 +972,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,7 +1021,7 @@ 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() @@ -1083,7 +1083,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) } @@ -1128,7 +1128,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 +1150,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, nil); err != nil { t.Fatalf("error inserting: %s", err) } r1c := root1.Commit() @@ -1160,10 +1160,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 +1179,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 +1198,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, nil); err != nil { t.Fatalf("error inserting stem: %v", err) } root.Commit() @@ -1215,7 +1215,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 +1237,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 +1331,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,7 +1341,7 @@ 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) } @@ -1403,7 +1403,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 +1421,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 +1459,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) } } @@ -1497,16 +1497,16 @@ func TestBatchMigratedKeyValues(t *testing.T) { nodeValues = append(nodeValues, curr) // Create all leaves in batch mode so we can optimize cryptography operations. - newLeaves, err := BatchNewLeafNode(nodeValues) + newLeaves, err := BatchNewLeafNode(nodeValues, 0) if err != nil { t.Fatalf("failed to create leaves: %v", err) } - if err := tree.(*InternalNode).InsertMigratedLeaves(newLeaves, nil); err != nil { + if err := tree.(*InternalNode).InsertMigratedLeaves(newLeaves, 0, nil); err != nil { t.Fatalf("failed to insert key: %v", err) } - if err = tree.(*InternalNode).InsertMigratedLeaves(newLeaves, nil); err != nil { + if err = tree.(*InternalNode).InsertMigratedLeaves(newLeaves, 0, nil); err != nil { t.Fatalf("failed to insert key: %v", err) } batchedRoot := tree.Commit().Bytes() @@ -1529,7 +1529,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 +1562,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() @@ -1589,11 +1589,11 @@ func BenchmarkBatchLeavesInsert(b *testing.B) { nodeValues = append(nodeValues, curr) // Create all leaves in batch mode so we can optimize cryptography operations. - newLeaves, err := BatchNewLeafNode(nodeValues) + newLeaves, err := BatchNewLeafNode(nodeValues, 0) if err != nil { b.Fatalf("failed to batch-create leaf node: %v", err) } - if err := tree.(*InternalNode).InsertMigratedLeaves(newLeaves, nil); err != nil { + if err := tree.(*InternalNode).InsertMigratedLeaves(newLeaves, 0, nil); err != nil { b.Fatalf("failed to insert key: %v", err) } @@ -1607,7 +1607,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,7 +1642,7 @@ 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) } @@ -1672,7 +1672,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 +1683,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 +1691,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") } @@ -1723,7 +1723,7 @@ const ( // Generate implements the quick.Generator interface from testing/quick // to generate random test cases. func (randTest) Generate(r *mRandV1.Rand, size int) reflect.Value { - var finishedFn = func() bool { + finishedFn := func() bool { if size == 0 { return true } @@ -1735,7 +1735,7 @@ func (randTest) Generate(r *mRandV1.Rand, size int) reflect.Value { func generateSteps(finished func() bool, r io.Reader) randTest { var allKeys [][]byte - var tmp = []byte{0} + tmp := []byte{0} genKey := func() []byte { _, err := r.Read(tmp) if err != nil { @@ -1798,13 +1798,13 @@ 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)) @@ -1862,13 +1862,13 @@ 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) } diff --git a/unknown.go b/unknown.go index 28d28666..e6d0e6fc 100644 --- a/unknown.go +++ b/unknown.go @@ -29,11 +29,11 @@ import "errors" type UnknownNode struct{} -func (UnknownNode) Insert([]byte, []byte, NodeResolverFn) error { +func (UnknownNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { return errMissingNodeInStateless } -func (UnknownNode) Delete([]byte, NodeResolverFn) (bool, error) { +func (UnknownNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { return false, errors.New("cant delete in a subtree missing form a stateless view") } diff --git a/unknown_test.go b/unknown_test.go index 8b34c9ad..b4e7ab70 100644 --- a/unknown_test.go +++ b/unknown_test.go @@ -7,10 +7,10 @@ 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 { From 32ce419e71b20a51bd68f5d897aee46344d2c55c Mon Sep 17 00:00:00 2001 From: weiihann Date: Thu, 10 Oct 2024 19:15:34 +0800 Subject: [PATCH 02/33] calculate leaf commitment for insert/delete --- tree.go | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/tree.go b/tree.go index 78b101c2..f0a0a44b 100644 --- a/tree.go +++ b/tree.go @@ -1183,7 +1183,7 @@ func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curEpoch StateEpoc return nil } -func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr) { +func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curEpoch StateEpoch) { // Calculate the Fr-delta. var deltaC Fr deltaC.Sub(&newC, &oldC) @@ -1192,6 +1192,10 @@ func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr) { var poly [NodeWidth]Fr poly[cxIndex] = deltaC + if n.lastEpoch != curEpoch { + poly[4].SetUint64(uint64(curEpoch)) + } + // Add delta to the current commitment. n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) } @@ -1231,7 +1235,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, curEpoch StateEpoch) error { // Update the corresponding C1 or C2 commitment. var c *Point var oldC Point @@ -1254,7 +1258,8 @@ 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], curEpoch) + n.updateLastEpoch(curEpoch) n.values[index] = value return nil @@ -1306,21 +1311,21 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) er 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.lastEpoch = curEpoch + n.updateC(c1Idx, frs[0], frs[1], curEpoch) + n.updateC(c2Idx, frs[2], frs[3], curEpoch) + n.updateLastEpoch(curEpoch) } 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.lastEpoch = curEpoch + n.updateC(c1Idx, frs[0], frs[1], curEpoch) + n.updateLastEpoch(curEpoch) } 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.lastEpoch = curEpoch + n.updateC(c2Idx, frs[0], frs[1], curEpoch) + n.updateLastEpoch(curEpoch) } return nil @@ -1393,8 +1398,17 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool // 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.lastEpoch != curEpoch { + var poly [5]Fr + poly[4].SetUint64(uint64(curEpoch)) + n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) + n.updateLastEpoch(curEpoch) + } + // Clear the corresponding commitment if k[StemSize] < 128 { n.c1 = nil @@ -1416,7 +1430,7 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool // 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, curEpoch) } func (n *LeafNode) Get(k []byte, _ NodeResolverFn) ([]byte, error) { @@ -1455,6 +1469,10 @@ func (n *LeafNode) Commit() *Point { return n.commitment } +func (n *LeafNode) updateLastEpoch(curEpoch StateEpoch) { + n.lastEpoch = curEpoch +} + // fillSuffixTreePoly takes one of the two suffix tree and // builds the associated polynomial, to be used to compute // the corresponding C{1,2} commitment. From 25ba7b9ce8712e47dc19e9fea3de2b131235abef Mon Sep 17 00:00:00 2001 From: weiihann Date: Thu, 10 Oct 2024 19:31:43 +0800 Subject: [PATCH 03/33] Add expiry access rules --- doc.go | 1 + tree.go | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/doc.go b/doc.go index 5274d525..938830e5 100644 --- a/doc.go +++ b/doc.go @@ -40,6 +40,7 @@ var ( errIsPOAStub = errors.New("trying to read/write a proof of absence leaf node") errExpiredLeafNode = errors.New("trying to access an expired leaf node") errExpiredNodeNotFound = errors.New("cannot find expired node when reviving") + errEpochExpired = errors.New("trying to access an expired node") ) const ( diff --git a/tree.go b/tree.go index f0a0a44b..ecb39590 100644 --- a/tree.go +++ b/tree.go @@ -1161,6 +1161,10 @@ func (n *LeafNode) Insert(key []byte, value []byte, curEpoch StateEpoch, _ NodeR return fmt.Errorf("invalid key size: %d", len(key)) } + if EpochExpired(n.lastEpoch, curEpoch) { + return errEpochExpired + } + stem := KeyToStem(key) if !bytes.Equal(stem, n.stem) { return fmt.Errorf("stems don't match: %x != %x", stem, n.stem) @@ -1339,6 +1343,10 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool return false, nil } + if EpochExpired(n.lastEpoch, curEpoch) { + return false, errEpochExpired + } + // Erase the value it used to contain original := n.values[k[StemSize]] // save original value n.values[k[StemSize]] = nil From ec74e561d4699186ba65a9c25e339776e63baf6a Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 11 Oct 2024 17:50:57 +0800 Subject: [PATCH 04/33] Add ExpiredLeafNode --- debug.go | 5 +++ doc.go | 4 +- encoding.go | 14 +++++++ expired_leaf.go | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ tree.go | 42 +++++++++++++++++--- 5 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 expired_leaf.go diff --git a/debug.go b/debug.go index e8ae7302..126e8859 100644 --- a/debug.go +++ b/debug.go @@ -44,4 +44,9 @@ type ( C2 [32]byte `json:"c2"` LastEpoch StateEpoch `json:"last_epoch"` } + + ExportableExpiredLeafNode struct { + Stem Stem `json:"stem"` + Commitment [32]byte `json:"commitment"` + } ) diff --git a/doc.go b/doc.go index 938830e5..37c44d7c 100644 --- a/doc.go +++ b/doc.go @@ -38,9 +38,7 @@ 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") - errExpiredLeafNode = errors.New("trying to access an expired leaf node") - errExpiredNodeNotFound = errors.New("cannot find expired node when reviving") - errEpochExpired = errors.New("trying to access an expired node") + errEpochExpired = errors.New("trying to access an expired leaf node") ) const ( diff --git a/encoding.go b/encoding.go index ed394141..4b48077c 100644 --- a/encoding.go +++ b/encoding.go @@ -63,6 +63,7 @@ const ( leafValueIndexSize = 1 singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + EpochSize eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBalanceSize + leafNonceSize + EpochSize + expiredLeafSize = nodeTypeSize + StemSize + banderwagon.UncompressedSize ) func bit(bitlist []byte, nr int) bool { @@ -80,6 +81,7 @@ var errSerializedPayloadTooShort = errors.New("verkle payload is too short") // - 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 { @@ -95,6 +97,8 @@ func ParseNode(serializedNode []byte, depth byte) (VerkleNode, error) { return parseEoAccountNode(serializedNode, depth) case singleSlotType: return parseSingleSlotNode(serializedNode, depth) + case expiredLeafType: // TODO(weiihann) + return parseExpiredLeafNode(serializedNode) default: return nil, ErrInvalidNodeEncoding } @@ -199,6 +203,16 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { return ln, nil } +func parseExpiredLeafNode(serialized []byte) (VerkleNode, error) { + l := &ExpiredLeafNode{} + 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 diff --git a/expired_leaf.go b/expired_leaf.go new file mode 100644 index 00000000..ff0180a8 --- /dev/null +++ b/expired_leaf.go @@ -0,0 +1,102 @@ +// 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 ( + "errors" +) + +type ExpiredLeafNode struct { + stem Stem + commitment *Point +} + +var errExpiredLeafNode = errors.New("trying to access an expired leaf node") + +func (ExpiredLeafNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { + return errExpiredLeafNode +} + +func (ExpiredLeafNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { + return false, errExpiredLeafNode +} + +func (ExpiredLeafNode) Get([]byte, NodeResolverFn) ([]byte, error) { + return nil, errExpiredLeafNode +} + +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, error) { + return nil, nil, nil, errExpiredLeafNode +} + +func (n ExpiredLeafNode) Serialize() ([]byte, error) { + cBytes := n.commitment.Bytes() + + 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)) + + if n.commitment != nil { + l.commitment = new(Point) + l.commitment.Set(n.commitment) + } + + return l +} + +func (n ExpiredLeafNode) toDot(string, string) string { + return "" +} + +func (n ExpiredLeafNode) setDepth(_ byte) { + panic("should not be try to set the depth of an ExpiredLeafNode node") +} + +func (n ExpiredLeafNode) Hash() *Fr { + var hash Fr + n.commitment.MapToScalarField(&hash) + return &hash +} diff --git a/tree.go b/tree.go index ecb39590..90715a3f 100644 --- a/tree.go +++ b/tree.go @@ -166,10 +166,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 ( @@ -227,6 +228,11 @@ func (n *InternalNode) toExportable() *ExportableInternalNode { C: child.commitment.Bytes(), C1: child.c1.Bytes(), } + case *ExpiredLeafNode: + exportable.Children[i] = &ExportableExpiredLeafNode{ + Stem: child.stem, + Commitment: child.commitment.Bytes(), + } default: panic("unexportable type") } @@ -402,6 +408,8 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S // recurse to handle the case of a LeafNode child that // splits. return n.InsertValuesAtStem(stem, values, curEpoch, resolver) + case *ExpiredLeafNode: + return errEpochExpired case *LeafNode: if equalPaths(child.stem, stem) { // We can't insert any values into a POA leaf node. @@ -532,7 +540,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) @@ -573,6 +581,8 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, resolver NodeResolverFn) ([][] // recurse to handle the case of a LeafNode child that // splits. return n.GetValuesAtStem(stem, resolver) + case *ExpiredLeafNode: + return nil, errEpochExpired case *LeafNode: if equalPaths(child.stem, stem) { // We can't return the values since it's a POA leaf node, so we know nothing @@ -610,6 +620,8 @@ func (n *InternalNode) Delete(key []byte, curEpoch StateEpoch, resolver NodeReso } n.children[nChild] = c return n.Delete(key, curEpoch, resolver) + case *ExpiredLeafNode: + return false, errEpochExpired default: n.cowChild(nChild) del, err := child.Delete(key, curEpoch, resolver) @@ -661,6 +673,8 @@ func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, } n.children[nChild] = c return n.DeleteAtStem(key, resolver) + case *ExpiredLeafNode: + return false, errEpochExpired case *LeafNode: if !bytes.Equal(child.stem, key[:31]) { return false, errDeleteMissing @@ -1790,6 +1804,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) } } @@ -1815,6 +1831,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] @@ -1841,6 +1870,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) From 3f23ce0422a7eb237dfdf7dceca149d7e69bb022 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 11 Oct 2024 18:00:55 +0800 Subject: [PATCH 05/33] add StateEpoch to Get --- empty.go | 2 +- empty_test.go | 2 +- expired_leaf.go | 2 +- hashednode.go | 2 +- hashednode_test.go | 2 +- proof_ipa.go | 4 ++-- proof_test.go | 4 ++-- tree.go | 18 +++++++++++------- tree_test.go | 30 +++++++++++++++--------------- unknown.go | 2 +- unknown_test.go | 2 +- 11 files changed, 37 insertions(+), 33 deletions(-) diff --git a/empty.go b/empty.go index a54bb55b..bb9a8571 100644 --- a/empty.go +++ b/empty.go @@ -39,7 +39,7 @@ func (Empty) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { return false, errors.New("cant delete an empty node") } -func (Empty) Get([]byte, NodeResolverFn) ([]byte, error) { +func (Empty) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { return nil, nil } diff --git a/empty_test.go b/empty_test.go index d48a1f33..318e110c 100644 --- a/empty_test.go +++ b/empty_test.go @@ -39,7 +39,7 @@ func TestEmptyFuncs(t *testing.T) { 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") } diff --git a/expired_leaf.go b/expired_leaf.go index ff0180a8..751e47e6 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -44,7 +44,7 @@ func (ExpiredLeafNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) return false, errExpiredLeafNode } -func (ExpiredLeafNode) Get([]byte, NodeResolverFn) ([]byte, error) { +func (ExpiredLeafNode) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { return nil, errExpiredLeafNode } diff --git a/hashednode.go b/hashednode.go index 69884741..881080c5 100644 --- a/hashednode.go +++ b/hashednode.go @@ -40,7 +40,7 @@ func (HashedNode) Delete([]byte, StateEpoch, 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, StateEpoch, NodeResolverFn) ([]byte, error) { return nil, errors.New("can not read from a hash node") } diff --git a/hashednode_test.go b/hashednode_test.go index bc5a3974..1c48d9db 100644 --- a/hashednode_test.go +++ b/hashednode_test.go @@ -39,7 +39,7 @@ func TestHashedNodeFuncs(t *testing.T) { 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") } diff --git a/proof_ipa.go b/proof_ipa.go index 7d44dd89..1844d4f9 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -211,7 +211,7 @@ 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], 0, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("error getting post-state value for key %x: %w", keys[i], err) } @@ -646,7 +646,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[:], 0, nil) if err != nil { return fmt.Errorf("could not find key %x in tree rebuilt from proof: %w", key, err) } diff --git a/proof_test.go b/proof_test.go index 80203f1b..b8d4c4a6 100644 --- a/proof_test.go +++ b/proof_test.go @@ -507,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, 0, nil) if err != nil { t.Fatalf("error while trying to read missing value: %v", err) } @@ -1132,7 +1132,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[:], 0, nil); err != errIsPOAStub { t.Fatalf("expected ErrPOALeafValue, got %v", err) } } diff --git a/tree.go b/tree.go index 90715a3f..b196ad8d 100644 --- a/tree.go +++ b/tree.go @@ -73,7 +73,7 @@ type VerkleNode interface { Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) // Get value at a given key - Get([]byte, NodeResolverFn) ([]byte, error) + Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) // Commit computes the commitment of the node. The // result (the curve point) is cached. @@ -558,7 +558,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, curEpoch StateEpoch, 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: @@ -580,10 +580,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, curEpoch, resolver) case *ExpiredLeafNode: return nil, errEpochExpired case *LeafNode: + if EpochExpired(child.lastEpoch, curEpoch) { + return nil, errEpochExpired + } + 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. @@ -594,7 +598,7 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, resolver NodeResolverFn) ([][] } return nil, nil case *InternalNode: - return child.GetValuesAtStem(stem, resolver) + return child.GetValuesAtStem(stem, curEpoch, resolver) default: return nil, errUnknownNodeType } @@ -784,11 +788,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, curEpoch StateEpoch, 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), curEpoch, resolver) if err != nil { return nil, err } @@ -1455,7 +1459,7 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool return false, n.updateLeaf(k[StemSize], nil, curEpoch) } -func (n *LeafNode) Get(k []byte, _ NodeResolverFn) ([]byte, error) { +func (n *LeafNode) Get(k []byte, curEpoch StateEpoch, _ NodeResolverFn) ([]byte, error) { if n.isPOAStub { return nil, errIsPOAStub } diff --git a/tree_test.go b/tree_test.go index 5304061c..a0c7f5dc 100644 --- a/tree_test.go +++ b/tree_test.go @@ -140,7 +140,7 @@ func TestGetTwoLeaves(t *testing.T) { 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) } @@ -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) } @@ -338,7 +338,7 @@ func TestDelLeaf(t *testing.T) { // skipcq: GO-R1005 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) } @@ -349,7 +349,7 @@ func TestDelLeaf(t *testing.T) { // skipcq: GO-R1005 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) } @@ -385,14 +385,14 @@ func TestDeleteAtStem(t *testing.T) { 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) } @@ -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) } @@ -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) } @@ -884,17 +884,17 @@ func TestGetResolveFromHash(t *testing.T) { 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) } @@ -1648,7 +1648,7 @@ func TestLeafNodeInsert(t *testing.T) { } // 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) } @@ -1809,7 +1809,7 @@ func runRandTest(rt randTest) error { } 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) @@ -1872,7 +1872,7 @@ func TestRandomExtracted(t *testing.T) { 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) } diff --git a/unknown.go b/unknown.go index e6d0e6fc..62d8232d 100644 --- a/unknown.go +++ b/unknown.go @@ -37,7 +37,7 @@ func (UnknownNode) Delete([]byte, StateEpoch, 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, StateEpoch, NodeResolverFn) ([]byte, error) { return nil, nil } diff --git a/unknown_test.go b/unknown_test.go index b4e7ab70..8fb1c404 100644 --- a/unknown_test.go +++ b/unknown_test.go @@ -13,7 +13,7 @@ func TestUnknownFuncs(t *testing.T) { 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 From de94975872a4f62e04c17a0e66a99f43da1412c1 Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 16 Oct 2024 17:26:24 +0800 Subject: [PATCH 06/33] change epoch to timestamps, add encoding test --- conversion.go | 19 ++++--- debug.go | 8 +-- empty.go | 6 +-- encoding.go | 14 +++--- encoding_test.go | 32 ++++++++++++ expired_leaf.go | 25 +++++----- hashednode.go | 6 +-- proof_ipa.go | 16 +++--- proof_test.go | 50 +++++++++---------- state_epoch.go | 27 ---------- timestamp.go | 19 +++++++ tree.go | 127 ++++++++++++++++++++++++----------------------- tree_test.go | 8 +-- unknown.go | 6 +-- 14 files changed, 193 insertions(+), 170 deletions(-) delete mode 100644 state_epoch.go create mode 100644 timestamp.go diff --git a/conversion.go b/conversion.go index 3eb78507..547199ee 100644 --- a/conversion.go +++ b/conversion.go @@ -13,14 +13,13 @@ import ( // BatchNewLeafNodeData is a struct that contains the data needed to create a new leaf node. type BatchNewLeafNodeData struct { - Stem Stem - Values map[byte][]byte - LastEpoch StateEpoch + Stem Stem + Values map[byte][]byte } // BatchNewLeafNode creates a new leaf node from the given data. It optimizes LeafNode creation // by batching expensive cryptography operations. It returns the LeafNodes sorted by stem. -func BatchNewLeafNode(nodesValues []BatchNewLeafNodeData, curEpoch StateEpoch) ([]LeafNode, error) { +func BatchNewLeafNode(nodesValues []BatchNewLeafNodeData) ([]LeafNode, error) { cfg := GetConfig() ret := make([]LeafNode, len(nodesValues)) @@ -46,7 +45,7 @@ func BatchNewLeafNode(nodesValues []BatchNewLeafNodeData, curEpoch StateEpoch) ( } var leaf *LeafNode - leaf, err := NewLeafNode(nv.Stem, valsslice, nv.LastEpoch) + leaf, err := NewLeafNode(nv.Stem, valsslice, 0) if err != nil { return err } @@ -98,7 +97,7 @@ func firstDiffByteIdx(stem1 []byte, stem2 []byte) int { panic("stems are equal") } -func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curEpoch StateEpoch, resolver NodeResolverFn) error { +func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curTs AccessTimestamp, resolver NodeResolverFn) error { sort.Slice(leaves, func(i, j int) bool { return bytes.Compare(leaves[i].stem, leaves[j].stem) < 0 }) @@ -133,13 +132,13 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curEpoch StateEpo start := currStemFirstByte end := i group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[start:end], curEpoch, resolver) + return n.insertMigratedLeavesSubtree(leaves[start:end], curTs, resolver) }) currStemFirstByte = i } } group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], curEpoch, resolver) + return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], curTs, resolver) }) if err := group.Wait(); err != nil { return fmt.Errorf("inserting migrated leaves: %w", err) @@ -148,7 +147,7 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curEpoch StateEpo return nil } -func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curEpoch StateEpoch, resolver NodeResolverFn) error { // skipcq: GO-R1005 +func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curTs AccessTimestamp, resolver NodeResolverFn) error { // skipcq: GO-R1005 for i := range leaves { ln := leaves[i] parent := n @@ -193,7 +192,7 @@ func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curEpoch S } } - if err := node.updateMultipleLeaves(nonPresentValues, curEpoch); err != nil { + if err := node.updateMultipleLeaves(nonPresentValues, curTs); err != nil { return fmt.Errorf("updating leaves: %s", err) } continue diff --git a/debug.go b/debug.go index 126e8859..ef534302 100644 --- a/debug.go +++ b/debug.go @@ -39,10 +39,10 @@ type ( Stem Stem `json:"stem"` Values [][]byte `json:"values"` - C [32]byte `json:"commitment"` - C1 [32]byte `json:"c1"` - C2 [32]byte `json:"c2"` - LastEpoch StateEpoch `json:"last_epoch"` + C [32]byte `json:"commitment"` + C1 [32]byte `json:"c1"` + C2 [32]byte `json:"c2"` + LastTs AccessTimestamp `json:"last_epoch"` } ExportableExpiredLeafNode struct { diff --git a/empty.go b/empty.go index bb9a8571..2e79c485 100644 --- a/empty.go +++ b/empty.go @@ -31,15 +31,15 @@ type Empty struct{} var errDirectInsertIntoEmptyNode = errors.New("an empty node should not be inserted directly into") -func (Empty) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { +func (Empty) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { return errDirectInsertIntoEmptyNode } -func (Empty) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { +func (Empty) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { return false, errors.New("cant delete an empty node") } -func (Empty) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { +func (Empty) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { return nil, nil } diff --git a/encoding.go b/encoding.go index 4b48077c..cb0760ba 100644 --- a/encoding.go +++ b/encoding.go @@ -56,7 +56,7 @@ const ( leafC1CommitmentOffset = leafCommitmentOffset + banderwagon.UncompressedSize leafC2CommitmentOffset = leafC1CommitmentOffset + banderwagon.UncompressedSize leafChildrenOffset = leafC2CommitmentOffset + banderwagon.UncompressedSize - leafLastEpochOffset = leafChildrenOffset + EpochSize + leafLastTsOffset = leafChildrenOffset + EpochSize leafBalanceSize = 32 leafNonceSize = 8 leafSlotSize = 32 @@ -78,9 +78,9 @@ 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. @@ -120,7 +120,7 @@ func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) { ln := NewLeafNodeWithNoComms( serialized[leafStemOffset:leafStemOffset+StemSize], values[:], - StateEpochFromBytes(serialized[leafLastEpochOffset:leafLastEpochOffset+EpochSize])) + AccessTimestampFromBytes(serialized[leafLastTsOffset:leafLastTsOffset+EpochSize])) ln.setDepth(depth) ln.c1 = new(Point) @@ -155,7 +155,7 @@ func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) { values[2] = nonce[:] // nonce values[3] = EmptyCodeHash[:] values[4] = zero32[:] // 0 code size - ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+EpochSize])) + ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:], AccessTimestampFromBytes(serialized[epochOffset:epochOffset+EpochSize])) ln.setDepth(depth) ln.c1 = new(Point) if err := ln.c1.SetBytesUncompressed(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+banderwagon.UncompressedSize], true); err != nil { @@ -173,7 +173,7 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { var values [NodeWidth][]byte offset := leafStemOffset epochOffset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize - ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+EpochSize])) + ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], AccessTimestampFromBytes(serialized[epochOffset:epochOffset+EpochSize])) offset += StemSize cnCommBytes := serialized[offset : offset+banderwagon.UncompressedSize] offset += banderwagon.UncompressedSize diff --git a/encoding_test.go b/encoding_test.go index 98144819..c95e8196 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -191,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[:31] + 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 index 751e47e6..58e2a2c2 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -25,27 +25,25 @@ package verkle -import ( - "errors" -) - type ExpiredLeafNode struct { stem Stem commitment *Point } -var errExpiredLeafNode = errors.New("trying to access an expired leaf node") +func NewExpiredLeafNode(stem Stem, commitment *Point) *ExpiredLeafNode { + return &ExpiredLeafNode{stem: stem, commitment: commitment} +} -func (ExpiredLeafNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { - return errExpiredLeafNode +func (ExpiredLeafNode) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { + return errEpochExpired } -func (ExpiredLeafNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { - return false, errExpiredLeafNode +func (ExpiredLeafNode) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { + return false, errEpochExpired } -func (ExpiredLeafNode) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { - return nil, errExpiredLeafNode +func (ExpiredLeafNode) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { + return nil, errEpochExpired } func (n ExpiredLeafNode) Commit() *Point { @@ -59,12 +57,13 @@ func (n ExpiredLeafNode) Commitment() *Point { return n.commitment } +// TODO(weiihann): prove that something was expired, for the block to be able to execute statelessly. func (n ExpiredLeafNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { - return nil, nil, nil, errExpiredLeafNode + return nil, nil, nil, errEpochExpired } func (n ExpiredLeafNode) Serialize() ([]byte, error) { - cBytes := n.commitment.Bytes() + cBytes := n.commitment.BytesUncompressedTrusted() var buf [expiredLeafSize]byte result := buf[:] diff --git a/hashednode.go b/hashednode.go index 881080c5..5566fb93 100644 --- a/hashednode.go +++ b/hashednode.go @@ -32,15 +32,15 @@ import ( type HashedNode struct{} -func (HashedNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { +func (HashedNode) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { return errInsertIntoHash } -func (HashedNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { +func (HashedNode) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { return false, errors.New("cant delete a hashed node in-place") } -func (HashedNode) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { +func (HashedNode) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { return nil, errors.New("can not read from a hash node") } diff --git a/proof_ipa.go b/proof_ipa.go index 1844d4f9..912f39aa 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -192,7 +192,7 @@ 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, curTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, error) { // go-ipa won't accept no key as an input, catch this corner case // and return an empty result. if len(keys) == 0 { @@ -211,7 +211,7 @@ 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], 0, resolver) + val, err := postroot.Get(keys[i], curTs, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("error getting post-state value for key %x: %w", keys[i], err) } @@ -226,8 +226,8 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, resol return pe, es, poas, postvals, 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, curTs AccessTimestamp, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { + pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, curTs, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("get commitments for multiproof: %s", err) } @@ -269,8 +269,8 @@ func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, resolver } // 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) +func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode, curTs AccessTimestamp) error { + pe, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, curTs, nil) if err != nil { return fmt.Errorf("error getting proof elements: %w", err) } @@ -624,7 +624,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, curTs AccessTimestamp) error { proof, err := DeserializeProof(vp, statediff) if err != nil { return fmt.Errorf("verkle proof deserialization error: %w", err) @@ -675,5 +675,5 @@ func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statedif return fmt.Errorf("post tree root mismatch: %x != %x", regeneratedPostTreeRoot, postStateRoot) } - return verifyVerkleProofWithPreState(proof, pretree) + return verifyVerkleProofWithPreState(proof, pretree, curTs) } diff --git a/proof_test.go b/proof_test.go index b8d4c4a6..193a8d48 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}, 0, 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)) @@ -64,7 +64,7 @@ func TestProofVerifyTwoLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -91,7 +91,7 @@ func TestProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -118,7 +118,7 @@ func TestMultiProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], 0, nil) pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) if err != nil { @@ -158,7 +158,7 @@ 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, 0, nil) pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { @@ -192,7 +192,7 @@ func TestMultiProofVerifyMultipleLeavesCommitmentRedundancy(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, nil) pe, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { @@ -216,7 +216,7 @@ func TestProofOfAbsenceInternalVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -236,7 +236,7 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -261,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}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -283,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}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -309,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]}, 0, nil); err != nil { b.Fatal(err) } } @@ -330,7 +330,7 @@ func BenchmarkProofVerification(b *testing.B) { } 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]}, 0, nil) b.ResetTimer() b.ReportAllocs() @@ -361,7 +361,7 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { } } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -400,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[:]}, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -441,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[:]}, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -471,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}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -492,7 +492,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { 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}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -554,7 +554,7 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) { 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}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -863,7 +863,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, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1012,7 +1012,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 } postroot.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, 0, nil) keys: for i := range proof.Keys { @@ -1051,7 +1051,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 t.Fatalf("error deserializing proof: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, root); err != nil { + if err = verifyVerkleProofWithPreState(dproof, root, 0); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(root), ToDot(postroot)) } @@ -1069,7 +1069,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 t.Fatalf("differing root commitments %x != %x", dpostroot.Commitment().Bytes(), postroot.Commitment().Bytes()) } - if err = verifyVerkleProofWithPreState(dproof, dpreroot); err != nil { + if err = verifyVerkleProofWithPreState(dproof, dpreroot, 0); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) } }) @@ -1089,7 +1089,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { // 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}, 0, nil) if err != nil { t.Fatal(err) } @@ -1169,7 +1169,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}, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1218,7 +1218,7 @@ func TestProveAbsenceInEmptyHalf(t *testing.T) { key2, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000100") key3, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { diff --git a/state_epoch.go b/state_epoch.go deleted file mode 100644 index 69a1a594..00000000 --- a/state_epoch.go +++ /dev/null @@ -1,27 +0,0 @@ -package verkle - -import ( - "encoding/binary" -) - -type StateEpoch uint64 - -const ( - NumActiveEpochs = 2 -) - -func EpochExpired(prev StateEpoch, cur StateEpoch) bool { - return cur-prev >= NumActiveEpochs -} - -// Convert epoch to bytes -func (e StateEpoch) Bytes() []byte { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(e)) - return b -} - -// Get the state epoch from bytes -func StateEpochFromBytes(b []byte) StateEpoch { - return StateEpoch(binary.BigEndian.Uint64(b)) -} diff --git a/timestamp.go b/timestamp.go new file mode 100644 index 00000000..56df1b64 --- /dev/null +++ b/timestamp.go @@ -0,0 +1,19 @@ +package verkle + +import ( + "encoding/binary" +) + +type AccessTimestamp uint64 + +const ( + NumActiveTimestamps = 2 +) + +func IsExpired(prev AccessTimestamp, cur AccessTimestamp) bool { + return cur-prev >= NumActiveTimestamps +} + +func AccessTimestampFromBytes(b []byte) AccessTimestamp { + return AccessTimestamp(binary.BigEndian.Uint64(b)) +} diff --git a/tree.go b/tree.go index b196ad8d..fde50a10 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, StateEpoch, NodeResolverFn) error + Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error // Delete a leaf with the given key - Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) + Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) // Get value at a given key - Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) + Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) // Commit computes the commitment of the node. The // result (the curve point) is cached. @@ -202,7 +203,7 @@ type ( // true in the context of a stateless tree. isPOAStub bool - lastEpoch StateEpoch + lastTs AccessTimestamp } ) @@ -274,7 +275,7 @@ func NewStatelessInternal(depth byte, comm *Point) VerkleNode { } // New creates a new leaf node -func NewLeafNode(stem Stem, values [][]byte, lastEpoch StateEpoch) (*LeafNode, error) { +func NewLeafNode(stem Stem, values [][]byte, lastTs AccessTimestamp) (*LeafNode, error) { cfg := GetConfig() // C1. @@ -317,8 +318,7 @@ func NewLeafNode(stem Stem, values [][]byte, lastEpoch StateEpoch) (*LeafNode, e return nil, fmt.Errorf("batch mapping to scalar fields: %s", err) } - // state epoch - poly[4].SetUint64(uint64(lastEpoch)) + poly[4].SetUint64(uint64(lastTs)) return &LeafNode{ // depth will be 0, but the commitment calculation @@ -328,20 +328,20 @@ func NewLeafNode(stem Stem, values [][]byte, lastEpoch StateEpoch) (*LeafNode, e commitment: cfg.CommitToPoly(poly[:], NodeWidth-5), c1: c1, c2: c2, - lastEpoch: lastEpoch, + lastTs: lastTs, }, 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, lastEpoch StateEpoch) *LeafNode { +func NewLeafNodeWithNoComms(stem Stem, values [][]byte, lastTs AccessTimestamp) *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, - lastEpoch: lastEpoch, + values: values, + stem: stem, + lastTs: lastTs, } } @@ -371,13 +371,13 @@ func (n *InternalNode) cowChild(index byte) { } } -func (n *InternalNode) Insert(key []byte, value []byte, curEpoch StateEpoch, resolver NodeResolverFn) error { +func (n *InternalNode) Insert(key []byte, value []byte, curTs AccessTimestamp, resolver NodeResolverFn) error { values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.InsertValuesAtStem(KeyToStem(key), values, curEpoch, resolver) + return n.InsertValuesAtStem(KeyToStem(key), values, curTs, resolver) } -func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch StateEpoch, resolver NodeResolverFn) error { +func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs AccessTimestamp, 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) { @@ -386,7 +386,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S case Empty: n.cowChild(nChild) var err error - n.children[nChild], err = NewLeafNode(stem, values, curEpoch) + n.children[nChild], err = NewLeafNode(stem, values, curTs) if err != nil { return err } @@ -407,7 +407,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S n.cowChild(nChild) // recurse to handle the case of a LeafNode child that // splits. - return n.InsertValuesAtStem(stem, values, curEpoch, resolver) + return n.InsertValuesAtStem(stem, values, curTs, resolver) case *ExpiredLeafNode: return errEpochExpired case *LeafNode: @@ -417,7 +417,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S return errIsPOAStub } n.cowChild(nChild) - return child.insertMultiple(stem, values, curEpoch) + return child.insertMultiple(stem, values, curTs) } n.cowChild(nChild) @@ -433,12 +433,12 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S nextWordInInsertedKey := offset2key(stem, n.depth+1) if nextWordInInsertedKey == nextWordInExistingKey { - return newBranch.InsertValuesAtStem(stem, values, curEpoch, resolver) + return newBranch.InsertValuesAtStem(stem, values, curTs, resolver) } // Next word differs, so this was the last level. // Insert it directly into its final slot. - leaf, err := NewLeafNode(stem, values, curEpoch) + leaf, err := NewLeafNode(stem, values, curTs) if err != nil { return err } @@ -447,7 +447,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S newBranch.children[nextWordInInsertedKey] = leaf case *InternalNode: n.cowChild(nChild) - return child.InsertValuesAtStem(stem, values, curEpoch, resolver) + return child.InsertValuesAtStem(stem, values, curTs, resolver) default: // It should be an UknownNode. return errUnknownNodeType } @@ -558,7 +558,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, curEpoch StateEpoch, resolver NodeResolverFn) ([][]byte, error) { +func (n *InternalNode) GetValuesAtStem(stem Stem, curTs AccessTimestamp, 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: @@ -580,11 +580,11 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, curEpoch StateEpoch, resolver n.children[nchild] = resolved // recurse to handle the case of a LeafNode child that // splits. - return n.GetValuesAtStem(stem, curEpoch, resolver) + return n.GetValuesAtStem(stem, curTs, resolver) case *ExpiredLeafNode: return nil, errEpochExpired case *LeafNode: - if EpochExpired(child.lastEpoch, curEpoch) { + if IsExpired(child.lastTs, curTs) { return nil, errEpochExpired } @@ -598,13 +598,13 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, curEpoch StateEpoch, resolver } return nil, nil case *InternalNode: - return child.GetValuesAtStem(stem, curEpoch, resolver) + return child.GetValuesAtStem(stem, curTs, resolver) default: return nil, errUnknownNodeType } } -func (n *InternalNode) Delete(key []byte, curEpoch StateEpoch, resolver NodeResolverFn) (bool, error) { +func (n *InternalNode) Delete(key []byte, curTs AccessTimestamp, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { case Empty: @@ -623,12 +623,12 @@ func (n *InternalNode) Delete(key []byte, curEpoch StateEpoch, resolver NodeReso return false, err } n.children[nChild] = c - return n.Delete(key, curEpoch, resolver) + return n.Delete(key, curTs, resolver) case *ExpiredLeafNode: return false, errEpochExpired default: n.cowChild(nChild) - del, err := child.Delete(key, curEpoch, resolver) + del, err := child.Delete(key, curTs, resolver) if err != nil { return false, err } @@ -788,11 +788,11 @@ func (n *InternalNode) FlushAtDepth(depth uint8, flush NodeFlushFn) { } } -func (n *InternalNode) Get(key []byte, curEpoch StateEpoch, resolver NodeResolverFn) ([]byte, error) { +func (n *InternalNode) Get(key []byte, curTs AccessTimestamp, 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), curEpoch, resolver) + stemValues, err := n.GetValuesAtStem(KeyToStem(key), curTs, resolver) if err != nil { return nil, err } @@ -1170,7 +1170,7 @@ func (n *InternalNode) touchCoW(index byte) { n.cowChild(index) } -func (n *LeafNode) Insert(key []byte, value []byte, curEpoch StateEpoch, _ NodeResolverFn) error { +func (n *LeafNode) Insert(key []byte, value []byte, curTs AccessTimestamp, _ NodeResolverFn) error { if n.isPOAStub { return errIsPOAStub } @@ -1179,7 +1179,7 @@ func (n *LeafNode) Insert(key []byte, value []byte, curEpoch StateEpoch, _ NodeR return fmt.Errorf("invalid key size: %d", len(key)) } - if EpochExpired(n.lastEpoch, curEpoch) { + if IsExpired(n.lastTs, curTs) { return errEpochExpired } @@ -1189,23 +1189,23 @@ func (n *LeafNode) Insert(key []byte, value []byte, curEpoch StateEpoch, _ NodeR } values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.insertMultiple(stem, values, curEpoch) + return n.insertMultiple(stem, values, curTs) } -func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curEpoch StateEpoch) error { +func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curTs AccessTimestamp) error { // Sanity check: ensure the stems are the same. if !equalPaths(stem, n.stem) { return errInsertIntoOtherStem } - if err := n.updateMultipleLeaves(values, curEpoch); err != nil { + if err := n.updateMultipleLeaves(values, curTs); err != nil { return err } return nil } -func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curEpoch StateEpoch) { +func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curTs AccessTimestamp) { // Calculate the Fr-delta. var deltaC Fr deltaC.Sub(&newC, &oldC) @@ -1214,8 +1214,8 @@ func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curEpoch StateEpoch) { var poly [NodeWidth]Fr poly[cxIndex] = deltaC - if n.lastEpoch != curEpoch { - poly[4].SetUint64(uint64(curEpoch)) + if n.lastTs != curTs { + poly[4].SetUint64(uint64(curTs)) } // Add delta to the current commitment. @@ -1257,7 +1257,7 @@ func (n *LeafNode) updateCn(index byte, value []byte, c *Point) error { return nil } -func (n *LeafNode) updateLeaf(index byte, value []byte, curEpoch StateEpoch) error { +func (n *LeafNode) updateLeaf(index byte, value []byte, curTs AccessTimestamp) error { // Update the corresponding C1 or C2 commitment. var c *Point var oldC Point @@ -1280,14 +1280,14 @@ func (n *LeafNode) updateLeaf(index byte, value []byte, curEpoch StateEpoch) err // 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], curEpoch) - n.updateLastEpoch(curEpoch) + n.updateC(cxIndex, frs[0], frs[1], curTs) + n.updateLastTs(curTs) n.values[index] = value return nil } -func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) error { // skipcq: GO-R1005 +func (n *LeafNode) updateMultipleLeaves(values [][]byte, curTs AccessTimestamp) 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. @@ -1333,21 +1333,21 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) er 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], curEpoch) - n.updateC(c2Idx, frs[2], frs[3], curEpoch) - n.updateLastEpoch(curEpoch) + n.updateC(c1Idx, frs[0], frs[1], curTs) + n.updateC(c2Idx, frs[2], frs[3], curTs) + n.updateLastTs(curTs) } 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], curEpoch) - n.updateLastEpoch(curEpoch) + n.updateC(c1Idx, frs[0], frs[1], curTs) + n.updateLastTs(curTs) } 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], curEpoch) - n.updateLastEpoch(curEpoch) + n.updateC(c2Idx, frs[0], frs[1], curTs) + n.updateLastTs(curTs) } return nil @@ -1355,13 +1355,13 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) er // 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, curEpoch StateEpoch, _ NodeResolverFn) (bool, error) { +func (n *LeafNode) Delete(k []byte, curTs AccessTimestamp, _ NodeResolverFn) (bool, error) { // Sanity check: ensure the key header is the same: if !equalPaths(k, n.stem) { return false, nil } - if EpochExpired(n.lastEpoch, curEpoch) { + if IsExpired(n.lastTs, curTs) { return false, errEpochExpired } @@ -1428,11 +1428,11 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool n.commitment.Sub(n.commitment, cfg.CommitToPoly(poly[:], 0)) // TODO(weiihann): can this be done together with the previous? - if n.lastEpoch != curEpoch { + if n.lastTs != curTs { var poly [5]Fr - poly[4].SetUint64(uint64(curEpoch)) + poly[4].SetUint64(uint64(curTs)) n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) - n.updateLastEpoch(curEpoch) + n.updateLastTs(curTs) } // Clear the corresponding commitment @@ -1456,10 +1456,10 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool // 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, curEpoch) + return false, n.updateLeaf(k[StemSize], nil, curTs) } -func (n *LeafNode) Get(k []byte, curEpoch StateEpoch, _ NodeResolverFn) ([]byte, error) { +func (n *LeafNode) Get(k []byte, curTs AccessTimestamp, _ NodeResolverFn) ([]byte, error) { if n.isPOAStub { return nil, errIsPOAStub } @@ -1495,8 +1495,8 @@ func (n *LeafNode) Commit() *Point { return n.commitment } -func (n *LeafNode) updateLastEpoch(curEpoch StateEpoch) { - n.lastEpoch = curEpoch +func (n *LeafNode) updateLastTs(curTs AccessTimestamp) { + n.lastTs = curTs } // fillSuffixTreePoly takes one of the two suffix tree and @@ -1699,7 +1699,7 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements } // 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 @@ -1963,7 +1963,8 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte - lastEpoch := n.lastEpoch.Bytes() + lastTs := make([]byte, EpochSize) + binary.BigEndian.PutUint64(lastTs, uint64(n.lastTs)) switch { case count == 1: var buf [singleSlotLeafSize]byte @@ -1972,7 +1973,7 @@ 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:], lastEpoch) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastTs) result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize] = byte(lastIdx) copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize+leafValueIndexSize:], n.values[lastIdx][:]) case isEoA: @@ -1982,7 +1983,7 @@ 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:], lastEpoch) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastTs) copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize:], n.values[1]) // copy balance copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize+leafBalanceSize:], n.values[2][:leafNonceSize]) // copy nonce default: @@ -1993,7 +1994,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafCommitmentOffset:], cBytes[:]) copy(result[leafC1CommitmentOffset:], c1Bytes[:]) copy(result[leafC2CommitmentOffset:], c2Bytes[:]) - copy(result[leafLastEpochOffset:], lastEpoch) + copy(result[leafLastTsOffset:], lastTs) copy(result[leafChildrenOffset:], children) } diff --git a/tree_test.go b/tree_test.go index a0c7f5dc..a119ca32 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1347,7 +1347,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { r := tree.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) @@ -1497,7 +1497,7 @@ func TestBatchMigratedKeyValues(t *testing.T) { nodeValues = append(nodeValues, curr) // Create all leaves in batch mode so we can optimize cryptography operations. - newLeaves, err := BatchNewLeafNode(nodeValues, 0) + newLeaves, err := BatchNewLeafNode(nodeValues) if err != nil { t.Fatalf("failed to create leaves: %v", err) } @@ -1589,7 +1589,7 @@ func BenchmarkBatchLeavesInsert(b *testing.B) { nodeValues = append(nodeValues, curr) // Create all leaves in batch mode so we can optimize cryptography operations. - newLeaves, err := BatchNewLeafNode(nodeValues, 0) + newLeaves, err := BatchNewLeafNode(nodeValues) if err != nil { b.Fatalf("failed to batch-create leaf node: %v", err) } @@ -1819,7 +1819,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, 0, 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) } diff --git a/unknown.go b/unknown.go index 62d8232d..25320690 100644 --- a/unknown.go +++ b/unknown.go @@ -29,15 +29,15 @@ import "errors" type UnknownNode struct{} -func (UnknownNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { +func (UnknownNode) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { return errMissingNodeInStateless } -func (UnknownNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { +func (UnknownNode) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { return false, errors.New("cant delete in a subtree missing form a stateless view") } -func (UnknownNode) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { +func (UnknownNode) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { return nil, nil } From d5368974fe22cbfc5581b08d9784d874d128f4d5 Mon Sep 17 00:00:00 2001 From: weiihann Date: Mon, 21 Oct 2024 14:34:06 +0800 Subject: [PATCH 07/33] add basic expired leaf test --- encoding_test.go | 2 +- expired_leaf_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 expired_leaf_test.go diff --git a/encoding_test.go b/encoding_test.go index c95e8196..fc75aaeb 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -197,7 +197,7 @@ func TestParseExpiredLeaf(t *testing.T) { srs := cfg.conf.SRS comm := srs[0] - stem := ffx32KeyTest[:31] + stem := ffx32KeyTest[:StemSize] el := NewExpiredLeafNode(stem, &comm) serialized, err := el.Serialize() diff --git a/expired_leaf_test.go b/expired_leaf_test.go new file mode 100644 index 00000000..086b57dd --- /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, 0, nil) + if !errors.Is(err, errEpochExpired) { + t.Fatalf("expected epoch expired error when inserting, got %v", err) + } + + _, err = leaf.Delete(zeroKeyTest, 0, nil) + if !errors.Is(err, errEpochExpired) { + t.Fatalf("expected epoch expired error when deleting, got %v", err) + } + + v, err := leaf.Get(zeroKeyTest, 0, nil) + if !errors.Is(err, errEpochExpired) { + t.Fatalf("expected epoch 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") + } +} From 342e6836186ea04154147cad34c236b2f816660e Mon Sep 17 00:00:00 2001 From: weiihann Date: Mon, 21 Oct 2024 15:25:30 +0800 Subject: [PATCH 08/33] add basic operation tests --- encoding.go | 2 +- expired_tree_test.go | 223 +++++++++++++++++++++++++++++++++++++++++++ tree.go | 5 + 3 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 expired_tree_test.go diff --git a/encoding.go b/encoding.go index cb0760ba..bea5a553 100644 --- a/encoding.go +++ b/encoding.go @@ -97,7 +97,7 @@ func ParseNode(serializedNode []byte, depth byte) (VerkleNode, error) { return parseEoAccountNode(serializedNode, depth) case singleSlotType: return parseSingleSlotNode(serializedNode, depth) - case expiredLeafType: // TODO(weiihann) + case expiredLeafType: return parseExpiredLeafNode(serializedNode) default: return nil, ErrInvalidNodeEncoding diff --git a/expired_tree_test.go b/expired_tree_test.go new file mode 100644 index 00000000..59e4b06d --- /dev/null +++ b/expired_tree_test.go @@ -0,0 +1,223 @@ +package verkle + +import ( + "bytes" + "errors" + "testing" +) + +func TestInsertSameLeafNoExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + if err := root.Insert(oneKeyTest, testValue, 1, 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.lastTs != 1 { + t.Fatalf("expected last accessed to be 1, got %d", leaf.lastTs) + } +} + +func TestInsertSameLeafExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + err := root.Insert(oneKeyTest, testValue, 2, nil) + if !errors.Is(err, errEpochExpired) { + t.Fatalf("expected epoch 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.lastTs != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastTs) + } +} + +func TestInsertDiffLeaf(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + if err := root.Insert(ffx32KeyTest, testValue, 2, 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.lastTs != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf0.lastTs) + } + + if leaff.lastTs != 2 { + t.Fatalf("expected last accessed to be 2, got %d", leaff.lastTs) + } +} + +func TestGetNoExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + val, err := root.Get(zeroKeyTest, 1, 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.lastTs != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastTs) + } +} + +func TestGetExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + val, err := root.Get(zeroKeyTest, 2, nil) + if !errors.Is(err, errEpochExpired) { + t.Fatalf("expected epoch 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.lastTs != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastTs) + } +} + +func TestDelLeafNoExpired(t *testing.T) { // skipcq: GO-R1005 + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + if _, err := root.Delete(zeroKeyTest, 1, 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) { // skipcq: GO-R1005 + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + + _, err := root.Delete(zeroKeyTest, 2, nil) + if !errors.Is(err, errEpochExpired) { + t.Fatalf("expected epoch 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.lastTs != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastTs) + } +} + +func TestRootCommitExpired(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, testValue, 0, 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) + } +} diff --git a/tree.go b/tree.go index fde50a10..8f4fe395 100644 --- a/tree.go +++ b/tree.go @@ -657,6 +657,7 @@ func (n *InternalNode) Delete(key []byte, curTs AccessTimestamp, resolver NodeRe // 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. +// TODO(weiihann): check if need to compare access timestamp func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { @@ -1198,6 +1199,10 @@ func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curTs AccessTimest return errInsertIntoOtherStem } + if IsExpired(n.lastTs, curTs) { + return errEpochExpired + } + if err := n.updateMultipleLeaves(values, curTs); err != nil { return err } From 5419a7dd94b2885a0b304104f8244083c68208a0 Mon Sep 17 00:00:00 2001 From: weiihann Date: Thu, 24 Oct 2024 21:42:48 +0800 Subject: [PATCH 09/33] support stateless proof revert unnecessary changes remove unnecessary changes --- conversion.go | 2 +- doc.go | 3 +- empty.go | 2 +- empty_test.go | 2 +- encoding.go | 5 +- expired_leaf.go | 54 ++++++---- expired_leaf_test.go | 6 +- expired_tree_test.go | 30 +++++- hashednode.go | 2 +- hashednode_test.go | 2 +- proof_ipa.go | 50 +++++++--- proof_json.go | 3 + proof_test.go | 230 +++++++++++++++++++++++++++++++++++++------ tree.go | 100 +++++++++++++------ tree_test.go | 26 ++--- unknown.go | 2 +- unknown_test.go | 2 +- 17 files changed, 402 insertions(+), 119 deletions(-) diff --git a/conversion.go b/conversion.go index 547199ee..e9b74ccd 100644 --- a/conversion.go +++ b/conversion.go @@ -104,7 +104,7 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curTs AccessTimes // We first mark all children of the subtreess that we'll update in parallel, // so the subtree updating doesn't produce a concurrent access to n.cowChild(...). - lastChildrenIdx := -1 + var lastChildrenIdx = -1 for i := range leaves { if int(leaves[i].stem[0]) != lastChildrenIdx { lastChildrenIdx = int(leaves[i].stem[0]) diff --git a/doc.go b/doc.go index 37c44d7c..23771329 100644 --- a/doc.go +++ b/doc.go @@ -38,7 +38,7 @@ 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") - errEpochExpired = errors.New("trying to access an expired leaf node") + errExpired = errors.New("trying to access an expired leaf node") ) const ( @@ -46,4 +46,5 @@ const ( extStatusAbsentEmpty = byte(iota) // missing child node along the path extStatusAbsentOther // path led to a node with a different stem extStatusPresent // stem was present + extStatusExpired // stem was present but expired ) diff --git a/empty.go b/empty.go index 2e79c485..bf2f6138 100644 --- a/empty.go +++ b/empty.go @@ -53,7 +53,7 @@ func (Empty) Commitment() *Point { return &id } -func (Empty) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (Empty) GetProofItems(keylist, AccessTimestamp, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { return nil, nil, nil, errors.New("trying to produce a commitment for an empty subtree") } diff --git a/empty_test.go b/empty_test.go index 318e110c..e237f44f 100644 --- a/empty_test.go +++ b/empty_test.go @@ -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, 0, nil); err == nil { t.Fatal("get proof items should error") } diff --git a/encoding.go b/encoding.go index bea5a553..4864b9d2 100644 --- a/encoding.go +++ b/encoding.go @@ -98,7 +98,7 @@ func ParseNode(serializedNode []byte, depth byte) (VerkleNode, error) { case singleSlotType: return parseSingleSlotNode(serializedNode, depth) case expiredLeafType: - return parseExpiredLeafNode(serializedNode) + return parseExpiredLeafNode(serializedNode, depth) default: return nil, ErrInvalidNodeEncoding } @@ -203,9 +203,10 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { return ln, nil } -func parseExpiredLeafNode(serialized []byte) (VerkleNode, error) { +func parseExpiredLeafNode(serialized []byte, depth byte) (VerkleNode, error) { l := &ExpiredLeafNode{} l.stem = serialized[leafStemOffset : leafStemOffset+StemSize] + l.setDepth(depth) l.commitment = new(Point) if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize:], true); err != nil { return nil, fmt.Errorf("setting commitment: %w", err) diff --git a/expired_leaf.go b/expired_leaf.go index 58e2a2c2..68cd52fc 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -28,41 +28,58 @@ package verkle 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 (ExpiredLeafNode) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { - return errEpochExpired +func (n *ExpiredLeafNode) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { + return errExpired } -func (ExpiredLeafNode) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { - return false, errEpochExpired +func (n *ExpiredLeafNode) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { + return false, errExpired } -func (ExpiredLeafNode) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { - return nil, errEpochExpired +func (n *ExpiredLeafNode) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { + return nil, errExpired } -func (n ExpiredLeafNode) Commit() *Point { +func (n *ExpiredLeafNode) Commit() *Point { if n.commitment == nil { panic("nil commitment") } return n.commitment } -func (n ExpiredLeafNode) Commitment() *Point { +func (n *ExpiredLeafNode) Commitment() *Point { return n.commitment } -// TODO(weiihann): prove that something was expired, for the block to be able to execute statelessly. -func (n ExpiredLeafNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { - return nil, nil, nil, errEpochExpired +func (n *ExpiredLeafNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { + var ( + pe = &ProofElements{ + Vals: make([][]byte, len(keys)), + ByPath: map[string]*Point{}, + } + esses []byte = nil + poass []Stem + ) + + for i := range keys { + pe.ByPath[string(keys[i][:n.depth])] = n.commitment + pe.Vals[i] = nil + + esses = append(esses, extStatusExpired|(n.depth<<3)) + poass = append(poass, n.stem) + } + + return pe, esses, poass, nil } -func (n ExpiredLeafNode) Serialize() ([]byte, error) { +func (n *ExpiredLeafNode) Serialize() ([]byte, error) { cBytes := n.commitment.BytesUncompressedTrusted() var buf [expiredLeafSize]byte @@ -74,10 +91,11 @@ func (n ExpiredLeafNode) Serialize() ([]byte, error) { return result, nil } -func (n ExpiredLeafNode) Copy() VerkleNode { +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) l.commitment.Set(n.commitment) @@ -86,15 +104,15 @@ func (n ExpiredLeafNode) Copy() VerkleNode { return l } -func (n ExpiredLeafNode) toDot(string, string) string { +func (n *ExpiredLeafNode) toDot(string, string) string { return "" } -func (n ExpiredLeafNode) setDepth(_ byte) { - panic("should not be try to set the depth of an ExpiredLeafNode node") +func (n *ExpiredLeafNode) setDepth(d byte) { + n.depth = d } -func (n ExpiredLeafNode) Hash() *Fr { +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 index 086b57dd..f933b5fa 100644 --- a/expired_leaf_test.go +++ b/expired_leaf_test.go @@ -14,17 +14,17 @@ func TestExpiredLeafBasic(t *testing.T) { leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], &comm) err := leaf.Insert(zeroKeyTest, zeroKeyTest, 0, nil) - if !errors.Is(err, errEpochExpired) { + if !errors.Is(err, errExpired) { t.Fatalf("expected epoch expired error when inserting, got %v", err) } _, err = leaf.Delete(zeroKeyTest, 0, nil) - if !errors.Is(err, errEpochExpired) { + if !errors.Is(err, errExpired) { t.Fatalf("expected epoch expired error when deleting, got %v", err) } v, err := leaf.Get(zeroKeyTest, 0, nil) - if !errors.Is(err, errEpochExpired) { + if !errors.Is(err, errExpired) { t.Fatalf("expected epoch expired error when getting, got %v", err) } if v != nil { diff --git a/expired_tree_test.go b/expired_tree_test.go index 59e4b06d..fabb45ba 100644 --- a/expired_tree_test.go +++ b/expired_tree_test.go @@ -45,7 +45,7 @@ func TestInsertSameLeafExpired(t *testing.T) { } err := root.Insert(oneKeyTest, testValue, 2, nil) - if !errors.Is(err, errEpochExpired) { + if !errors.Is(err, errExpired) { t.Fatalf("expected epoch expired error when inserting, got %v", err) } @@ -138,7 +138,7 @@ func TestGetExpired(t *testing.T) { } val, err := root.Get(zeroKeyTest, 2, nil) - if !errors.Is(err, errEpochExpired) { + if !errors.Is(err, errExpired) { t.Fatalf("expected epoch expired error when getting, got %v", err) } @@ -156,7 +156,7 @@ func TestGetExpired(t *testing.T) { } } -func TestDelLeafNoExpired(t *testing.T) { // skipcq: GO-R1005 +func TestDelLeafNoExpired(t *testing.T) { t.Parallel() root := New() @@ -174,7 +174,7 @@ func TestDelLeafNoExpired(t *testing.T) { // skipcq: GO-R1005 } } -func TestDelLeafExpired(t *testing.T) { // skipcq: GO-R1005 +func TestDelLeafExpired(t *testing.T) { t.Parallel() root := New() @@ -183,7 +183,7 @@ func TestDelLeafExpired(t *testing.T) { // skipcq: GO-R1005 } _, err := root.Delete(zeroKeyTest, 2, nil) - if !errors.Is(err, errEpochExpired) { + if !errors.Is(err, errExpired) { t.Fatalf("expected epoch expired error when deleting, got %v", err) } @@ -221,3 +221,23 @@ func TestRootCommitExpired(t *testing.T) { t.Fatalf("expected commitment to be %x, got %x", &init, comm) } } + +func TestRootCommitDiffTimestamp(t *testing.T) { + t.Parallel() + + root1 := New() + if err := root1.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + comm1 := root1.Commit() + + root2 := New() + if err := root2.Insert(zeroKeyTest, testValue, 2, nil); err != nil { + t.Fatalf("error inserting: %v", err) + } + comm2 := root2.Commit() + + if comm1.Equal(comm2) { + t.Fatalf("expected different commitments, got %x", comm1) + } +} diff --git a/hashednode.go b/hashednode.go index 5566fb93..e2e4e24d 100644 --- a/hashednode.go +++ b/hashednode.go @@ -58,7 +58,7 @@ func (HashedNode) Commitment() *Point { panic("can not get commitment of a hash node") } -func (HashedNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (HashedNode) GetProofItems(keylist, AccessTimestamp, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { return nil, nil, nil, errors.New("can not get the full path, and there is no proof of absence") } diff --git a/hashednode_test.go b/hashednode_test.go index 1c48d9db..fb533075 100644 --- a/hashednode_test.go +++ b/hashednode_test.go @@ -46,7 +46,7 @@ func TestHashedNodeFuncs(t *testing.T) { 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, 0, 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/proof_ipa.go b/proof_ipa.go index 912f39aa..90c086bc 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -126,6 +126,7 @@ type SuffixStateDiffs []SuffixStateDiff type StemStateDiff struct { Stem [StemSize]byte `json:"stem"` SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"` + Resurrected bool `json:"resurrected"` } type StateDiff []StemStateDiff @@ -135,6 +136,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].Resurrected = sd[i].Resurrected for j := range sd[i].SuffixDiffs { ret[i].SuffixDiffs[j].Suffix = sd[i].SuffixDiffs[j].Suffix if sd[i].SuffixDiffs[j].CurrentValue != nil { @@ -186,20 +188,20 @@ func (sd StateDiff) Equal(other StateDiff) error { func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { sort.Sort(keylist(keys)) - return root.GetProofItems(keylist(keys), resolver) + return root.GetProofItems(keylist(keys), curTs, resolver) } // 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, curTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, error) { +func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, preTs, postTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, 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") } - pe, es, poas, err := GetCommitmentsForMultiproof(preroot, keys, resolver) + pe, es, poas, err := GetCommitmentsForMultiproof(preroot, keys, preTs, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("error getting pre-state proof data: %w", err) } @@ -211,7 +213,7 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, curTs // 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], curTs, resolver) + val, err := postroot.Get(keys[i], postTs, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("error getting post-state value for key %x: %w", keys[i], err) } @@ -226,8 +228,8 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, curTs return pe, es, poas, postvals, nil } -func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, curTs AccessTimestamp, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { - pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, curTs, resolver) +func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, preTs, postTs AccessTimestamp, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { + pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, preTs, postTs, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("get commitments for multiproof: %s", err) } @@ -269,8 +271,8 @@ func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, curTs Acc } // verifyVerkleProofWithPreState takes a proof and a trusted tree root and verifies that the proof is valid. -func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode, curTs AccessTimestamp) error { - pe, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, curTs, nil) +func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode, preTs AccessTimestamp) error { + pe, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, preTs, 0, nil) if err != nil { return fmt.Errorf("error getting proof elements: %w", err) } @@ -316,13 +318,21 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { var stemdiff *StemStateDiff var statediff StateDiff + var checkResurrect bool + var curExtStatus byte + + curEsInd := -1 // index of the current extension status for i, key := range proof.Keys { stem := KeyToStem(key) if stemdiff == nil || !bytes.Equal(stemdiff.Stem[:], stem) { statediff = append(statediff, StemStateDiff{}) stemdiff = &statediff[len(statediff)-1] copy(stemdiff.Stem[:], stem) + checkResurrect = true + curEsInd += 1 + curExtStatus = proof.ExtStatus[curEsInd] } + stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{Suffix: key[StemSize]}) newsd := &stemdiff.SuffixDiffs[len(stemdiff.SuffixDiffs)-1] @@ -350,6 +360,13 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { copy(aligned[:valueLen], proof.PostValues[i]) newsd.NewValue = (*[32]byte)(unsafe.Pointer(&aligned[0])) } + + // if the current extension status is expired and the post value is not nil, + // then it means that the leaf has been resurrected. + if checkResurrect && (curExtStatus&3 == extStatusExpired) && (proof.PostValues[i] != nil) { + stemdiff.Resurrected = true + checkResurrect = false + } } return &VerkleProof{ @@ -544,6 +561,16 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // si.has_c2 = si.has_c2 || (k[StemSize] >= 128) } } + case extStatusExpired: + // All keys that are part of a proof of expiry, must contain empty + // prestate values. If that isn't the case, the proof is invalid. + for j := range proof.Keys { // TODO: DoS risk, use map or binary search. + if bytes.HasPrefix(proof.Keys[j], stems[i]) && proof.PreValues[j] != nil { + return nil, fmt.Errorf("proof of absence (empty) stem %x has a value", si.stem) + } + } + si.stem = poas[0] + poas = poas[1:] default: return nil, fmt.Errorf("invalid extension status: %d", si.stemType) } @@ -585,7 +612,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, postTs AccessTimestamp) (VerkleNode, error) { postroot := preroot.Copy() for _, stemstatediff := range statediff { @@ -606,8 +633,7 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff) (Verkle if overwrites { var stem [StemSize]byte copy(stem[:StemSize], stemstatediff.Stem[:]) - // TODO(weiihann): double check the epoch - if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, 0, nil); err != nil { + if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postTs, stemstatediff.Resurrected, nil); err != nil { return nil, fmt.Errorf("error overwriting value in post state: %w", err) } } @@ -666,7 +692,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, curTs) 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..5c4e3d24 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"` + Resurrected bool `json:"resurrected"` } func (ssd StemStateDiff) MarshalJSON() ([]byte, error) { return json.Marshal(&stemStateDiffMarshaller{ Stem: HexToPrefixedString(ssd.Stem[:]), SuffixDiffs: ssd.SuffixDiffs, + Resurrected: ssd.Resurrected, }) } @@ -207,6 +209,7 @@ func (ssd *StemStateDiff) UnmarshalJSON(data []byte) error { } *ssd = StemStateDiff{ SuffixDiffs: aux.SuffixDiffs, + Resurrected: aux.Resurrected, } copy(ssd.Stem[:], stem) return nil diff --git a/proof_test.go b/proof_test.go index 193a8d48..6f3d98a9 100644 --- a/proof_test.go +++ b/proof_test.go @@ -29,6 +29,7 @@ import ( "crypto/rand" "encoding/hex" "encoding/json" + "errors" "fmt" "reflect" "testing" @@ -42,7 +43,7 @@ func TestProofEmptyTree(t *testing.T) { root := New() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, 0, 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)) @@ -64,7 +65,7 @@ func TestProofVerifyTwoLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -91,7 +92,7 @@ func TestProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -118,9 +119,9 @@ func TestMultiProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], 0, 0, nil) - pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) + pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], 0, nil) if err != nil { t.Fatal(err) } @@ -158,9 +159,9 @@ func TestMultiProofVerifyMultipleLeavesWithAbsentStem(t *testing.T) { absent[3] = 1 // and the stem differs keys = append(keys, absent) - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, 0, nil) - pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, nil) + pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, 0, nil) if err != nil { t.Fatal(err) } @@ -192,9 +193,9 @@ func TestMultiProofVerifyMultipleLeavesCommitmentRedundancy(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, 0, nil) - pe, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) + pe, _, _, err := GetCommitmentsForMultiproof(root, keys, 0, nil) if err != nil { t.Fatal(err) } @@ -216,7 +217,7 @@ func TestProofOfAbsenceInternalVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -236,7 +237,7 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -261,7 +262,7 @@ func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { return ret }() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -283,7 +284,7 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { }() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -309,7 +310,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]}, 0, nil); err != nil { + if _, _, _, _, err := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, 0, 0, nil); err != nil { b.Fatal(err) } } @@ -330,7 +331,7 @@ func BenchmarkProofVerification(b *testing.B) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, 0, 0, nil) b.ResetTimer() b.ReportAllocs() @@ -361,7 +362,7 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { } } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -400,7 +401,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -441,7 +442,7 @@ func TestProofDeserialize(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -454,7 +455,7 @@ func TestProofDeserialize(t *testing.T) { } _ = deserialized - pe, _, _, err := root.GetProofItems(keylist{absentkey[:]}, nil) + pe, _, _, err := root.GetProofItems(keylist{absentkey[:]}, 0, nil) if err != nil { t.Fatal(err) } @@ -471,7 +472,7 @@ func TestProofOfAbsenceEdgeCase(t *testing.T) { root.Commit() ret, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030303") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, 0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -492,7 +493,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030301") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, 0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -554,7 +555,7 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030200") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, 0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, 0, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -625,9 +626,10 @@ func TestStemStateDiffJSONMarshalUn(t *testing.T) { 0xDD, 0xEE, 0xFF, 0x00, }, }}, + Resurrected: true, } - expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}]}` + expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}],"resurrected":true}` actualJSON, err := json.Marshal(ssd) if err != nil { t.Errorf("error marshalling SuffixStateDiff to JSON: %v", err) @@ -837,6 +839,111 @@ func TestProofOfAbsenceBorderCaseReversed(t *testing.T) { testSerializeDeserializeProof(t, insertKVs, proveKeys) } +func TestProofOfExpiryOneLeaf(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, zeroKeyTest, 2, nil); err != nil { + t.Fatalf("could not insert key: %v", err) + } + if err := root.Insert(oneKeyTest, zeroKeyTest, 2, nil); err != nil { + t.Fatalf("could not insert key: %v", err) + } + init := root.Commit() + + leaf := root.(*InternalNode).children[0].(*LeafNode) + + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf.setDepth(1) + root.(*InternalNode).children[0] = expiredLeaf + + comm := root.Commit() + if !comm.Equal(init) { + t.Fatalf("expected commitment to be %x, got %x", init, comm) + } + + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest}, 0, 0, nil) + if err != nil { + t.Fatalf("could not make verkle proof: %v", err) + } + + cfg := GetConfig() + if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { + t.Fatalf("could not verify verkle proof: %s", ToDot(root)) + } + + deserialized, err := PreStateTreeFromProof(proof, init) + if err != nil { + t.Fatalf("error deserializing %v", err) + } + + _, err = deserialized.Get(zeroKeyTest, 0, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected error getting key %x, got %v", zeroKeyTest, err) + } + + if !deserialized.Commit().Equal(init) { + t.Fatalf("expected commitment to be different from %x, got %x", init, deserialized.Commit()) + } +} + +func TestProofOfExpiryMultipleLeaves(t *testing.T) { + t.Parallel() + + root := New() + if err := root.Insert(zeroKeyTest, zeroKeyTest, 2, nil); err != nil { + t.Fatalf("could not insert key: %v", err) + } + if err := root.Insert(ffx32KeyTest, zeroKeyTest, 2, nil); err != nil { + t.Fatalf("could not insert key: %v", err) + } + init := root.Commit() + + leaf0 := root.(*InternalNode).children[0].(*LeafNode) + expiredLeaf0 := NewExpiredLeafNode(leaf0.stem, leaf0.commitment) + expiredLeaf0.setDepth(1) + root.(*InternalNode).children[0] = expiredLeaf0 + + leaff := root.(*InternalNode).children[255].(*LeafNode) + expiredLeaff := NewExpiredLeafNode(leaff.stem, leaff.commitment) + expiredLeaff.setDepth(1) + root.(*InternalNode).children[255] = expiredLeaff + + comm := root.Commit() + if !comm.Equal(init) { + t.Fatalf("expected commitment to be %x, got %x", init, comm) + } + + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest, ffx32KeyTest}, 0, 0, nil) + if err != nil { + t.Fatalf("could not make verkle proof: %v", err) + } + + cfg := GetConfig() + if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { + t.Fatalf("could not verify verkle proof: %s", ToDot(root)) + } + + deserialized, err := PreStateTreeFromProof(proof, init) + if err != nil { + t.Fatalf("error deserializing %v", err) + } + + _, err = deserialized.Get(zeroKeyTest, 0, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected error getting key %x, got %v", zeroKeyTest, err) + } + + _, err = deserialized.Get(ffx32KeyTest, 0, nil) + if !errors.Is(err, errExpired) { + t.Fatalf("expected error getting key %x, got %v", ffx32KeyTest, err) + } + + if !deserialized.Commit().Equal(init) { + t.Fatalf("expected commitment to be different from %x, got %x", init, deserialized.Commit()) + } +} + func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, proveKeys keylist) { t.Helper() @@ -863,7 +970,7 @@ func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, pr proveKVs[string(key)] = value } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, 0, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1012,7 +1119,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 } postroot.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, 0, 0, nil) keys: for i := range proof.Keys { @@ -1060,7 +1167,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) } @@ -1076,6 +1183,69 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 } } +// TODO(weiihann): add more test cases similar to TestProofVerificationWithPostState +func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { + t.Parallel() + + preTs := AccessTimestamp(0) + postTs := AccessTimestamp(2) + + preRoot := New() + if err := preRoot.Insert(zeroKeyTest, zeroKeyTest, preTs, nil); err != nil { + t.Fatalf("could not insert key: %v", err) + } + rootC := preRoot.Commit() + + leaf := preRoot.(*InternalNode).children[0].(*LeafNode) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf.setDepth(1) + preRoot.(*InternalNode).children[0] = expiredLeaf + + postRoot := New() + if err := postRoot.Insert(zeroKeyTest, fourtyKeyTest, postTs, nil); err != nil { + t.Fatalf("could not insert key: %v", err) + } + + proof, _, _, _, _ := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, preTs, postTs, nil) + + 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, preTs); err != nil { + t.Fatalf("could not verify verkle proof: %v", err) + } + + dpreroot, err := PreStateTreeFromProof(dproof, rootC) + if err != nil { + t.Fatalf("error recreating pre tree: %v", err) + } + + dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff, postTs) + if err != nil { + t.Fatalf("error recreating post tree: %v", err) + } + + if err = verifyVerkleProofWithPreState(dproof, dpreroot, preTs); err != nil { + t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) + } + + got, err := dpostroot.Get(zeroKeyTest, postTs, 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() @@ -1089,7 +1259,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { // 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}, 0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, keylist{absentKey}, 0, 0, nil) if err != nil { t.Fatal(err) } @@ -1114,7 +1284,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}, 0, nil) if err != nil { t.Fatal(err) } @@ -1169,7 +1339,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}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, 0, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1218,7 +1388,7 @@ func TestProveAbsenceInEmptyHalf(t *testing.T) { key2, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000100") key3, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, 0, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { diff --git a/tree.go b/tree.go index 8f4fe395..49843b0c 100644 --- a/tree.go +++ b/tree.go @@ -91,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, AccessTimestamp, NodeResolverFn) (*ProofElements, []byte, []Stem, error) // Serialize encodes the node to RLP. Serialize() ([]byte, error) @@ -374,10 +374,10 @@ func (n *InternalNode) cowChild(index byte) { func (n *InternalNode) Insert(key []byte, value []byte, curTs AccessTimestamp, resolver NodeResolverFn) error { values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.InsertValuesAtStem(KeyToStem(key), values, curTs, resolver) + return n.InsertValuesAtStem(KeyToStem(key), values, curTs, false, resolver) } -func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs AccessTimestamp, resolver NodeResolverFn) error { +func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs AccessTimestamp, isResurrect 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) { @@ -407,9 +407,21 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs Acce n.cowChild(nChild) // recurse to handle the case of a LeafNode child that // splits. - return n.InsertValuesAtStem(stem, values, curTs, resolver) + return n.InsertValuesAtStem(stem, values, curTs, isResurrect, resolver) case *ExpiredLeafNode: - return errEpochExpired + if !isResurrect { + return errExpired + } + + // create a new leaf node with the given values + leaf, err := NewLeafNode(stem, values, curTs) + if err != nil { + return err + } + leaf.setDepth(n.depth + 1) + n.children[nChild] = leaf + + return nil case *LeafNode: if equalPaths(child.stem, stem) { // We can't insert any values into a POA leaf node. @@ -433,7 +445,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs Acce nextWordInInsertedKey := offset2key(stem, n.depth+1) if nextWordInInsertedKey == nextWordInExistingKey { - return newBranch.InsertValuesAtStem(stem, values, curTs, resolver) + return newBranch.InsertValuesAtStem(stem, values, curTs, isResurrect, resolver) } // Next word differs, so this was the last level. @@ -447,7 +459,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs Acce newBranch.children[nextWordInInsertedKey] = leaf case *InternalNode: n.cowChild(nChild) - return child.InsertValuesAtStem(stem, values, curTs, resolver) + return child.InsertValuesAtStem(stem, values, curTs, isResurrect, resolver) default: // It should be an UknownNode. return errUnknownNodeType } @@ -527,6 +539,20 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point for b, value := range stemInfo.values { newchild.values[b] = value } + case extStatusExpired: + if len(comms) == 0 { + return comms, fmt.Errorf("missing commitment for stem %x", stemInfo.stem) + } + if len(stemInfo.stem) != StemSize { + return comms, fmt.Errorf("invalid stem size %d", len(stemInfo.stem)) + } + newchild := &ExpiredLeafNode{ + commitment: comms[0], + stem: stemInfo.stem, + depth: n.depth + 1, + } + n.children[path[0]] = newchild + comms = comms[1:] default: return comms, fmt.Errorf("invalid stem type %d", stemInfo.stemType) } @@ -582,10 +608,10 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, curTs AccessTimestamp, resolve // splits. return n.GetValuesAtStem(stem, curTs, resolver) case *ExpiredLeafNode: - return nil, errEpochExpired + return nil, errExpired case *LeafNode: if IsExpired(child.lastTs, curTs) { - return nil, errEpochExpired + return nil, errExpired } if equalPaths(child.stem, stem) { @@ -625,7 +651,7 @@ func (n *InternalNode) Delete(key []byte, curTs AccessTimestamp, resolver NodeRe n.children[nChild] = c return n.Delete(key, curTs, resolver) case *ExpiredLeafNode: - return false, errEpochExpired + return false, errExpired default: n.cowChild(nChild) del, err := child.Delete(key, curTs, resolver) @@ -679,7 +705,7 @@ func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, n.children[nChild] = c return n.DeleteAtStem(key, resolver) case *ExpiredLeafNode: - return false, errEpochExpired + return false, errExpired case *LeafNode: if !bytes.Equal(child.stem, key[:31]) { return false, errDeleteMissing @@ -963,7 +989,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, curTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { var ( groups = groupKeys(keys, n.depth) pe = &ProofElements{ @@ -1034,15 +1060,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 { + 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])) @@ -1060,7 +1084,7 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr continue } - pec, es, other, err := n.children[childIdx].GetProofItems(group, resolver) + pec, es, other, err := n.children[childIdx].GetProofItems(group, curTs, resolver) if err != nil { // TODO: add a test case to cover this scenario. return nil, nil, nil, err @@ -1181,7 +1205,7 @@ func (n *LeafNode) Insert(key []byte, value []byte, curTs AccessTimestamp, _ Nod } if IsExpired(n.lastTs, curTs) { - return errEpochExpired + return errExpired } stem := KeyToStem(key) @@ -1200,7 +1224,7 @@ func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curTs AccessTimest } if IsExpired(n.lastTs, curTs) { - return errEpochExpired + return errExpired } if err := n.updateMultipleLeaves(values, curTs); err != nil { @@ -1367,7 +1391,7 @@ func (n *LeafNode) Delete(k []byte, curTs AccessTimestamp, _ NodeResolverFn) (bo } if IsExpired(n.lastTs, curTs) { - return false, errEpochExpired + return false, errExpired } // Erase the value it used to contain @@ -1552,7 +1576,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, curTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { // skipcq: GO-R1005 var ( poly [NodeWidth]Fr // top-level polynomial pe = &ProofElements{ @@ -1565,10 +1589,23 @@ 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 and proof-of-expiry stems ) - // Initialize the top-level polynomial with 1 + stem + C1 + C2 + // If the leaf node is expired, generate the proof of expiry. + if IsExpired(n.lastTs, curTs) { + for i := range keys { + pe.ByPath[string(keys[i][:n.depth])] = n.commitment + pe.Vals[i] = nil + + esses = append(esses, extStatusExpired|(n.depth<<3)) + poass = append(poass, n.stem) + } + + return &ProofElements{}, esses, poass, nil + } + + // Initialize the top-level polynomial with 1 + stem + C1 + C2 + lastTs 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) @@ -1616,12 +1653,21 @@ func (n *LeafNode) GetProofItems(keys keylist, _ NodeResolverFn) (*ProofElements pe.Fis = append(pe.Fis, poly[:]) } + // add last accessed timestamp + poly[4].SetUint64(uint64(n.lastTs)) + 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, @@ -1634,7 +1680,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{}{} @@ -1690,7 +1735,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{}{} diff --git a/tree_test.go b/tree_test.go index a119ca32..b5f4989c 100644 --- a/tree_test.go +++ b/tree_test.go @@ -500,7 +500,7 @@ func TestDeletePrune(t *testing.T) { // skipcq: GO-R1005 // their hashed values. It then tries to delete the hashed values, which should // fail. func TestDeleteHash(t *testing.T) { - // TODO: fix this test when we take a final decision about FlushAtDepth API. + //TODO: fix this test when we take a final decision about FlushAtDepth API. t.SkipNow() key1, _ := hex.DecodeString("0105000000000000000000000000000000000000000000000000000000000000") @@ -544,7 +544,7 @@ func TestDeleteUnequalPath(t *testing.T) { } func TestDeleteResolve(t *testing.T) { - // TODO: fix this test when we take a final decision about FlushAtDepth API. + //TODO: fix this test when we take a final decision about FlushAtDepth API. t.SkipNow() key1, _ := hex.DecodeString("0105000000000000000000000000000000000000000000000000000000000000") @@ -851,7 +851,7 @@ func isLeafEqual(a, b *LeafNode) bool { } func TestGetResolveFromHash(t *testing.T) { - // TODO: fix this test when we take a final decision about FlushAtDepth API. + //TODO: fix this test when we take a final decision about FlushAtDepth API. t.SkipNow() var count uint @@ -926,7 +926,7 @@ func TestGetKey(t *testing.T) { } func TestInsertIntoHashedNode(t *testing.T) { - // TODO: fix this test when we take a final decision about FlushAtDepth API. + //TODO: fix this test when we take a final decision about FlushAtDepth API. t.SkipNow() root := New() @@ -1025,7 +1025,7 @@ func TestEmptyCommitment(t *testing.T) { t.Fatalf("inserting into the original failed: %v", err) } root.Commit() - pe, _, _, err := root.GetProofItems(keylist{ffx32KeyTest}, nil) + pe, _, _, err := root.GetProofItems(keylist{ffx32KeyTest}, 0, nil) if err != nil { t.Fatal(err) } @@ -1092,7 +1092,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}, 0, nil) if err != nil { t.Fatal(err) } @@ -1150,7 +1150,7 @@ func TestInsertStem(t *testing.T) { values[5] = zeroKeyTest values[192] = fourtyKeyTest - if err := root1.(*InternalNode).InsertValuesAtStem(KeyToStem(fourtyKeyTest), values, 0, 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() @@ -1198,7 +1198,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, 0, 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() @@ -1347,7 +1347,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { r := tree.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, 0, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) @@ -1368,7 +1368,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { if err != nil { t.Fatal(err) } - pe, _, _, err := droot.GetProofItems(keys, nil) + pe, _, _, err := droot.GetProofItems(keylist(keys), 0, nil) if err != nil { t.Fatal(err) } @@ -1723,7 +1723,7 @@ const ( // Generate implements the quick.Generator interface from testing/quick // to generate random test cases. func (randTest) Generate(r *mRandV1.Rand, size int) reflect.Value { - finishedFn := func() bool { + var finishedFn = func() bool { if size == 0 { return true } @@ -1735,7 +1735,7 @@ func (randTest) Generate(r *mRandV1.Rand, size int) reflect.Value { func generateSteps(finished func() bool, r io.Reader) randTest { var allKeys [][]byte - tmp := []byte{0} + var tmp = []byte{0} genKey := func() []byte { _, err := r.Read(tmp) if err != nil { @@ -1819,7 +1819,7 @@ func runRandTest(rt randTest) error { continue } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, 0, 0, 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) } diff --git a/unknown.go b/unknown.go index 25320690..11ed7ee8 100644 --- a/unknown.go +++ b/unknown.go @@ -51,7 +51,7 @@ func (UnknownNode) Commitment() *Point { return &id } -func (UnknownNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (UnknownNode) GetProofItems(keylist, AccessTimestamp, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { return nil, nil, nil, errors.New("can't generate proof items for unknown node") } diff --git a/unknown_test.go b/unknown_test.go index 8fb1c404..a0fca436 100644 --- a/unknown_test.go +++ b/unknown_test.go @@ -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, 0, nil); err == nil { t.Errorf("got nil error when getting proof items from a hashed node") } if _, err := un.Serialize(); err == nil { From d03767341ba07b45864f5deb138e34cb4e94db0b Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 30 Oct 2024 13:54:36 +0800 Subject: [PATCH 10/33] change access timestamp to state epoch --- conversion.go | 10 +-- debug.go | 2 +- empty.go | 8 +-- encoding.go | 12 ++-- epoch.go | 19 ++++++ expired_leaf.go | 8 +-- expired_tree_test.go | 30 ++++----- hashednode.go | 8 +-- proof_ipa.go | 26 ++++---- proof_test.go | 18 +++--- timestamp.go | 19 ------ tree.go | 145 ++++++++++++++++++++++--------------------- unknown.go | 8 +-- 13 files changed, 157 insertions(+), 156 deletions(-) create mode 100644 epoch.go delete mode 100644 timestamp.go diff --git a/conversion.go b/conversion.go index e9b74ccd..64ac70c3 100644 --- a/conversion.go +++ b/conversion.go @@ -97,7 +97,7 @@ func firstDiffByteIdx(stem1 []byte, stem2 []byte) int { panic("stems are equal") } -func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curTs AccessTimestamp, resolver NodeResolverFn) error { +func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curEpoch StateEpoch, resolver NodeResolverFn) error { sort.Slice(leaves, func(i, j int) bool { return bytes.Compare(leaves[i].stem, leaves[j].stem) < 0 }) @@ -132,13 +132,13 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curTs AccessTimes start := currStemFirstByte end := i group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[start:end], curTs, resolver) + return n.insertMigratedLeavesSubtree(leaves[start:end], curEpoch, resolver) }) currStemFirstByte = i } } group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], curTs, resolver) + return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], curEpoch, resolver) }) if err := group.Wait(); err != nil { return fmt.Errorf("inserting migrated leaves: %w", err) @@ -147,7 +147,7 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curTs AccessTimes return nil } -func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curTs AccessTimestamp, resolver NodeResolverFn) error { // skipcq: GO-R1005 +func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curEpoch StateEpoch, resolver NodeResolverFn) error { // skipcq: GO-R1005 for i := range leaves { ln := leaves[i] parent := n @@ -192,7 +192,7 @@ func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curTs Acce } } - if err := node.updateMultipleLeaves(nonPresentValues, curTs); err != nil { + if err := node.updateMultipleLeaves(nonPresentValues, curEpoch); err != nil { return fmt.Errorf("updating leaves: %s", err) } continue diff --git a/debug.go b/debug.go index ef534302..1ec6caa8 100644 --- a/debug.go +++ b/debug.go @@ -42,7 +42,7 @@ type ( C [32]byte `json:"commitment"` C1 [32]byte `json:"c1"` C2 [32]byte `json:"c2"` - LastTs AccessTimestamp `json:"last_epoch"` + LastEpoch StateEpoch `json:"last_epoch"` } ExportableExpiredLeafNode struct { diff --git a/empty.go b/empty.go index bf2f6138..f744d407 100644 --- a/empty.go +++ b/empty.go @@ -31,15 +31,15 @@ type Empty struct{} var errDirectInsertIntoEmptyNode = errors.New("an empty node should not be inserted directly into") -func (Empty) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { +func (Empty) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { return errDirectInsertIntoEmptyNode } -func (Empty) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { +func (Empty) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { return false, errors.New("cant delete an empty node") } -func (Empty) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { +func (Empty) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { return nil, nil } @@ -53,7 +53,7 @@ func (Empty) Commitment() *Point { return &id } -func (Empty) GetProofItems(keylist, AccessTimestamp, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (Empty) GetProofItems(keylist, StateEpoch, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { return nil, nil, nil, errors.New("trying to produce a commitment for an empty subtree") } diff --git a/encoding.go b/encoding.go index 4864b9d2..c2890fd1 100644 --- a/encoding.go +++ b/encoding.go @@ -78,9 +78,9 @@ 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. @@ -120,7 +120,7 @@ func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) { ln := NewLeafNodeWithNoComms( serialized[leafStemOffset:leafStemOffset+StemSize], values[:], - AccessTimestampFromBytes(serialized[leafLastTsOffset:leafLastTsOffset+EpochSize])) + StateEpochFromBytes(serialized[leafLastTsOffset:leafLastTsOffset+EpochSize])) ln.setDepth(depth) ln.c1 = new(Point) @@ -155,7 +155,7 @@ func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) { values[2] = nonce[:] // nonce values[3] = EmptyCodeHash[:] values[4] = zero32[:] // 0 code size - ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:], AccessTimestampFromBytes(serialized[epochOffset:epochOffset+EpochSize])) + ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+EpochSize])) ln.setDepth(depth) ln.c1 = new(Point) if err := ln.c1.SetBytesUncompressed(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+banderwagon.UncompressedSize], true); err != nil { @@ -173,7 +173,7 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { var values [NodeWidth][]byte offset := leafStemOffset epochOffset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize - ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], AccessTimestampFromBytes(serialized[epochOffset:epochOffset+EpochSize])) + ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+EpochSize])) offset += StemSize cnCommBytes := serialized[offset : offset+banderwagon.UncompressedSize] offset += banderwagon.UncompressedSize diff --git a/epoch.go b/epoch.go new file mode 100644 index 00000000..4c0701bf --- /dev/null +++ b/epoch.go @@ -0,0 +1,19 @@ +package verkle + +import ( + "encoding/binary" +) + +type StateEpoch uint64 + +const ( + NumActiveEpochs = 2 +) + +func IsExpired(prev StateEpoch, cur StateEpoch) bool { + return cur-prev >= NumActiveEpochs +} + +func StateEpochFromBytes(b []byte) StateEpoch { + return StateEpoch(binary.BigEndian.Uint64(b)) +} diff --git a/expired_leaf.go b/expired_leaf.go index 68cd52fc..84c24b61 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -35,15 +35,15 @@ func NewExpiredLeafNode(stem Stem, commitment *Point) *ExpiredLeafNode { return &ExpiredLeafNode{stem: stem, commitment: commitment} } -func (n *ExpiredLeafNode) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { +func (n *ExpiredLeafNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { return errExpired } -func (n *ExpiredLeafNode) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { +func (n *ExpiredLeafNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { return false, errExpired } -func (n *ExpiredLeafNode) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { +func (n *ExpiredLeafNode) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { return nil, errExpired } @@ -58,7 +58,7 @@ func (n *ExpiredLeafNode) Commitment() *Point { return n.commitment } -func (n *ExpiredLeafNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (n *ExpiredLeafNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { var ( pe = &ProofElements{ Vals: make([][]byte, len(keys)), diff --git a/expired_tree_test.go b/expired_tree_test.go index fabb45ba..a879941a 100644 --- a/expired_tree_test.go +++ b/expired_tree_test.go @@ -31,8 +31,8 @@ func TestInsertSameLeafNoExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaf.values[oneKeyTest[StemSize]]) } - if leaf.lastTs != 1 { - t.Fatalf("expected last accessed to be 1, got %d", leaf.lastTs) + if leaf.lastEpoch != 1 { + t.Fatalf("expected last accessed to be 1, got %d", leaf.lastEpoch) } } @@ -58,8 +58,8 @@ func TestInsertSameLeafExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaf.values[zeroKeyTest[StemSize]]) } - if leaf.lastTs != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastTs) + if leaf.lastEpoch != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastEpoch) } } @@ -93,12 +93,12 @@ func TestInsertDiffLeaf(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaff.values[ffx32KeyTest[StemSize]]) } - if leaf0.lastTs != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf0.lastTs) + if leaf0.lastEpoch != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf0.lastEpoch) } - if leaff.lastTs != 2 { - t.Fatalf("expected last accessed to be 2, got %d", leaff.lastTs) + if leaff.lastEpoch != 2 { + t.Fatalf("expected last accessed to be 2, got %d", leaff.lastEpoch) } } @@ -124,8 +124,8 @@ func TestGetNoExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, val) } - if leaf.lastTs != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastTs) + if leaf.lastEpoch != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastEpoch) } } @@ -151,8 +151,8 @@ func TestGetExpired(t *testing.T) { t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) } - if leaf.lastTs != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastTs) + if leaf.lastEpoch != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastEpoch) } } @@ -192,8 +192,8 @@ func TestDelLeafExpired(t *testing.T) { t.Fatalf("expected empty node, got %T", root.(*InternalNode).children[0]) } - if leaf.lastTs != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastTs) + if leaf.lastEpoch != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastEpoch) } } @@ -222,7 +222,7 @@ func TestRootCommitExpired(t *testing.T) { } } -func TestRootCommitDiffTimestamp(t *testing.T) { +func TestRootCommitDiffEpoch(t *testing.T) { t.Parallel() root1 := New() diff --git a/hashednode.go b/hashednode.go index e2e4e24d..7613cd64 100644 --- a/hashednode.go +++ b/hashednode.go @@ -32,15 +32,15 @@ import ( type HashedNode struct{} -func (HashedNode) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { +func (HashedNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { return errInsertIntoHash } -func (HashedNode) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { +func (HashedNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { return false, errors.New("cant delete a hashed node in-place") } -func (HashedNode) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { +func (HashedNode) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { return nil, errors.New("can not read from a hash node") } @@ -58,7 +58,7 @@ func (HashedNode) Commitment() *Point { panic("can not get commitment of a hash node") } -func (HashedNode) GetProofItems(keylist, AccessTimestamp, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (HashedNode) GetProofItems(keylist, StateEpoch, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { return nil, nil, nil, errors.New("can not get the full path, and there is no proof of absence") } diff --git a/proof_ipa.go b/proof_ipa.go index 90c086bc..0e25fbaf 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -188,20 +188,20 @@ func (sd StateDiff) Equal(other StateDiff) error { func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { sort.Sort(keylist(keys)) - return root.GetProofItems(keylist(keys), curTs, resolver) + return root.GetProofItems(keylist(keys), curEpoch, resolver) } // 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, preTs, postTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, error) { +func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, preEpoch, postEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, 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") } - pe, es, poas, err := GetCommitmentsForMultiproof(preroot, keys, preTs, resolver) + pe, es, poas, err := GetCommitmentsForMultiproof(preroot, keys, preEpoch, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("error getting pre-state proof data: %w", err) } @@ -213,7 +213,7 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, preTs // 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], postTs, resolver) + val, err := postroot.Get(keys[i], postEpoch, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("error getting post-state value for key %x: %w", keys[i], err) } @@ -228,8 +228,8 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, preTs return pe, es, poas, postvals, nil } -func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, preTs, postTs AccessTimestamp, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { - pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, preTs, postTs, resolver) +func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, preEpoch, postEpoch StateEpoch, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { + pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, preEpoch, postEpoch, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("get commitments for multiproof: %s", err) } @@ -271,8 +271,8 @@ func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, preTs, po } // verifyVerkleProofWithPreState takes a proof and a trusted tree root and verifies that the proof is valid. -func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode, preTs AccessTimestamp) error { - pe, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, preTs, 0, nil) +func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode, preEpoch StateEpoch) error { + pe, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, preEpoch, 0, nil) if err != nil { return fmt.Errorf("error getting proof elements: %w", err) } @@ -612,7 +612,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, postTs AccessTimestamp) (VerkleNode, error) { +func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postEpoch StateEpoch) (VerkleNode, error) { postroot := preroot.Copy() for _, stemstatediff := range statediff { @@ -633,7 +633,7 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postTs if overwrites { var stem [StemSize]byte copy(stem[:StemSize], stemstatediff.Stem[:]) - if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postTs, stemstatediff.Resurrected, nil); err != nil { + if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postEpoch, stemstatediff.Resurrected, nil); err != nil { return nil, fmt.Errorf("error overwriting value in post state: %w", err) } } @@ -650,7 +650,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, curTs AccessTimestamp) error { +func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statediff StateDiff, curEpoch StateEpoch) error { proof, err := DeserializeProof(vp, statediff) if err != nil { return fmt.Errorf("verkle proof deserialization error: %w", err) @@ -692,7 +692,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, curTs) + posttree, err := PostStateTreeFromStateDiff(pretree, statediff, curEpoch) if err != nil { return fmt.Errorf("error rebuilding the post-tree from proof: %w", err) } @@ -701,5 +701,5 @@ func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statedif return fmt.Errorf("post tree root mismatch: %x != %x", regeneratedPostTreeRoot, postStateRoot) } - return verifyVerkleProofWithPreState(proof, pretree, curTs) + return verifyVerkleProofWithPreState(proof, pretree, curEpoch) } diff --git a/proof_test.go b/proof_test.go index 6f3d98a9..53380203 100644 --- a/proof_test.go +++ b/proof_test.go @@ -1187,11 +1187,11 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Parallel() - preTs := AccessTimestamp(0) - postTs := AccessTimestamp(2) + preEpoch := StateEpoch(0) + postEpoch := StateEpoch(2) preRoot := New() - if err := preRoot.Insert(zeroKeyTest, zeroKeyTest, preTs, nil); err != nil { + if err := preRoot.Insert(zeroKeyTest, zeroKeyTest, preEpoch, nil); err != nil { t.Fatalf("could not insert key: %v", err) } rootC := preRoot.Commit() @@ -1202,11 +1202,11 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { preRoot.(*InternalNode).children[0] = expiredLeaf postRoot := New() - if err := postRoot.Insert(zeroKeyTest, fourtyKeyTest, postTs, nil); err != nil { + if err := postRoot.Insert(zeroKeyTest, fourtyKeyTest, postEpoch, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - proof, _, _, _, _ := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, preTs, postTs, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, preEpoch, postEpoch, nil) p, diff, err := SerializeProof(proof) if err != nil { @@ -1218,7 +1218,7 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Fatalf("error deserializing proof: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, preRoot, preTs); err != nil { + if err = verifyVerkleProofWithPreState(dproof, preRoot, preEpoch); err != nil { t.Fatalf("could not verify verkle proof: %v", err) } @@ -1227,16 +1227,16 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Fatalf("error recreating pre tree: %v", err) } - dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff, postTs) + dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff, postEpoch) if err != nil { t.Fatalf("error recreating post tree: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, dpreroot, preTs); err != nil { + if err = verifyVerkleProofWithPreState(dproof, dpreroot, preEpoch); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) } - got, err := dpostroot.Get(zeroKeyTest, postTs, nil) + got, err := dpostroot.Get(zeroKeyTest, postEpoch, nil) if err != nil { t.Fatalf("error getting key: %v", err) } diff --git a/timestamp.go b/timestamp.go deleted file mode 100644 index 56df1b64..00000000 --- a/timestamp.go +++ /dev/null @@ -1,19 +0,0 @@ -package verkle - -import ( - "encoding/binary" -) - -type AccessTimestamp uint64 - -const ( - NumActiveTimestamps = 2 -) - -func IsExpired(prev AccessTimestamp, cur AccessTimestamp) bool { - return cur-prev >= NumActiveTimestamps -} - -func AccessTimestampFromBytes(b []byte) AccessTimestamp { - return AccessTimestamp(binary.BigEndian.Uint64(b)) -} diff --git a/tree.go b/tree.go index 49843b0c..5903082a 100644 --- a/tree.go +++ b/tree.go @@ -68,13 +68,13 @@ func KeyToStem(key []byte) Stem { type VerkleNode interface { // Insert or Update value into the tree - Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error + Insert([]byte, []byte, StateEpoch, NodeResolverFn) error // Delete a leaf with the given key - Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) + Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) // Get value at a given key - Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) + Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) // Commit computes the commitment of the node. The // result (the curve point) is cached. @@ -91,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, AccessTimestamp, NodeResolverFn) (*ProofElements, []byte, []Stem, error) + GetProofItems(keylist, StateEpoch, NodeResolverFn) (*ProofElements, []byte, []Stem, error) // Serialize encodes the node to RLP. Serialize() ([]byte, error) @@ -203,7 +203,7 @@ type ( // true in the context of a stateless tree. isPOAStub bool - lastTs AccessTimestamp + lastEpoch StateEpoch } ) @@ -228,6 +228,7 @@ func (n *InternalNode) toExportable() *ExportableInternalNode { Values: child.values, C: child.commitment.Bytes(), C1: child.c1.Bytes(), + LastEpoch: child.lastEpoch, } case *ExpiredLeafNode: exportable.Children[i] = &ExportableExpiredLeafNode{ @@ -275,7 +276,7 @@ func NewStatelessInternal(depth byte, comm *Point) VerkleNode { } // New creates a new leaf node -func NewLeafNode(stem Stem, values [][]byte, lastTs AccessTimestamp) (*LeafNode, error) { +func NewLeafNode(stem Stem, values [][]byte, lastEpoch StateEpoch) (*LeafNode, error) { cfg := GetConfig() // C1. @@ -318,7 +319,7 @@ func NewLeafNode(stem Stem, values [][]byte, lastTs AccessTimestamp) (*LeafNode, return nil, fmt.Errorf("batch mapping to scalar fields: %s", err) } - poly[4].SetUint64(uint64(lastTs)) + poly[4].SetUint64(uint64(lastEpoch)) return &LeafNode{ // depth will be 0, but the commitment calculation @@ -328,20 +329,20 @@ func NewLeafNode(stem Stem, values [][]byte, lastTs AccessTimestamp) (*LeafNode, commitment: cfg.CommitToPoly(poly[:], NodeWidth-5), c1: c1, c2: c2, - lastTs: lastTs, + lastEpoch: lastEpoch, }, 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, lastTs AccessTimestamp) *LeafNode { +func NewLeafNodeWithNoComms(stem Stem, values [][]byte, lastEpoch StateEpoch) *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, - lastTs: lastTs, + lastEpoch: lastEpoch, } } @@ -371,13 +372,13 @@ func (n *InternalNode) cowChild(index byte) { } } -func (n *InternalNode) Insert(key []byte, value []byte, curTs AccessTimestamp, resolver NodeResolverFn) error { +func (n *InternalNode) Insert(key []byte, value []byte, curEpoch StateEpoch, resolver NodeResolverFn) error { values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.InsertValuesAtStem(KeyToStem(key), values, curTs, false, resolver) + return n.InsertValuesAtStem(KeyToStem(key), values, curEpoch, false, resolver) } -func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs AccessTimestamp, isResurrect bool, resolver NodeResolverFn) error { +func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch StateEpoch, isResurrect 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) { @@ -386,7 +387,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs Acce case Empty: n.cowChild(nChild) var err error - n.children[nChild], err = NewLeafNode(stem, values, curTs) + n.children[nChild], err = NewLeafNode(stem, values, curEpoch) if err != nil { return err } @@ -407,14 +408,14 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs Acce n.cowChild(nChild) // recurse to handle the case of a LeafNode child that // splits. - return n.InsertValuesAtStem(stem, values, curTs, isResurrect, resolver) + return n.InsertValuesAtStem(stem, values, curEpoch, isResurrect, resolver) case *ExpiredLeafNode: if !isResurrect { return errExpired } // create a new leaf node with the given values - leaf, err := NewLeafNode(stem, values, curTs) + leaf, err := NewLeafNode(stem, values, curEpoch) if err != nil { return err } @@ -429,7 +430,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs Acce return errIsPOAStub } n.cowChild(nChild) - return child.insertMultiple(stem, values, curTs) + return child.insertMultiple(stem, values, curEpoch) } n.cowChild(nChild) @@ -445,12 +446,12 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs Acce nextWordInInsertedKey := offset2key(stem, n.depth+1) if nextWordInInsertedKey == nextWordInExistingKey { - return newBranch.InsertValuesAtStem(stem, values, curTs, isResurrect, resolver) + return newBranch.InsertValuesAtStem(stem, values, curEpoch, isResurrect, resolver) } // Next word differs, so this was the last level. // Insert it directly into its final slot. - leaf, err := NewLeafNode(stem, values, curTs) + leaf, err := NewLeafNode(stem, values, curEpoch) if err != nil { return err } @@ -459,7 +460,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curTs Acce newBranch.children[nextWordInInsertedKey] = leaf case *InternalNode: n.cowChild(nChild) - return child.InsertValuesAtStem(stem, values, curTs, isResurrect, resolver) + return child.InsertValuesAtStem(stem, values, curEpoch, isResurrect, resolver) default: // It should be an UknownNode. return errUnknownNodeType } @@ -584,7 +585,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, curTs AccessTimestamp, resolver NodeResolverFn) ([][]byte, error) { +func (n *InternalNode) GetValuesAtStem(stem Stem, curEpoch StateEpoch, 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: @@ -606,11 +607,11 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, curTs AccessTimestamp, resolve n.children[nchild] = resolved // recurse to handle the case of a LeafNode child that // splits. - return n.GetValuesAtStem(stem, curTs, resolver) + return n.GetValuesAtStem(stem, curEpoch, resolver) case *ExpiredLeafNode: return nil, errExpired case *LeafNode: - if IsExpired(child.lastTs, curTs) { + if IsExpired(child.lastEpoch, curEpoch) { return nil, errExpired } @@ -624,13 +625,13 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, curTs AccessTimestamp, resolve } return nil, nil case *InternalNode: - return child.GetValuesAtStem(stem, curTs, resolver) + return child.GetValuesAtStem(stem, curEpoch, resolver) default: return nil, errUnknownNodeType } } -func (n *InternalNode) Delete(key []byte, curTs AccessTimestamp, resolver NodeResolverFn) (bool, error) { +func (n *InternalNode) Delete(key []byte, curEpoch StateEpoch, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { case Empty: @@ -649,12 +650,12 @@ func (n *InternalNode) Delete(key []byte, curTs AccessTimestamp, resolver NodeRe return false, err } n.children[nChild] = c - return n.Delete(key, curTs, resolver) + return n.Delete(key, curEpoch, resolver) case *ExpiredLeafNode: return false, errExpired default: n.cowChild(nChild) - del, err := child.Delete(key, curTs, resolver) + del, err := child.Delete(key, curEpoch, resolver) if err != nil { return false, err } @@ -683,7 +684,7 @@ func (n *InternalNode) Delete(key []byte, curTs AccessTimestamp, resolver NodeRe // 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. -// TODO(weiihann): check if need to compare access timestamp +// TODO(weiihann): check if need to compare access epochs func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { @@ -815,11 +816,11 @@ func (n *InternalNode) FlushAtDepth(depth uint8, flush NodeFlushFn) { } } -func (n *InternalNode) Get(key []byte, curTs AccessTimestamp, resolver NodeResolverFn) ([]byte, error) { +func (n *InternalNode) Get(key []byte, curEpoch StateEpoch, 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), curTs, resolver) + stemValues, err := n.GetValuesAtStem(KeyToStem(key), curEpoch, resolver) if err != nil { return nil, err } @@ -989,7 +990,7 @@ func groupKeys(keys keylist, depth byte) []keylist { return groups } -func (n *InternalNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (n *InternalNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { var ( groups = groupKeys(keys, n.depth) pe = &ProofElements{ @@ -1084,7 +1085,7 @@ func (n *InternalNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolv continue } - pec, es, other, err := n.children[childIdx].GetProofItems(group, curTs, resolver) + pec, es, other, err := n.children[childIdx].GetProofItems(group, curEpoch, resolver) if err != nil { // TODO: add a test case to cover this scenario. return nil, nil, nil, err @@ -1195,7 +1196,7 @@ func (n *InternalNode) touchCoW(index byte) { n.cowChild(index) } -func (n *LeafNode) Insert(key []byte, value []byte, curTs AccessTimestamp, _ NodeResolverFn) error { +func (n *LeafNode) Insert(key []byte, value []byte, curEpoch StateEpoch, _ NodeResolverFn) error { if n.isPOAStub { return errIsPOAStub } @@ -1204,7 +1205,7 @@ func (n *LeafNode) Insert(key []byte, value []byte, curTs AccessTimestamp, _ Nod return fmt.Errorf("invalid key size: %d", len(key)) } - if IsExpired(n.lastTs, curTs) { + if IsExpired(n.lastEpoch, curEpoch) { return errExpired } @@ -1214,27 +1215,27 @@ func (n *LeafNode) Insert(key []byte, value []byte, curTs AccessTimestamp, _ Nod } values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.insertMultiple(stem, values, curTs) + return n.insertMultiple(stem, values, curEpoch) } -func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curTs AccessTimestamp) error { +func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curEpoch StateEpoch) error { // Sanity check: ensure the stems are the same. if !equalPaths(stem, n.stem) { return errInsertIntoOtherStem } - if IsExpired(n.lastTs, curTs) { + if IsExpired(n.lastEpoch, curEpoch) { return errExpired } - if err := n.updateMultipleLeaves(values, curTs); err != nil { + if err := n.updateMultipleLeaves(values, curEpoch); err != nil { return err } return nil } -func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curTs AccessTimestamp) { +func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curEpoch StateEpoch) { // Calculate the Fr-delta. var deltaC Fr deltaC.Sub(&newC, &oldC) @@ -1243,8 +1244,8 @@ func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curTs AccessTimestamp) var poly [NodeWidth]Fr poly[cxIndex] = deltaC - if n.lastTs != curTs { - poly[4].SetUint64(uint64(curTs)) + if n.lastEpoch != curEpoch { + poly[4].SetUint64(uint64(curEpoch)) } // Add delta to the current commitment. @@ -1286,7 +1287,7 @@ func (n *LeafNode) updateCn(index byte, value []byte, c *Point) error { return nil } -func (n *LeafNode) updateLeaf(index byte, value []byte, curTs AccessTimestamp) error { +func (n *LeafNode) updateLeaf(index byte, value []byte, curEpoch StateEpoch) error { // Update the corresponding C1 or C2 commitment. var c *Point var oldC Point @@ -1309,14 +1310,14 @@ func (n *LeafNode) updateLeaf(index byte, value []byte, curTs AccessTimestamp) e // 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], curTs) - n.updateLastTs(curTs) + n.updateC(cxIndex, frs[0], frs[1], curEpoch) + n.updateLastTs(curEpoch) n.values[index] = value return nil } -func (n *LeafNode) updateMultipleLeaves(values [][]byte, curTs AccessTimestamp) error { // skipcq: GO-R1005 +func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) 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. @@ -1362,21 +1363,21 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curTs AccessTimestamp) 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], curTs) - n.updateC(c2Idx, frs[2], frs[3], curTs) - n.updateLastTs(curTs) + n.updateC(c1Idx, frs[0], frs[1], curEpoch) + n.updateC(c2Idx, frs[2], frs[3], curEpoch) + n.updateLastTs(curEpoch) } 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], curTs) - n.updateLastTs(curTs) + n.updateC(c1Idx, frs[0], frs[1], curEpoch) + n.updateLastTs(curEpoch) } 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], curTs) - n.updateLastTs(curTs) + n.updateC(c2Idx, frs[0], frs[1], curEpoch) + n.updateLastTs(curEpoch) } return nil @@ -1384,13 +1385,13 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curTs AccessTimestamp) // 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, curTs AccessTimestamp, _ NodeResolverFn) (bool, error) { +func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool, error) { // Sanity check: ensure the key header is the same: if !equalPaths(k, n.stem) { return false, nil } - if IsExpired(n.lastTs, curTs) { + if IsExpired(n.lastEpoch, curEpoch) { return false, errExpired } @@ -1457,11 +1458,11 @@ func (n *LeafNode) Delete(k []byte, curTs AccessTimestamp, _ NodeResolverFn) (bo n.commitment.Sub(n.commitment, cfg.CommitToPoly(poly[:], 0)) // TODO(weiihann): can this be done together with the previous? - if n.lastTs != curTs { + if n.lastEpoch != curEpoch { var poly [5]Fr - poly[4].SetUint64(uint64(curTs)) + poly[4].SetUint64(uint64(curEpoch)) n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) - n.updateLastTs(curTs) + n.updateLastTs(curEpoch) } // Clear the corresponding commitment @@ -1485,10 +1486,10 @@ func (n *LeafNode) Delete(k []byte, curTs AccessTimestamp, _ NodeResolverFn) (bo // 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, curTs) + return false, n.updateLeaf(k[StemSize], nil, curEpoch) } -func (n *LeafNode) Get(k []byte, curTs AccessTimestamp, _ NodeResolverFn) ([]byte, error) { +func (n *LeafNode) Get(k []byte, curEpoch StateEpoch, _ NodeResolverFn) ([]byte, error) { if n.isPOAStub { return nil, errIsPOAStub } @@ -1524,8 +1525,8 @@ func (n *LeafNode) Commit() *Point { return n.commitment } -func (n *LeafNode) updateLastTs(curTs AccessTimestamp) { - n.lastTs = curTs +func (n *LeafNode) updateLastTs(curEpoch StateEpoch) { + n.lastEpoch = curEpoch } // fillSuffixTreePoly takes one of the two suffix tree and @@ -1576,7 +1577,7 @@ func leafToComms(poly []Fr, val []byte) error { return nil } -func (n *LeafNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { // skipcq: GO-R1005 +func (n *LeafNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { // skipcq: GO-R1005 var ( poly [NodeWidth]Fr // top-level polynomial pe = &ProofElements{ @@ -1593,7 +1594,7 @@ func (n *LeafNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolver N ) // If the leaf node is expired, generate the proof of expiry. - if IsExpired(n.lastTs, curTs) { + if IsExpired(n.lastEpoch, curEpoch) { for i := range keys { pe.ByPath[string(keys[i][:n.depth])] = n.commitment pe.Vals[i] = nil @@ -1605,7 +1606,7 @@ func (n *LeafNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolver N return &ProofElements{}, esses, poass, nil } - // Initialize the top-level polynomial with 1 + stem + C1 + C2 + lastTs + // Initialize the top-level polynomial with 1 + stem + C1 + C2 + lastEpoch 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) @@ -1653,8 +1654,8 @@ func (n *LeafNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolver N pe.Fis = append(pe.Fis, poly[:]) } - // add last accessed timestamp - poly[4].SetUint64(uint64(n.lastTs)) + // add last accessed epoch + poly[4].SetUint64(uint64(n.lastEpoch)) pe.Cis = append(pe.Cis, n.commitment) pe.Zis = append(pe.Zis, 4) pe.Yis = append(pe.Yis, &poly[4]) @@ -1748,7 +1749,7 @@ func (n *LeafNode) GetProofItems(keys keylist, curTs AccessTimestamp, resolver N } // 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 @@ -2012,8 +2013,8 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte - lastTs := make([]byte, EpochSize) - binary.BigEndian.PutUint64(lastTs, uint64(n.lastTs)) + lastEpoch := make([]byte, EpochSize) + binary.BigEndian.PutUint64(lastEpoch, uint64(n.lastEpoch)) switch { case count == 1: var buf [singleSlotLeafSize]byte @@ -2022,7 +2023,7 @@ 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:], lastTs) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastEpoch) result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize] = byte(lastIdx) copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize+leafValueIndexSize:], n.values[lastIdx][:]) case isEoA: @@ -2032,7 +2033,7 @@ 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:], lastTs) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastEpoch) copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize:], n.values[1]) // copy balance copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize+leafBalanceSize:], n.values[2][:leafNonceSize]) // copy nonce default: @@ -2043,7 +2044,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafCommitmentOffset:], cBytes[:]) copy(result[leafC1CommitmentOffset:], c1Bytes[:]) copy(result[leafC2CommitmentOffset:], c2Bytes[:]) - copy(result[leafLastTsOffset:], lastTs) + copy(result[leafLastTsOffset:], lastEpoch) copy(result[leafChildrenOffset:], children) } diff --git a/unknown.go b/unknown.go index 11ed7ee8..88107572 100644 --- a/unknown.go +++ b/unknown.go @@ -29,15 +29,15 @@ import "errors" type UnknownNode struct{} -func (UnknownNode) Insert([]byte, []byte, AccessTimestamp, NodeResolverFn) error { +func (UnknownNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { return errMissingNodeInStateless } -func (UnknownNode) Delete([]byte, AccessTimestamp, NodeResolverFn) (bool, error) { +func (UnknownNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { return false, errors.New("cant delete in a subtree missing form a stateless view") } -func (UnknownNode) Get([]byte, AccessTimestamp, NodeResolverFn) ([]byte, error) { +func (UnknownNode) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { return nil, nil } @@ -51,7 +51,7 @@ func (UnknownNode) Commitment() *Point { return &id } -func (UnknownNode) GetProofItems(keylist, AccessTimestamp, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (UnknownNode) GetProofItems(keylist, StateEpoch, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { return nil, nil, nil, errors.New("can't generate proof items for unknown node") } From 4f15b6ccee0aee0bd20a3532d41e5a9a760d6d4f Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 30 Oct 2024 13:57:30 +0800 Subject: [PATCH 11/33] implement toDot for expired leaf --- expired_leaf.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/expired_leaf.go b/expired_leaf.go index 84c24b61..9aa48ebd 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -25,6 +25,10 @@ package verkle +import ( + "fmt" +) + type ExpiredLeafNode struct { stem Stem commitment *Point @@ -104,8 +108,10 @@ func (n *ExpiredLeafNode) Copy() VerkleNode { return l } -func (n *ExpiredLeafNode) toDot(string, string) string { - return "" +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) { From 3d0659acb2f3e53ed70a4309eb414e3392dd1f73 Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 30 Oct 2024 14:00:55 +0800 Subject: [PATCH 12/33] make epoch size internal --- config.go | 2 +- encoding.go | 18 +++++++++--------- encoding_test.go | 2 +- tree.go | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/config.go b/config.go index 6661f97a..a5b72c57 100644 --- a/config.go +++ b/config.go @@ -35,7 +35,7 @@ const ( NodeWidth = 256 NodeBitWidth byte = 8 StemSize = 31 - EpochSize = 8 + epochSize = 8 ) func equalPaths(key1, key2 []byte) bool { diff --git a/encoding.go b/encoding.go index c2890fd1..1c94aa05 100644 --- a/encoding.go +++ b/encoding.go @@ -56,13 +56,13 @@ const ( leafC1CommitmentOffset = leafCommitmentOffset + banderwagon.UncompressedSize leafC2CommitmentOffset = leafC1CommitmentOffset + banderwagon.UncompressedSize leafChildrenOffset = leafC2CommitmentOffset + banderwagon.UncompressedSize - leafLastTsOffset = leafChildrenOffset + EpochSize + leafLastTsOffset = leafChildrenOffset + epochSize leafBalanceSize = 32 leafNonceSize = 8 leafSlotSize = 32 leafValueIndexSize = 1 - singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + EpochSize - eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBalanceSize + leafNonceSize + EpochSize + singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + epochSize + eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBalanceSize + leafNonceSize + epochSize expiredLeafSize = nodeTypeSize + StemSize + banderwagon.UncompressedSize ) @@ -120,7 +120,7 @@ func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) { ln := NewLeafNodeWithNoComms( serialized[leafStemOffset:leafStemOffset+StemSize], values[:], - StateEpochFromBytes(serialized[leafLastTsOffset:leafLastTsOffset+EpochSize])) + StateEpochFromBytes(serialized[leafLastTsOffset:leafLastTsOffset+epochSize])) ln.setDepth(depth) ln.c1 = new(Point) @@ -145,8 +145,8 @@ 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 + EpochSize - epochOffset := offset - EpochSize + offset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize + epochSize + epochOffset := offset - epochSize values[0] = zero32[:] // 0 version values[1] = serialized[offset : offset+leafBalanceSize] // balance var nonce [32]byte @@ -155,7 +155,7 @@ func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) { values[2] = nonce[:] // nonce values[3] = EmptyCodeHash[:] values[4] = zero32[:] // 0 code size - ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+EpochSize])) + ln := NewLeafNodeWithNoComms(serialized[leafStemOffset:leafStemOffset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+epochSize])) ln.setDepth(depth) ln.c1 = new(Point) if err := ln.c1.SetBytesUncompressed(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+banderwagon.UncompressedSize], true); err != nil { @@ -173,12 +173,12 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { var values [NodeWidth][]byte offset := leafStemOffset epochOffset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize - ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+EpochSize])) + ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+epochSize])) offset += StemSize cnCommBytes := serialized[offset : offset+banderwagon.UncompressedSize] offset += banderwagon.UncompressedSize rootCommBytes := serialized[offset : offset+banderwagon.UncompressedSize] - offset += banderwagon.UncompressedSize + EpochSize + offset += banderwagon.UncompressedSize + epochSize idx := serialized[offset] offset += leafValueIndexSize values[idx] = serialized[offset : offset+leafSlotSize] // copy slot diff --git a/encoding_test.go b/encoding_test.go index fc75aaeb..d05f731b 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -30,7 +30,7 @@ func TestLeafStemLength(t *testing.T) { if err != nil { t.Fatal(err) } - if len(ser) != nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+EpochSize { + if len(ser) != nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+epochSize { 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) } } diff --git a/tree.go b/tree.go index 5903082a..46564639 100644 --- a/tree.go +++ b/tree.go @@ -2013,7 +2013,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte - lastEpoch := make([]byte, EpochSize) + lastEpoch := make([]byte, epochSize) binary.BigEndian.PutUint64(lastEpoch, uint64(n.lastEpoch)) switch { case count == 1: @@ -2024,8 +2024,8 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafStemOffset+StemSize:], c1Bytes[:]) copy(result[leafStemOffset+StemSize+banderwagon.UncompressedSize:], cBytes[:]) copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastEpoch) - result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize] = byte(lastIdx) - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize+leafValueIndexSize:], n.values[lastIdx][:]) + result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+epochSize] = byte(lastIdx) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+epochSize+leafValueIndexSize:], n.values[lastIdx][:]) case isEoA: var buf [eoaLeafSize]byte result = buf[:] @@ -2034,10 +2034,10 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafStemOffset+StemSize:], c1Bytes[:]) copy(result[leafStemOffset+StemSize+banderwagon.UncompressedSize:], cBytes[:]) copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastEpoch) - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize:], n.values[1]) // copy balance - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+EpochSize+leafBalanceSize:], n.values[2][:leafNonceSize]) // copy nonce + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+epochSize:], n.values[1]) // copy balance + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+epochSize+leafBalanceSize:], n.values[2][:leafNonceSize]) // copy nonce default: - result = make([]byte, nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+EpochSize+len(children)) + result = make([]byte, nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+epochSize+len(children)) result[0] = leafType copy(result[leafStemOffset:], n.stem[:StemSize]) copy(result[leafBitlistOffset:], bitlist[:]) From d59b6205edbd0f3617f99bd4d6026a5d09c4f8db Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 30 Oct 2024 14:05:30 +0800 Subject: [PATCH 13/33] fix bug in leaf commitment --- encoding.go | 4 ++-- tree.go | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/encoding.go b/encoding.go index 1c94aa05..1c7cd3eb 100644 --- a/encoding.go +++ b/encoding.go @@ -56,7 +56,7 @@ const ( leafC1CommitmentOffset = leafCommitmentOffset + banderwagon.UncompressedSize leafC2CommitmentOffset = leafC1CommitmentOffset + banderwagon.UncompressedSize leafChildrenOffset = leafC2CommitmentOffset + banderwagon.UncompressedSize - leafLastTsOffset = leafChildrenOffset + epochSize + leafLastEpochOffset = leafChildrenOffset + epochSize leafBalanceSize = 32 leafNonceSize = 8 leafSlotSize = 32 @@ -120,7 +120,7 @@ func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) { ln := NewLeafNodeWithNoComms( serialized[leafStemOffset:leafStemOffset+StemSize], values[:], - StateEpochFromBytes(serialized[leafLastTsOffset:leafLastTsOffset+epochSize])) + StateEpochFromBytes(serialized[leafLastEpochOffset:leafLastEpochOffset+epochSize])) ln.setDepth(depth) ln.c1 = new(Point) diff --git a/tree.go b/tree.go index 46564639..13080d23 100644 --- a/tree.go +++ b/tree.go @@ -1310,8 +1310,8 @@ func (n *LeafNode) updateLeaf(index byte, value []byte, curEpoch StateEpoch) err // 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.updateLastEpoch(curEpoch) n.updateC(cxIndex, frs[0], frs[1], curEpoch) - n.updateLastTs(curEpoch) n.values[index] = value return nil @@ -1365,19 +1365,19 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) er } n.updateC(c1Idx, frs[0], frs[1], curEpoch) n.updateC(c2Idx, frs[2], frs[3], curEpoch) - n.updateLastTs(curEpoch) + n.updateLastEpoch(curEpoch) } 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], curEpoch) - n.updateLastTs(curEpoch) + n.updateLastEpoch(curEpoch) } 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], curEpoch) - n.updateLastTs(curEpoch) + n.updateLastEpoch(curEpoch) } return nil @@ -1462,7 +1462,7 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool var poly [5]Fr poly[4].SetUint64(uint64(curEpoch)) n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) - n.updateLastTs(curEpoch) + n.updateLastEpoch(curEpoch) } // Clear the corresponding commitment @@ -1525,7 +1525,7 @@ func (n *LeafNode) Commit() *Point { return n.commitment } -func (n *LeafNode) updateLastTs(curEpoch StateEpoch) { +func (n *LeafNode) updateLastEpoch(curEpoch StateEpoch) { n.lastEpoch = curEpoch } @@ -2044,7 +2044,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafCommitmentOffset:], cBytes[:]) copy(result[leafC1CommitmentOffset:], c1Bytes[:]) copy(result[leafC2CommitmentOffset:], c2Bytes[:]) - copy(result[leafLastTsOffset:], lastEpoch) + copy(result[leafLastEpochOffset:], lastEpoch) copy(result[leafChildrenOffset:], children) } From cc4367221688efd843cbc6bed72143d7706f4ab7 Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 30 Oct 2024 14:06:28 +0800 Subject: [PATCH 14/33] minor formatting improvements --- tree.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 13080d23..9c8c6515 100644 --- a/tree.go +++ b/tree.go @@ -1654,8 +1654,8 @@ func (n *LeafNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver Nod pe.Fis = append(pe.Fis, poly[:]) } - // add last accessed epoch poly[4].SetUint64(uint64(n.lastEpoch)) + pe.Cis = append(pe.Cis, n.commitment) pe.Zis = append(pe.Zis, 4) pe.Yis = append(pe.Yis, &poly[4]) From 861826a47e1b025d856dc0edea055fdecb0899bf Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 30 Oct 2024 14:33:51 +0800 Subject: [PATCH 15/33] make state epoch uint16 --- epoch.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/epoch.go b/epoch.go index 4c0701bf..12d2633c 100644 --- a/epoch.go +++ b/epoch.go @@ -4,7 +4,7 @@ import ( "encoding/binary" ) -type StateEpoch uint64 +type StateEpoch uint16 const ( NumActiveEpochs = 2 @@ -15,5 +15,5 @@ func IsExpired(prev StateEpoch, cur StateEpoch) bool { } func StateEpochFromBytes(b []byte) StateEpoch { - return StateEpoch(binary.BigEndian.Uint64(b)) + return StateEpoch(binary.BigEndian.Uint16(b)) } From e558f36a7eca5b91ae73e2a30a7fc712bf451b77 Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 30 Oct 2024 14:46:19 +0800 Subject: [PATCH 16/33] remove epoch from GetProofItems --- empty.go | 2 +- empty_test.go | 2 +- expired_leaf.go | 2 +- hashednode.go | 2 +- hashednode_test.go | 2 +- proof_ipa.go | 12 ++++----- proof_test.go | 62 +++++++++++++++++++++++----------------------- tree.go | 25 ++++++------------- tree_test.go | 10 ++++---- unknown.go | 2 +- unknown_test.go | 2 +- 11 files changed, 56 insertions(+), 67 deletions(-) diff --git a/empty.go b/empty.go index f744d407..bb9a8571 100644 --- a/empty.go +++ b/empty.go @@ -53,7 +53,7 @@ func (Empty) Commitment() *Point { return &id } -func (Empty) GetProofItems(keylist, StateEpoch, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (Empty) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { return nil, nil, nil, errors.New("trying to produce a commitment for an empty subtree") } diff --git a/empty_test.go b/empty_test.go index e237f44f..318e110c 100644 --- a/empty_test.go +++ b/empty_test.go @@ -51,7 +51,7 @@ func TestEmptyFuncs(t *testing.T) { t.Fatal("commitment and commit mismatch") } - if _, _, _, err := e.GetProofItems(nil, 0, nil); err == nil { + if _, _, _, err := e.GetProofItems(nil, nil); err == nil { t.Fatal("get proof items should error") } diff --git a/expired_leaf.go b/expired_leaf.go index 9aa48ebd..f4fdab42 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -62,7 +62,7 @@ func (n *ExpiredLeafNode) Commitment() *Point { return n.commitment } -func (n *ExpiredLeafNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (n *ExpiredLeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { var ( pe = &ProofElements{ Vals: make([][]byte, len(keys)), diff --git a/hashednode.go b/hashednode.go index 7613cd64..881080c5 100644 --- a/hashednode.go +++ b/hashednode.go @@ -58,7 +58,7 @@ func (HashedNode) Commitment() *Point { panic("can not get commitment of a hash node") } -func (HashedNode) GetProofItems(keylist, StateEpoch, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +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") } diff --git a/hashednode_test.go b/hashednode_test.go index fb533075..1c48d9db 100644 --- a/hashednode_test.go +++ b/hashednode_test.go @@ -46,7 +46,7 @@ func TestHashedNodeFuncs(t *testing.T) { if v != nil { t.Fatal("non-nil get from a hashed node") } - if _, _, _, err := e.GetProofItems(nil, 0, 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/proof_ipa.go b/proof_ipa.go index 0e25fbaf..9fc25e93 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -188,20 +188,20 @@ func (sd StateDiff) Equal(other StateDiff) error { func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { sort.Sort(keylist(keys)) - return root.GetProofItems(keylist(keys), curEpoch, resolver) + return root.GetProofItems(keylist(keys), resolver) } // 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, preEpoch, postEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, error) { +func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, postEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, 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") } - pe, es, poas, err := GetCommitmentsForMultiproof(preroot, keys, preEpoch, resolver) + pe, es, poas, err := GetCommitmentsForMultiproof(preroot, keys, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("error getting pre-state proof data: %w", err) } @@ -228,8 +228,8 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, preEp return pe, es, poas, postvals, nil } -func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, preEpoch, postEpoch StateEpoch, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { - pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, preEpoch, postEpoch, resolver) +func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, postEpoch StateEpoch, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { + pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, postEpoch, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("get commitments for multiproof: %s", err) } @@ -272,7 +272,7 @@ func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, preEpoch, // verifyVerkleProofWithPreState takes a proof and a trusted tree root and verifies that the proof is valid. func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode, preEpoch StateEpoch) error { - pe, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, preEpoch, 0, nil) + pe, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, 0, nil) if err != nil { return fmt.Errorf("error getting proof elements: %w", err) } diff --git a/proof_test.go b/proof_test.go index 53380203..84174242 100644 --- a/proof_test.go +++ b/proof_test.go @@ -43,7 +43,7 @@ func TestProofEmptyTree(t *testing.T) { root := New() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, 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)) @@ -65,7 +65,7 @@ func TestProofVerifyTwoLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -92,7 +92,7 @@ func TestProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -119,9 +119,9 @@ func TestMultiProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], 0, nil) - pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], 0, nil) + pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) if err != nil { t.Fatal(err) } @@ -159,9 +159,9 @@ func TestMultiProofVerifyMultipleLeavesWithAbsentStem(t *testing.T) { absent[3] = 1 // and the stem differs keys = append(keys, absent) - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, nil) - pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, 0, nil) + pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { t.Fatal(err) } @@ -193,9 +193,9 @@ func TestMultiProofVerifyMultipleLeavesCommitmentRedundancy(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, nil) - pe, _, _, err := GetCommitmentsForMultiproof(root, keys, 0, nil) + pe, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { t.Fatal(err) } @@ -217,7 +217,7 @@ func TestProofOfAbsenceInternalVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -237,7 +237,7 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -262,7 +262,7 @@ func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { return ret }() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -284,7 +284,7 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { }() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -310,7 +310,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]}, 0, 0, nil); err != nil { + if _, _, _, _, err := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, 0, nil); err != nil { b.Fatal(err) } } @@ -331,7 +331,7 @@ func BenchmarkProofVerification(b *testing.B) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, 0, nil) b.ResetTimer() b.ReportAllocs() @@ -362,7 +362,7 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { } } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -401,7 +401,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -442,7 +442,7 @@ func TestProofDeserialize(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -455,7 +455,7 @@ func TestProofDeserialize(t *testing.T) { } _ = deserialized - pe, _, _, err := root.GetProofItems(keylist{absentkey[:]}, 0, nil) + pe, _, _, err := root.GetProofItems(keylist{absentkey[:]}, nil) if err != nil { t.Fatal(err) } @@ -472,7 +472,7 @@ func TestProofOfAbsenceEdgeCase(t *testing.T) { root.Commit() ret, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030303") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, 0, 0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -493,7 +493,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030301") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, 0, 0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -555,7 +555,7 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030200") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, 0, 0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, 0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -862,7 +862,7 @@ func TestProofOfExpiryOneLeaf(t *testing.T) { t.Fatalf("expected commitment to be %x, got %x", init, comm) } - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest}, 0, 0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest}, 0, nil) if err != nil { t.Fatalf("could not make verkle proof: %v", err) } @@ -914,7 +914,7 @@ func TestProofOfExpiryMultipleLeaves(t *testing.T) { t.Fatalf("expected commitment to be %x, got %x", init, comm) } - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest, ffx32KeyTest}, 0, 0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest, ffx32KeyTest}, 0, nil) if err != nil { t.Fatalf("could not make verkle proof: %v", err) } @@ -970,7 +970,7 @@ func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, pr proveKVs[string(key)] = value } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1119,7 +1119,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 } postroot.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, 0, nil) keys: for i := range proof.Keys { @@ -1206,7 +1206,7 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Fatalf("could not insert key: %v", err) } - proof, _, _, _, _ := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, preEpoch, postEpoch, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, postEpoch, nil) p, diff, err := SerializeProof(proof) if err != nil { @@ -1259,7 +1259,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { // 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}, 0, 0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, keylist{absentKey}, 0, nil) if err != nil { t.Fatal(err) } @@ -1284,7 +1284,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { } // From the rebuilt tree, validate the proof. - pe, _, _, err := GetCommitmentsForMultiproof(droot, keylist{absentKey}, 0, nil) + pe, _, _, err := GetCommitmentsForMultiproof(droot, keylist{absentKey}, nil) if err != nil { t.Fatal(err) } @@ -1339,7 +1339,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}, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1388,7 +1388,7 @@ func TestProveAbsenceInEmptyHalf(t *testing.T) { key2, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000100") key3, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, 0, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, 0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { diff --git a/tree.go b/tree.go index 9c8c6515..3234cdc1 100644 --- a/tree.go +++ b/tree.go @@ -91,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, StateEpoch, NodeResolverFn) (*ProofElements, []byte, []Stem, error) + GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) // Serialize encodes the node to RLP. Serialize() ([]byte, error) @@ -990,7 +990,7 @@ func groupKeys(keys keylist, depth byte) []keylist { return groups } -func (n *InternalNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { var ( groups = groupKeys(keys, n.depth) pe = &ProofElements{ @@ -1085,7 +1085,7 @@ func (n *InternalNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver continue } - pec, es, other, err := n.children[childIdx].GetProofItems(group, curEpoch, resolver) + pec, es, other, err := n.children[childIdx].GetProofItems(group, resolver) if err != nil { // TODO: add a test case to cover this scenario. return nil, nil, nil, err @@ -1577,7 +1577,7 @@ func leafToComms(poly []Fr, val []byte) error { return nil } -func (n *LeafNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { // skipcq: GO-R1005 +func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { // skipcq: GO-R1005 var ( poly [NodeWidth]Fr // top-level polynomial pe = &ProofElements{ @@ -1593,19 +1593,6 @@ func (n *LeafNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver Nod poass []Stem // list of proof-of-absence and proof-of-expiry stems ) - // If the leaf node is expired, generate the proof of expiry. - if IsExpired(n.lastEpoch, curEpoch) { - for i := range keys { - pe.ByPath[string(keys[i][:n.depth])] = n.commitment - pe.Vals[i] = nil - - esses = append(esses, extStatusExpired|(n.depth<<3)) - poass = append(poass, n.stem) - } - - return &ProofElements{}, esses, poass, nil - } - // Initialize the top-level polynomial with 1 + stem + C1 + C2 + lastEpoch poly[0].SetUint64(1) if err := StemFromLEBytes(&poly[1], n.stem); err != nil { @@ -1654,8 +1641,10 @@ func (n *LeafNode) GetProofItems(keys keylist, curEpoch StateEpoch, resolver Nod pe.Fis = append(pe.Fis, poly[:]) } + // We do not check for expiry, as we assume that the expiry detection is done before + // proof creation. poly[4].SetUint64(uint64(n.lastEpoch)) - + pe.Cis = append(pe.Cis, n.commitment) pe.Zis = append(pe.Zis, 4) pe.Yis = append(pe.Yis, &poly[4]) diff --git a/tree_test.go b/tree_test.go index b5f4989c..e9799adc 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1025,7 +1025,7 @@ func TestEmptyCommitment(t *testing.T) { t.Fatalf("inserting into the original failed: %v", err) } root.Commit() - pe, _, _, err := root.GetProofItems(keylist{ffx32KeyTest}, 0, nil) + pe, _, _, err := root.GetProofItems(keylist{ffx32KeyTest}, nil) if err != nil { t.Fatal(err) } @@ -1092,7 +1092,7 @@ func TestGetProofItemsNoPoaIfStemPresent(t *testing.T) { key1, _ := hex.DecodeString("ffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") key2, _ := hex.DecodeString("ffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - _, esses, poas, err := root.GetProofItems(keylist{key1, key2, ffx32KeyTest}, 0, nil) + _, esses, poas, err := root.GetProofItems(keylist{key1, key2, ffx32KeyTest}, nil) if err != nil { t.Fatal(err) } @@ -1347,7 +1347,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { r := tree.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, 0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) @@ -1368,7 +1368,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { if err != nil { t.Fatal(err) } - pe, _, _, err := droot.GetProofItems(keylist(keys), 0, nil) + pe, _, _, err := droot.GetProofItems(keylist(keys), nil) if err != nil { t.Fatal(err) } @@ -1819,7 +1819,7 @@ func runRandTest(rt randTest) error { continue } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, 0, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, 0, 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) } diff --git a/unknown.go b/unknown.go index 88107572..62d8232d 100644 --- a/unknown.go +++ b/unknown.go @@ -51,7 +51,7 @@ func (UnknownNode) Commitment() *Point { return &id } -func (UnknownNode) GetProofItems(keylist, StateEpoch, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (UnknownNode) GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, error) { return nil, nil, nil, errors.New("can't generate proof items for unknown node") } diff --git a/unknown_test.go b/unknown_test.go index a0fca436..8fb1c404 100644 --- a/unknown_test.go +++ b/unknown_test.go @@ -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, 0, 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 { From 9e5c6fadf56de6f1118d52e5f799bec5dcd73a7e Mon Sep 17 00:00:00 2001 From: weiihann Date: Tue, 19 Nov 2024 09:56:01 +0800 Subject: [PATCH 17/33] change epoch to period --- config.go | 2 +- conversion.go | 10 +-- debug.go | 2 +- empty.go | 6 +- encoding.go | 26 ++++---- encoding_test.go | 2 +- epoch.go | 8 +-- expired_leaf.go | 6 +- expired_leaf_test.go | 6 +- expired_tree_test.go | 34 +++++------ hashednode.go | 6 +- proof_ipa.go | 10 +-- proof_test.go | 4 +- tree.go | 142 +++++++++++++++++++++---------------------- unknown.go | 6 +- 15 files changed, 135 insertions(+), 135 deletions(-) diff --git a/config.go b/config.go index a5b72c57..63261ec8 100644 --- a/config.go +++ b/config.go @@ -35,7 +35,7 @@ const ( NodeWidth = 256 NodeBitWidth byte = 8 StemSize = 31 - epochSize = 8 + periodSize = 8 ) func equalPaths(key1, key2 []byte) bool { diff --git a/conversion.go b/conversion.go index 64ac70c3..0650aca0 100644 --- a/conversion.go +++ b/conversion.go @@ -97,7 +97,7 @@ func firstDiffByteIdx(stem1 []byte, stem2 []byte) int { panic("stems are equal") } -func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curEpoch StateEpoch, resolver NodeResolverFn) error { +func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curPeriod StatePeriod, resolver NodeResolverFn) error { sort.Slice(leaves, func(i, j int) bool { return bytes.Compare(leaves[i].stem, leaves[j].stem) < 0 }) @@ -132,13 +132,13 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curEpoch StateEpo start := currStemFirstByte end := i group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[start:end], curEpoch, resolver) + return n.insertMigratedLeavesSubtree(leaves[start:end], curPeriod, resolver) }) currStemFirstByte = i } } group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], curEpoch, resolver) + return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], curPeriod, resolver) }) if err := group.Wait(); err != nil { return fmt.Errorf("inserting migrated leaves: %w", err) @@ -147,7 +147,7 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curEpoch StateEpo return nil } -func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curEpoch StateEpoch, resolver NodeResolverFn) error { // skipcq: GO-R1005 +func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curPeriod StatePeriod, resolver NodeResolverFn) error { // skipcq: GO-R1005 for i := range leaves { ln := leaves[i] parent := n @@ -192,7 +192,7 @@ func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curEpoch S } } - if err := node.updateMultipleLeaves(nonPresentValues, curEpoch); err != nil { + if err := node.updateMultipleLeaves(nonPresentValues, curPeriod); err != nil { return fmt.Errorf("updating leaves: %s", err) } continue diff --git a/debug.go b/debug.go index 1ec6caa8..0e6c53c0 100644 --- a/debug.go +++ b/debug.go @@ -42,7 +42,7 @@ type ( C [32]byte `json:"commitment"` C1 [32]byte `json:"c1"` C2 [32]byte `json:"c2"` - LastEpoch StateEpoch `json:"last_epoch"` + LastPeriod StatePeriod `json:"last_period"` } ExportableExpiredLeafNode struct { diff --git a/empty.go b/empty.go index bb9a8571..a18e95e0 100644 --- a/empty.go +++ b/empty.go @@ -31,15 +31,15 @@ type Empty struct{} var errDirectInsertIntoEmptyNode = errors.New("an empty node should not be inserted directly into") -func (Empty) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { +func (Empty) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { return errDirectInsertIntoEmptyNode } -func (Empty) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { +func (Empty) Delete([]byte, StatePeriod, NodeResolverFn) (bool, error) { return false, errors.New("cant delete an empty node") } -func (Empty) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { +func (Empty) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } diff --git a/encoding.go b/encoding.go index 513c03ac..8b6e81fd 100644 --- a/encoding.go +++ b/encoding.go @@ -56,12 +56,12 @@ const ( leafC1CommitmentOffset = leafCommitmentOffset + banderwagon.UncompressedSize leafC2CommitmentOffset = leafC1CommitmentOffset + banderwagon.UncompressedSize leafChildrenOffset = leafC2CommitmentOffset + banderwagon.UncompressedSize - leafLastEpochOffset = leafChildrenOffset + epochSize + leafLastEpochOffset = leafChildrenOffset + periodSize leafBasicDataSize = 32 leafSlotSize = 32 leafValueIndexSize = 1 - singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + epochSize - eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize + epochSize + singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + periodSize + eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize + periodSize expiredLeafSize = nodeTypeSize + StemSize + banderwagon.UncompressedSize ) @@ -77,9 +77,9 @@ 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. @@ -119,7 +119,7 @@ func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) { ln := NewLeafNodeWithNoComms( serialized[leafStemOffset:leafStemOffset+StemSize], values[:], - StateEpochFromBytes(serialized[leafLastEpochOffset:leafLastEpochOffset+epochSize])) + StatePeriodFromBytes(serialized[leafLastEpochOffset:leafLastEpochOffset+periodSize])) ln.setDepth(depth) ln.c1 = new(Point) @@ -144,11 +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 + epochSize - epochOffset := offset - epochSize + 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[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+epochSize])) + 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 { @@ -165,13 +165,13 @@ func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) { func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { var values [NodeWidth][]byte offset := leafStemOffset - epochOffset := leafStemOffset + StemSize + 2*banderwagon.UncompressedSize - ln := NewLeafNodeWithNoComms(serialized[offset:offset+StemSize], values[:], StateEpochFromBytes(serialized[epochOffset:epochOffset+epochSize])) + 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 + epochSize + offset += banderwagon.UncompressedSize + periodSize idx := serialized[offset] offset += leafValueIndexSize values[idx] = serialized[offset : offset+leafSlotSize] // copy slot diff --git a/encoding_test.go b/encoding_test.go index d05f731b..0c7cec99 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -30,7 +30,7 @@ func TestLeafStemLength(t *testing.T) { if err != nil { t.Fatal(err) } - if len(ser) != nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+epochSize { + 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) } } diff --git a/epoch.go b/epoch.go index 12d2633c..0a2c1847 100644 --- a/epoch.go +++ b/epoch.go @@ -4,16 +4,16 @@ import ( "encoding/binary" ) -type StateEpoch uint16 +type StatePeriod uint16 const ( NumActiveEpochs = 2 ) -func IsExpired(prev StateEpoch, cur StateEpoch) bool { +func IsExpired(prev StatePeriod, cur StatePeriod) bool { return cur-prev >= NumActiveEpochs } -func StateEpochFromBytes(b []byte) StateEpoch { - return StateEpoch(binary.BigEndian.Uint16(b)) +func StatePeriodFromBytes(b []byte) StatePeriod { + return StatePeriod(binary.BigEndian.Uint16(b)) } diff --git a/expired_leaf.go b/expired_leaf.go index f4fdab42..d698ddd4 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -39,15 +39,15 @@ func NewExpiredLeafNode(stem Stem, commitment *Point) *ExpiredLeafNode { return &ExpiredLeafNode{stem: stem, commitment: commitment} } -func (n *ExpiredLeafNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { +func (n *ExpiredLeafNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { return errExpired } -func (n *ExpiredLeafNode) Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) { +func (n *ExpiredLeafNode) Delete([]byte, StatePeriod, NodeResolverFn) (bool, error) { return false, errExpired } -func (n *ExpiredLeafNode) Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) { +func (n *ExpiredLeafNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, errExpired } diff --git a/expired_leaf_test.go b/expired_leaf_test.go index f933b5fa..f25dc352 100644 --- a/expired_leaf_test.go +++ b/expired_leaf_test.go @@ -15,17 +15,17 @@ func TestExpiredLeafBasic(t *testing.T) { err := leaf.Insert(zeroKeyTest, zeroKeyTest, 0, nil) if !errors.Is(err, errExpired) { - t.Fatalf("expected epoch expired error when inserting, got %v", err) + t.Fatalf("expected period expired error when inserting, got %v", err) } _, err = leaf.Delete(zeroKeyTest, 0, nil) if !errors.Is(err, errExpired) { - t.Fatalf("expected epoch expired error when deleting, got %v", err) + t.Fatalf("expected period expired error when deleting, got %v", err) } v, err := leaf.Get(zeroKeyTest, 0, nil) if !errors.Is(err, errExpired) { - t.Fatalf("expected epoch expired error when getting, got %v", err) + t.Fatalf("expected period expired error when getting, got %v", err) } if v != nil { t.Fatal("expected nil value when getting") diff --git a/expired_tree_test.go b/expired_tree_test.go index a879941a..a74d3805 100644 --- a/expired_tree_test.go +++ b/expired_tree_test.go @@ -31,8 +31,8 @@ func TestInsertSameLeafNoExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaf.values[oneKeyTest[StemSize]]) } - if leaf.lastEpoch != 1 { - t.Fatalf("expected last accessed to be 1, got %d", leaf.lastEpoch) + if leaf.lastPeriod != 1 { + t.Fatalf("expected last accessed to be 1, got %d", leaf.lastPeriod) } } @@ -46,7 +46,7 @@ func TestInsertSameLeafExpired(t *testing.T) { err := root.Insert(oneKeyTest, testValue, 2, nil) if !errors.Is(err, errExpired) { - t.Fatalf("expected epoch expired error when inserting, got %v", err) + t.Fatalf("expected period expired error when inserting, got %v", err) } leaf, ok := root.(*InternalNode).children[0].(*LeafNode) @@ -58,8 +58,8 @@ func TestInsertSameLeafExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaf.values[zeroKeyTest[StemSize]]) } - if leaf.lastEpoch != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastEpoch) + if leaf.lastPeriod != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) } } @@ -93,12 +93,12 @@ func TestInsertDiffLeaf(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaff.values[ffx32KeyTest[StemSize]]) } - if leaf0.lastEpoch != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf0.lastEpoch) + if leaf0.lastPeriod != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf0.lastPeriod) } - if leaff.lastEpoch != 2 { - t.Fatalf("expected last accessed to be 2, got %d", leaff.lastEpoch) + if leaff.lastPeriod != 2 { + t.Fatalf("expected last accessed to be 2, got %d", leaff.lastPeriod) } } @@ -124,8 +124,8 @@ func TestGetNoExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, val) } - if leaf.lastEpoch != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastEpoch) + if leaf.lastPeriod != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) } } @@ -139,7 +139,7 @@ func TestGetExpired(t *testing.T) { val, err := root.Get(zeroKeyTest, 2, nil) if !errors.Is(err, errExpired) { - t.Fatalf("expected epoch expired error when getting, got %v", err) + t.Fatalf("expected period expired error when getting, got %v", err) } if val != nil { @@ -151,8 +151,8 @@ func TestGetExpired(t *testing.T) { t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) } - if leaf.lastEpoch != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastEpoch) + if leaf.lastPeriod != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) } } @@ -184,7 +184,7 @@ func TestDelLeafExpired(t *testing.T) { _, err := root.Delete(zeroKeyTest, 2, nil) if !errors.Is(err, errExpired) { - t.Fatalf("expected epoch expired error when deleting, got %v", err) + t.Fatalf("expected period expired error when deleting, got %v", err) } leaf, ok := root.(*InternalNode).children[0].(*LeafNode) @@ -192,8 +192,8 @@ func TestDelLeafExpired(t *testing.T) { t.Fatalf("expected empty node, got %T", root.(*InternalNode).children[0]) } - if leaf.lastEpoch != 0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastEpoch) + if leaf.lastPeriod != 0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) } } diff --git a/hashednode.go b/hashednode.go index 881080c5..48e109b2 100644 --- a/hashednode.go +++ b/hashednode.go @@ -32,15 +32,15 @@ import ( type HashedNode struct{} -func (HashedNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { +func (HashedNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { return errInsertIntoHash } -func (HashedNode) Delete([]byte, StateEpoch, 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, StateEpoch, NodeResolverFn) ([]byte, error) { +func (HashedNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, errors.New("can not read from a hash node") } diff --git a/proof_ipa.go b/proof_ipa.go index d3d52fdc..720af58d 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -194,7 +194,7 @@ 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, postEpoch StateEpoch, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, error) { +func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, postEpoch StatePeriod, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, error) { // go-ipa won't accept no key as an input, catch this corner case // and return an empty result. if len(keys) == 0 { @@ -228,7 +228,7 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, postE return pe, es, poas, postvals, nil } -func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, postEpoch StateEpoch, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { +func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, postEpoch StatePeriod, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, postEpoch, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("get commitments for multiproof: %s", err) @@ -612,7 +612,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, postEpoch StateEpoch) (VerkleNode, error) { +func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postEpoch StatePeriod) (VerkleNode, error) { postroot := preroot.Copy() for _, stemstatediff := range statediff { @@ -650,7 +650,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, curEpoch StateEpoch) error { +func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statediff StateDiff, curPeriod StatePeriod) error { proof, err := DeserializeProof(vp, statediff) if err != nil { return fmt.Errorf("verkle proof deserialization error: %w", err) @@ -692,7 +692,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, curEpoch) + posttree, err := PostStateTreeFromStateDiff(pretree, statediff, curPeriod) if err != nil { return fmt.Errorf("error rebuilding the post-tree from proof: %w", err) } diff --git a/proof_test.go b/proof_test.go index 6b4c1102..c7975013 100644 --- a/proof_test.go +++ b/proof_test.go @@ -1187,8 +1187,8 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Parallel() - preEpoch := StateEpoch(0) - postEpoch := StateEpoch(2) + preEpoch := StatePeriod(0) + postEpoch := StatePeriod(2) preRoot := New() if err := preRoot.Insert(zeroKeyTest, zeroKeyTest, preEpoch, nil); err != nil { diff --git a/tree.go b/tree.go index 4db43416..56e67689 100644 --- a/tree.go +++ b/tree.go @@ -68,13 +68,13 @@ func KeyToStem(key []byte) Stem { type VerkleNode interface { // Insert or Update value into the tree - Insert([]byte, []byte, StateEpoch, NodeResolverFn) error + Insert([]byte, []byte, StatePeriod, NodeResolverFn) error // Delete a leaf with the given key - Delete([]byte, StateEpoch, NodeResolverFn) (bool, error) + Delete([]byte, StatePeriod, NodeResolverFn) (bool, error) // Get value at a given key - Get([]byte, StateEpoch, NodeResolverFn) ([]byte, error) + Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) // Commit computes the commitment of the node. The // result (the curve point) is cached. @@ -203,7 +203,7 @@ type ( // true in the context of a stateless tree. isPOAStub bool - lastEpoch StateEpoch + lastPeriod StatePeriod } ) @@ -228,7 +228,7 @@ func (n *InternalNode) toExportable() *ExportableInternalNode { Values: child.values, C: child.commitment.Bytes(), C1: child.c1.Bytes(), - LastEpoch: child.lastEpoch, + LastPeriod: child.lastPeriod, } case *ExpiredLeafNode: exportable.Children[i] = &ExportableExpiredLeafNode{ @@ -276,7 +276,7 @@ func NewStatelessInternal(depth byte, comm *Point) VerkleNode { } // New creates a new leaf node -func NewLeafNode(stem Stem, values [][]byte, lastEpoch StateEpoch) (*LeafNode, error) { +func NewLeafNode(stem Stem, values [][]byte, lastPeriod StatePeriod) (*LeafNode, error) { cfg := GetConfig() // C1. @@ -319,7 +319,7 @@ func NewLeafNode(stem Stem, values [][]byte, lastEpoch StateEpoch) (*LeafNode, e return nil, fmt.Errorf("batch mapping to scalar fields: %s", err) } - poly[4].SetUint64(uint64(lastEpoch)) + poly[4].SetUint64(uint64(lastPeriod)) return &LeafNode{ // depth will be 0, but the commitment calculation @@ -329,20 +329,20 @@ func NewLeafNode(stem Stem, values [][]byte, lastEpoch StateEpoch) (*LeafNode, e commitment: cfg.CommitToPoly(poly[:], NodeWidth-5), c1: c1, c2: c2, - lastEpoch: lastEpoch, + lastPeriod: lastPeriod, }, 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, lastEpoch StateEpoch) *LeafNode { +func NewLeafNodeWithNoComms(stem Stem, values [][]byte, lastPeriod 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, - lastEpoch: lastEpoch, + lastPeriod: lastPeriod, } } @@ -372,13 +372,13 @@ func (n *InternalNode) cowChild(index byte) { } } -func (n *InternalNode) Insert(key []byte, value []byte, curEpoch StateEpoch, 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, curEpoch, false, resolver) + return n.InsertValuesAtStem(KeyToStem(key), values, curPeriod, false, resolver) } -func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch StateEpoch, isResurrect bool, resolver NodeResolverFn) error { +func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod StatePeriod, isResurrect 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) { @@ -387,7 +387,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S case Empty: n.cowChild(nChild) var err error - n.children[nChild], err = NewLeafNode(stem, values, curEpoch) + n.children[nChild], err = NewLeafNode(stem, values, curPeriod) if err != nil { return err } @@ -408,14 +408,14 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S n.cowChild(nChild) // recurse to handle the case of a LeafNode child that // splits. - return n.InsertValuesAtStem(stem, values, curEpoch, isResurrect, resolver) + return n.InsertValuesAtStem(stem, values, curPeriod, isResurrect, resolver) case *ExpiredLeafNode: if !isResurrect { return errExpired } // create a new leaf node with the given values - leaf, err := NewLeafNode(stem, values, curEpoch) + leaf, err := NewLeafNode(stem, values, curPeriod) if err != nil { return err } @@ -430,7 +430,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S return errIsPOAStub } n.cowChild(nChild) - return child.insertMultiple(stem, values, curEpoch) + return child.insertMultiple(stem, values, curPeriod) } n.cowChild(nChild) @@ -446,12 +446,12 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S nextWordInInsertedKey := offset2key(stem, n.depth+1) if nextWordInInsertedKey == nextWordInExistingKey { - return newBranch.InsertValuesAtStem(stem, values, curEpoch, isResurrect, resolver) + return newBranch.InsertValuesAtStem(stem, values, curPeriod, isResurrect, resolver) } // Next word differs, so this was the last level. // Insert it directly into its final slot. - leaf, err := NewLeafNode(stem, values, curEpoch) + leaf, err := NewLeafNode(stem, values, curPeriod) if err != nil { return err } @@ -460,7 +460,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curEpoch S newBranch.children[nextWordInInsertedKey] = leaf case *InternalNode: n.cowChild(nChild) - return child.InsertValuesAtStem(stem, values, curEpoch, isResurrect, resolver) + return child.InsertValuesAtStem(stem, values, curPeriod, isResurrect, resolver) default: // It should be an UknownNode. return errUnknownNodeType } @@ -585,7 +585,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, curEpoch StateEpoch, 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: @@ -607,11 +607,11 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, curEpoch StateEpoch, resolver n.children[nchild] = resolved // recurse to handle the case of a LeafNode child that // splits. - return n.GetValuesAtStem(stem, curEpoch, resolver) + return n.GetValuesAtStem(stem, curPeriod, resolver) case *ExpiredLeafNode: return nil, errExpired case *LeafNode: - if IsExpired(child.lastEpoch, curEpoch) { + if IsExpired(child.lastPeriod, curPeriod) { return nil, errExpired } @@ -625,13 +625,13 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, curEpoch StateEpoch, resolver } return nil, nil case *InternalNode: - return child.GetValuesAtStem(stem, curEpoch, resolver) + return child.GetValuesAtStem(stem, curPeriod, resolver) default: return nil, errUnknownNodeType } } -func (n *InternalNode) Delete(key []byte, curEpoch StateEpoch, 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: @@ -650,12 +650,12 @@ func (n *InternalNode) Delete(key []byte, curEpoch StateEpoch, resolver NodeReso return false, err } n.children[nChild] = c - return n.Delete(key, curEpoch, resolver) + return n.Delete(key, curPeriod, resolver) case *ExpiredLeafNode: return false, errExpired default: n.cowChild(nChild) - del, err := child.Delete(key, curEpoch, resolver) + del, err := child.Delete(key, curPeriod, resolver) if err != nil { return false, err } @@ -684,7 +684,7 @@ func (n *InternalNode) Delete(key []byte, curEpoch StateEpoch, resolver NodeReso // 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. -// TODO(weiihann): check if need to compare access epochs +// TODO(weiihann): check if need to compare access periods func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { @@ -816,11 +816,11 @@ func (n *InternalNode) FlushAtDepth(depth uint8, flush NodeFlushFn) { } } -func (n *InternalNode) Get(key []byte, curEpoch StateEpoch, 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), curEpoch, resolver) + stemValues, err := n.GetValuesAtStem(KeyToStem(key), curPeriod, resolver) if err != nil { return nil, err } @@ -1196,7 +1196,7 @@ func (n *InternalNode) touchCoW(index byte) { n.cowChild(index) } -func (n *LeafNode) Insert(key []byte, value []byte, curEpoch StateEpoch, _ NodeResolverFn) error { +func (n *LeafNode) Insert(key []byte, value []byte, curPeriod StatePeriod, _ NodeResolverFn) error { if n.isPOAStub { return errIsPOAStub } @@ -1205,7 +1205,7 @@ func (n *LeafNode) Insert(key []byte, value []byte, curEpoch StateEpoch, _ NodeR return fmt.Errorf("invalid key size: %d", len(key)) } - if IsExpired(n.lastEpoch, curEpoch) { + if IsExpired(n.lastPeriod, curPeriod) { return errExpired } @@ -1215,27 +1215,27 @@ func (n *LeafNode) Insert(key []byte, value []byte, curEpoch StateEpoch, _ NodeR } values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.insertMultiple(stem, values, curEpoch) + return n.insertMultiple(stem, values, curPeriod) } -func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curEpoch StateEpoch) error { +func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curPeriod StatePeriod) error { // Sanity check: ensure the stems are the same. if !equalPaths(stem, n.stem) { return errInsertIntoOtherStem } - if IsExpired(n.lastEpoch, curEpoch) { + if IsExpired(n.lastPeriod, curPeriod) { return errExpired } - if err := n.updateMultipleLeaves(values, curEpoch); err != nil { + if err := n.updateMultipleLeaves(values, curPeriod); err != nil { return err } return nil } -func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curEpoch StateEpoch) { +func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curPeriod StatePeriod) { // Calculate the Fr-delta. var deltaC Fr deltaC.Sub(&newC, &oldC) @@ -1244,8 +1244,8 @@ func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curEpoch StateEpoch) { var poly [NodeWidth]Fr poly[cxIndex] = deltaC - if n.lastEpoch != curEpoch { - poly[4].SetUint64(uint64(curEpoch)) + if n.lastPeriod != curPeriod { + poly[4].SetUint64(uint64(curPeriod)) } // Add delta to the current commitment. @@ -1287,7 +1287,7 @@ func (n *LeafNode) updateCn(index byte, value []byte, c *Point) error { return nil } -func (n *LeafNode) updateLeaf(index byte, value []byte, curEpoch StateEpoch) 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 @@ -1310,14 +1310,14 @@ func (n *LeafNode) updateLeaf(index byte, value []byte, curEpoch StateEpoch) err // 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.updateLastEpoch(curEpoch) - n.updateC(cxIndex, frs[0], frs[1], curEpoch) + n.updateLastEpoch(curPeriod) + n.updateC(cxIndex, frs[0], frs[1], curPeriod) n.values[index] = value return nil } -func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) 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. @@ -1363,21 +1363,21 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) er 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], curEpoch) - n.updateC(c2Idx, frs[2], frs[3], curEpoch) - n.updateLastEpoch(curEpoch) + n.updateC(c1Idx, frs[0], frs[1], curPeriod) + n.updateC(c2Idx, frs[2], frs[3], curPeriod) + n.updateLastEpoch(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], curEpoch) - n.updateLastEpoch(curEpoch) + n.updateC(c1Idx, frs[0], frs[1], curPeriod) + n.updateLastEpoch(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], curEpoch) - n.updateLastEpoch(curEpoch) + n.updateC(c2Idx, frs[0], frs[1], curPeriod) + n.updateLastEpoch(curPeriod) } return nil @@ -1385,13 +1385,13 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curEpoch StateEpoch) er // 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, curEpoch StateEpoch, _ 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 IsExpired(n.lastEpoch, curEpoch) { + if IsExpired(n.lastPeriod, curPeriod) { return false, errExpired } @@ -1458,11 +1458,11 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool n.commitment.Sub(n.commitment, cfg.CommitToPoly(poly[:], 0)) // TODO(weiihann): can this be done together with the previous? - if n.lastEpoch != curEpoch { + if n.lastPeriod != curPeriod { var poly [5]Fr - poly[4].SetUint64(uint64(curEpoch)) + poly[4].SetUint64(uint64(curPeriod)) n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) - n.updateLastEpoch(curEpoch) + n.updateLastEpoch(curPeriod) } // Clear the corresponding commitment @@ -1486,10 +1486,10 @@ func (n *LeafNode) Delete(k []byte, curEpoch StateEpoch, _ NodeResolverFn) (bool // 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, curEpoch) + return false, n.updateLeaf(k[StemSize], nil, curPeriod) } -func (n *LeafNode) Get(k []byte, curEpoch StateEpoch, _ NodeResolverFn) ([]byte, error) { +func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byte, error) { if n.isPOAStub { return nil, errIsPOAStub } @@ -1525,8 +1525,8 @@ func (n *LeafNode) Commit() *Point { return n.commitment } -func (n *LeafNode) updateLastEpoch(curEpoch StateEpoch) { - n.lastEpoch = curEpoch +func (n *LeafNode) updateLastEpoch(curPeriod StatePeriod) { + n.lastPeriod = curPeriod } // fillSuffixTreePoly takes one of the two suffix tree and @@ -1593,7 +1593,7 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofE poass []Stem // list of proof-of-absence and proof-of-expiry stems ) - // Initialize the top-level polynomial with 1 + stem + C1 + C2 + lastEpoch + // Initialize the top-level polynomial with 1 + stem + C1 + C2 + lastPeriod 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) @@ -1643,7 +1643,7 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofE // We do not check for expiry, as we assume that the expiry detection is done before // proof creation. - poly[4].SetUint64(uint64(n.lastEpoch)) + poly[4].SetUint64(uint64(n.lastPeriod)) pe.Cis = append(pe.Cis, n.commitment) pe.Zis = append(pe.Zis, 4) @@ -1738,7 +1738,7 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofE } // 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 @@ -1989,8 +1989,8 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte - lastEpoch := make([]byte, epochSize) - binary.BigEndian.PutUint64(lastEpoch, uint64(n.lastEpoch)) + lastPeriod := make([]byte, periodSize) + binary.BigEndian.PutUint64(lastPeriod, uint64(n.lastPeriod)) switch { case count == 1: var buf [singleSlotLeafSize]byte @@ -2003,9 +2003,9 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafStemOffset+StemSize:], c2Bytes[:]) } copy(result[leafStemOffset+StemSize+banderwagon.UncompressedSize:], cBytes[:]) - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastEpoch) - result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+epochSize] = byte(lastIdx) - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+epochSize+leafValueIndexSize:], n.values[lastIdx][:]) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastPeriod) + 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[:] @@ -2013,17 +2013,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:], lastEpoch) - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+epochSize:], n.values[0]) // copy basic data + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastPeriod) + copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize+periodSize:], n.values[0]) // copy basic data default: - result = make([]byte, nodeTypeSize+StemSize+bitlistSize+3*banderwagon.UncompressedSize+epochSize+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[leafLastEpochOffset:], lastEpoch) + copy(result[leafLastEpochOffset:], lastPeriod) copy(result[leafChildrenOffset:], children) } diff --git a/unknown.go b/unknown.go index 62d8232d..98b05640 100644 --- a/unknown.go +++ b/unknown.go @@ -29,15 +29,15 @@ import "errors" type UnknownNode struct{} -func (UnknownNode) Insert([]byte, []byte, StateEpoch, NodeResolverFn) error { +func (UnknownNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { return errMissingNodeInStateless } -func (UnknownNode) Delete([]byte, StateEpoch, 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, StateEpoch, NodeResolverFn) ([]byte, error) { +func (UnknownNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } From f631e65699d733e246fef4c42af7445b34f2190e Mon Sep 17 00:00:00 2001 From: weiihann Date: Tue, 19 Nov 2024 10:26:42 +0800 Subject: [PATCH 18/33] refactor isExpired --- epoch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epoch.go b/epoch.go index 0a2c1847..6ec66023 100644 --- a/epoch.go +++ b/epoch.go @@ -11,7 +11,7 @@ const ( ) func IsExpired(prev StatePeriod, cur StatePeriod) bool { - return cur-prev >= NumActiveEpochs + return cur >= prev+NumActiveEpochs // TODO(weiihann): deal with overflow } func StatePeriodFromBytes(b []byte) StatePeriod { From ed32767f4b360f6099df7118164b4f20d7e5bb3e Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 18 Dec 2024 19:18:50 +0800 Subject: [PATCH 19/33] update expiry check --- epoch.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/epoch.go b/epoch.go index 6ec66023..6f177fe1 100644 --- a/epoch.go +++ b/epoch.go @@ -10,8 +10,8 @@ const ( NumActiveEpochs = 2 ) -func IsExpired(prev StatePeriod, cur StatePeriod) bool { - return cur >= prev+NumActiveEpochs // TODO(weiihann): deal with overflow +func IsExpired(prev, cur StatePeriod) bool { + return cur > prev && cur - prev >= NumActiveEpochs // TODO(weiihann): deal with overflow } func StatePeriodFromBytes(b []byte) StatePeriod { From d68c4aed139cf3b726b124789d908728c967cc57 Mon Sep 17 00:00:00 2001 From: weiihann Date: Wed, 18 Dec 2024 19:19:07 +0800 Subject: [PATCH 20/33] use period consts --- proof_test.go | 137 ++++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 66 deletions(-) diff --git a/proof_test.go b/proof_test.go index c7975013..6e11bb5b 100644 --- a/proof_test.go +++ b/proof_test.go @@ -37,13 +37,18 @@ import ( "github.com/crate-crypto/go-ipa/common" ) +const ( + period0 = 0 + period2 = 2 +) + func TestProofEmptyTree(t *testing.T) { t.Parallel() root := New() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, 0, 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)) @@ -54,18 +59,18 @@ func TestProofVerifyTwoLeaves(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatal(err) } - if err := root.Insert(oneKeyTest, zeroKeyTest, 0, 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, 0, 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}, 0, 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 { @@ -86,13 +91,13 @@ func TestProofVerifyMultipleLeaves(t *testing.T) { t.Fatalf("could not read random bytes: %v", err) } keys[i] = key - if err := root.Insert(key, fourtyKeyTest, 0, 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]}, 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 { @@ -113,13 +118,13 @@ func TestMultiProofVerifyMultipleLeaves(t *testing.T) { t.Fatalf("could not read random bytes: %v", err) } keys[i] = key - if err := root.Insert(key, fourtyKeyTest, 0, 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], 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], period0, nil) pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) if err != nil { @@ -142,7 +147,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, 0, 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 { @@ -159,7 +164,7 @@ func TestMultiProofVerifyMultipleLeavesWithAbsentStem(t *testing.T) { absent[3] = 1 // and the stem differs keys = append(keys, absent) - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { @@ -184,16 +189,16 @@ func TestMultiProofVerifyMultipleLeavesCommitmentRedundancy(t *testing.T) { keys := make([][]byte, 2) root := New() keys[0] = zeroKeyTest - if err := root.Insert(keys[0], fourtyKeyTest, 0, 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, 0, 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, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) pe, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { @@ -209,15 +214,15 @@ func TestProofOfAbsenceInternalVerify(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, 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, 0, 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}, 0, 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 { @@ -229,15 +234,15 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, 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, 0, 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}, 0, 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 { @@ -249,10 +254,10 @@ func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, 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, 0, nil); err != nil { + if err := root.Insert(ffx32KeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } root.Commit() @@ -262,7 +267,7 @@ func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { return ret }() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, 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 { @@ -274,7 +279,7 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } @@ -284,7 +289,7 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { }() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, 0, 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 { @@ -301,7 +306,7 @@ func BenchmarkProofCalculation(b *testing.B) { b.Fatal(err) } keys[i] = key - if err := root.Insert(key, zeroKeyTest, 0, nil); err != nil { + if err := root.Insert(key, zeroKeyTest, period0, nil); err != nil { b.Fatal(err) } } @@ -310,7 +315,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]}, 0, nil); err != nil { + if _, _, _, _, err := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, nil); err != nil { b.Fatal(err) } } @@ -325,13 +330,13 @@ func BenchmarkProofVerification(b *testing.B) { b.Fatal(err) } keys[i] = key - if err := root.Insert(key, zeroKeyTest, 0, 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]}, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, nil) b.ResetTimer() b.ReportAllocs() @@ -357,12 +362,12 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { t.Fatalf("could not read random bytes: %v", err) } keys[i] = key - if err := root.Insert(key, fourtyKeyTest, 0, 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]}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -388,7 +393,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { key := make([]byte, 32) key[2] = byte(i) keys[i] = key - if err := root.Insert(key, fourtyKeyTest, 0, nil); err != nil { + if err := root.Insert(key, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -401,7 +406,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -429,7 +434,7 @@ func TestProofDeserialize(t *testing.T) { key := make([]byte, 32) key[2] = byte(i) keys[i] = key - if err := root.Insert(key, fourtyKeyTest, 0, nil); err != nil { + if err := root.Insert(key, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -442,7 +447,7 @@ func TestProofDeserialize(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -472,7 +477,7 @@ func TestProofOfAbsenceEdgeCase(t *testing.T) { root.Commit() ret, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030303") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, 0, 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") @@ -486,14 +491,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, 0, 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}, 0, 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") @@ -508,7 +513,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { t.Fatalf("error deserializing %v", err) } - got, err := deserialized.Get(ret1, 0, nil) + got, err := deserialized.Get(ret1, period0, nil) if err != nil { t.Fatalf("error while trying to read missing value: %v", err) } @@ -520,7 +525,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, 0, nil) + err = deserialized.Insert(ret3, testValue, period0, nil) if err != nil { t.Fatalf("error inserting value in proof-of-asbsence stem: %v", err) } @@ -548,14 +553,14 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) { root := New() key, _ := hex.DecodeString("0403030303030303030303030303030303030303030303030303030303030000") - if err := root.Insert(key, testValue, 0, 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}, 0, 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") @@ -843,10 +848,10 @@ func TestProofOfExpiryOneLeaf(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, 2, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period2, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - if err := root.Insert(oneKeyTest, zeroKeyTest, 2, nil); err != nil { + if err := root.Insert(oneKeyTest, zeroKeyTest, period2, nil); err != nil { t.Fatalf("could not insert key: %v", err) } init := root.Commit() @@ -862,7 +867,7 @@ func TestProofOfExpiryOneLeaf(t *testing.T) { t.Fatalf("expected commitment to be %x, got %x", init, comm) } - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest}, 0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest}, period0, nil) if err != nil { t.Fatalf("could not make verkle proof: %v", err) } @@ -877,7 +882,7 @@ func TestProofOfExpiryOneLeaf(t *testing.T) { t.Fatalf("error deserializing %v", err) } - _, err = deserialized.Get(zeroKeyTest, 0, nil) + _, err = deserialized.Get(zeroKeyTest, period0, nil) if !errors.Is(err, errExpired) { t.Fatalf("expected error getting key %x, got %v", zeroKeyTest, err) } @@ -891,10 +896,10 @@ func TestProofOfExpiryMultipleLeaves(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, 2, nil); err != nil { + if err := root.Insert(zeroKeyTest, zeroKeyTest, period2, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - if err := root.Insert(ffx32KeyTest, zeroKeyTest, 2, nil); err != nil { + if err := root.Insert(ffx32KeyTest, zeroKeyTest, period2, nil); err != nil { t.Fatalf("could not insert key: %v", err) } init := root.Commit() @@ -914,7 +919,7 @@ func TestProofOfExpiryMultipleLeaves(t *testing.T) { t.Fatalf("expected commitment to be %x, got %x", init, comm) } - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest, ffx32KeyTest}, 0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest, ffx32KeyTest}, period0, nil) if err != nil { t.Fatalf("could not make verkle proof: %v", err) } @@ -929,12 +934,12 @@ func TestProofOfExpiryMultipleLeaves(t *testing.T) { t.Fatalf("error deserializing %v", err) } - _, err = deserialized.Get(zeroKeyTest, 0, nil) + _, err = deserialized.Get(zeroKeyTest, period0, nil) if !errors.Is(err, errExpired) { t.Fatalf("expected error getting key %x, got %v", zeroKeyTest, err) } - _, err = deserialized.Get(ffx32KeyTest, 0, nil) + _, err = deserialized.Get(ffx32KeyTest, period0, nil) if !errors.Is(err, errExpired) { t.Fatalf("expected error getting key %x, got %v", ffx32KeyTest, err) } @@ -950,7 +955,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, 0, nil); err != nil { + if err := root.Insert([]byte(k), v, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } } @@ -970,7 +975,7 @@ func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, pr proveKVs[string(key)] = value } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1105,7 +1110,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], 0, 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) } } @@ -1113,13 +1118,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], 0, 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, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, period0, nil) keys: for i := range proof.Keys { @@ -1252,14 +1257,14 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { // Create a tree with only one key. root := New() presentKey, _ := hex.DecodeString("4000000000000000000000000000000000000000000000000000000000000000") - if err := root.Insert(presentKey, zeroKeyTest, 0, 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}, 0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, keylist{absentKey}, period0, nil) if err != nil { t.Fatal(err) } @@ -1302,7 +1307,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { var key [32]byte copy(key[:], presentKey) key[StemSize] = byte(i) - if _, err := droot.Get(key[:], 0, nil); err != errIsPOAStub { + if _, err := droot.Get(key[:], period0, nil); err != errIsPOAStub { t.Fatalf("expected ErrPOALeafValue, got %v", err) } } @@ -1313,7 +1318,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { var key [32]byte copy(key[:], presentKey) key[StemSize] = byte(i) - if err := droot.Insert(key[:], zeroKeyTest, 0, nil); err != errIsPOAStub { + if err := droot.Insert(key[:], zeroKeyTest, period0, nil); err != errIsPOAStub { t.Fatalf("expected ErrPOALeafValue, got %v", err) } } @@ -1328,10 +1333,10 @@ func TestDoubleProofOfAbsence(t *testing.T) { key11, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000001") key12, _ := hex.DecodeString("0003000000000000000000000000000000000000000000000000000000000001") - if err := root.Insert(key11, fourtyKeyTest, 0, 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, 0, nil); err != nil { + if err := root.Insert(key12, fourtyKeyTest, period0, nil); err != nil { t.Fatalf("could not insert key: %v", err) } @@ -1339,7 +1344,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}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1379,16 +1384,16 @@ func TestProveAbsenceInEmptyHalf(t *testing.T) { key1, _ := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000FF") - if err := root.Insert(key1, fourtyKeyTest, 0, 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, 0, 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}, 0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { From 84e62fb90d8ef8337aafbed26d1766e402708835 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 20 Dec 2024 12:25:38 +0800 Subject: [PATCH 21/33] refresh period upon a read --- conversion.go | 10 +++++----- tree.go | 23 +++++++++++++++++------ tree_test.go | 6 +++--- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/conversion.go b/conversion.go index 0650aca0..dcffed3f 100644 --- a/conversion.go +++ b/conversion.go @@ -97,7 +97,7 @@ func firstDiffByteIdx(stem1 []byte, stem2 []byte) int { panic("stems are equal") } -func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curPeriod StatePeriod, resolver NodeResolverFn) error { +func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, resolver NodeResolverFn) error { sort.Slice(leaves, func(i, j int) bool { return bytes.Compare(leaves[i].stem, leaves[j].stem) < 0 }) @@ -132,13 +132,13 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curPeriod StatePe start := currStemFirstByte end := i group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[start:end], curPeriod, resolver) + return n.insertMigratedLeavesSubtree(leaves[start:end], resolver) }) currStemFirstByte = i } } group.Go(func() error { - return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], curPeriod, resolver) + return n.insertMigratedLeavesSubtree(leaves[currStemFirstByte:], resolver) }) if err := group.Wait(); err != nil { return fmt.Errorf("inserting migrated leaves: %w", err) @@ -147,7 +147,7 @@ func (n *InternalNode) InsertMigratedLeaves(leaves []LeafNode, curPeriod StatePe return nil } -func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curPeriod StatePeriod, resolver NodeResolverFn) error { // skipcq: GO-R1005 +func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, resolver NodeResolverFn) error { // skipcq: GO-R1005 for i := range leaves { ln := leaves[i] parent := n @@ -192,7 +192,7 @@ func (n *InternalNode) insertMigratedLeavesSubtree(leaves []LeafNode, curPeriod } } - if err := node.updateMultipleLeaves(nonPresentValues, curPeriod); err != nil { + if err := node.updateMultipleLeaves(nonPresentValues, 0); err != nil { return fmt.Errorf("updating leaves: %s", err) } continue diff --git a/tree.go b/tree.go index 56e67689..81d16f1e 100644 --- a/tree.go +++ b/tree.go @@ -468,6 +468,11 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod return nil } +// TODO(weiihann): implement this +func (n *InternalNode) Revive(stem Stem, values [][]byte, curPeriod StatePeriod) error { + return n.InsertValuesAtStem(stem, values, curPeriod, true, 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 @@ -1310,7 +1315,7 @@ func (n *LeafNode) updateLeaf(index byte, value []byte, curPeriod StatePeriod) e // 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.updateLastEpoch(curPeriod) + n.updatePeriod(curPeriod) n.updateC(cxIndex, frs[0], frs[1], curPeriod) n.values[index] = value @@ -1365,19 +1370,19 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curPeriod StatePeriod) } n.updateC(c1Idx, frs[0], frs[1], curPeriod) n.updateC(c2Idx, frs[2], frs[3], curPeriod) - n.updateLastEpoch(curPeriod) + n.updatePeriod(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], curPeriod) - n.updateLastEpoch(curPeriod) + n.updatePeriod(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], curPeriod) - n.updateLastEpoch(curPeriod) + n.updatePeriod(curPeriod) } return nil @@ -1462,7 +1467,7 @@ func (n *LeafNode) Delete(k []byte, curPeriod StatePeriod, _ NodeResolverFn) (bo var poly [5]Fr poly[4].SetUint64(uint64(curPeriod)) n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) - n.updateLastEpoch(curPeriod) + n.updatePeriod(curPeriod) } // Clear the corresponding commitment @@ -1494,6 +1499,10 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt return nil, errIsPOAStub } + if IsExpired(n.lastPeriod, 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 @@ -1501,6 +1510,8 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt // 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 } @@ -1525,7 +1536,7 @@ func (n *LeafNode) Commit() *Point { return n.commitment } -func (n *LeafNode) updateLastEpoch(curPeriod StatePeriod) { +func (n *LeafNode) updatePeriod(curPeriod StatePeriod) { n.lastPeriod = curPeriod } diff --git a/tree_test.go b/tree_test.go index e9799adc..598e9e04 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1502,11 +1502,11 @@ func TestBatchMigratedKeyValues(t *testing.T) { t.Fatalf("failed to create leaves: %v", err) } - if err := tree.(*InternalNode).InsertMigratedLeaves(newLeaves, 0, nil); err != nil { + if err := tree.(*InternalNode).InsertMigratedLeaves(newLeaves, nil); err != nil { t.Fatalf("failed to insert key: %v", err) } - if err = tree.(*InternalNode).InsertMigratedLeaves(newLeaves, 0, nil); err != nil { + if err = tree.(*InternalNode).InsertMigratedLeaves(newLeaves, nil); err != nil { t.Fatalf("failed to insert key: %v", err) } batchedRoot := tree.Commit().Bytes() @@ -1593,7 +1593,7 @@ func BenchmarkBatchLeavesInsert(b *testing.B) { if err != nil { b.Fatalf("failed to batch-create leaf node: %v", err) } - if err := tree.(*InternalNode).InsertMigratedLeaves(newLeaves, 0, nil); err != nil { + if err := tree.(*InternalNode).InsertMigratedLeaves(newLeaves, nil); err != nil { b.Fatalf("failed to insert key: %v", err) } From 2bb9b2e628a639370a15a79c5e0c65edfa997033 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 20 Dec 2024 12:33:03 +0800 Subject: [PATCH 22/33] compare periods at DeleteAtStem --- tree.go | 10 +++++++--- tree_test.go | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tree.go b/tree.go index 81d16f1e..c48e24d1 100644 --- a/tree.go +++ b/tree.go @@ -690,7 +690,7 @@ func (n *InternalNode) Delete(key []byte, curPeriod StatePeriod, resolver NodeRe // 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. // TODO(weiihann): check if need to compare access periods -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: @@ -709,7 +709,7 @@ 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: @@ -717,6 +717,10 @@ func (n *InternalNode) DeleteAtStem(key []byte, resolver NodeResolverFn) (bool, return false, errDeleteMissing } + if IsExpired(child.lastPeriod, curPeriod) { + return false, errExpired + } + n.cowChild(nChild) n.children[nChild] = Empty{} @@ -732,7 +736,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 } diff --git a/tree_test.go b/tree_test.go index 598e9e04..675af940 100644 --- a/tree_test.go +++ b/tree_test.go @@ -381,7 +381,7 @@ func TestDeleteAtStem(t *testing.T) { 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) } @@ -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) } } From 1a73d65ae532caa9e492f37bdc0c9d6bba43f466 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 20 Dec 2024 15:54:30 +0800 Subject: [PATCH 23/33] add Revive() to VerkleNode interface --- doc.go | 1 + empty.go | 4 +++ expired_leaf.go | 5 +++ hashednode.go | 4 +++ proof_ipa.go | 11 +++++-- tree.go | 86 +++++++++++++++++++++++++++++++++++++------------ tree_test.go | 4 +-- unknown.go | 4 +++ 8 files changed, 93 insertions(+), 26 deletions(-) diff --git a/doc.go b/doc.go index 23771329..8ab409df 100644 --- a/doc.go +++ b/doc.go @@ -39,6 +39,7 @@ var ( 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") + errNotExpired = errors.New("trying to revive a non-expired leaf node") ) const ( diff --git a/empty.go b/empty.go index a18e95e0..2bde666f 100644 --- a/empty.go +++ b/empty.go @@ -43,6 +43,10 @@ func (Empty) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } +func (Empty) Revive(Stem, [][]byte, StatePeriod, NodeResolverFn) error { + return errors.New("cannot revive an empty node") +} + func (n Empty) Commit() *Point { return n.Commitment() } diff --git a/expired_leaf.go b/expired_leaf.go index d698ddd4..86eff088 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -27,6 +27,7 @@ package verkle import ( "fmt" + "errors" ) type ExpiredLeafNode struct { @@ -51,6 +52,10 @@ func (n *ExpiredLeafNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, erro return nil, errExpired } +func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, NodeResolverFn) error { + return errors.New("cannot revive an expired leaf node directly") +} + func (n *ExpiredLeafNode) Commit() *Point { if n.commitment == nil { panic("nil commitment") diff --git a/hashednode.go b/hashednode.go index 48e109b2..7e436e16 100644 --- a/hashednode.go +++ b/hashednode.go @@ -44,6 +44,10 @@ 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, 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() diff --git a/proof_ipa.go b/proof_ipa.go index 720af58d..6bf3189a 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -630,10 +630,15 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postEpo } } + var stem [StemSize]byte + copy(stem[:StemSize], stemstatediff.Stem[:]) + + if stemstatediff.Resurrected { + postroot.Revive(stem[:], values, postEpoch, nil) + } + if overwrites { - var stem [StemSize]byte - copy(stem[:StemSize], stemstatediff.Stem[:]) - if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postEpoch, stemstatediff.Resurrected, nil); err != nil { + if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postEpoch, nil); err != nil { return nil, fmt.Errorf("error overwriting value in post state: %w", err) } } diff --git a/tree.go b/tree.go index c48e24d1..5f66a8c4 100644 --- a/tree.go +++ b/tree.go @@ -99,6 +99,8 @@ type VerkleNode interface { // Copy a node and its children Copy() VerkleNode + Revive(Stem, [][]byte, StatePeriod, NodeResolverFn) error + // toDot returns a string representing this subtree in DOT language toDot(string, string) string @@ -375,10 +377,10 @@ func (n *InternalNode) cowChild(index byte) { 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, curPeriod, false, resolver) + return n.InsertValuesAtStem(KeyToStem(key), values, curPeriod, resolver) } -func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod StatePeriod, isResurrect bool, resolver NodeResolverFn) error { +func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod StatePeriod, 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) { @@ -408,21 +410,9 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod n.cowChild(nChild) // recurse to handle the case of a LeafNode child that // splits. - return n.InsertValuesAtStem(stem, values, curPeriod, isResurrect, resolver) + return n.InsertValuesAtStem(stem, values, curPeriod, resolver) case *ExpiredLeafNode: - if !isResurrect { - return errExpired - } - - // create a new leaf node with the given values - leaf, err := NewLeafNode(stem, values, curPeriod) - if err != nil { - return err - } - leaf.setDepth(n.depth + 1) - n.children[nChild] = leaf - - return nil + return errExpired case *LeafNode: if equalPaths(child.stem, stem) { // We can't insert any values into a POA leaf node. @@ -446,7 +436,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod nextWordInInsertedKey := offset2key(stem, n.depth+1) if nextWordInInsertedKey == nextWordInExistingKey { - return newBranch.InsertValuesAtStem(stem, values, curPeriod, isResurrect, resolver) + return newBranch.InsertValuesAtStem(stem, values, curPeriod, resolver) } // Next word differs, so this was the last level. @@ -460,7 +450,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod newBranch.children[nextWordInInsertedKey] = leaf case *InternalNode: n.cowChild(nChild) - return child.InsertValuesAtStem(stem, values, curPeriod, isResurrect, resolver) + return child.InsertValuesAtStem(stem, values, curPeriod, resolver) default: // It should be an UknownNode. return errUnknownNodeType } @@ -468,9 +458,46 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod return nil } -// TODO(weiihann): implement this -func (n *InternalNode) Revive(stem Stem, values [][]byte, curPeriod StatePeriod) error { - return n.InsertValuesAtStem(stem, values, curPeriod, true, nil) +func (n *InternalNode) Revive(stem Stem, values [][]byte, curPeriod StatePeriod, resolver NodeResolverFn) error { + 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, curPeriod, resolver) + case *ExpiredLeafNode: + // create a new leaf node with the given values + leaf, err := NewLeafNode(stem, values, curPeriod) + if err != nil { + return err + } + + leaf.setDepth(n.depth + 1) + n.children[nChild] = leaf + + return nil + case *LeafNode: + return child.Revive(stem, values, curPeriod, resolver) + } + + return nil } // CreatePath inserts a given stem in the tree, placing it as @@ -1520,6 +1547,23 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt return n.values[k[StemSize]], nil } +func (n *LeafNode) Revive(stem Stem, values [][]byte, curPeriod StatePeriod, resolver NodeResolverFn) error { + // TODO(weiihann): double confirm this, do we want to just refresh period instead of returning error? + if !IsExpired(n.lastPeriod, curPeriod) { + return errNotExpired + } + + // Ensure the values are the same + for i := range values { + if !bytes.Equal(n.values[i], values[i]) { + return errors.New("values mismatch in revive") + } + } + + n.updatePeriod(curPeriod) + return nil +} + func (n *LeafNode) Hash() *Fr { // TODO cache this in a subsequent PR, not done here // to reduce complexity. diff --git a/tree_test.go b/tree_test.go index 675af940..45e9118b 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1150,7 +1150,7 @@ func TestInsertStem(t *testing.T) { values[5] = zeroKeyTest values[192] = fourtyKeyTest - if err := root1.(*InternalNode).InsertValuesAtStem(KeyToStem(fourtyKeyTest), values, 0, false, nil); err != nil { + if err := root1.(*InternalNode).InsertValuesAtStem(KeyToStem(fourtyKeyTest), values, 0, nil); err != nil { t.Fatalf("error inserting: %s", err) } r1c := root1.Commit() @@ -1198,7 +1198,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, 0, false, nil); err != nil { + if err := root.(*InternalNode).InsertValuesAtStem(KeyToStem(zeroKeyTest), newValues, 0, nil); err != nil { t.Fatalf("error inserting stem: %v", err) } root.Commit() diff --git a/unknown.go b/unknown.go index 98b05640..446fb4e9 100644 --- a/unknown.go +++ b/unknown.go @@ -41,6 +41,10 @@ func (UnknownNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } +func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, NodeResolverFn) error { + return errors.New("cannot revive an unknown node") +} + func (n UnknownNode) Commit() *Point { return n.Commitment() } From 64151cb193e45452f527bd234a69933a419929a6 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 27 Dec 2024 15:50:14 +0800 Subject: [PATCH 24/33] add Revive() --- doc.go | 5 ++++- empty.go | 2 +- expired_leaf.go | 2 +- hashednode.go | 2 +- proof_ipa.go | 9 ++++++--- tree.go | 49 +++++++++++++++++++++++++++++++------------------ unknown.go | 2 +- 7 files changed, 45 insertions(+), 26 deletions(-) diff --git a/doc.go b/doc.go index 8ab409df..295ad0a0 100644 --- a/doc.go +++ b/doc.go @@ -39,7 +39,10 @@ var ( 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") - errNotExpired = errors.New("trying to revive a non-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") ) const ( diff --git a/empty.go b/empty.go index 2bde666f..f8db089b 100644 --- a/empty.go +++ b/empty.go @@ -43,7 +43,7 @@ func (Empty) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } -func (Empty) Revive(Stem, [][]byte, StatePeriod, NodeResolverFn) error { +func (Empty) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { return errors.New("cannot revive an empty node") } diff --git a/expired_leaf.go b/expired_leaf.go index 86eff088..b65479d6 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -52,7 +52,7 @@ func (n *ExpiredLeafNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, erro return nil, errExpired } -func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, NodeResolverFn) error { +func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { return errors.New("cannot revive an expired leaf node directly") } diff --git a/hashednode.go b/hashednode.go index 7e436e16..89c72040 100644 --- a/hashednode.go +++ b/hashednode.go @@ -44,7 +44,7 @@ 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, NodeResolverFn) error { +func (HashedNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { return errors.New("cannot revive a hashed node") } diff --git a/proof_ipa.go b/proof_ipa.go index 6bf3189a..c169bf2b 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -634,7 +634,10 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postEpo copy(stem[:StemSize], stemstatediff.Stem[:]) if stemstatediff.Resurrected { - postroot.Revive(stem[:], values, postEpoch, nil) + // TODO(weiihann): handle this + if err := postroot.Revive(stem[:], values, postEpoch, postEpoch, nil); err != nil { + return nil, fmt.Errorf("error reviving in post state: %w", err) + } } if overwrites { @@ -655,7 +658,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, curPeriod StatePeriod) 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) @@ -697,7 +700,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, curPeriod) + posttree, err := PostStateTreeFromStateDiff(pretree, statediff, postPeriod) if err != nil { return fmt.Errorf("error rebuilding the post-tree from proof: %w", err) } diff --git a/tree.go b/tree.go index 5f66a8c4..9e355b32 100644 --- a/tree.go +++ b/tree.go @@ -99,7 +99,7 @@ type VerkleNode interface { // Copy a node and its children Copy() VerkleNode - Revive(Stem, [][]byte, StatePeriod, NodeResolverFn) error + Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error // toDot returns a string representing this subtree in DOT language toDot(string, string) string @@ -458,7 +458,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod return nil } -func (n *InternalNode) Revive(stem Stem, values [][]byte, curPeriod StatePeriod, resolver NodeResolverFn) error { +func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn) error { nChild := offset2key(stem, n.depth) switch child := n.children[nChild].(type) { @@ -481,20 +481,24 @@ func (n *InternalNode) Revive(stem Stem, values [][]byte, curPeriod StatePeriod, } n.children[nChild] = resolved n.cowChild(nChild) - return n.Revive(stem, values, curPeriod, resolver) + return n.Revive(stem, values, oldPeriod, curPeriod, resolver) case *ExpiredLeafNode: - // create a new leaf node with the given values - leaf, err := NewLeafNode(stem, values, curPeriod) + // reconstruct expired leaf node + leaf, err := NewLeafNode(stem, values, oldPeriod) if err != nil { return err } + if !child.Commitment().Equal(leaf.Commitment()) { + return errReviveCommitmentMismatch + } + leaf.setDepth(n.depth + 1) n.children[nChild] = leaf return nil case *LeafNode: - return child.Revive(stem, values, curPeriod, resolver) + return child.Revive(stem, values, oldPeriod, curPeriod, resolver) } return nil @@ -1547,21 +1551,30 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt return n.values[k[StemSize]], nil } -func (n *LeafNode) Revive(stem Stem, values [][]byte, curPeriod StatePeriod, resolver NodeResolverFn) error { - // TODO(weiihann): double confirm this, do we want to just refresh period instead of returning error? - if !IsExpired(n.lastPeriod, curPeriod) { - return errNotExpired - } +func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn) error { + // No-op if already in current period + if n.lastPeriod == curPeriod { + return nil + } - // Ensure the values are the same - for i := range values { - if !bytes.Equal(n.values[i], values[i]) { - return errors.New("values mismatch in revive") - } + // Verify period, stem and values match + if n.lastPeriod != 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 + } + } - n.updatePeriod(curPeriod) - return nil + n.updatePeriod(curPeriod) + return nil } func (n *LeafNode) Hash() *Fr { diff --git a/unknown.go b/unknown.go index 446fb4e9..b7f4f157 100644 --- a/unknown.go +++ b/unknown.go @@ -41,7 +41,7 @@ func (UnknownNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } -func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, NodeResolverFn) error { +func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { return errors.New("cannot revive an unknown node") } From b6705968935cafaf6ce12f52f3eb6eafa320b801 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 27 Dec 2024 22:32:40 +0800 Subject: [PATCH 25/33] add lastPeriod to ExpiredLeafNode --- config.go | 2 +- debug.go | 1 + encoding.go | 7 ++++--- encoding_test.go | 6 +++++- expired_leaf.go | 12 +++++++++--- expired_leaf_test.go | 2 +- expired_tree_test.go | 2 +- proof_ipa.go | 7 +++++++ proof_test.go | 8 ++++---- tree.go | 4 +++- 10 files changed, 36 insertions(+), 15 deletions(-) diff --git a/config.go b/config.go index 63261ec8..57d8fbee 100644 --- a/config.go +++ b/config.go @@ -35,7 +35,7 @@ const ( NodeWidth = 256 NodeBitWidth byte = 8 StemSize = 31 - periodSize = 8 + periodSize = 2 ) func equalPaths(key1, key2 []byte) bool { diff --git a/debug.go b/debug.go index 0e6c53c0..40127d99 100644 --- a/debug.go +++ b/debug.go @@ -47,6 +47,7 @@ type ( ExportableExpiredLeafNode struct { Stem Stem `json:"stem"` + LastPeriod StatePeriod `json:"last_period"` Commitment [32]byte `json:"commitment"` } ) diff --git a/encoding.go b/encoding.go index 8b6e81fd..f5e83fa0 100644 --- a/encoding.go +++ b/encoding.go @@ -62,7 +62,7 @@ const ( leafValueIndexSize = 1 singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + periodSize eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize + periodSize - expiredLeafSize = nodeTypeSize + StemSize + banderwagon.UncompressedSize + expiredLeafSize = nodeTypeSize + StemSize + periodSize + banderwagon.UncompressedSize ) func bit(bitlist []byte, nr int) bool { @@ -198,10 +198,11 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { func parseExpiredLeafNode(serialized []byte, depth byte) (VerkleNode, error) { l := &ExpiredLeafNode{} - l.stem = serialized[leafStemOffset : leafStemOffset+StemSize] l.setDepth(depth) + l.stem = serialized[leafStemOffset : leafStemOffset+StemSize] + l.lastPeriod = StatePeriodFromBytes(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+periodSize]) l.commitment = new(Point) - if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize:], true); err != nil { + if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize+periodSize:], true); err != nil { return nil, fmt.Errorf("setting commitment: %w", err) } return l, nil diff --git a/encoding_test.go b/encoding_test.go index 0c7cec99..87b241e8 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -198,7 +198,7 @@ func TestParseExpiredLeaf(t *testing.T) { comm := srs[0] stem := ffx32KeyTest[:StemSize] - el := NewExpiredLeafNode(stem, &comm) + el := NewExpiredLeafNode(stem, period2, &comm) serialized, err := el.Serialize() if err != nil { @@ -222,4 +222,8 @@ func TestParseExpiredLeaf(t *testing.T) { if !el2.commitment.Equal(&comm) { t.Fatalf("invalid commitment, got %x, expected %x", el2.commitment, comm) } + + if el2.lastPeriod != period2 { + t.Fatalf("invalid last period, got %d, expected %d", el2.lastPeriod, period2) + } } diff --git a/expired_leaf.go b/expired_leaf.go index b65479d6..5728599f 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -28,16 +28,18 @@ package verkle import ( "fmt" "errors" + "encoding/binary" ) type ExpiredLeafNode struct { stem Stem + lastPeriod StatePeriod 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 NewExpiredLeafNode(stem Stem, lastPeriod StatePeriod, commitment *Point) *ExpiredLeafNode { + return &ExpiredLeafNode{stem: stem, lastPeriod: lastPeriod, commitment: commitment} } func (n *ExpiredLeafNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { @@ -95,7 +97,11 @@ func (n *ExpiredLeafNode) Serialize() ([]byte, error) { result := buf[:] result[0] = expiredLeafType copy(result[leafStemOffset:], n.stem[:StemSize]) - copy(result[leafStemOffset+StemSize:], cBytes[:]) + + lastPeriod := make([]byte, periodSize) + binary.BigEndian.PutUint16(lastPeriod, uint16(n.lastPeriod)) + copy(result[leafStemOffset+StemSize:], lastPeriod) + copy(result[leafStemOffset+StemSize+periodSize:], cBytes[:]) return result, nil } diff --git a/expired_leaf_test.go b/expired_leaf_test.go index f25dc352..f25494fd 100644 --- a/expired_leaf_test.go +++ b/expired_leaf_test.go @@ -11,7 +11,7 @@ func TestExpiredLeafBasic(t *testing.T) { cfg := GetConfig() srs := cfg.conf.SRS comm := srs[0] - leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], &comm) + leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], period0, &comm) err := leaf.Insert(zeroKeyTest, zeroKeyTest, 0, nil) if !errors.Is(err, errExpired) { diff --git a/expired_tree_test.go b/expired_tree_test.go index a74d3805..8a490a8f 100644 --- a/expired_tree_test.go +++ b/expired_tree_test.go @@ -213,7 +213,7 @@ func TestRootCommitExpired(t *testing.T) { var init Point init.Set(root.Commit()) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) root.(*InternalNode).children[0] = expiredLeaf comm := root.Commit() diff --git a/proof_ipa.go b/proof_ipa.go index c169bf2b..a7b1a758 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -105,16 +105,23 @@ func (vp *VerkleProof) Equal(other *VerkleProof) error { return nil } +// TODO(weiihann): add PoeStems for proof of expiry? It should be a list of (stem, lastPeriod) type Proof struct { Multipoint *ipa.MultiProof // multipoint argument ExtStatus []byte // the extension status of each stem Cs []*Point // commitments, sorted by their path in the tree PoaStems []Stem // stems proving another stem is absent + // PoeInfos []PoeInfo // stems proving another stem is expired Keys [][]byte PreValues [][]byte PostValues [][]byte } +type PoeInfo struct { + Stem Stem + Period StatePeriod +} + type SuffixStateDiff struct { Suffix byte `json:"suffix"` CurrentValue *[32]byte `json:"currentValue"` diff --git a/proof_test.go b/proof_test.go index 6e11bb5b..a5fe6c6f 100644 --- a/proof_test.go +++ b/proof_test.go @@ -858,7 +858,7 @@ func TestProofOfExpiryOneLeaf(t *testing.T) { leaf := root.(*InternalNode).children[0].(*LeafNode) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) expiredLeaf.setDepth(1) root.(*InternalNode).children[0] = expiredLeaf @@ -905,12 +905,12 @@ func TestProofOfExpiryMultipleLeaves(t *testing.T) { init := root.Commit() leaf0 := root.(*InternalNode).children[0].(*LeafNode) - expiredLeaf0 := NewExpiredLeafNode(leaf0.stem, leaf0.commitment) + expiredLeaf0 := NewExpiredLeafNode(leaf0.stem, leaf0.lastPeriod, leaf0.commitment) expiredLeaf0.setDepth(1) root.(*InternalNode).children[0] = expiredLeaf0 leaff := root.(*InternalNode).children[255].(*LeafNode) - expiredLeaff := NewExpiredLeafNode(leaff.stem, leaff.commitment) + expiredLeaff := NewExpiredLeafNode(leaff.stem, leaff.lastPeriod, leaff.commitment) expiredLeaff.setDepth(1) root.(*InternalNode).children[255] = expiredLeaff @@ -1202,7 +1202,7 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { rootC := preRoot.Commit() leaf := preRoot.(*InternalNode).children[0].(*LeafNode) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) expiredLeaf.setDepth(1) preRoot.(*InternalNode).children[0] = expiredLeaf diff --git a/tree.go b/tree.go index 9e355b32..1517a212 100644 --- a/tree.go +++ b/tree.go @@ -235,6 +235,7 @@ func (n *InternalNode) toExportable() *ExportableInternalNode { case *ExpiredLeafNode: exportable.Children[i] = &ExportableExpiredLeafNode{ Stem: child.stem, + LastPeriod: child.lastPeriod, Commitment: child.commitment.Bytes(), } default: @@ -583,6 +584,7 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point if len(stemInfo.stem) != StemSize { return comms, fmt.Errorf("invalid stem size %d", len(stemInfo.stem)) } + // TODO(weiihann): add last period newchild := &ExpiredLeafNode{ commitment: comms[0], stem: stemInfo.stem, @@ -2062,7 +2064,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte lastPeriod := make([]byte, periodSize) - binary.BigEndian.PutUint64(lastPeriod, uint64(n.lastPeriod)) + binary.BigEndian.PutUint16(lastPeriod, uint16(n.lastPeriod)) switch { case count == 1: var buf [singleSlotLeafSize]byte From 36036fb03d644e3dbc86395f81443f5d7fe39ce2 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 27 Dec 2024 22:39:51 +0800 Subject: [PATCH 26/33] Revert "add lastPeriod to ExpiredLeafNode" This reverts commit b6705968935cafaf6ce12f52f3eb6eafa320b801. --- config.go | 2 +- debug.go | 1 - encoding.go | 7 +++---- encoding_test.go | 6 +----- expired_leaf.go | 12 +++--------- expired_leaf_test.go | 2 +- expired_tree_test.go | 2 +- proof_ipa.go | 7 ------- proof_test.go | 8 ++++---- tree.go | 4 +--- 10 files changed, 15 insertions(+), 36 deletions(-) diff --git a/config.go b/config.go index 57d8fbee..63261ec8 100644 --- a/config.go +++ b/config.go @@ -35,7 +35,7 @@ const ( NodeWidth = 256 NodeBitWidth byte = 8 StemSize = 31 - periodSize = 2 + periodSize = 8 ) func equalPaths(key1, key2 []byte) bool { diff --git a/debug.go b/debug.go index 40127d99..0e6c53c0 100644 --- a/debug.go +++ b/debug.go @@ -47,7 +47,6 @@ type ( ExportableExpiredLeafNode struct { Stem Stem `json:"stem"` - LastPeriod StatePeriod `json:"last_period"` Commitment [32]byte `json:"commitment"` } ) diff --git a/encoding.go b/encoding.go index f5e83fa0..8b6e81fd 100644 --- a/encoding.go +++ b/encoding.go @@ -62,7 +62,7 @@ const ( leafValueIndexSize = 1 singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + periodSize eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize + periodSize - expiredLeafSize = nodeTypeSize + StemSize + periodSize + banderwagon.UncompressedSize + expiredLeafSize = nodeTypeSize + StemSize + banderwagon.UncompressedSize ) func bit(bitlist []byte, nr int) bool { @@ -198,11 +198,10 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { func parseExpiredLeafNode(serialized []byte, depth byte) (VerkleNode, error) { l := &ExpiredLeafNode{} - l.setDepth(depth) l.stem = serialized[leafStemOffset : leafStemOffset+StemSize] - l.lastPeriod = StatePeriodFromBytes(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+periodSize]) + l.setDepth(depth) l.commitment = new(Point) - if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize+periodSize:], true); err != nil { + if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize:], true); err != nil { return nil, fmt.Errorf("setting commitment: %w", err) } return l, nil diff --git a/encoding_test.go b/encoding_test.go index 87b241e8..0c7cec99 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -198,7 +198,7 @@ func TestParseExpiredLeaf(t *testing.T) { comm := srs[0] stem := ffx32KeyTest[:StemSize] - el := NewExpiredLeafNode(stem, period2, &comm) + el := NewExpiredLeafNode(stem, &comm) serialized, err := el.Serialize() if err != nil { @@ -222,8 +222,4 @@ func TestParseExpiredLeaf(t *testing.T) { if !el2.commitment.Equal(&comm) { t.Fatalf("invalid commitment, got %x, expected %x", el2.commitment, comm) } - - if el2.lastPeriod != period2 { - t.Fatalf("invalid last period, got %d, expected %d", el2.lastPeriod, period2) - } } diff --git a/expired_leaf.go b/expired_leaf.go index 5728599f..b65479d6 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -28,18 +28,16 @@ package verkle import ( "fmt" "errors" - "encoding/binary" ) type ExpiredLeafNode struct { stem Stem - lastPeriod StatePeriod commitment *Point depth byte // used for proof only, not commitment calculation } -func NewExpiredLeafNode(stem Stem, lastPeriod StatePeriod, commitment *Point) *ExpiredLeafNode { - return &ExpiredLeafNode{stem: stem, lastPeriod: lastPeriod, commitment: commitment} +func NewExpiredLeafNode(stem Stem, commitment *Point) *ExpiredLeafNode { + return &ExpiredLeafNode{stem: stem, commitment: commitment} } func (n *ExpiredLeafNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { @@ -97,11 +95,7 @@ func (n *ExpiredLeafNode) Serialize() ([]byte, error) { result := buf[:] result[0] = expiredLeafType copy(result[leafStemOffset:], n.stem[:StemSize]) - - lastPeriod := make([]byte, periodSize) - binary.BigEndian.PutUint16(lastPeriod, uint16(n.lastPeriod)) - copy(result[leafStemOffset+StemSize:], lastPeriod) - copy(result[leafStemOffset+StemSize+periodSize:], cBytes[:]) + copy(result[leafStemOffset+StemSize:], cBytes[:]) return result, nil } diff --git a/expired_leaf_test.go b/expired_leaf_test.go index f25494fd..f25dc352 100644 --- a/expired_leaf_test.go +++ b/expired_leaf_test.go @@ -11,7 +11,7 @@ func TestExpiredLeafBasic(t *testing.T) { cfg := GetConfig() srs := cfg.conf.SRS comm := srs[0] - leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], period0, &comm) + leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], &comm) err := leaf.Insert(zeroKeyTest, zeroKeyTest, 0, nil) if !errors.Is(err, errExpired) { diff --git a/expired_tree_test.go b/expired_tree_test.go index 8a490a8f..a74d3805 100644 --- a/expired_tree_test.go +++ b/expired_tree_test.go @@ -213,7 +213,7 @@ func TestRootCommitExpired(t *testing.T) { var init Point init.Set(root.Commit()) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) root.(*InternalNode).children[0] = expiredLeaf comm := root.Commit() diff --git a/proof_ipa.go b/proof_ipa.go index a7b1a758..c169bf2b 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -105,23 +105,16 @@ func (vp *VerkleProof) Equal(other *VerkleProof) error { return nil } -// TODO(weiihann): add PoeStems for proof of expiry? It should be a list of (stem, lastPeriod) type Proof struct { Multipoint *ipa.MultiProof // multipoint argument ExtStatus []byte // the extension status of each stem Cs []*Point // commitments, sorted by their path in the tree PoaStems []Stem // stems proving another stem is absent - // PoeInfos []PoeInfo // stems proving another stem is expired Keys [][]byte PreValues [][]byte PostValues [][]byte } -type PoeInfo struct { - Stem Stem - Period StatePeriod -} - type SuffixStateDiff struct { Suffix byte `json:"suffix"` CurrentValue *[32]byte `json:"currentValue"` diff --git a/proof_test.go b/proof_test.go index a5fe6c6f..6e11bb5b 100644 --- a/proof_test.go +++ b/proof_test.go @@ -858,7 +858,7 @@ func TestProofOfExpiryOneLeaf(t *testing.T) { leaf := root.(*InternalNode).children[0].(*LeafNode) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) expiredLeaf.setDepth(1) root.(*InternalNode).children[0] = expiredLeaf @@ -905,12 +905,12 @@ func TestProofOfExpiryMultipleLeaves(t *testing.T) { init := root.Commit() leaf0 := root.(*InternalNode).children[0].(*LeafNode) - expiredLeaf0 := NewExpiredLeafNode(leaf0.stem, leaf0.lastPeriod, leaf0.commitment) + expiredLeaf0 := NewExpiredLeafNode(leaf0.stem, leaf0.commitment) expiredLeaf0.setDepth(1) root.(*InternalNode).children[0] = expiredLeaf0 leaff := root.(*InternalNode).children[255].(*LeafNode) - expiredLeaff := NewExpiredLeafNode(leaff.stem, leaff.lastPeriod, leaff.commitment) + expiredLeaff := NewExpiredLeafNode(leaff.stem, leaff.commitment) expiredLeaff.setDepth(1) root.(*InternalNode).children[255] = expiredLeaff @@ -1202,7 +1202,7 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { rootC := preRoot.Commit() leaf := preRoot.(*InternalNode).children[0].(*LeafNode) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) expiredLeaf.setDepth(1) preRoot.(*InternalNode).children[0] = expiredLeaf diff --git a/tree.go b/tree.go index 1517a212..9e355b32 100644 --- a/tree.go +++ b/tree.go @@ -235,7 +235,6 @@ func (n *InternalNode) toExportable() *ExportableInternalNode { case *ExpiredLeafNode: exportable.Children[i] = &ExportableExpiredLeafNode{ Stem: child.stem, - LastPeriod: child.lastPeriod, Commitment: child.commitment.Bytes(), } default: @@ -584,7 +583,6 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point if len(stemInfo.stem) != StemSize { return comms, fmt.Errorf("invalid stem size %d", len(stemInfo.stem)) } - // TODO(weiihann): add last period newchild := &ExpiredLeafNode{ commitment: comms[0], stem: stemInfo.stem, @@ -2064,7 +2062,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte lastPeriod := make([]byte, periodSize) - binary.BigEndian.PutUint16(lastPeriod, uint16(n.lastPeriod)) + binary.BigEndian.PutUint64(lastPeriod, uint64(n.lastPeriod)) switch { case count == 1: var buf [singleSlotLeafSize]byte From 1ce49aacd56f57fd2c9039fe583c1bd0c9ea7a79 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 27 Dec 2024 22:40:45 +0800 Subject: [PATCH 27/33] fix period encoding bug --- config.go | 2 +- tree.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index 63261ec8..57d8fbee 100644 --- a/config.go +++ b/config.go @@ -35,7 +35,7 @@ const ( NodeWidth = 256 NodeBitWidth byte = 8 StemSize = 31 - periodSize = 8 + periodSize = 2 ) func equalPaths(key1, key2 []byte) bool { diff --git a/tree.go b/tree.go index 9e355b32..339b4ddc 100644 --- a/tree.go +++ b/tree.go @@ -2062,7 +2062,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte lastPeriod := make([]byte, periodSize) - binary.BigEndian.PutUint64(lastPeriod, uint64(n.lastPeriod)) + binary.BigEndian.PutUint16(lastPeriod, uint16(n.lastPeriod)) switch { case count == 1: var buf [singleSlotLeafSize]byte From c42a5c235559865c0f492b711561951279f8db9b Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 27 Dec 2024 23:24:03 +0800 Subject: [PATCH 28/33] add `skipVerify` in Revive() --- empty.go | 2 +- expired_leaf.go | 2 +- hashednode.go | 2 +- epoch.go => period.go | 3 ++- proof_ipa.go | 9 ++++---- proof_test.go | 1 - tree.go | 50 +++++++++++++++++++++++++++---------------- tree_test.go | 24 ++++++++++++++++++++- unknown.go | 2 +- 9 files changed, 65 insertions(+), 30 deletions(-) rename epoch.go => period.go (73%) diff --git a/empty.go b/empty.go index f8db089b..dae79787 100644 --- a/empty.go +++ b/empty.go @@ -43,7 +43,7 @@ func (Empty) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } -func (Empty) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { +func (Empty) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error { return errors.New("cannot revive an empty node") } diff --git a/expired_leaf.go b/expired_leaf.go index b65479d6..dadcfb06 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -52,7 +52,7 @@ func (n *ExpiredLeafNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, erro return nil, errExpired } -func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { +func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error { return errors.New("cannot revive an expired leaf node directly") } diff --git a/hashednode.go b/hashednode.go index 89c72040..6c4125bf 100644 --- a/hashednode.go +++ b/hashednode.go @@ -44,7 +44,7 @@ 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, NodeResolverFn) error { +func (HashedNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error { return errors.New("cannot revive a hashed node") } diff --git a/epoch.go b/period.go similarity index 73% rename from epoch.go rename to period.go index 6f177fe1..efbfb03c 100644 --- a/epoch.go +++ b/period.go @@ -8,10 +8,11 @@ type StatePeriod uint16 const ( NumActiveEpochs = 2 + period0 = 0 ) func IsExpired(prev, cur StatePeriod) bool { - return cur > prev && cur - prev >= NumActiveEpochs // TODO(weiihann): deal with overflow + return cur > prev && cur - prev >= NumActiveEpochs } func StatePeriodFromBytes(b []byte) StatePeriod { diff --git a/proof_ipa.go b/proof_ipa.go index c169bf2b..a726b695 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -634,13 +634,12 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postEpo copy(stem[:StemSize], stemstatediff.Stem[:]) if stemstatediff.Resurrected { - // TODO(weiihann): handle this - if err := postroot.Revive(stem[:], values, postEpoch, postEpoch, nil); err != nil { + // We skip verifying the revive of reconstructed leaf node because the post values may already be + // modified after reviving + if err := postroot.Revive(stem[:], values, period0, postEpoch, nil, true); err != nil { return nil, fmt.Errorf("error reviving in post state: %w", err) } - } - - if overwrites { + } else if overwrites { if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postEpoch, nil); err != nil { return nil, fmt.Errorf("error overwriting value in post state: %w", err) } diff --git a/proof_test.go b/proof_test.go index 6e11bb5b..2c0db9d0 100644 --- a/proof_test.go +++ b/proof_test.go @@ -38,7 +38,6 @@ import ( ) const ( - period0 = 0 period2 = 2 ) diff --git a/tree.go b/tree.go index 339b4ddc..749d5eb7 100644 --- a/tree.go +++ b/tree.go @@ -99,7 +99,7 @@ type VerkleNode interface { // Copy a node and its children Copy() VerkleNode - Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error + Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error // toDot returns a string representing this subtree in DOT language toDot(string, string) string @@ -458,7 +458,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod return nil } -func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn) error { +func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn, skipVerify bool) error { nChild := offset2key(stem, n.depth) switch child := n.children[nChild].(type) { @@ -481,24 +481,32 @@ func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod S } n.children[nChild] = resolved n.cowChild(nChild) - return n.Revive(stem, values, oldPeriod, curPeriod, resolver) + return n.Revive(stem, values, oldPeriod, curPeriod, resolver, skipVerify) case *ExpiredLeafNode: - // reconstruct expired leaf node - leaf, err := NewLeafNode(stem, values, oldPeriod) + // Create new leaf node with appropriate period based on verification mode + period := curPeriod + if !skipVerify { + period = oldPeriod + } + + leaf, err := NewLeafNode(stem, values, period) if err != nil { return err } - - if !child.Commitment().Equal(leaf.Commitment()) { + + // Verify commitment matches if needed + if !skipVerify && !child.Commitment().Equal(leaf.Commitment()) { return errReviveCommitmentMismatch } - + + // Update period and set as child leaf.setDepth(n.depth + 1) + leaf.updatePeriod(curPeriod) n.children[nChild] = leaf - + return nil case *LeafNode: - return child.Revive(stem, values, oldPeriod, curPeriod, resolver) + return child.Revive(stem, values, oldPeriod, curPeriod, resolver, skipVerify) } return nil @@ -1350,7 +1358,7 @@ func (n *LeafNode) updateLeaf(index byte, value []byte, curPeriod StatePeriod) e // 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.updatePeriod(curPeriod) + n.lastPeriod = curPeriod n.updateC(cxIndex, frs[0], frs[1], curPeriod) n.values[index] = value @@ -1405,19 +1413,19 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curPeriod StatePeriod) } n.updateC(c1Idx, frs[0], frs[1], curPeriod) n.updateC(c2Idx, frs[2], frs[3], curPeriod) - n.updatePeriod(curPeriod) + n.lastPeriod = 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], curPeriod) - n.updatePeriod(curPeriod) + n.lastPeriod = 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], curPeriod) - n.updatePeriod(curPeriod) + n.lastPeriod = curPeriod } return nil @@ -1499,9 +1507,6 @@ func (n *LeafNode) Delete(k []byte, curPeriod StatePeriod, _ NodeResolverFn) (bo // TODO(weiihann): can this be done together with the previous? if n.lastPeriod != curPeriod { - var poly [5]Fr - poly[4].SetUint64(uint64(curPeriod)) - n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) n.updatePeriod(curPeriod) } @@ -1551,7 +1556,7 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt return n.values[k[StemSize]], nil } -func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn) error { +func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn, skipVerify bool) error { // No-op if already in current period if n.lastPeriod == curPeriod { return nil @@ -1597,8 +1602,17 @@ 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 period is already the same + if n.lastPeriod == curPeriod { + return + } n.lastPeriod = curPeriod + + var poly [5]Fr + poly[4].SetUint64(uint64(curPeriod)) + n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) } // fillSuffixTreePoly takes one of the two suffix tree and diff --git a/tree_test.go b/tree_test.go index 45e9118b..72b80cb4 100644 --- a/tree_test.go +++ b/tree_test.go @@ -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) @@ -1879,4 +1901,4 @@ func TestRandomExtracted(t *testing.T) { 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 b7f4f157..1a2fbb0a 100644 --- a/unknown.go +++ b/unknown.go @@ -41,7 +41,7 @@ func (UnknownNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } -func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { +func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error { return errors.New("cannot revive an unknown node") } From 971c099bcc69431740773498ee72399f3bf9e1b8 Mon Sep 17 00:00:00 2001 From: weiihann Date: Sat, 28 Dec 2024 00:50:42 +0800 Subject: [PATCH 29/33] remove skipVerify --- empty.go | 2 +- expired_leaf.go | 2 +- hashednode.go | 2 +- proof_ipa.go | 2 +- tree.go | 20 +++++++------------- unknown.go | 2 +- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/empty.go b/empty.go index dae79787..f8db089b 100644 --- a/empty.go +++ b/empty.go @@ -43,7 +43,7 @@ func (Empty) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } -func (Empty) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error { +func (Empty) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { return errors.New("cannot revive an empty node") } diff --git a/expired_leaf.go b/expired_leaf.go index dadcfb06..b65479d6 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -52,7 +52,7 @@ func (n *ExpiredLeafNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, erro return nil, errExpired } -func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error { +func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { return errors.New("cannot revive an expired leaf node directly") } diff --git a/hashednode.go b/hashednode.go index 6c4125bf..89c72040 100644 --- a/hashednode.go +++ b/hashednode.go @@ -44,7 +44,7 @@ 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, NodeResolverFn, bool) error { +func (HashedNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { return errors.New("cannot revive a hashed node") } diff --git a/proof_ipa.go b/proof_ipa.go index a726b695..6c9e2556 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -636,7 +636,7 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postEpo if stemstatediff.Resurrected { // We skip verifying the revive of reconstructed leaf node because the post values may already be // modified after reviving - if err := postroot.Revive(stem[:], values, period0, postEpoch, nil, true); err != nil { + if err := postroot.Revive(stem[:], values, period0, postEpoch, nil); err != nil { return nil, fmt.Errorf("error reviving in post state: %w", err) } } else if overwrites { diff --git a/tree.go b/tree.go index 749d5eb7..a9a25434 100644 --- a/tree.go +++ b/tree.go @@ -99,7 +99,7 @@ type VerkleNode interface { // Copy a node and its children Copy() VerkleNode - Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error + Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error // toDot returns a string representing this subtree in DOT language toDot(string, string) string @@ -458,7 +458,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod return nil } -func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn, skipVerify bool) error { +func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn) error { nChild := offset2key(stem, n.depth) switch child := n.children[nChild].(type) { @@ -481,21 +481,15 @@ func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod S } n.children[nChild] = resolved n.cowChild(nChild) - return n.Revive(stem, values, oldPeriod, curPeriod, resolver, skipVerify) + return n.Revive(stem, values, oldPeriod, curPeriod, resolver) case *ExpiredLeafNode: - // Create new leaf node with appropriate period based on verification mode - period := curPeriod - if !skipVerify { - period = oldPeriod - } - - leaf, err := NewLeafNode(stem, values, period) + leaf, err := NewLeafNode(stem, values, oldPeriod) if err != nil { return err } // Verify commitment matches if needed - if !skipVerify && !child.Commitment().Equal(leaf.Commitment()) { + if !child.Commitment().Equal(leaf.Commitment()) { return errReviveCommitmentMismatch } @@ -506,7 +500,7 @@ func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod S return nil case *LeafNode: - return child.Revive(stem, values, oldPeriod, curPeriod, resolver, skipVerify) + return child.Revive(stem, values, oldPeriod, curPeriod, resolver) } return nil @@ -1556,7 +1550,7 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt return n.values[k[StemSize]], nil } -func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn, skipVerify bool) error { +func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn) error { // No-op if already in current period if n.lastPeriod == curPeriod { return nil diff --git a/unknown.go b/unknown.go index 1a2fbb0a..b7f4f157 100644 --- a/unknown.go +++ b/unknown.go @@ -41,7 +41,7 @@ func (UnknownNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } -func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn, bool) error { +func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { return errors.New("cannot revive an unknown node") } From 4cee9df0e2fd91c951f67d6697ef561d773ce16c Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 27 Dec 2024 22:32:40 +0800 Subject: [PATCH 30/33] add lastPeriod to ExpiredLeafNode --- debug.go | 1 + encoding.go | 7 ++++--- encoding_test.go | 6 +++++- expired_leaf.go | 12 +++++++++--- expired_leaf_test.go | 2 +- expired_tree_test.go | 2 +- proof_ipa.go | 7 +++++++ proof_test.go | 8 ++++---- tree.go | 2 ++ 9 files changed, 34 insertions(+), 13 deletions(-) diff --git a/debug.go b/debug.go index 0e6c53c0..40127d99 100644 --- a/debug.go +++ b/debug.go @@ -47,6 +47,7 @@ type ( ExportableExpiredLeafNode struct { Stem Stem `json:"stem"` + LastPeriod StatePeriod `json:"last_period"` Commitment [32]byte `json:"commitment"` } ) diff --git a/encoding.go b/encoding.go index 8b6e81fd..f5e83fa0 100644 --- a/encoding.go +++ b/encoding.go @@ -62,7 +62,7 @@ const ( leafValueIndexSize = 1 singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + periodSize eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize + periodSize - expiredLeafSize = nodeTypeSize + StemSize + banderwagon.UncompressedSize + expiredLeafSize = nodeTypeSize + StemSize + periodSize + banderwagon.UncompressedSize ) func bit(bitlist []byte, nr int) bool { @@ -198,10 +198,11 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) { func parseExpiredLeafNode(serialized []byte, depth byte) (VerkleNode, error) { l := &ExpiredLeafNode{} - l.stem = serialized[leafStemOffset : leafStemOffset+StemSize] l.setDepth(depth) + l.stem = serialized[leafStemOffset : leafStemOffset+StemSize] + l.lastPeriod = StatePeriodFromBytes(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+periodSize]) l.commitment = new(Point) - if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize:], true); err != nil { + if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize+periodSize:], true); err != nil { return nil, fmt.Errorf("setting commitment: %w", err) } return l, nil diff --git a/encoding_test.go b/encoding_test.go index 0c7cec99..87b241e8 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -198,7 +198,7 @@ func TestParseExpiredLeaf(t *testing.T) { comm := srs[0] stem := ffx32KeyTest[:StemSize] - el := NewExpiredLeafNode(stem, &comm) + el := NewExpiredLeafNode(stem, period2, &comm) serialized, err := el.Serialize() if err != nil { @@ -222,4 +222,8 @@ func TestParseExpiredLeaf(t *testing.T) { if !el2.commitment.Equal(&comm) { t.Fatalf("invalid commitment, got %x, expected %x", el2.commitment, comm) } + + if el2.lastPeriod != period2 { + t.Fatalf("invalid last period, got %d, expected %d", el2.lastPeriod, period2) + } } diff --git a/expired_leaf.go b/expired_leaf.go index b65479d6..5728599f 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -28,16 +28,18 @@ package verkle import ( "fmt" "errors" + "encoding/binary" ) type ExpiredLeafNode struct { stem Stem + lastPeriod StatePeriod 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 NewExpiredLeafNode(stem Stem, lastPeriod StatePeriod, commitment *Point) *ExpiredLeafNode { + return &ExpiredLeafNode{stem: stem, lastPeriod: lastPeriod, commitment: commitment} } func (n *ExpiredLeafNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { @@ -95,7 +97,11 @@ func (n *ExpiredLeafNode) Serialize() ([]byte, error) { result := buf[:] result[0] = expiredLeafType copy(result[leafStemOffset:], n.stem[:StemSize]) - copy(result[leafStemOffset+StemSize:], cBytes[:]) + + lastPeriod := make([]byte, periodSize) + binary.BigEndian.PutUint16(lastPeriod, uint16(n.lastPeriod)) + copy(result[leafStemOffset+StemSize:], lastPeriod) + copy(result[leafStemOffset+StemSize+periodSize:], cBytes[:]) return result, nil } diff --git a/expired_leaf_test.go b/expired_leaf_test.go index f25dc352..f25494fd 100644 --- a/expired_leaf_test.go +++ b/expired_leaf_test.go @@ -11,7 +11,7 @@ func TestExpiredLeafBasic(t *testing.T) { cfg := GetConfig() srs := cfg.conf.SRS comm := srs[0] - leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], &comm) + leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], period0, &comm) err := leaf.Insert(zeroKeyTest, zeroKeyTest, 0, nil) if !errors.Is(err, errExpired) { diff --git a/expired_tree_test.go b/expired_tree_test.go index a74d3805..8a490a8f 100644 --- a/expired_tree_test.go +++ b/expired_tree_test.go @@ -213,7 +213,7 @@ func TestRootCommitExpired(t *testing.T) { var init Point init.Set(root.Commit()) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) root.(*InternalNode).children[0] = expiredLeaf comm := root.Commit() diff --git a/proof_ipa.go b/proof_ipa.go index 6c9e2556..e6eaf0ef 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -105,16 +105,23 @@ func (vp *VerkleProof) Equal(other *VerkleProof) error { return nil } +// TODO(weiihann): add PoeStems for proof of expiry? It should be a list of (stem, lastPeriod) type Proof struct { Multipoint *ipa.MultiProof // multipoint argument ExtStatus []byte // the extension status of each stem Cs []*Point // commitments, sorted by their path in the tree PoaStems []Stem // stems proving another stem is absent + // PoeInfos []PoeInfo // stems proving another stem is expired Keys [][]byte PreValues [][]byte PostValues [][]byte } +type PoeInfo struct { + Stem Stem + Period StatePeriod +} + type SuffixStateDiff struct { Suffix byte `json:"suffix"` CurrentValue *[32]byte `json:"currentValue"` diff --git a/proof_test.go b/proof_test.go index 2c0db9d0..6a6bbfd3 100644 --- a/proof_test.go +++ b/proof_test.go @@ -857,7 +857,7 @@ func TestProofOfExpiryOneLeaf(t *testing.T) { leaf := root.(*InternalNode).children[0].(*LeafNode) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) expiredLeaf.setDepth(1) root.(*InternalNode).children[0] = expiredLeaf @@ -904,12 +904,12 @@ func TestProofOfExpiryMultipleLeaves(t *testing.T) { init := root.Commit() leaf0 := root.(*InternalNode).children[0].(*LeafNode) - expiredLeaf0 := NewExpiredLeafNode(leaf0.stem, leaf0.commitment) + expiredLeaf0 := NewExpiredLeafNode(leaf0.stem, leaf0.lastPeriod, leaf0.commitment) expiredLeaf0.setDepth(1) root.(*InternalNode).children[0] = expiredLeaf0 leaff := root.(*InternalNode).children[255].(*LeafNode) - expiredLeaff := NewExpiredLeafNode(leaff.stem, leaff.commitment) + expiredLeaff := NewExpiredLeafNode(leaff.stem, leaff.lastPeriod, leaff.commitment) expiredLeaff.setDepth(1) root.(*InternalNode).children[255] = expiredLeaff @@ -1201,7 +1201,7 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { rootC := preRoot.Commit() leaf := preRoot.(*InternalNode).children[0].(*LeafNode) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) expiredLeaf.setDepth(1) preRoot.(*InternalNode).children[0] = expiredLeaf diff --git a/tree.go b/tree.go index a9a25434..432a9e22 100644 --- a/tree.go +++ b/tree.go @@ -235,6 +235,7 @@ func (n *InternalNode) toExportable() *ExportableInternalNode { case *ExpiredLeafNode: exportable.Children[i] = &ExportableExpiredLeafNode{ Stem: child.stem, + LastPeriod: child.lastPeriod, Commitment: child.commitment.Bytes(), } default: @@ -585,6 +586,7 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point if len(stemInfo.stem) != StemSize { return comms, fmt.Errorf("invalid stem size %d", len(stemInfo.stem)) } + // TODO(weiihann): add last period newchild := &ExpiredLeafNode{ commitment: comms[0], stem: stemInfo.stem, From fe3e25ccbc3ac2f94354b3e54f5b227eb03790e0 Mon Sep 17 00:00:00 2001 From: weiihann Date: Sat, 28 Dec 2024 01:35:11 +0800 Subject: [PATCH 31/33] add isExpired --- tree.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tree.go b/tree.go index 432a9e22..ce2dc0cf 100644 --- a/tree.go +++ b/tree.go @@ -651,7 +651,7 @@ func (n *InternalNode) GetValuesAtStem(stem Stem, curPeriod StatePeriod, resolve case *ExpiredLeafNode: return nil, errExpired case *LeafNode: - if IsExpired(child.lastPeriod, curPeriod) { + if child.isExpired(curPeriod) { return nil, errExpired } @@ -752,7 +752,7 @@ func (n *InternalNode) DeleteAtStem(key []byte, curPeriod StatePeriod, resolver return false, errDeleteMissing } - if IsExpired(child.lastPeriod, curPeriod) { + if child.isExpired(curPeriod) { return false, errExpired } @@ -1249,7 +1249,7 @@ func (n *LeafNode) Insert(key []byte, value []byte, curPeriod StatePeriod, _ Nod return fmt.Errorf("invalid key size: %d", len(key)) } - if IsExpired(n.lastPeriod, curPeriod) { + if n.isExpired(curPeriod) { return errExpired } @@ -1268,7 +1268,7 @@ func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curPeriod StatePer return errInsertIntoOtherStem } - if IsExpired(n.lastPeriod, curPeriod) { + if n.isExpired(curPeriod) { return errExpired } @@ -1435,7 +1435,7 @@ func (n *LeafNode) Delete(k []byte, curPeriod StatePeriod, _ NodeResolverFn) (bo return false, nil } - if IsExpired(n.lastPeriod, curPeriod) { + if n.isExpired(curPeriod) { return false, errExpired } @@ -1535,7 +1535,7 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt return nil, errIsPOAStub } - if IsExpired(n.lastPeriod, curPeriod) { + if n.isExpired(curPeriod) { return nil, errExpired } @@ -1611,6 +1611,10 @@ func (n *LeafNode) updatePeriod(curPeriod StatePeriod) { n.commitment.Add(n.commitment, cfg.CommitToPoly(poly[:], 0)) } +func (n *LeafNode) isExpired(cur StatePeriod) bool { + return IsExpired(n.lastPeriod, 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. From dd87e7fadc8d4ec47dba2bcf47ff9ccc8f313e80 Mon Sep 17 00:00:00 2001 From: weiihann Date: Fri, 3 Jan 2025 21:49:40 +0800 Subject: [PATCH 32/33] fix stateless proof --- empty.go | 6 +- empty_test.go | 2 +- expired_leaf.go | 6 +- expired_tree_test.go | 151 ++++++++++++++++++++++++++++++++++++------- hashednode.go | 6 +- hashednode_test.go | 2 +- period.go | 4 +- proof_ipa.go | 81 ++++++++++++----------- proof_json.go | 6 +- proof_test.go | 90 ++++++++++++-------------- tree.go | 95 +++++++++++++++++++-------- tree_test.go | 10 +-- unknown.go | 6 +- unknown_test.go | 2 +- 14 files changed, 306 insertions(+), 161 deletions(-) diff --git a/empty.go b/empty.go index f8db089b..4529bc77 100644 --- a/empty.go +++ b/empty.go @@ -43,7 +43,7 @@ func (Empty) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } -func (Empty) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { +func (Empty) Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error { return errors.New("cannot revive an empty node") } @@ -57,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, StatePeriod) (*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 318e110c..8f2177b8 100644 --- a/empty_test.go +++ b/empty_test.go @@ -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, period0); err == nil { t.Fatal("get proof items should error") } diff --git a/expired_leaf.go b/expired_leaf.go index 5728599f..14e1afbc 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -54,7 +54,7 @@ func (n *ExpiredLeafNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, erro return nil, errExpired } -func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { +func (n *ExpiredLeafNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error { return errors.New("cannot revive an expired leaf node directly") } @@ -69,7 +69,7 @@ func (n *ExpiredLeafNode) Commitment() *Point { return n.commitment } -func (n *ExpiredLeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { +func (n *ExpiredLeafNode) GetProofItems(keys keylist, resolver NodeResolverFn, _ StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { var ( pe = &ProofElements{ Vals: make([][]byte, len(keys)), @@ -87,7 +87,7 @@ func (n *ExpiredLeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) ( poass = append(poass, n.stem) } - return pe, esses, poass, nil + return pe, esses, poass, []StatePeriod{n.lastPeriod}, nil } func (n *ExpiredLeafNode) Serialize() ([]byte, error) { diff --git a/expired_tree_test.go b/expired_tree_test.go index 8a490a8f..9bfb7a56 100644 --- a/expired_tree_test.go +++ b/expired_tree_test.go @@ -10,11 +10,11 @@ func TestInsertSameLeafNoExpired(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - if err := root.Insert(oneKeyTest, testValue, 1, nil); err != nil { + if err := root.Insert(oneKeyTest, testValue, period1, nil); err != nil { t.Fatalf("error inserting: %v", err) } @@ -31,7 +31,7 @@ func TestInsertSameLeafNoExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaf.values[oneKeyTest[StemSize]]) } - if leaf.lastPeriod != 1 { + if leaf.lastPeriod != period1 { t.Fatalf("expected last accessed to be 1, got %d", leaf.lastPeriod) } } @@ -40,11 +40,11 @@ func TestInsertSameLeafExpired(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - err := root.Insert(oneKeyTest, testValue, 2, nil) + err := root.Insert(oneKeyTest, testValue, period2, nil) if !errors.Is(err, errExpired) { t.Fatalf("expected period expired error when inserting, got %v", err) } @@ -58,7 +58,7 @@ func TestInsertSameLeafExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaf.values[zeroKeyTest[StemSize]]) } - if leaf.lastPeriod != 0 { + if leaf.lastPeriod != period0 { t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) } } @@ -67,11 +67,11 @@ func TestInsertDiffLeaf(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - if err := root.Insert(ffx32KeyTest, testValue, 2, nil); err != nil { + if err := root.Insert(ffx32KeyTest, testValue, period2, nil); err != nil { t.Fatalf("error inserting: %v", err) } @@ -93,24 +93,63 @@ func TestInsertDiffLeaf(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaff.values[ffx32KeyTest[StemSize]]) } - if leaf0.lastPeriod != 0 { + if leaf0.lastPeriod != period0 { t.Fatalf("expected last accessed to be 0, got %d", leaf0.lastPeriod) } - if leaff.lastPeriod != 2 { + if leaff.lastPeriod != period2 { t.Fatalf("expected last accessed to be 2, got %d", leaff.lastPeriod) } } +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.lastPeriod, 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, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - val, err := root.Get(zeroKeyTest, 1, nil) + val, err := root.Get(zeroKeyTest, period1, nil) if err != nil { t.Fatalf("error getting: %v", err) } @@ -124,7 +163,7 @@ func TestGetNoExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, val) } - if leaf.lastPeriod != 0 { + if leaf.lastPeriod != period0 { t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) } } @@ -133,11 +172,11 @@ func TestGetExpired(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - val, err := root.Get(zeroKeyTest, 2, nil) + val, err := root.Get(zeroKeyTest, period2, nil) if !errors.Is(err, errExpired) { t.Fatalf("expected period expired error when getting, got %v", err) } @@ -151,7 +190,7 @@ func TestGetExpired(t *testing.T) { t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) } - if leaf.lastPeriod != 0 { + if leaf.lastPeriod != period0 { t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) } } @@ -160,11 +199,11 @@ func TestDelLeafNoExpired(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - if _, err := root.Delete(zeroKeyTest, 1, nil); err != nil { + if _, err := root.Delete(zeroKeyTest, period1, nil); err != nil { t.Fatalf("error deleting: %v", err) } @@ -178,11 +217,11 @@ func TestDelLeafExpired(t *testing.T) { t.Parallel() root := New() - if err := root.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { t.Fatalf("error inserting: %v", err) } - _, err := root.Delete(zeroKeyTest, 2, nil) + _, err := root.Delete(zeroKeyTest, period2, nil) if !errors.Is(err, errExpired) { t.Fatalf("expected period expired error when deleting, got %v", err) } @@ -192,16 +231,80 @@ func TestDelLeafExpired(t *testing.T) { t.Fatalf("expected empty node, got %T", root.(*InternalNode).children[0]) } - if leaf.lastPeriod != 0 { + if leaf.lastPeriod != period0 { t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) } } +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, ok := root.(*InternalNode).children[0].(*LeafNode) + if !ok { + t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + } + + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) + root.(*InternalNode).children[0] = expiredLeaf + + if err := root.Revive(leaf.stem, leaf.values, leaf.lastPeriod, period2, 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.lastPeriod != period2 { + t.Fatalf("expected last accessed to be 2, got %d", rLeaf.lastPeriod) + } +} + +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.lastPeriod, 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.lastPeriod != period0 { + t.Fatalf("expected last accessed to be 0, got %d", rLeaf.lastPeriod) + } + + 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, 0, nil); err != nil { + if err := root.Insert(zeroKeyTest, testValue, period0, nil); err != nil { t.Fatalf("error inserting: %v", err) } @@ -226,13 +329,13 @@ func TestRootCommitDiffEpoch(t *testing.T) { t.Parallel() root1 := New() - if err := root1.Insert(zeroKeyTest, testValue, 0, nil); err != nil { + 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, 2, nil); err != nil { + if err := root2.Insert(zeroKeyTest, testValue, period2, nil); err != nil { t.Fatalf("error inserting: %v", err) } comm2 := root2.Commit() diff --git a/hashednode.go b/hashednode.go index 89c72040..c64a0120 100644 --- a/hashednode.go +++ b/hashednode.go @@ -44,7 +44,7 @@ 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, NodeResolverFn) error { +func (HashedNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error { return errors.New("cannot revive a hashed node") } @@ -62,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, StatePeriod) (*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 1c48d9db..ad79f9b0 100644 --- a/hashednode_test.go +++ b/hashednode_test.go @@ -46,7 +46,7 @@ func TestHashedNodeFuncs(t *testing.T) { 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, period0); 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 index efbfb03c..35edec5d 100644 --- a/period.go +++ b/period.go @@ -8,7 +8,9 @@ type StatePeriod uint16 const ( NumActiveEpochs = 2 - period0 = 0 + period0 = StatePeriod(0) + period1 = StatePeriod(1) + period2 = StatePeriod(2) ) func IsExpired(prev, cur StatePeriod) bool { diff --git a/proof_ipa.go b/proof_ipa.go index e6eaf0ef..3d3dfb70 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -105,23 +105,17 @@ func (vp *VerkleProof) Equal(other *VerkleProof) error { return nil } -// TODO(weiihann): add PoeStems for proof of expiry? It should be a list of (stem, lastPeriod) type Proof struct { Multipoint *ipa.MultiProof // multipoint argument ExtStatus []byte // the extension status of each stem Cs []*Point // commitments, sorted by their path in the tree PoaStems []Stem // stems proving another stem is absent - // PoeInfos []PoeInfo // stems proving another stem is expired + ExpiryPeriods []StatePeriod // periods of expired stems Keys [][]byte PreValues [][]byte PostValues [][]byte } -type PoeInfo struct { - Stem Stem - Period StatePeriod -} - type SuffixStateDiff struct { Suffix byte `json:"suffix"` CurrentValue *[32]byte `json:"currentValue"` @@ -133,7 +127,7 @@ type SuffixStateDiffs []SuffixStateDiff type StemStateDiff struct { Stem [StemSize]byte `json:"stem"` SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"` - Resurrected bool `json:"resurrected"` + PeriodExpired *StatePeriod `json:"periodExpired,omitempty"` } type StateDiff []StemStateDiff @@ -143,7 +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].Resurrected = sd[i].Resurrected + ret[i].PeriodExpired = sd[i].PeriodExpired for j := range sd[i].SuffixDiffs { ret[i].SuffixDiffs[j].Suffix = sd[i].SuffixDiffs[j].Suffix if sd[i].SuffixDiffs[j].CurrentValue != nil { @@ -193,24 +187,24 @@ 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, period StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { sort.Sort(keylist(keys)) - return root.GetProofItems(keylist(keys), resolver) + return root.GetProofItems(keylist(keys), resolver, period) } // 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, postEpoch StatePeriod, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, error) { +func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, prePeriod, postEpoch 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, eps, err := GetCommitmentsForMultiproof(preroot, keys, resolver, prePeriod) 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 @@ -222,7 +216,7 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, postE for i := range keys { val, err := postroot.Get(keys[i], postEpoch, 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 @@ -232,11 +226,12 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, postE // [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: expiry stems, also included in proof elements for serialization + return pe, es, poas, postvals, eps, nil } -func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, postEpoch StatePeriod, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { - pe, es, poas, postvals, err := getProofElementsFromTree(preroot, postroot, keys, postEpoch, resolver) +func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, prePeriod, postEpoch StatePeriod, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { + pe, es, poas, postvals, eps, err := getProofElementsFromTree(preroot, postroot, keys, prePeriod, postEpoch, resolver) if err != nil { return nil, nil, nil, nil, fmt.Errorf("get commitments for multiproof: %s", err) } @@ -270,6 +265,7 @@ func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, postEpoch Cs: cis, ExtStatus: es, PoaStems: poas, + ExpiryPeriods: eps, Keys: keys, PreValues: pe.Vals, PostValues: postvals, @@ -278,8 +274,8 @@ func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, postEpoch } // 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, 0, nil) +func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode, prePeriod StatePeriod) error { + pe, _, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, prePeriod, period0,nil) if err != nil { return fmt.Errorf("error getting proof elements: %w", err) } @@ -325,19 +321,20 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { var stemdiff *StemStateDiff var statediff StateDiff - var checkResurrect bool var curExtStatus byte + var checkStemExpiry bool - curEsInd := -1 // index of the current extension status + curExtInd := -1 // index of the current extension status + curExpInd := 0 // index of the current expiry period for i, key := range proof.Keys { stem := KeyToStem(key) if stemdiff == nil || !bytes.Equal(stemdiff.Stem[:], stem) { statediff = append(statediff, StemStateDiff{}) stemdiff = &statediff[len(statediff)-1] copy(stemdiff.Stem[:], stem) - checkResurrect = true - curEsInd += 1 - curExtStatus = proof.ExtStatus[curEsInd] + checkStemExpiry = true + curExtInd += 1 + curExtStatus = proof.ExtStatus[curExtInd] } stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{Suffix: key[StemSize]}) @@ -368,11 +365,11 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { newsd.NewValue = (*[32]byte)(unsafe.Pointer(&aligned[0])) } - // if the current extension status is expired and the post value is not nil, - // then it means that the leaf has been resurrected. - if checkResurrect && (curExtStatus&3 == extStatusExpired) && (proof.PostValues[i] != nil) { - stemdiff.Resurrected = true - checkResurrect = false + if checkStemExpiry && curExtStatus&3 == extStatusExpired { + checkStemExpiry = false + period := proof.ExpiryPeriods[curExpInd] + stemdiff.PeriodExpired = &period + curExpInd += 1 } } @@ -399,6 +396,7 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { extStatus []byte commitments []*Point multipoint ipa.MultiProof + expiryPeriods []StatePeriod ) poaStems = make([]Stem, len(vp.OtherStems)) @@ -454,6 +452,10 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { postvalues = append(postvalues, nil) } } + + if stemdiff.PeriodExpired != nil { + expiryPeriods = append(expiryPeriods, *stemdiff.PeriodExpired) + } } proof := Proof{ @@ -461,6 +463,7 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { extStatus, commitments, poaStems, + expiryPeriods, keys, prevalues, postvalues, @@ -474,6 +477,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. @@ -499,6 +503,7 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // paths [][]byte err error poas = proof.PoaStems + eps = proof.ExpiryPeriods ) // The proof of absence stems must be sorted. If that isn't the case, the proof is invalid. @@ -577,7 +582,9 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // } } si.stem = poas[0] + si.period = eps[0] poas = poas[1:] + eps = eps[1:] default: return nil, fmt.Errorf("invalid extension status: %d", si.stemType) } @@ -600,7 +607,7 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // for i, k := range proof.Keys { if len(proof.PreValues[i]) == 0 { // Skip the nil keys, they are here to prove - // an absence. + // an absence or expiry. continue } @@ -640,10 +647,8 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postEpo var stem [StemSize]byte copy(stem[:StemSize], stemstatediff.Stem[:]) - if stemstatediff.Resurrected { - // We skip verifying the revive of reconstructed leaf node because the post values may already be - // modified after reviving - if err := postroot.Revive(stem[:], values, period0, postEpoch, nil); err != nil { + if stemstatediff.PeriodExpired != nil { + if err := postroot.Revive(stem[:], values, *stemstatediff.PeriodExpired, postEpoch, true, nil); err != nil { return nil, fmt.Errorf("error reviving in post state: %w", err) } } else if overwrites { @@ -664,7 +669,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, postPeriod StatePeriod) error { +func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statediff StateDiff, prePeriod, postPeriod StatePeriod) error { proof, err := DeserializeProof(vp, statediff) if err != nil { return fmt.Errorf("verkle proof deserialization error: %w", err) @@ -715,5 +720,5 @@ func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statedif return fmt.Errorf("post tree root mismatch: %x != %x", regeneratedPostTreeRoot, postStateRoot) } - return verifyVerkleProofWithPreState(proof, pretree) + return verifyVerkleProofWithPreState(proof, pretree, prePeriod) } diff --git a/proof_json.go b/proof_json.go index 5c4e3d24..9b2be63c 100644 --- a/proof_json.go +++ b/proof_json.go @@ -186,14 +186,14 @@ func (vp *VerkleProof) UnmarshalJSON(data []byte) error { type stemStateDiffMarshaller struct { Stem string `json:"stem"` SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"` - Resurrected bool `json:"resurrected"` + PeriodExpired *StatePeriod `json:"periodExpired,omitempty"` } func (ssd StemStateDiff) MarshalJSON() ([]byte, error) { return json.Marshal(&stemStateDiffMarshaller{ Stem: HexToPrefixedString(ssd.Stem[:]), SuffixDiffs: ssd.SuffixDiffs, - Resurrected: ssd.Resurrected, + PeriodExpired: ssd.PeriodExpired, }) } @@ -209,7 +209,7 @@ func (ssd *StemStateDiff) UnmarshalJSON(data []byte) error { } *ssd = StemStateDiff{ SuffixDiffs: aux.SuffixDiffs, - Resurrected: aux.Resurrected, + PeriodExpired: aux.PeriodExpired, } copy(ssd.Stem[:], stem) return nil diff --git a/proof_test.go b/proof_test.go index 6a6bbfd3..6546d36b 100644 --- a/proof_test.go +++ b/proof_test.go @@ -37,17 +37,13 @@ import ( "github.com/crate-crypto/go-ipa/common" ) -const ( - period2 = 2 -) - func TestProofEmptyTree(t *testing.T) { t.Parallel() root := New() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, 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)) @@ -69,7 +65,7 @@ func TestProofVerifyTwoLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -96,7 +92,7 @@ func TestProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -123,9 +119,9 @@ func TestMultiProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], period0, period0, nil) - pe, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) + pe, _, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil, period0) if err != nil { t.Fatal(err) } @@ -163,9 +159,9 @@ func TestMultiProofVerifyMultipleLeavesWithAbsentStem(t *testing.T) { absent[3] = 1 // and the stem differs keys = append(keys, absent) - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, period0, nil) - pe, _, isabsent, err := GetCommitmentsForMultiproof(root, keys, nil) + pe, _, isabsent, _, err := GetCommitmentsForMultiproof(root, keys, nil, period0) if err != nil { t.Fatal(err) } @@ -197,9 +193,9 @@ func TestMultiProofVerifyMultipleLeavesCommitmentRedundancy(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, period0, nil) - pe, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) + pe, _, _, _, err := GetCommitmentsForMultiproof(root, keys, nil, period0) if err != nil { t.Fatal(err) } @@ -221,7 +217,7 @@ func TestProofOfAbsenceInternalVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -241,7 +237,7 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -266,7 +262,7 @@ func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { return ret }() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -288,7 +284,7 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { }() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { @@ -314,7 +310,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]}, period0, nil); err != nil { + if _, _, _, _, err := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, period0, nil); err != nil { b.Fatal(err) } } @@ -335,7 +331,7 @@ func BenchmarkProofVerification(b *testing.B) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, period0, nil) b.ResetTimer() b.ReportAllocs() @@ -366,7 +362,7 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { } } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -405,7 +401,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -446,7 +442,7 @@ func TestProofDeserialize(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -459,7 +455,7 @@ func TestProofDeserialize(t *testing.T) { } _ = deserialized - pe, _, _, err := root.GetProofItems(keylist{absentkey[:]}, nil) + pe, _, _, _, err := root.GetProofItems(keylist{absentkey[:]}, nil, period0) if err != nil { t.Fatal(err) } @@ -476,7 +472,7 @@ func TestProofOfAbsenceEdgeCase(t *testing.T) { root.Commit() ret, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030303") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, period0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -497,7 +493,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030301") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, period0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -559,7 +555,7 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030200") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, period0, nil) + proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, period0, period0, nil) cfg := GetConfig() if ok, err := verifyVerkleProof(proof, cs, zis, yis, cfg); !ok || err != nil { t.Fatal("could not verify proof") @@ -630,10 +626,10 @@ func TestStemStateDiffJSONMarshalUn(t *testing.T) { 0xDD, 0xEE, 0xFF, 0x00, }, }}, - Resurrected: true, + PeriodExpired: func() *StatePeriod { p := StatePeriod(100); return &p }(), } - expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}],"resurrected":true}` + expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}],"periodExpired":100}` actualJSON, err := json.Marshal(ssd) if err != nil { t.Errorf("error marshalling SuffixStateDiff to JSON: %v", err) @@ -866,7 +862,7 @@ func TestProofOfExpiryOneLeaf(t *testing.T) { t.Fatalf("expected commitment to be %x, got %x", init, comm) } - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest}, period0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest}, period0, period0, nil) if err != nil { t.Fatalf("could not make verkle proof: %v", err) } @@ -918,7 +914,7 @@ func TestProofOfExpiryMultipleLeaves(t *testing.T) { t.Fatalf("expected commitment to be %x, got %x", init, comm) } - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest, ffx32KeyTest}, period0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest, ffx32KeyTest}, period0, period0, nil) if err != nil { t.Fatalf("could not make verkle proof: %v", err) } @@ -974,7 +970,7 @@ func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, pr proveKVs[string(key)] = value } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, period0, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1123,7 +1119,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 } postroot.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, period0, period0, nil) keys: for i := range proof.Keys { @@ -1162,7 +1158,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 t.Fatalf("error deserializing proof: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, root); err != nil { + if err = verifyVerkleProofWithPreState(dproof, root, period0); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(root), ToDot(postroot)) } @@ -1180,7 +1176,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 t.Fatalf("differing root commitments %x != %x", dpostroot.Commitment().Bytes(), postroot.Commitment().Bytes()) } - if err = verifyVerkleProofWithPreState(dproof, dpreroot); err != nil { + if err = verifyVerkleProofWithPreState(dproof, dpreroot, period0); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) } }) @@ -1191,11 +1187,11 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Parallel() - preEpoch := StatePeriod(0) - postEpoch := StatePeriod(2) + prePeriod := period0 + postPeriod := period2 preRoot := New() - if err := preRoot.Insert(zeroKeyTest, zeroKeyTest, preEpoch, nil); err != nil { + if err := preRoot.Insert(zeroKeyTest, zeroKeyTest, prePeriod, nil); err != nil { t.Fatalf("could not insert key: %v", err) } rootC := preRoot.Commit() @@ -1206,11 +1202,11 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { preRoot.(*InternalNode).children[0] = expiredLeaf postRoot := New() - if err := postRoot.Insert(zeroKeyTest, fourtyKeyTest, postEpoch, nil); err != nil { + if err := postRoot.Insert(zeroKeyTest, fourtyKeyTest, postPeriod, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - proof, _, _, _, _ := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, postEpoch, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, prePeriod, postPeriod, nil) p, diff, err := SerializeProof(proof) if err != nil { @@ -1222,7 +1218,7 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Fatalf("error deserializing proof: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, preRoot); err != nil { + if err = verifyVerkleProofWithPreState(dproof, preRoot, prePeriod); err != nil { t.Fatalf("could not verify verkle proof: %v", err) } @@ -1231,16 +1227,16 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Fatalf("error recreating pre tree: %v", err) } - dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff, postEpoch) + dpostroot, err := PostStateTreeFromStateDiff(dpreroot, diff, postPeriod) if err != nil { t.Fatalf("error recreating post tree: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, dpreroot); err != nil { + if err = verifyVerkleProofWithPreState(dproof, dpreroot, prePeriod); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) } - got, err := dpostroot.Get(zeroKeyTest, postEpoch, nil) + got, err := dpostroot.Get(zeroKeyTest, postPeriod, nil) if err != nil { t.Fatalf("error getting key: %v", err) } @@ -1263,7 +1259,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { // 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}, period0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, keylist{absentKey}, period0, period0, nil) if err != nil { t.Fatal(err) } @@ -1288,7 +1284,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, period0) if err != nil { t.Fatal(err) } @@ -1343,7 +1339,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}, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1392,7 +1388,7 @@ func TestProveAbsenceInEmptyHalf(t *testing.T) { key2, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000100") key3, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { diff --git a/tree.go b/tree.go index ce2dc0cf..b17c6c50 100644 --- a/tree.go +++ b/tree.go @@ -91,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, StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) // Serialize encodes the node to RLP. Serialize() ([]byte, error) @@ -99,7 +99,7 @@ type VerkleNode interface { // Copy a node and its children Copy() VerkleNode - Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error + Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error // toDot returns a string representing this subtree in DOT language toDot(string, string) string @@ -413,7 +413,30 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod // splits. return n.InsertValuesAtStem(stem, values, curPeriod, resolver) case *ExpiredLeafNode: - return errExpired + // 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, 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. @@ -459,7 +482,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod return nil } -func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn) error { +func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, skipReviveVerify bool,resolver NodeResolverFn) error { nChild := offset2key(stem, n.depth) switch child := n.children[nChild].(type) { @@ -482,8 +505,18 @@ func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod S } n.children[nChild] = resolved n.cowChild(nChild) - return n.Revive(stem, values, oldPeriod, curPeriod, resolver) + return n.Revive(stem, values, oldPeriod, curPeriod, skipReviveVerify, resolver) case *ExpiredLeafNode: + if skipReviveVerify { + leaf, err := NewLeafNode(stem, values, curPeriod) + if err != nil { + return err + } + leaf.setDepth(n.depth + 1) + n.children[nChild] = leaf + return nil + } + leaf, err := NewLeafNode(stem, values, oldPeriod) if err != nil { return err @@ -501,7 +534,7 @@ func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod S return nil case *LeafNode: - return child.Revive(stem, values, oldPeriod, curPeriod, resolver) + return child.Revive(stem, values, oldPeriod, curPeriod, skipReviveVerify, resolver) } return nil @@ -586,10 +619,10 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point if len(stemInfo.stem) != StemSize { return comms, fmt.Errorf("invalid stem size %d", len(stemInfo.stem)) } - // TODO(weiihann): add last period newchild := &ExpiredLeafNode{ commitment: comms[0], stem: stemInfo.stem, + lastPeriod: stemInfo.period, depth: n.depth + 1, } n.children[path[0]] = newchild @@ -724,7 +757,6 @@ func (n *InternalNode) Delete(key []byte, curPeriod StatePeriod, resolver NodeRe // 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. -// TODO(weiihann): check if need to compare access periods func (n *InternalNode) DeleteAtStem(key []byte, curPeriod StatePeriod, resolver NodeResolverFn) (bool, error) { nChild := offset2key(key, n.depth) switch child := n.children[nChild].(type) { @@ -1034,7 +1066,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, curPeriod StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { var ( groups = groupKeys(keys, n.depth) pe = &ProofElements{ @@ -1047,6 +1079,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 + expiryPeriods []StatePeriod ) // fill in the polynomial for this node @@ -1062,15 +1095,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 { @@ -1083,7 +1116,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 { @@ -1108,7 +1141,7 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr switch n.children[childIdx].(type) { case UnknownNode: // TODO: add a test case to cover this scenario. - return nil, nil, nil, errMissingNodeInStateless + return nil, nil, nil, nil,errMissingNodeInStateless case Empty: // Special case of a proof of absence: no children // commitment, as the value is 0. @@ -1129,17 +1162,18 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr continue } - pec, es, other, err := n.children[childIdx].GetProofItems(group, resolver) + pec, es, other, otherEs, err := n.children[childIdx].GetProofItems(group, resolver, curPeriod) 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...) + expiryPeriods = append(expiryPeriods, otherEs...) } - return pe, esses, poass, nil + return pe, esses, poass, expiryPeriods, nil } // Serialize returns the serialized form of the internal node. @@ -1552,7 +1586,7 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt return n.values[k[StemSize]], nil } -func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, resolver NodeResolverFn) error { +func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, _ bool, resolver NodeResolverFn) error { // No-op if already in current period if n.lastPeriod == curPeriod { return nil @@ -1663,7 +1697,7 @@ func leafToComms(poly []Fr, val []byte) error { return nil } -func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, error) { // skipcq: GO-R1005 +func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn, curPeriod StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { // skipcq: GO-R1005 var ( poly [NodeWidth]Fr // top-level polynomial pe = &ProofElements{ @@ -1676,13 +1710,20 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofE } esses []byte = nil // list of extension statuses - poass []Stem // list of proof-of-absence and proof-of-expiry stems + poass []Stem // list of proof-of-absence ) + // Leaf node is expired but not yet garbage collected, create a temporary expired leaf node + // and return its proof items. + if n.isExpired(curPeriod) { + expireLeaf := NewExpiredLeafNode(n.stem, n.lastPeriod, n.commitment) + return expireLeaf.GetProofItems(keys, resolver, curPeriod) + } + // Initialize the top-level polynomial with 1 + stem + C1 + C2 + lastPeriod 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 @@ -1704,14 +1745,14 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofE // 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 { @@ -1727,8 +1768,6 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofE pe.Fis = append(pe.Fis, poly[:]) } - // We do not check for expiry, as we assume that the expiry detection is done before - // proof creation. poly[4].SetUint64(uint64(n.lastPeriod)) pe.Cis = append(pe.Cis, n.commitment) @@ -1781,12 +1820,12 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofE ) 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 } @@ -1820,7 +1859,7 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofE pe.ByPath[slotPath] = scomm } - return pe, esses, poass, nil + return pe, esses, poass, nil, nil } // Serialize serializes a LeafNode. diff --git a/tree_test.go b/tree_test.go index 72b80cb4..840d67da 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1047,7 +1047,7 @@ func TestEmptyCommitment(t *testing.T) { 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, period0) if err != nil { t.Fatal(err) } @@ -1114,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, period0) if err != nil { t.Fatal(err) } @@ -1369,7 +1369,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { r := tree.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, period0, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) @@ -1390,7 +1390,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { if err != nil { t.Fatal(err) } - pe, _, _, err := droot.GetProofItems(keylist(keys), nil) + pe, _, _, _, err := droot.GetProofItems(keylist(keys), nil, period0) if err != nil { t.Fatal(err) } @@ -1841,7 +1841,7 @@ func runRandTest(rt randTest) error { continue } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, 0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, period0, 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) } diff --git a/unknown.go b/unknown.go index b7f4f157..093b6737 100644 --- a/unknown.go +++ b/unknown.go @@ -41,7 +41,7 @@ func (UnknownNode) Get([]byte, StatePeriod, NodeResolverFn) ([]byte, error) { return nil, nil } -func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, NodeResolverFn) error { +func (UnknownNode) Revive(Stem, [][]byte, StatePeriod, StatePeriod, bool, NodeResolverFn) error { return errors.New("cannot revive an unknown node") } @@ -55,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, StatePeriod) (*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 8fb1c404..d78688b9 100644 --- a/unknown_test.go +++ b/unknown_test.go @@ -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, period0); err == nil { t.Errorf("got nil error when getting proof items from a hashed node") } if _, err := un.Serialize(); err == nil { From 2950a747bf0d8b4831354c73d9a7394e1fcc0c07 Mon Sep 17 00:00:00 2001 From: weiihann Date: Sun, 5 Jan 2025 14:31:51 +0800 Subject: [PATCH 33/33] Remove expiry and add period in stateless proof edits --- debug.go | 3 +- doc.go | 2 +- empty.go | 2 +- empty_test.go | 2 +- encoding.go | 16 ++-- encoding_test.go | 6 +- expired_leaf.go | 36 ++------ expired_leaf_test.go | 8 +- expired_tree_test.go | 89 ++++++++++++-------- hashednode.go | 2 +- hashednode_test.go | 2 +- proof_ipa.go | 94 ++++++++------------- proof_json.go | 6 +- proof_test.go | 191 +++++++++++-------------------------------- tree.go | 163 +++++++++++++++++------------------- tree_test.go | 14 ++-- unknown.go | 2 +- unknown_test.go | 2 +- 18 files changed, 245 insertions(+), 395 deletions(-) diff --git a/debug.go b/debug.go index 40127d99..4c854d00 100644 --- a/debug.go +++ b/debug.go @@ -42,12 +42,11 @@ type ( C [32]byte `json:"commitment"` C1 [32]byte `json:"c1"` C2 [32]byte `json:"c2"` - LastPeriod StatePeriod `json:"last_period"` + Period StatePeriod `json:"period"` } ExportableExpiredLeafNode struct { Stem Stem `json:"stem"` - LastPeriod StatePeriod `json:"last_period"` Commitment [32]byte `json:"commitment"` } ) diff --git a/doc.go b/doc.go index 295ad0a0..6aa2d553 100644 --- a/doc.go +++ b/doc.go @@ -43,6 +43,7 @@ var ( 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 ( @@ -50,5 +51,4 @@ const ( extStatusAbsentEmpty = byte(iota) // missing child node along the path extStatusAbsentOther // path led to a node with a different stem extStatusPresent // stem was present - extStatusExpired // stem was present but expired ) diff --git a/empty.go b/empty.go index 4529bc77..342baa03 100644 --- a/empty.go +++ b/empty.go @@ -57,7 +57,7 @@ func (Empty) Commitment() *Point { return &id } -func (Empty) GetProofItems(keylist, NodeResolverFn, StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { +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") } diff --git a/empty_test.go b/empty_test.go index 8f2177b8..c4a2eef2 100644 --- a/empty_test.go +++ b/empty_test.go @@ -51,7 +51,7 @@ func TestEmptyFuncs(t *testing.T) { t.Fatal("commitment and commit mismatch") } - if _, _, _, _, err := e.GetProofItems(nil, nil, period0); 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 f5e83fa0..7ac8800f 100644 --- a/encoding.go +++ b/encoding.go @@ -56,13 +56,13 @@ const ( leafC1CommitmentOffset = leafCommitmentOffset + banderwagon.UncompressedSize leafC2CommitmentOffset = leafC1CommitmentOffset + banderwagon.UncompressedSize leafChildrenOffset = leafC2CommitmentOffset + banderwagon.UncompressedSize - leafLastEpochOffset = leafChildrenOffset + periodSize + leafLastPeriodOffset = leafChildrenOffset + periodSize leafBasicDataSize = 32 leafSlotSize = 32 leafValueIndexSize = 1 singleSlotLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafValueIndexSize + leafSlotSize + periodSize eoaLeafSize = nodeTypeSize + StemSize + 2*banderwagon.UncompressedSize + leafBasicDataSize + periodSize - expiredLeafSize = nodeTypeSize + StemSize + periodSize + banderwagon.UncompressedSize + expiredLeafSize = nodeTypeSize + StemSize + banderwagon.UncompressedSize ) func bit(bitlist []byte, nr int) bool { @@ -77,9 +77,9 @@ 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. @@ -119,7 +119,7 @@ func parseLeafNode(serialized []byte, depth byte) (VerkleNode, error) { ln := NewLeafNodeWithNoComms( serialized[leafStemOffset:leafStemOffset+StemSize], values[:], - StatePeriodFromBytes(serialized[leafLastEpochOffset:leafLastEpochOffset+periodSize])) + StatePeriodFromBytes(serialized[leafLastPeriodOffset:leafLastPeriodOffset+periodSize])) ln.setDepth(depth) ln.c1 = new(Point) @@ -200,9 +200,8 @@ func parseExpiredLeafNode(serialized []byte, depth byte) (VerkleNode, error) { l := &ExpiredLeafNode{} l.setDepth(depth) l.stem = serialized[leafStemOffset : leafStemOffset+StemSize] - l.lastPeriod = StatePeriodFromBytes(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+periodSize]) l.commitment = new(Point) - if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize+periodSize:], true); err != nil { + if err := l.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize:], true); err != nil { return nil, fmt.Errorf("setting commitment: %w", err) } return l, nil @@ -225,6 +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 87b241e8..0c7cec99 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -198,7 +198,7 @@ func TestParseExpiredLeaf(t *testing.T) { comm := srs[0] stem := ffx32KeyTest[:StemSize] - el := NewExpiredLeafNode(stem, period2, &comm) + el := NewExpiredLeafNode(stem, &comm) serialized, err := el.Serialize() if err != nil { @@ -222,8 +222,4 @@ func TestParseExpiredLeaf(t *testing.T) { if !el2.commitment.Equal(&comm) { t.Fatalf("invalid commitment, got %x, expected %x", el2.commitment, comm) } - - if el2.lastPeriod != period2 { - t.Fatalf("invalid last period, got %d, expected %d", el2.lastPeriod, period2) - } } diff --git a/expired_leaf.go b/expired_leaf.go index 14e1afbc..7dfbda03 100644 --- a/expired_leaf.go +++ b/expired_leaf.go @@ -28,18 +28,16 @@ package verkle import ( "fmt" "errors" - "encoding/binary" ) type ExpiredLeafNode struct { stem Stem - lastPeriod StatePeriod commitment *Point depth byte // used for proof only, not commitment calculation } -func NewExpiredLeafNode(stem Stem, lastPeriod StatePeriod, commitment *Point) *ExpiredLeafNode { - return &ExpiredLeafNode{stem: stem, lastPeriod: lastPeriod, commitment: commitment} +func NewExpiredLeafNode(stem Stem, commitment *Point) *ExpiredLeafNode { + return &ExpiredLeafNode{stem: stem, commitment: commitment} } func (n *ExpiredLeafNode) Insert([]byte, []byte, StatePeriod, NodeResolverFn) error { @@ -69,25 +67,8 @@ func (n *ExpiredLeafNode) Commitment() *Point { return n.commitment } -func (n *ExpiredLeafNode) GetProofItems(keys keylist, resolver NodeResolverFn, _ StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { - var ( - pe = &ProofElements{ - Vals: make([][]byte, len(keys)), - ByPath: map[string]*Point{}, - } - esses []byte = nil - poass []Stem - ) - - for i := range keys { - pe.ByPath[string(keys[i][:n.depth])] = n.commitment - pe.Vals[i] = nil - - esses = append(esses, extStatusExpired|(n.depth<<3)) - poass = append(poass, n.stem) - } - - return pe, esses, poass, []StatePeriod{n.lastPeriod}, nil +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) { @@ -97,11 +78,7 @@ func (n *ExpiredLeafNode) Serialize() ([]byte, error) { result := buf[:] result[0] = expiredLeafType copy(result[leafStemOffset:], n.stem[:StemSize]) - - lastPeriod := make([]byte, periodSize) - binary.BigEndian.PutUint16(lastPeriod, uint16(n.lastPeriod)) - copy(result[leafStemOffset+StemSize:], lastPeriod) - copy(result[leafStemOffset+StemSize+periodSize:], cBytes[:]) + copy(result[leafStemOffset+StemSize:], cBytes[:]) return result, nil } @@ -112,8 +89,7 @@ func (n *ExpiredLeafNode) Copy() VerkleNode { l.depth = n.depth copy(l.stem, n.stem) if n.commitment != nil { - l.commitment = new(Point) - l.commitment.Set(n.commitment) + l.commitment = new(Point).Set(n.commitment) } return l diff --git a/expired_leaf_test.go b/expired_leaf_test.go index f25494fd..527dac9a 100644 --- a/expired_leaf_test.go +++ b/expired_leaf_test.go @@ -11,19 +11,19 @@ func TestExpiredLeafBasic(t *testing.T) { cfg := GetConfig() srs := cfg.conf.SRS comm := srs[0] - leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], period0, &comm) + leaf := NewExpiredLeafNode(zeroKeyTest[:StemSize], &comm) - err := leaf.Insert(zeroKeyTest, zeroKeyTest, 0, nil) + 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, 0, nil) + _, 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, 0, nil) + v, err := leaf.Get(zeroKeyTest, period2, nil) if !errors.Is(err, errExpired) { t.Fatalf("expected period expired error when getting, got %v", err) } diff --git a/expired_tree_test.go b/expired_tree_test.go index 9bfb7a56..46e7fb08 100644 --- a/expired_tree_test.go +++ b/expired_tree_test.go @@ -31,8 +31,8 @@ func TestInsertSameLeafNoExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaf.values[oneKeyTest[StemSize]]) } - if leaf.lastPeriod != period1 { - t.Fatalf("expected last accessed to be 1, got %d", leaf.lastPeriod) + if leaf.period != period1 { + t.Fatalf("expected last accessed to be 1, got %d", leaf.period) } } @@ -58,8 +58,8 @@ func TestInsertSameLeafExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaf.values[zeroKeyTest[StemSize]]) } - if leaf.lastPeriod != period0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) + if leaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.period) } } @@ -93,12 +93,12 @@ func TestInsertDiffLeaf(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, leaff.values[ffx32KeyTest[StemSize]]) } - if leaf0.lastPeriod != period0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf0.lastPeriod) + if leaf0.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf0.period) } - if leaff.lastPeriod != period2 { - t.Fatalf("expected last accessed to be 2, got %d", leaff.lastPeriod) + if leaff.period != period2 { + t.Fatalf("expected last accessed to be 2, got %d", leaff.period) } } @@ -115,7 +115,7 @@ func TestInsertExpiredLeafSibling(t *testing.T) { t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) } - root.(*InternalNode).children[0] = NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) + 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) @@ -163,8 +163,8 @@ func TestGetNoExpired(t *testing.T) { t.Fatalf("expected value %x, got %x", testValue, val) } - if leaf.lastPeriod != period0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) + if leaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.period) } } @@ -190,8 +190,8 @@ func TestGetExpired(t *testing.T) { t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) } - if leaf.lastPeriod != period0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) + if leaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", leaf.period) } } @@ -231,8 +231,28 @@ func TestDelLeafExpired(t *testing.T) { t.Fatalf("expected empty node, got %T", root.(*InternalNode).children[0]) } - if leaf.lastPeriod != period0 { - t.Fatalf("expected last accessed to be 0, got %d", leaf.lastPeriod) + 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()) } } @@ -244,25 +264,30 @@ func TestReviveExpired(t *testing.T) { 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]) - } - - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) + 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.lastPeriod, period2, false, nil); err != nil { + 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, ok := root.(*InternalNode).children[0].(*LeafNode) - if !ok { - t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) + 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 rLeaf.lastPeriod != period2 { - t.Fatalf("expected last accessed to be 2, got %d", rLeaf.lastPeriod) + if !comm1.Equal(comm2) { + t.Fatalf("expected commitment to be %x, got %x", comm1, comm2) } } @@ -281,7 +306,7 @@ func TestReviveNoExpired(t *testing.T) { comm := root.Commit() - if err := root.Revive(leaf.stem, leaf.values, leaf.lastPeriod, period0, false, nil); err != nil { + if err := root.Revive(leaf.stem, leaf.values, leaf.period, period0, false, nil); err != nil { t.Fatalf("error reviving: %v", err) } @@ -290,8 +315,8 @@ func TestReviveNoExpired(t *testing.T) { t.Fatalf("expected leaf node, got %T", root.(*InternalNode).children[0]) } - if rLeaf.lastPeriod != period0 { - t.Fatalf("expected last accessed to be 0, got %d", rLeaf.lastPeriod) + if rLeaf.period != period0 { + t.Fatalf("expected last accessed to be 0, got %d", rLeaf.period) } rComm := root.Commit() @@ -316,7 +341,7 @@ func TestRootCommitExpired(t *testing.T) { var init Point init.Set(root.Commit()) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) + expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.commitment) root.(*InternalNode).children[0] = expiredLeaf comm := root.Commit() @@ -343,4 +368,4 @@ func TestRootCommitDiffEpoch(t *testing.T) { 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 c64a0120..4ed7b062 100644 --- a/hashednode.go +++ b/hashednode.go @@ -62,7 +62,7 @@ func (HashedNode) Commitment() *Point { panic("can not get commitment of a hash node") } -func (HashedNode) GetProofItems(keylist, NodeResolverFn, StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { +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") } diff --git a/hashednode_test.go b/hashednode_test.go index ad79f9b0..c4ccece6 100644 --- a/hashednode_test.go +++ b/hashednode_test.go @@ -46,7 +46,7 @@ func TestHashedNodeFuncs(t *testing.T) { if v != nil { t.Fatal("non-nil get from a hashed node") } - if _, _, _, _, err := e.GetProofItems(nil, nil, period0); 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/proof_ipa.go b/proof_ipa.go index 3d3dfb70..69c444d6 100644 --- a/proof_ipa.go +++ b/proof_ipa.go @@ -110,10 +110,10 @@ type Proof struct { ExtStatus []byte // the extension status of each stem Cs []*Point // commitments, sorted by their path in the tree PoaStems []Stem // stems proving another stem is absent - ExpiryPeriods []StatePeriod // periods of expired stems Keys [][]byte PreValues [][]byte PostValues [][]byte + PrePeriods []StatePeriod } type SuffixStateDiff struct { @@ -127,7 +127,7 @@ type SuffixStateDiffs []SuffixStateDiff type StemStateDiff struct { Stem [StemSize]byte `json:"stem"` SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"` - PeriodExpired *StatePeriod `json:"periodExpired,omitempty"` + PrePeriod StatePeriod `json:"prePeriod,omitempty"` } type StateDiff []StemStateDiff @@ -137,7 +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].PeriodExpired = sd[i].PeriodExpired + 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 { @@ -187,22 +187,22 @@ func (sd StateDiff) Equal(other StateDiff) error { return nil } -func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeResolverFn, period StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { +func GetCommitmentsForMultiproof(root VerkleNode, keys [][]byte, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) { sort.Sort(keylist(keys)) - return root.GetProofItems(keylist(keys), resolver, period) + return root.GetProofItems(keylist(keys), resolver) } // 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, prePeriod, postEpoch StatePeriod, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, [][]byte, []StatePeriod, 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, nil, errors.New("no key provided for proof") } - pe, es, poas, eps, err := GetCommitmentsForMultiproof(preroot, keys, resolver, prePeriod) + pe, es, poas, ps, err := GetCommitmentsForMultiproof(preroot, keys, resolver) if err != nil { return nil, nil, nil, nil, nil, fmt.Errorf("error getting pre-state proof data: %w", err) } @@ -214,7 +214,7 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, prePe // 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], postEpoch, resolver) + val, err := postroot.Get(keys[i], postPeriod, resolver) if err != nil { return nil, nil, nil, nil, nil, fmt.Errorf("error getting post-state value for key %x: %w", keys[i], err) } @@ -226,12 +226,12 @@ func getProofElementsFromTree(preroot, postroot VerkleNode, keys [][]byte, prePe // [0:3]: proof elements of the pre-state trie for serialization, // 3: values to be inserted in the post-state trie for serialization - // 4: expiry stems, also included in proof elements for serialization - return pe, es, poas, postvals, eps, nil + // 4: pre-state periods of each stem + return pe, es, poas, postvals, ps, nil } -func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, prePeriod, postEpoch StatePeriod, resolver NodeResolverFn) (*Proof, []*Point, []byte, []*Fr, error) { - pe, es, poas, postvals, eps, err := getProofElementsFromTree(preroot, postroot, keys, prePeriod, postEpoch, 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) } @@ -265,17 +265,17 @@ func MakeVerkleMultiProof(preroot, postroot VerkleNode, keys [][]byte, prePeriod Cs: cis, ExtStatus: es, PoaStems: poas, - ExpiryPeriods: eps, 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, prePeriod StatePeriod) error { - pe, _, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, prePeriod, period0,nil) +func verifyVerkleProofWithPreState(proof *Proof, preroot VerkleNode) error { + pe, _, _, _, _, err := getProofElementsFromTree(preroot, nil, proof.Keys, period0, nil) if err != nil { return fmt.Errorf("error getting proof elements: %w", err) } @@ -321,26 +321,19 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { var stemdiff *StemStateDiff var statediff StateDiff - var curExtStatus byte - var checkStemExpiry bool - curExtInd := -1 // index of the current extension status - curExpInd := 0 // index of the current expiry period for i, key := range proof.Keys { stem := KeyToStem(key) if stemdiff == nil || !bytes.Equal(stemdiff.Stem[:], stem) { statediff = append(statediff, StemStateDiff{}) stemdiff = &statediff[len(statediff)-1] copy(stemdiff.Stem[:], stem) - checkStemExpiry = true - curExtInd += 1 - curExtStatus = proof.ExtStatus[curExtInd] } stemdiff.SuffixDiffs = append(stemdiff.SuffixDiffs, SuffixStateDiff{Suffix: key[StemSize]}) newsd := &stemdiff.SuffixDiffs[len(stemdiff.SuffixDiffs)-1] - valueLen := len(proof.PreValues[i]) + var valueLen = len(proof.PreValues[i]) switch valueLen { case 0: // null value @@ -364,13 +357,6 @@ func SerializeProof(proof *Proof) (*VerkleProof, StateDiff, error) { copy(aligned[:valueLen], proof.PostValues[i]) newsd.NewValue = (*[32]byte)(unsafe.Pointer(&aligned[0])) } - - if checkStemExpiry && curExtStatus&3 == extStatusExpired { - checkStemExpiry = false - period := proof.ExpiryPeriods[curExpInd] - stemdiff.PeriodExpired = &period - curExpInd += 1 - } } return &VerkleProof{ @@ -396,7 +382,7 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { extStatus []byte commitments []*Point multipoint ipa.MultiProof - expiryPeriods []StatePeriod + prePeriods []StatePeriod ) poaStems = make([]Stem, len(vp.OtherStems)) @@ -453,9 +439,7 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { } } - if stemdiff.PeriodExpired != nil { - expiryPeriods = append(expiryPeriods, *stemdiff.PeriodExpired) - } + prePeriods = append(prePeriods, stemdiff.PrePeriod) } proof := Proof{ @@ -463,10 +447,10 @@ func DeserializeProof(vp *VerkleProof, statediff StateDiff) (*Proof, error) { extStatus, commitments, poaStems, - expiryPeriods, keys, prevalues, postvalues, + prePeriods, } return &proof, nil } @@ -503,7 +487,7 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // paths [][]byte err error poas = proof.PoaStems - eps = proof.ExpiryPeriods + ps = proof.PrePeriods ) // The proof of absence stems must be sorted. If that isn't the case, the proof is invalid. @@ -570,21 +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) } } - case extStatusExpired: - // All keys that are part of a proof of expiry, must contain empty - // prestate values. If that isn't the case, the proof is invalid. - for j := range proof.Keys { // TODO: DoS risk, use map or binary search. - if bytes.HasPrefix(proof.Keys[j], stems[i]) && proof.PreValues[j] != nil { - return nil, fmt.Errorf("proof of absence (empty) stem %x has a value", si.stem) - } - } - si.stem = poas[0] - si.period = eps[0] - poas = poas[1:] - eps = eps[1:] + si.period = ps[0] + ps = ps[1:] default: return nil, fmt.Errorf("invalid extension status: %d", si.stemType) } @@ -607,7 +582,7 @@ func PreStateTreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) { // for i, k := range proof.Keys { if len(proof.PreValues[i]) == 0 { // Skip the nil keys, they are here to prove - // an absence or expiry. + // an absence. continue } @@ -626,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, postEpoch StatePeriod) (VerkleNode, error) { +func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postPeriod StatePeriod) (VerkleNode, error) { postroot := preroot.Copy() for _, stemstatediff := range statediff { @@ -644,15 +619,10 @@ func PostStateTreeFromStateDiff(preroot VerkleNode, statediff StateDiff, postEpo } } - var stem [StemSize]byte - copy(stem[:StemSize], stemstatediff.Stem[:]) - - if stemstatediff.PeriodExpired != nil { - if err := postroot.Revive(stem[:], values, *stemstatediff.PeriodExpired, postEpoch, true, nil); err != nil { - return nil, fmt.Errorf("error reviving in post state: %w", err) - } - } else if overwrites { - if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postEpoch, nil); err != nil { + if overwrites { + var stem [StemSize]byte + copy(stem[:StemSize], stemstatediff.Stem[:]) + if err := postroot.(*InternalNode).InsertValuesAtStem(stem[:], values, postPeriod, true, nil); err != nil { return nil, fmt.Errorf("error overwriting value in post state: %w", err) } } @@ -669,7 +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, prePeriod, postPeriod StatePeriod) 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) @@ -691,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[:], 0, 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) } @@ -720,5 +690,5 @@ func Verify(vp *VerkleProof, preStateRoot []byte, postStateRoot []byte, statedif return fmt.Errorf("post tree root mismatch: %x != %x", regeneratedPostTreeRoot, postStateRoot) } - return verifyVerkleProofWithPreState(proof, pretree, prePeriod) + return verifyVerkleProofWithPreState(proof, pretree) } diff --git a/proof_json.go b/proof_json.go index 9b2be63c..76e7ebb7 100644 --- a/proof_json.go +++ b/proof_json.go @@ -186,14 +186,14 @@ func (vp *VerkleProof) UnmarshalJSON(data []byte) error { type stemStateDiffMarshaller struct { Stem string `json:"stem"` SuffixDiffs SuffixStateDiffs `json:"suffixDiffs"` - PeriodExpired *StatePeriod `json:"periodExpired,omitempty"` + PrePeriod StatePeriod `json:"prePeriod"` } func (ssd StemStateDiff) MarshalJSON() ([]byte, error) { return json.Marshal(&stemStateDiffMarshaller{ Stem: HexToPrefixedString(ssd.Stem[:]), SuffixDiffs: ssd.SuffixDiffs, - PeriodExpired: ssd.PeriodExpired, + PrePeriod: ssd.PrePeriod, }) } @@ -209,7 +209,7 @@ func (ssd *StemStateDiff) UnmarshalJSON(data []byte) error { } *ssd = StemStateDiff{ SuffixDiffs: aux.SuffixDiffs, - PeriodExpired: aux.PeriodExpired, + PrePeriod: aux.PrePeriod, } copy(ssd.Stem[:], stem) return nil diff --git a/proof_test.go b/proof_test.go index 6546d36b..58a1d2ba 100644 --- a/proof_test.go +++ b/proof_test.go @@ -29,7 +29,6 @@ import ( "crypto/rand" "encoding/hex" "encoding/json" - "errors" "fmt" "reflect" "testing" @@ -43,7 +42,7 @@ func TestProofEmptyTree(t *testing.T) { root := New() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, period0, 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)) @@ -65,7 +64,7 @@ func TestProofVerifyTwoLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, period0, 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 { @@ -92,7 +91,7 @@ func TestProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, period0, 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 { @@ -119,9 +118,9 @@ func TestMultiProofVerifyMultipleLeaves(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys[0:2], period0, nil) - pe, _, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil, period0) + pe, _, _, _, err := GetCommitmentsForMultiproof(root, keys[0:2], nil) if err != nil { t.Fatal(err) } @@ -159,9 +158,9 @@ func TestMultiProofVerifyMultipleLeavesWithAbsentStem(t *testing.T) { absent[3] = 1 // and the stem differs keys = append(keys, absent) - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) - pe, _, isabsent, _, err := GetCommitmentsForMultiproof(root, keys, nil, period0) + pe, _, isabsent, _, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { t.Fatal(err) } @@ -193,9 +192,9 @@ func TestMultiProofVerifyMultipleLeavesCommitmentRedundancy(t *testing.T) { } root.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keys, period0, nil) - pe, _, _, _, err := GetCommitmentsForMultiproof(root, keys, nil, period0) + pe, _, _, _, err := GetCommitmentsForMultiproof(root, keys, nil) if err != nil { t.Fatal(err) } @@ -217,7 +216,7 @@ func TestProofOfAbsenceInternalVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, period0, period0, 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 { @@ -237,7 +236,7 @@ func TestProofOfAbsenceLeafVerify(t *testing.T) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{oneKeyTest}, period0, period0, 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 { @@ -262,7 +261,7 @@ func TestProofOfAbsenceLeafVerifyOtherSuffix(t *testing.T) { return ret }() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, period0, period0, 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 { @@ -284,7 +283,7 @@ func TestProofOfAbsenceStemVerify(t *testing.T) { }() root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{key}, period0, period0, 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 { @@ -310,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]}, period0, period0, nil); err != nil { + if _, _, _, _, err := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, nil); err != nil { b.Fatal(err) } } @@ -331,7 +330,7 @@ func BenchmarkProofVerification(b *testing.B) { } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[len(keys)/2]}, period0, nil) b.ResetTimer() b.ReportAllocs() @@ -362,7 +361,7 @@ func TestProofSerializationNoAbsentStem(t *testing.T) { } } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{keys[0]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -401,7 +400,7 @@ func TestProofSerializationWithAbsentStem(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -442,7 +441,7 @@ func TestProofDeserialize(t *testing.T) { absentkey[2] = 2 absentkey[3] = 1 - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, [][]byte{absentkey[:]}, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { @@ -455,7 +454,7 @@ func TestProofDeserialize(t *testing.T) { } _ = deserialized - pe, _, _, _, err := root.GetProofItems(keylist{absentkey[:]}, nil, period0) + pe, _, _, _, err := root.GetProofItems(keylist{absentkey[:]}, nil) if err != nil { t.Fatal(err) } @@ -472,7 +471,7 @@ func TestProofOfAbsenceEdgeCase(t *testing.T) { root.Commit() ret, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030303") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret}, period0, period0, 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") @@ -493,7 +492,7 @@ func TestProofOfAbsenceOtherMultipleLeaves(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030301") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, period0, period0, 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") @@ -555,7 +554,7 @@ func TestProofOfAbsenceNoneMultipleStems(t *testing.T) { ret1, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030300") ret2, _ := hex.DecodeString("0303030303030303030303030303030303030303030303030303030303030200") - proof, cs, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ret1, ret2}, period0, period0, 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") @@ -626,10 +625,10 @@ func TestStemStateDiffJSONMarshalUn(t *testing.T) { 0xDD, 0xEE, 0xFF, 0x00, }, }}, - PeriodExpired: func() *StatePeriod { p := StatePeriod(100); return &p }(), + PrePeriod: StatePeriod(100), } - expectedJSON := `{"stem":"0x0a000000000000000000000000000000000000000000000000000000000000","suffixDiffs":[{"suffix":65,"currentValue":"0x102030405060708090a0b0c0d0e0f000112233445566778899aabbccddeeff00","newValue":null}],"periodExpired":100}` + 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) @@ -839,111 +838,6 @@ func TestProofOfAbsenceBorderCaseReversed(t *testing.T) { testSerializeDeserializeProof(t, insertKVs, proveKeys) } -func TestProofOfExpiryOneLeaf(t *testing.T) { - t.Parallel() - - root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, period2, nil); err != nil { - t.Fatalf("could not insert key: %v", err) - } - if err := root.Insert(oneKeyTest, zeroKeyTest, period2, nil); err != nil { - t.Fatalf("could not insert key: %v", err) - } - init := root.Commit() - - leaf := root.(*InternalNode).children[0].(*LeafNode) - - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) - expiredLeaf.setDepth(1) - root.(*InternalNode).children[0] = expiredLeaf - - comm := root.Commit() - if !comm.Equal(init) { - t.Fatalf("expected commitment to be %x, got %x", init, comm) - } - - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest}, period0, period0, nil) - if err != nil { - t.Fatalf("could not make verkle proof: %v", err) - } - - cfg := GetConfig() - if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { - t.Fatalf("could not verify verkle proof: %s", ToDot(root)) - } - - deserialized, err := PreStateTreeFromProof(proof, init) - if err != nil { - t.Fatalf("error deserializing %v", err) - } - - _, err = deserialized.Get(zeroKeyTest, period0, nil) - if !errors.Is(err, errExpired) { - t.Fatalf("expected error getting key %x, got %v", zeroKeyTest, err) - } - - if !deserialized.Commit().Equal(init) { - t.Fatalf("expected commitment to be different from %x, got %x", init, deserialized.Commit()) - } -} - -func TestProofOfExpiryMultipleLeaves(t *testing.T) { - t.Parallel() - - root := New() - if err := root.Insert(zeroKeyTest, zeroKeyTest, period2, nil); err != nil { - t.Fatalf("could not insert key: %v", err) - } - if err := root.Insert(ffx32KeyTest, zeroKeyTest, period2, nil); err != nil { - t.Fatalf("could not insert key: %v", err) - } - init := root.Commit() - - leaf0 := root.(*InternalNode).children[0].(*LeafNode) - expiredLeaf0 := NewExpiredLeafNode(leaf0.stem, leaf0.lastPeriod, leaf0.commitment) - expiredLeaf0.setDepth(1) - root.(*InternalNode).children[0] = expiredLeaf0 - - leaff := root.(*InternalNode).children[255].(*LeafNode) - expiredLeaff := NewExpiredLeafNode(leaff.stem, leaff.lastPeriod, leaff.commitment) - expiredLeaff.setDepth(1) - root.(*InternalNode).children[255] = expiredLeaff - - comm := root.Commit() - if !comm.Equal(init) { - t.Fatalf("expected commitment to be %x, got %x", init, comm) - } - - proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, [][]byte{zeroKeyTest, ffx32KeyTest}, period0, period0, nil) - if err != nil { - t.Fatalf("could not make verkle proof: %v", err) - } - - cfg := GetConfig() - if ok, err := verifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil { - t.Fatalf("could not verify verkle proof: %s", ToDot(root)) - } - - deserialized, err := PreStateTreeFromProof(proof, init) - if err != nil { - t.Fatalf("error deserializing %v", err) - } - - _, err = deserialized.Get(zeroKeyTest, period0, nil) - if !errors.Is(err, errExpired) { - t.Fatalf("expected error getting key %x, got %v", zeroKeyTest, err) - } - - _, err = deserialized.Get(ffx32KeyTest, period0, nil) - if !errors.Is(err, errExpired) { - t.Fatalf("expected error getting key %x, got %v", ffx32KeyTest, err) - } - - if !deserialized.Commit().Equal(init) { - t.Fatalf("expected commitment to be different from %x, got %x", init, deserialized.Commit()) - } -} - func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, proveKeys keylist) { t.Helper() @@ -970,7 +864,7 @@ func testSerializeDeserializeProof(t *testing.T, insertKVs map[string][]byte, pr proveKVs[string(key)] = value } - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, proveKeys, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1119,7 +1013,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 } postroot.Commit() - proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, postroot, data.keystoprove, period0, nil) keys: for i := range proof.Keys { @@ -1158,7 +1052,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 t.Fatalf("error deserializing proof: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, root, period0); err != nil { + if err = verifyVerkleProofWithPreState(dproof, root); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(root), ToDot(postroot)) } @@ -1176,7 +1070,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 t.Fatalf("differing root commitments %x != %x", dpostroot.Commitment().Bytes(), postroot.Commitment().Bytes()) } - if err = verifyVerkleProofWithPreState(dproof, dpreroot, period0); err != nil { + if err = verifyVerkleProofWithPreState(dproof, dpreroot); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) } }) @@ -1184,7 +1078,7 @@ func TestProofVerificationWithPostState(t *testing.T) { // skipcq: GO-R1005 } // TODO(weiihann): add more test cases similar to TestProofVerificationWithPostState -func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { +func TestProofVerificationPreStateExpiredPostStateRevived(t *testing.T) { t.Parallel() prePeriod := period0 @@ -1194,19 +1088,26 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { if err := preRoot.Insert(zeroKeyTest, zeroKeyTest, prePeriod, nil); err != nil { t.Fatalf("could not insert key: %v", err) } - rootC := preRoot.Commit() + preReviveRoot := new(Point).Set(preRoot.Commit()) leaf := preRoot.(*InternalNode).children[0].(*LeafNode) - expiredLeaf := NewExpiredLeafNode(leaf.stem, leaf.lastPeriod, leaf.commitment) + 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, _, _, _, _ := MakeVerkleMultiProof(preRoot, postRoot, keylist{zeroKeyTest}, prePeriod, postPeriod, nil) + 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 { @@ -1218,11 +1119,11 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Fatalf("error deserializing proof: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, preRoot, prePeriod); err != nil { + if err = verifyVerkleProofWithPreState(dproof, preRoot); err != nil { t.Fatalf("could not verify verkle proof: %v", err) } - dpreroot, err := PreStateTreeFromProof(dproof, rootC) + dpreroot, err := PreStateTreeFromProof(dproof, preReviveRoot) if err != nil { t.Fatalf("error recreating pre tree: %v", err) } @@ -1232,7 +1133,7 @@ func TestProofVerificationPreStateExpiredPostStateResurrected(t *testing.T) { t.Fatalf("error recreating post tree: %v", err) } - if err = verifyVerkleProofWithPreState(dproof, dpreroot, prePeriod); err != nil { + if err = verifyVerkleProofWithPreState(dproof, dpreroot); err != nil { t.Fatalf("could not verify verkle proof: %v, original: %s reconstructed: %s", err, ToDot(dpreroot), ToDot(dpostroot)) } @@ -1259,7 +1160,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { // 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}, period0, period0, nil) + proof, cis, zis, yis, err := MakeVerkleMultiProof(root, nil, keylist{absentKey}, period0, nil) if err != nil { t.Fatal(err) } @@ -1284,7 +1185,7 @@ func TestGenerateProofWithOnlyAbsentKeys(t *testing.T) { } // From the rebuilt tree, validate the proof. - pe, _, _, _, err := GetCommitmentsForMultiproof(droot, keylist{absentKey}, nil, period0) + pe, _, _, _, err := GetCommitmentsForMultiproof(droot, keylist{absentKey}, nil) if err != nil { t.Fatal(err) } @@ -1339,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}, period0, period0, nil) + proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, nil) serialized, statediff, err := SerializeProof(proof) if err != nil { @@ -1388,7 +1289,7 @@ func TestProveAbsenceInEmptyHalf(t *testing.T) { key2, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000100") key3, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - proof, _, _, _, _ := MakeVerkleMultiProof(root, nil, keylist{key2, key3}, period0, period0, 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 b17c6c50..db1a7d1e 100644 --- a/tree.go +++ b/tree.go @@ -91,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, StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) + GetProofItems(keylist, NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) // Serialize encodes the node to RLP. Serialize() ([]byte, error) @@ -205,7 +205,7 @@ type ( // true in the context of a stateless tree. isPOAStub bool - lastPeriod StatePeriod + period StatePeriod // the last accessed period } ) @@ -230,12 +230,11 @@ func (n *InternalNode) toExportable() *ExportableInternalNode { Values: child.values, C: child.commitment.Bytes(), C1: child.c1.Bytes(), - LastPeriod: child.lastPeriod, + Period: child.period, } case *ExpiredLeafNode: exportable.Children[i] = &ExportableExpiredLeafNode{ Stem: child.stem, - LastPeriod: child.lastPeriod, Commitment: child.commitment.Bytes(), } default: @@ -279,7 +278,7 @@ func NewStatelessInternal(depth byte, comm *Point) VerkleNode { } // New creates a new leaf node -func NewLeafNode(stem Stem, values [][]byte, lastPeriod StatePeriod) (*LeafNode, error) { +func NewLeafNode(stem Stem, values [][]byte, period StatePeriod) (*LeafNode, error) { cfg := GetConfig() // C1. @@ -322,7 +321,7 @@ func NewLeafNode(stem Stem, values [][]byte, lastPeriod StatePeriod) (*LeafNode, return nil, fmt.Errorf("batch mapping to scalar fields: %s", err) } - poly[4].SetUint64(uint64(lastPeriod)) + poly[4].SetUint64(uint64(period)) return &LeafNode{ // depth will be 0, but the commitment calculation @@ -332,20 +331,20 @@ func NewLeafNode(stem Stem, values [][]byte, lastPeriod StatePeriod) (*LeafNode, commitment: cfg.CommitToPoly(poly[:], NodeWidth-5), c1: c1, c2: c2, - lastPeriod: lastPeriod, + 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, lastPeriod StatePeriod) *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, - lastPeriod: lastPeriod, + period: period, } } @@ -378,10 +377,10 @@ func (n *InternalNode) cowChild(index byte) { 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, curPeriod, resolver) + return n.InsertValuesAtStem(KeyToStem(key), values, curPeriod, false,resolver) } -func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod StatePeriod, 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) { @@ -411,7 +410,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod n.cowChild(nChild) // recurse to handle the case of a LeafNode child that // splits. - return n.InsertValuesAtStem(stem, values, curPeriod, 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) { @@ -427,7 +426,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod nextWordInInsertedKey := offset2key(stem, n.depth+1) if nextWordInInsertedKey == nextWordInExistingKey { - return newBranch.InsertValuesAtStem(stem, values, curPeriod, resolver) + return newBranch.InsertValuesAtStem(stem, values, curPeriod, skipPeriodCheck, resolver) } leaf, err := NewLeafNode(stem, values, curPeriod) @@ -444,7 +443,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod return errIsPOAStub } n.cowChild(nChild) - return child.insertMultiple(stem, values, curPeriod) + return child.insertMultiple(stem, values, curPeriod, skipPeriodCheck) } n.cowChild(nChild) @@ -460,7 +459,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod nextWordInInsertedKey := offset2key(stem, n.depth+1) if nextWordInInsertedKey == nextWordInExistingKey { - return newBranch.InsertValuesAtStem(stem, values, curPeriod, resolver) + return newBranch.InsertValuesAtStem(stem, values, curPeriod, skipPeriodCheck, resolver) } // Next word differs, so this was the last level. @@ -474,7 +473,7 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod newBranch.children[nextWordInInsertedKey] = leaf case *InternalNode: n.cowChild(nChild) - return child.InsertValuesAtStem(stem, values, curPeriod, resolver) + return child.InsertValuesAtStem(stem, values, curPeriod, skipPeriodCheck, resolver) default: // It should be an UknownNode. return errUnknownNodeType } @@ -482,7 +481,11 @@ func (n *InternalNode) InsertValuesAtStem(stem Stem, values [][]byte, curPeriod return nil } -func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, skipReviveVerify bool,resolver NodeResolverFn) error { +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) { @@ -505,36 +508,28 @@ func (n *InternalNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod S } n.children[nChild] = resolved n.cowChild(nChild) - return n.Revive(stem, values, oldPeriod, curPeriod, skipReviveVerify, resolver) + return n.Revive(stem, values, oldPeriod, curPeriod, skipUpdate, resolver) case *ExpiredLeafNode: - if skipReviveVerify { - leaf, err := NewLeafNode(stem, values, curPeriod) - if err != nil { - return err - } - leaf.setDepth(n.depth + 1) - n.children[nChild] = leaf - return nil - } - leaf, err := NewLeafNode(stem, values, oldPeriod) if err != nil { return err } - // Verify commitment matches if needed + // Verify commitment matches if !child.Commitment().Equal(leaf.Commitment()) { return errReviveCommitmentMismatch } - // Update period and set as child leaf.setDepth(n.depth + 1) - leaf.updatePeriod(curPeriod) + if !skipUpdate { + leaf.updatePeriod(curPeriod) + } n.children[nChild] = leaf - + n.cowChild(nChild) + return nil case *LeafNode: - return child.Revive(stem, values, oldPeriod, curPeriod, skipReviveVerify, resolver) + return child.Revive(stem, values, oldPeriod, curPeriod, skipUpdate, resolver) } return nil @@ -587,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 @@ -612,21 +608,6 @@ func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point for b, value := range stemInfo.values { newchild.values[b] = value } - case extStatusExpired: - if len(comms) == 0 { - return comms, fmt.Errorf("missing commitment for stem %x", stemInfo.stem) - } - if len(stemInfo.stem) != StemSize { - return comms, fmt.Errorf("invalid stem size %d", len(stemInfo.stem)) - } - newchild := &ExpiredLeafNode{ - commitment: comms[0], - stem: stemInfo.stem, - lastPeriod: stemInfo.period, - depth: n.depth + 1, - } - n.children[path[0]] = newchild - comms = comms[1:] default: return comms, fmt.Errorf("invalid stem type %d", stemInfo.stemType) } @@ -1066,7 +1047,7 @@ func groupKeys(keys keylist, depth byte) []keylist { return groups } -func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn, curPeriod StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { +func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*ProofElements, []byte, []Stem, []StatePeriod, error) { var ( groups = groupKeys(keys, n.depth) pe = &ProofElements{ @@ -1079,7 +1060,7 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn, curP esses []byte = nil // list of extension statuses poass []Stem // list of proof-of-absence stems - expiryPeriods []StatePeriod + ps []StatePeriod ) // fill in the polynomial for this node @@ -1162,7 +1143,7 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn, curP continue } - pec, es, other, otherEs, err := n.children[childIdx].GetProofItems(group, resolver, curPeriod) + 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, nil, err @@ -1170,10 +1151,10 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn, curP pe.Merge(pec) poass = append(poass, other...) esses = append(esses, es...) - expiryPeriods = append(expiryPeriods, otherEs...) + ps = append(ps, otherPs...) } - return pe, esses, poass, expiryPeriods, nil + return pe, esses, poass, ps, nil } // Serialize returns the serialized form of the internal node. @@ -1293,16 +1274,16 @@ func (n *LeafNode) Insert(key []byte, value []byte, curPeriod StatePeriod, _ Nod } values := make([][]byte, NodeWidth) values[key[StemSize]] = value - return n.insertMultiple(stem, values, curPeriod) + return n.insertMultiple(stem, values, curPeriod, false) } -func (n *LeafNode) insertMultiple(stem Stem, values [][]byte, curPeriod StatePeriod) 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 } - if n.isExpired(curPeriod) { + if !skipPeriodCheck && n.isExpired(curPeriod) { return errExpired } @@ -1322,8 +1303,8 @@ func (n *LeafNode) updateC(cxIndex int, newC Fr, oldC Fr, curPeriod StatePeriod) var poly [NodeWidth]Fr poly[cxIndex] = deltaC - if n.lastPeriod != curPeriod { - poly[4].SetUint64(uint64(curPeriod)) + if n.period < curPeriod { + poly[4].SetUint64(uint64(curPeriod - n.period)) } // Add delta to the current commitment. @@ -1388,9 +1369,9 @@ func (n *LeafNode) updateLeaf(index byte, value []byte, curPeriod StatePeriod) e // 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.lastPeriod = curPeriod n.updateC(cxIndex, frs[0], frs[1], curPeriod) - + + n.period = curPeriod n.values[index] = value return nil } @@ -1443,19 +1424,19 @@ func (n *LeafNode) updateMultipleLeaves(values [][]byte, curPeriod StatePeriod) } n.updateC(c1Idx, frs[0], frs[1], curPeriod) n.updateC(c2Idx, frs[2], frs[3], curPeriod) - n.lastPeriod = 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], curPeriod) - n.lastPeriod = 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], curPeriod) - n.lastPeriod = curPeriod + n.period = curPeriod } return nil @@ -1536,7 +1517,7 @@ func (n *LeafNode) Delete(k []byte, curPeriod StatePeriod, _ NodeResolverFn) (bo n.commitment.Sub(n.commitment, cfg.CommitToPoly(poly[:], 0)) // TODO(weiihann): can this be done together with the previous? - if n.lastPeriod != curPeriod { + if n.period != curPeriod { n.updatePeriod(curPeriod) } @@ -1586,14 +1567,18 @@ func (n *LeafNode) Get(k []byte, curPeriod StatePeriod, _ NodeResolverFn) ([]byt return n.values[k[StemSize]], nil } -func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod StatePeriod, _ bool, resolver NodeResolverFn) error { +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.lastPeriod == curPeriod { + if n.period == curPeriod { return nil } // Verify period, stem and values match - if n.lastPeriod != oldPeriod { + if n.period != oldPeriod { return errRevivePeriodMismatch } if !bytes.Equal(n.stem, stem) { @@ -1608,7 +1593,10 @@ func (n *LeafNode) Revive(stem Stem, values [][]byte, oldPeriod, curPeriod State } } - n.updatePeriod(curPeriod) + if !skipUpdate { + n.updatePeriod(curPeriod) + } + return nil } @@ -1634,19 +1622,20 @@ func (n *LeafNode) Commit() *Point { // updatePeriod updates the last period of the leaf node and updates the commitment func (n *LeafNode) updatePeriod(curPeriod StatePeriod) { - // no-op if the period is already the same - if n.lastPeriod == curPeriod { + // no-op if the current period is the same + if curPeriod <= n.period { return } - n.lastPeriod = curPeriod var poly [5]Fr - poly[4].SetUint64(uint64(curPeriod)) + 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.lastPeriod, cur) + return IsExpired(n.period, cur) } // fillSuffixTreePoly takes one of the two suffix tree and @@ -1697,7 +1686,7 @@ func leafToComms(poly []Fr, val []byte) error { return nil } -func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn, curPeriod StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, 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{ @@ -1711,16 +1700,10 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn, curPerio esses []byte = nil // list of extension statuses poass []Stem // list of proof-of-absence + ps []StatePeriod ) - // Leaf node is expired but not yet garbage collected, create a temporary expired leaf node - // and return its proof items. - if n.isExpired(curPeriod) { - expireLeaf := NewExpiredLeafNode(n.stem, n.lastPeriod, n.commitment) - return expireLeaf.GetProofItems(keys, resolver, curPeriod) - } - - // Initialize the top-level polynomial with 1 + stem + C1 + C2 + lastPeriod + // 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, nil, fmt.Errorf("error serializing stem '%x': %w", n.stem, err) @@ -1768,7 +1751,7 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn, curPerio pe.Fis = append(pe.Fis, poly[:]) } - poly[4].SetUint64(uint64(n.lastPeriod)) + poly[4].SetUint64(uint64(n.period)) pe.Cis = append(pe.Cis, n.commitment) pe.Zis = append(pe.Zis, 4) @@ -1859,11 +1842,11 @@ func (n *LeafNode) GetProofItems(keys keylist, resolver NodeResolverFn, curPerio pe.ByPath[slotPath] = scomm } - return pe, esses, poass, nil, 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 @@ -2114,8 +2097,8 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B // Create the serialization. var result []byte - lastPeriod := make([]byte, periodSize) - binary.BigEndian.PutUint16(lastPeriod, uint16(n.lastPeriod)) + period := make([]byte, periodSize) + binary.BigEndian.PutUint16(period, uint16(n.period)) switch { case count == 1: var buf [singleSlotLeafSize]byte @@ -2128,7 +2111,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafStemOffset+StemSize:], c2Bytes[:]) } copy(result[leafStemOffset+StemSize+banderwagon.UncompressedSize:], cBytes[:]) - copy(result[leafStemOffset+StemSize+2*banderwagon.UncompressedSize:], lastPeriod) + 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: @@ -2138,7 +2121,7 @@ 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:], lastPeriod) + 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+periodSize+len(children)) @@ -2148,7 +2131,7 @@ func (n *LeafNode) serializeLeafWithUncompressedCommitments(cBytes, c1Bytes, c2B copy(result[leafCommitmentOffset:], cBytes[:]) copy(result[leafC1CommitmentOffset:], c1Bytes[:]) copy(result[leafC2CommitmentOffset:], c2Bytes[:]) - copy(result[leafLastEpochOffset:], lastPeriod) + copy(result[leafLastPeriodOffset:], period) copy(result[leafChildrenOffset:], children) } diff --git a/tree_test.go b/tree_test.go index 840d67da..7d2fd87a 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1047,7 +1047,7 @@ func TestEmptyCommitment(t *testing.T) { t.Fatalf("inserting into the original failed: %v", err) } root.Commit() - pe, _, _, _, err := root.GetProofItems(keylist{ffx32KeyTest}, nil, period0) + pe, _, _, _, err := root.GetProofItems(keylist{ffx32KeyTest}, nil) if err != nil { t.Fatal(err) } @@ -1114,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, period0) + _, esses, poas, _, err := root.GetProofItems(keylist{key1, key2, ffx32KeyTest}, nil) if err != nil { t.Fatal(err) } @@ -1172,7 +1172,7 @@ func TestInsertStem(t *testing.T) { values[5] = zeroKeyTest values[192] = fourtyKeyTest - if err := root1.(*InternalNode).InsertValuesAtStem(KeyToStem(fourtyKeyTest), values, 0, 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() @@ -1220,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, 0, 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() @@ -1369,7 +1369,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { r := tree.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, period0, period0, nil) + proof, cis, zis, yis, _ := MakeVerkleMultiProof(tree, nil, keys, period0, nil) vp, statediff, err := SerializeProof(proof) if err != nil { t.Fatal(err) @@ -1390,7 +1390,7 @@ func TestRustBanderwagonBlock48(t *testing.T) { if err != nil { t.Fatal(err) } - pe, _, _, _, err := droot.GetProofItems(keylist(keys), nil, period0) + pe, _, _, _, err := droot.GetProofItems(keylist(keys), nil) if err != nil { t.Fatal(err) } @@ -1841,7 +1841,7 @@ func runRandTest(rt randTest) error { continue } root.Commit() - proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, period0, period0, 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) } diff --git a/unknown.go b/unknown.go index 093b6737..0c42f261 100644 --- a/unknown.go +++ b/unknown.go @@ -55,7 +55,7 @@ func (UnknownNode) Commitment() *Point { return &id } -func (UnknownNode) GetProofItems(keylist, NodeResolverFn, StatePeriod) (*ProofElements, []byte, []Stem, []StatePeriod, error) { +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") } diff --git a/unknown_test.go b/unknown_test.go index d78688b9..9e219128 100644 --- a/unknown_test.go +++ b/unknown_test.go @@ -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, period0); 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 {