diff --git a/merkle/merkle.go b/merkle/merkle.go new file mode 100644 index 0000000000..49383945db --- /dev/null +++ b/merkle/merkle.go @@ -0,0 +1,42 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package merkle + +import ( + "context" + + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/database/memdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/x/merkledb" +) + +// Generate merkle root for a set of items +func GenerateMerkleRoot(ctx context.Context, config merkledb.Config, merkleItems [][]byte, consumeBytes bool) (ids.ID, merkledb.MerkleDB, error) { + batchOps := make([]database.BatchOp, 0, len(merkleItems)) + + for _, item := range merkleItems { + batchOps = append(batchOps, database.BatchOp{ + Key: item, + Value: item, + }) + } + + db, err := merkledb.New(ctx, memdb.New(), config) + if err != nil { + return ids.Empty, nil, err + } + + view, err := db.NewView(ctx, merkledb.ViewChanges{BatchOps: batchOps, ConsumeBytes: consumeBytes}) + if err != nil { + return ids.Empty, nil, err + } + + root, err := view.GetMerkleRoot(ctx) + if err != nil { + return ids.Empty, nil, err + } + + return root, db, nil +} diff --git a/merkle/merkle_test.go b/merkle/merkle_test.go new file mode 100644 index 0000000000..9c3fc7d630 --- /dev/null +++ b/merkle/merkle_test.go @@ -0,0 +1,64 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package merkle + +import ( + "context" + "crypto/rand" + "strconv" + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/trace" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/x/merkledb" + "github.com/stretchr/testify/require" +) + +var ( + resRoot ids.ID + resDB merkledb.MerkleDB + resErr error +) + +func BenchmarkMerkleTxRoot(b *testing.B) { + for _, size := range []int{10, 100, 1000, 10000} { + ctx := context.TODO() + tracer := trace.Noop + merkleItems := make([][]byte, 0, size) + for i := 0; i < size; i++ { + item := make([]byte, 32) + _, err := rand.Read(item) + require.NoError(b, err) + merkleItems = append(merkleItems, item) + } + + var root ids.ID + var db merkledb.MerkleDB + var err error + + defaultConfig := merkledb.Config{ + BranchFactor: merkledb.BranchFactor16, + HistoryLength: 100, + IntermediateNodeCacheSize: units.MiB, + ValueNodeCacheSize: units.MiB, + Tracer: tracer, + } + + b.Run(strconv.Itoa(size), func(b *testing.B) { + for n := 0; n < b.N; n++ { + for i := 0; i < size; i++ { + root, db, err = GenerateMerkleRoot(ctx, defaultConfig, merkleItems, false) + } + } + }) + + // avoid compiler optimizations to cancel out the bench + resRoot = root + resDB = db + resErr = err + } + + b.ReportAllocs() +}