Files
verkle/tree_test.go
zeekayandHanzo Dev 2eaa28dc0f Go 1.27.1, and the code it can say more simply
Modules declare go 1.27.1 and the build images follow. go fix applied the
modernizers: any, range over int, and a promoted-field key in a struct
literal. omitzero is not applied, since it changes what the JSON says.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-16 19:14:41 -07:00

516 lines
14 KiB
Go

// 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 <https://unlicense.org>
package verkle
import (
"bytes"
"encoding/binary"
"math/big"
"math/rand"
"sort"
"testing"
"time"
"github.com/luxfi/geth/common"
"github.com/protolambda/go-kzg/bls"
)
var testValue = []byte("hello")
var (
zeroKeyTest = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
oneKeyTest = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")
fourtyKeyTest = common.Hex2Bytes("4000000000000000000000000000000000000000000000000000000000000000")
ffx32KeyTest = common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
)
var s1, lg1 []bls.G1Point
var s2 []bls.G2Point
func TestInsertIntoRoot(t *testing.T) {
root := New(10)
err := root.Insert(zeroKeyTest, testValue)
if err != nil {
t.Fatalf("error inserting: %v", err)
}
leaf, ok := root.(*InternalNode).children[0].(*leafNode)
if !ok {
t.Fatalf("invalid leaf node type %v", root.(*InternalNode).children[0])
}
if !bytes.Equal(leaf.value[:], testValue) {
t.Fatalf("did not find correct value in trie %x != %x", testValue, leaf.value[:])
}
}
func TestInsertTwoLeaves(t *testing.T) {
root := New(10)
root.Insert(zeroKeyTest, testValue)
root.Insert(ffx32KeyTest, testValue)
leaf0, ok := root.(*InternalNode).children[0].(*leafNode)
if !ok {
t.Fatalf("invalid leaf node type %v", root.(*InternalNode).children[0])
}
leaff, ok := root.(*InternalNode).children[1023].(*leafNode)
if !ok {
t.Fatalf("invalid leaf node type %v", root.(*InternalNode).children[1023])
}
if !bytes.Equal(leaf0.value[:], testValue) {
t.Fatalf("did not find correct value in trie %x != %x", testValue, leaf0.value[:])
}
if !bytes.Equal(leaff.value[:], testValue) {
t.Fatalf("did not find correct value in trie %x != %x", testValue, leaff.value[:])
}
}
func TestGetTwoLeaves(t *testing.T) {
root := New(10)
root.Insert(zeroKeyTest, testValue)
root.Insert(ffx32KeyTest, testValue)
val, err := root.Get(zeroKeyTest)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(val, testValue) {
t.Fatalf("got a different value from the tree than expected %x != %x", val, testValue)
}
val, err = root.Get(oneKeyTest)
if err != errValueNotPresent {
t.Fatalf("wrong error type, expected %v, got %v", errValueNotPresent, err)
}
if val != nil {
t.Fatalf("got a different value from the tree than expected %x != nil", val)
}
}
func TestTreeHashing(t *testing.T) {
root := New(10)
root.Insert(zeroKeyTest, testValue)
root.Insert(ffx32KeyTest, testValue)
root.Hash()
}
func TestComputeRootCommitmentThreeLeaves(t *testing.T) {
root := New(10)
root.Insert(zeroKeyTest, testValue)
root.Insert(fourtyKeyTest, testValue)
root.Insert(ffx32KeyTest, testValue)
expected := []byte{137, 46, 141, 157, 55, 243, 191, 123, 197, 83, 9, 229, 155, 145, 185, 155, 171, 133, 195, 118, 100, 193, 107, 202, 170, 6, 51, 189, 99, 62, 244, 70, 199, 253, 80, 218, 171, 68, 89, 136, 222, 166, 5, 209, 92, 255, 140, 164}
comm := root.ComputeCommitment()
got := bls.ToCompressedG1(comm)
if !bytes.Equal(got, expected) {
t.Fatalf("incorrect root commitment %x != %x", got, expected)
}
}
func TestComputeRootCommitmentOnlineThreeLeaves(t *testing.T) {
root := New(10)
root.InsertOrdered(zeroKeyTest, testValue, nil)
root.InsertOrdered(fourtyKeyTest, testValue, nil)
root.InsertOrdered(ffx32KeyTest, testValue, nil)
// This still needs to be called, so that the root
// commitment is calculated.
comm := root.ComputeCommitment()
expected := []byte{137, 46, 141, 157, 55, 243, 191, 123, 197, 83, 9, 229, 155, 145, 185, 155, 171, 133, 195, 118, 100, 193, 107, 202, 170, 6, 51, 189, 99, 62, 244, 70, 199, 253, 80, 218, 171, 68, 89, 136, 222, 166, 5, 209, 92, 255, 140, 164}
got := bls.ToCompressedG1(comm)
if !bytes.Equal(got, expected) {
t.Fatalf("incorrect root commitment %x != %x", got, expected)
}
}
func TestComputeRootCommitmentThreeLeavesDeep(t *testing.T) {
root := New(10)
root.Insert(zeroKeyTest, testValue)
root.Insert(oneKeyTest, testValue)
root.Insert(ffx32KeyTest, testValue)
expected := []byte{180, 224, 116, 69, 8, 16, 10, 46, 12, 87, 199, 139, 17, 157, 123, 95, 113, 9, 180, 227, 72, 13, 125, 20, 35, 52, 98, 119, 121, 181, 253, 151, 253, 0, 62, 206, 64, 49, 8, 93, 140, 128, 232, 208, 102, 248, 81, 206}
comm := root.ComputeCommitment()
got := bls.ToCompressedG1(comm)
if !bytes.Equal(got, expected) {
t.Fatalf("incorrect root commitment %x != %x", got, expected)
}
}
func TestComputeRootCommitmentOnlineThreeLeavesDeep(t *testing.T) {
root := New(10)
root.InsertOrdered(zeroKeyTest, testValue, nil)
root.InsertOrdered(oneKeyTest, testValue, nil)
root.InsertOrdered(ffx32KeyTest, testValue, nil)
expected := []byte{180, 224, 116, 69, 8, 16, 10, 46, 12, 87, 199, 139, 17, 157, 123, 95, 113, 9, 180, 227, 72, 13, 125, 20, 35, 52, 98, 119, 121, 181, 253, 151, 253, 0, 62, 206, 64, 49, 8, 93, 140, 128, 232, 208, 102, 248, 81, 206}
comm := root.ComputeCommitment()
got := bls.ToCompressedG1(comm)
if !bytes.Equal(got, expected) {
t.Fatalf("incorrect root commitment %x != %x", got, expected)
}
}
func TestComputeRootCommitmentOnlineThreeLeavesFlush(t *testing.T) {
flush := make(chan FlushableNode)
go func() {
root := New(10)
root.InsertOrdered(zeroKeyTest, testValue, flush)
root.InsertOrdered(fourtyKeyTest, testValue, flush)
root.InsertOrdered(ffx32KeyTest, testValue, flush)
root.(*InternalNode).Flush(flush)
close(flush)
}()
count := 0
for f := range flush {
_, isLeaf := f.Node.(*leafNode)
_, isInternal := f.Node.(*InternalNode)
if !isLeaf && !isInternal {
t.Fatal("invalid node type received, expected leaf")
}
count++
}
if count != 4 {
t.Fatalf("incorrect number of flushed leaves 4 != %d", count)
}
}
func TestComputeRootCommitmentTwoLeaves(t *testing.T) {
root := New(10)
root.Insert(zeroKeyTest, testValue)
root.Insert(ffx32KeyTest, testValue)
expected := []byte{178, 195, 197, 132, 158, 141, 115, 80, 222, 187, 37, 145, 15, 184, 242, 86, 101, 164, 144, 51, 239, 90, 232, 100, 78, 178, 253, 145, 36, 168, 30, 75, 100, 185, 100, 14, 198, 48, 14, 95, 3, 252, 185, 73, 183, 195, 153, 44}
comm := root.ComputeCommitment()
got := bls.ToCompressedG1(comm)
if !bytes.Equal(got, expected) {
t.Fatalf("incorrect root commitment %x != %x", got, expected)
}
}
func TestHashToFrTrailingZeroBytes(t *testing.T) {
modulus, ok := big.NewInt(0).SetString("52435875175126190479447740508185965837690552500527637822603658699938581184513", 10)
if !ok {
panic("could not get modulus")
}
h := common.HexToHash("c79e576e0f534a5bbed66b32e5022a9d624b4415779b369a62b2e7a6c3d8e000")
var out bls.Fr
hashToFr(&out, h, modulus)
h2 := common.HexToHash("c79e576e0f534a5bbed66b32e5022a9d624b4415779b369a62b2e7a6c3d8e000")
var expected bls.Fr
bls.FrFrom32(&expected, h2)
if !bls.EqualFr(&out, &expected) {
t.Fatalf("incorrect value received, got %x != %x", out, expected)
}
}
func TestOffset2Key8BitsWide(t *testing.T) {
key := common.Hex2Bytes("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
for i := range 32 {
childId := offset2Key(key, i*8, 8)
if childId != uint(i) {
t.Fatalf("error getting child number in key %d != %d", childId, i)
}
}
}
func TestOffset2Key10BitsWide(t *testing.T) {
key := common.Hex2Bytes("00001008030100501807020090280b0300d0380f040110481305015058170601")
for i := range 25 {
childId := offset2Key(key, i*10, 10)
if childId != uint(i) {
t.Fatalf("error getting child number in key %d != %d", childId, i)
}
}
if childIdx := offset2Key(key, 250, 10); childIdx != 16 {
t.Fatalf("error getting last child number in key %d != %d", childIdx, 16)
}
}
func TestComputeRootCommitmentTwoLeaves256(t *testing.T) {
root := New(8)
root.Insert(zeroKeyTest, testValue)
root.Insert(ffx32KeyTest, testValue)
expected := common.Hex2Bytes("84c699b01f33e5af95d4dd3a09257c442606a0d812600bdfcc06f45be6eba3cc767fa837e47e5c1ac51956424c9258dd")
comm := root.ComputeCommitment()
got := bls.ToCompressedG1(comm)
if !bytes.Equal(got, expected) {
t.Fatalf("incorrect root commitment %x != %x", got, expected)
}
}
func TestInsertVsOrdered(t *testing.T) {
n := 10000
rand.Seed(time.Now().UnixNano())
value := []byte("value")
keys := randomKeys(n)
sortedKeys := make([][]byte, n)
copy(sortedKeys[:], keys[:])
sort.Slice(sortedKeys, func(i, j int) bool { return bytes.Compare(sortedKeys[i], sortedKeys[j]) < 0 })
root1 := New(10)
for _, k := range keys {
root1.Insert(k, value)
}
root2 := New(10)
for _, k := range sortedKeys {
root2.InsertOrdered(k, value, nil)
}
h1 := root1.Hash().Bytes()
h2 := root2.Hash().Bytes()
if !bytes.Equal(h1, h2) {
t.Error("Insert and InsertOrdered produce different trees")
}
}
func TestFlush1kLeaves(t *testing.T) {
n := 1000
rand.Seed(time.Now().UnixNano())
keys := randomKeysSorted(n)
value := []byte("value")
flush := make(chan FlushableNode)
go func() {
root := New(10)
for _, k := range keys {
root.InsertOrdered(k, value, flush)
}
root.(*InternalNode).Flush(flush)
close(flush)
}()
count := 0
leaves := 0
for f := range flush {
_, isLeaf := f.Node.(*leafNode)
_, isInternal := f.Node.(*InternalNode)
if !isLeaf && !isInternal {
t.Fatal("invalid node type received, expected leaf")
}
if isLeaf {
leaves++
}
count++
}
if leaves != n {
t.Errorf("number of flushed leaves incorrect. Expected %d got %d\n", n, leaves)
}
if count <= n {
t.Errorf("number of flushed nodes incorrect. Expected %d got %d\n", n, leaves)
}
}
func TestCachedCommitment(t *testing.T) {
value := []byte("value")
key1 := common.Hex2Bytes("0105000000000000000000000000000000000000000000000000000000000000")
key2 := common.Hex2Bytes("0107000000000000000000000000000000000000000000000000000000000000")
key3 := common.Hex2Bytes("0405000000000000000000000000000000000000000000000000000000000000")
key4 := common.Hex2Bytes("0407000000000000000000000000000000000000000000000000000000000000")
tree := New(8)
tree.Insert(key1, value)
tree.Insert(key2, value)
tree.Insert(key3, value)
tree.ComputeCommitment()
if tree.(*InternalNode).commitment == nil {
t.Error("root has not cached commitment")
}
tree.Insert(key4, value)
if tree.(*InternalNode).commitment != nil {
t.Error("root has stale commitment")
}
if tree.(*InternalNode).children[4].(*InternalNode).commitment != nil {
t.Error("internal node has stale commitment")
}
if tree.(*InternalNode).children[1].(*InternalNode).commitment == nil {
t.Error("internal node has mistakenly cleared cached commitment")
}
}
func BenchmarkCommit1kLeaves(b *testing.B) {
benchmarkCommitNLeaves(b, 1000)
}
func BenchmarkCommit10kLeaves(b *testing.B) {
benchmarkCommitNLeaves(b, 10000)
}
func BenchmarkCommitFullNode(b *testing.B) {
value := []byte("value")
keys := make([][]byte, 1024)
for i := range 1024 {
key := make([]byte, 32)
binary.BigEndian.PutUint16(key[:2], uint16(i)<<6)
keys[i] = key
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
root := New(10)
for _, k := range keys {
if err := root.Insert(k, value); err != nil {
b.Fatal(err)
}
}
root.ComputeCommitment()
}
}
func benchmarkCommitNLeaves(b *testing.B, n int) {
type kv struct {
k []byte
v []byte
}
kvs := make([]kv, n)
sortedKVs := make([]kv, n)
rand.Seed(time.Now().UnixNano())
for i := range n {
key := make([]byte, 32)
val := make([]byte, 32)
rand.Read(key)
rand.Read(val)
kvs[i] = kv{k: key, v: val}
sortedKVs[i] = kv{k: key, v: val}
}
// InsertOrder assumes keys are sorted
sortKVs := func(src []kv) {
sort.Slice(src, func(i, j int) bool { return bytes.Compare(src[i].k, src[j].k) < 0 })
}
sortKVs(sortedKVs)
b.Run("insert", func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
root := New(10)
for _, el := range kvs {
if err := root.Insert(el.k, el.v); err != nil {
b.Error(err)
}
}
root.ComputeCommitment()
}
})
b.Run("insertOrdered", func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
root := New(10)
for _, el := range sortedKVs {
if err := root.InsertOrdered(el.k, el.v, nil); err != nil {
b.Fatal(err)
}
}
root.ComputeCommitment()
}
})
}
func BenchmarkModifyLeaves(b *testing.B) {
rand.Seed(time.Now().UnixNano())
n := 200000
toEdit := 10000
val := []byte{0}
keys := make([][]byte, n)
root := New(10)
for i := range n {
key := make([]byte, 32)
rand.Read(key)
keys[i] = key
root.Insert(key, val)
}
root.ComputeCommitment()
b.ResetTimer()
b.ReportAllocs()
val = make([]byte, 4)
for i := 0; i < b.N; i++ {
binary.BigEndian.PutUint32(val, uint32(i))
for range toEdit {
k := keys[rand.Intn(n)]
if err := root.Insert(k, val); err != nil {
b.Error(err)
}
}
root.ComputeCommitment()
}
}
func randomKeys(n int) [][]byte {
keys := make([][]byte, n)
for i := range n {
key := make([]byte, 32)
rand.Read(key)
keys[i] = key
}
return keys
}
func randomKeysSorted(n int) [][]byte {
keys := randomKeys(n)
sort.Slice(keys, func(i, j int) bool { return bytes.Compare(keys[i], keys[j]) < 0 })
return keys
}