diff --git a/chain/block.go b/chain/block.go index bdbce99364..35e748af90 100644 --- a/chain/block.go +++ b/chain/block.go @@ -4,8 +4,11 @@ package chain import ( + "bytes" "context" + "crypto/sha256" "encoding/binary" + "errors" "fmt" "time" @@ -17,6 +20,7 @@ import ( "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/x/merkledb" + "github.com/cbergoon/merkletree" "go.opentelemetry.io/otel/attribute" oteltrace "go.opentelemetry.io/otel/trace" "go.uber.org/zap" @@ -35,13 +39,36 @@ var ( _ block.StateSummary = &SyncableBlock{} ) +type TrieItem struct { + x []byte +} + +// CalculateHash hashes the values of a TestContent +func (t TrieItem) CalculateHash() ([]byte, error) { + h := sha256.New() + if _, err := h.Write([]byte(t.x)); err != nil { + return nil, err + } + + return h.Sum(nil), nil +} + +// Equals tests for equality of two Contents +func (t TrieItem) Equals(other merkletree.Content) (bool, error) { + otherTI, ok := other.(TrieItem) + if !ok { + return false, errors.New("value is not of type TestContent") + } + return bytes.Equal(t.x, otherTI.x), nil +} + type StatefulBlock struct { Prnt ids.ID `json:"parent"` Tmstmp int64 `json:"timestamp"` Hght uint64 `json:"height"` - Txs []*Transaction `json:"txs"` - TxsRoot []byte `json:"txsRoot"` + Txs []*Transaction `json:"txs"` + BlockHash []byte `json:"txsRoot"` // StateRoot is the root of the post-execution state // of [Prnt]. @@ -285,12 +312,32 @@ func (b *StatelessBlock) initializeBuilt( b.results = results b.feeManager = feeManager b.txsSet = set.NewSet[ids.ID](len(b.Txs)) + + // block hash generation + var items2prove []merkletree.Content + // state root + items2prove = append(items2prove, TrieItem{x: b.StateRoot[:]}) + // transactions for _, tx := range b.Txs { + items2prove = append(items2prove, TrieItem{x: tx.Bytes()}) b.txsSet.Add(tx.ID()) if tx.WarpMessage != nil { b.containsWarp = true } } + // transaction results + for _, result := range b.results { + items2prove = append(items2prove, TrieItem{x: result.Output}) + } + + trie, err := merkletree.NewTree(items2prove) + // will only happen when no item is provided + // never happen since at least one item(state root) is put in + if err != nil { + return err + } + b.BlockHash = trie.MerkleRoot() + return nil } diff --git a/chain/builder.go b/chain/builder.go index 2ae2e6701c..689d387048 100644 --- a/chain/builder.go +++ b/chain/builder.go @@ -4,7 +4,6 @@ package chain import ( - "bytes" "context" "encoding/binary" "errors" @@ -18,12 +17,9 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/set" - "github.com/cbergoon/merkletree" "go.opentelemetry.io/otel/attribute" "go.uber.org/zap" - "crypto/sha256" - "github.com/ava-labs/hypersdk/executor" "github.com/ava-labs/hypersdk/keys" "github.com/ava-labs/hypersdk/tstate" @@ -38,29 +34,6 @@ const ( stopBuildingThreshold = 2_048 // units ) -type TestContent struct { - x []byte -} - -// CalculateHash hashes the values of a TestContent -func (t TestContent) CalculateHash() ([]byte, error) { - h := sha256.New() - if _, err := h.Write([]byte(t.x)); err != nil { - return nil, err - } - - return h.Sum(nil), nil -} - -// Equals tests for equality of two Contents -func (t TestContent) Equals(other merkletree.Content) (bool, error) { - otherTC, ok := other.(TestContent) - if !ok { - return false, errors.New("value is not of type TestContent") - } - return bytes.Equal(t.x, otherTC.x), nil -} - var errBlockFull = errors.New("block full") func HandlePreExecute(log logging.Logger, err error) bool { @@ -155,8 +128,6 @@ func BuildBlock( txsAttempted = 0 results = []*Result{} - txSuccessList []merkletree.Content - vdrState = vm.ValidatorState() sm = vm.StateManager() @@ -165,9 +136,6 @@ func BuildBlock( prepareStreamLock sync.Mutex ) - // prevent empty list - txSuccessList = append(txSuccessList, TestContent{x: make([]byte, 32)}) - // Batch fetch items from mempool to unblock incoming RPC/Gossip traffic mempool.StartStreaming(ctx) b.Txs = []*Transaction{} @@ -413,7 +381,6 @@ func BuildBlock( tsv.Commit() b.Txs = append(b.Txs, tx) results = append(results, result) - txSuccessList = append(txSuccessList, TestContent{x: tx.Bytes()}) if tx.WarpMessage != nil { if warpErr == nil { // Add a bit if the warp message was verified @@ -448,13 +415,6 @@ func BuildBlock( } } - trie, err := merkletree.NewTree(txSuccessList) - if err != nil { - b.vm.Logger().Warn("unable to build trie", zap.Error(err)) - return nil, err - } - b.TxsRoot = trie.MerkleRoot() - // Wait for stream preparation to finish to make // sure all transactions are returned to the mempool. go func() { diff --git a/examples/tokenvm/go.mod b/examples/tokenvm/go.mod index 4db9a6ba70..61839ce700 100644 --- a/examples/tokenvm/go.mod +++ b/examples/tokenvm/go.mod @@ -28,6 +28,7 @@ require ( github.com/bep/debounce v1.2.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect + github.com/cbergoon/merkletree v0.2.0 // indirect github.com/cenkalti/backoff/v4 v4.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect diff --git a/examples/tokenvm/go.sum b/examples/tokenvm/go.sum index c08f58deec..25472becf6 100644 --- a/examples/tokenvm/go.sum +++ b/examples/tokenvm/go.sum @@ -97,6 +97,8 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cbergoon/merkletree v0.2.0 h1:Bttqr3OuoiZEo4ed1L7fTasHka9II+BF9fhBfbNEEoQ= +github.com/cbergoon/merkletree v0.2.0/go.mod h1:5c15eckUgiucMGDOCanvalj/yJnD+KAZj1qyJtRW5aM= github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=