mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
feat: implement `ComputeFieldRootsForBlockBody` function (#14278)
* feat: (WIP)implement ``ComputeFieldRootsForBlockBody`` function to compute roots of fields in BlockBody * calculate field roots upto ``deposits`` * add some required constants fix some errors * implement ``ComputeFieldRootsForBlockBody`` function for all fields in ``BeaconBlockBody`` * populate `fieldRoots` based on block body version * fix constants * `bazel run //:gazelle -- fix` * remove nested `if`s * formatting 1 Co-authored-by: Radosław Kapka <radoslaw.kapka@gmail.com> * formatting 2 Co-authored-by: Radosław Kapka <radoslaw.kapka@gmail.com> * remove unrequired check Co-authored-by: Radosław Kapka <radoslaw.kapka@gmail.com> * update naming 1 Co-authored-by: Radosław Kapka <radoslaw.kapka@gmail.com> * update function name * add test for Phase0 block body * update Phase0 test * update phase0 test without setting any fields * fix some errors in Phase0 test * don't set fields * add altair test * add tests upto Electra * fix the function for deneb and newer forks * update dependencies * add checks to ensure interface implements the asserted type --------- Co-authored-by: Radosław Kapka <radoslaw.kapka@gmail.com> Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -8,6 +8,7 @@ go_library(
|
||||
"get_payload.go",
|
||||
"getters.go",
|
||||
"kzg.go",
|
||||
"proofs.go",
|
||||
"proto.go",
|
||||
"roblob.go",
|
||||
"roblock.go",
|
||||
@@ -23,6 +24,7 @@ go_library(
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
"//crypto/hash/htr:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//encoding/ssz:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
@@ -32,6 +34,7 @@ go_library(
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_fastssz//:go_default_library",
|
||||
"@com_github_prysmaticlabs_gohashtree//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -43,6 +46,7 @@ go_test(
|
||||
"factory_test.go",
|
||||
"getters_test.go",
|
||||
"kzg_test.go",
|
||||
"proofs_test.go",
|
||||
"proto_test.go",
|
||||
"roblob_test.go",
|
||||
"roblock_test.go",
|
||||
|
||||
@@ -503,6 +503,135 @@ func hydrateBeaconBlockBody() *eth.BeaconBlockBody {
|
||||
}
|
||||
}
|
||||
|
||||
func hydrateBeaconBlockBodyAltair() *eth.BeaconBlockBodyAltair {
|
||||
return ð.BeaconBlockBodyAltair{
|
||||
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
|
||||
Graffiti: make([]byte, fieldparams.RootLength),
|
||||
Eth1Data: ð.Eth1Data{
|
||||
DepositRoot: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
SyncAggregate: ð.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, 64),
|
||||
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func hydrateBeaconBlockBodyBellatrix() *eth.BeaconBlockBodyBellatrix {
|
||||
return ð.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
|
||||
Graffiti: make([]byte, fieldparams.RootLength),
|
||||
Eth1Data: ð.Eth1Data{
|
||||
DepositRoot: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
SyncAggregate: ð.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, 64),
|
||||
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
ExecutionPayload: &pb.ExecutionPayload{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
ExtraData: make([]byte, 0),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func hydrateBeaconBlockBodyCapella() *eth.BeaconBlockBodyCapella {
|
||||
return ð.BeaconBlockBodyCapella{
|
||||
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
|
||||
Graffiti: make([]byte, fieldparams.RootLength),
|
||||
Eth1Data: ð.Eth1Data{
|
||||
DepositRoot: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
SyncAggregate: ð.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength),
|
||||
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
ExecutionPayload: &pb.ExecutionPayloadCapella{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
ExtraData: make([]byte, 0),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
Withdrawals: make([]*pb.Withdrawal, 0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func hydrateBeaconBlockBodyDeneb() *eth.BeaconBlockBodyDeneb {
|
||||
return ð.BeaconBlockBodyDeneb{
|
||||
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
|
||||
Graffiti: make([]byte, fieldparams.RootLength),
|
||||
Eth1Data: ð.Eth1Data{
|
||||
DepositRoot: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
SyncAggregate: ð.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength),
|
||||
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
ExecutionPayload: &pb.ExecutionPayloadDeneb{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
ExtraData: make([]byte, 0),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
Withdrawals: make([]*pb.Withdrawal, 0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func hydrateBeaconBlockBodyElectra() *eth.BeaconBlockBodyElectra {
|
||||
return ð.BeaconBlockBodyElectra{
|
||||
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
|
||||
Graffiti: make([]byte, fieldparams.RootLength),
|
||||
Eth1Data: ð.Eth1Data{
|
||||
DepositRoot: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
SyncAggregate: ð.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength),
|
||||
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
ExecutionPayload: &pb.ExecutionPayloadElectra{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, 256),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
ExtraData: make([]byte, 0),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
Transactions: make([][]byte, 0),
|
||||
Withdrawals: make([]*pb.Withdrawal, 0),
|
||||
DepositRequests: make([]*pb.DepositRequest, 0),
|
||||
WithdrawalRequests: make([]*pb.WithdrawalRequest, 0),
|
||||
ConsolidationRequests: make([]*pb.ConsolidationRequest, 0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreElectraFailsInterfaceAssertion(t *testing.T) {
|
||||
var epd interfaces.ExecutionData = &executionPayloadDeneb{}
|
||||
_, ok := epd.(interfaces.ExecutionDataElectra)
|
||||
|
||||
174
consensus-types/blocks/proofs.go
Normal file
174
consensus-types/blocks/proofs.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package blocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/hash/htr"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
func ComputeBlockBodyFieldRoots(ctx context.Context, blockBody *BeaconBlockBody) ([][]byte, error) {
|
||||
_, span := trace.StartSpan(ctx, "blocks.ComputeBlockBodyFieldRoots")
|
||||
defer span.End()
|
||||
|
||||
if blockBody == nil {
|
||||
return nil, errNilBlockBody
|
||||
}
|
||||
|
||||
var fieldRoots [][]byte
|
||||
switch blockBody.version {
|
||||
case version.Phase0:
|
||||
fieldRoots = make([][]byte, 8)
|
||||
case version.Altair:
|
||||
fieldRoots = make([][]byte, 9)
|
||||
case version.Bellatrix:
|
||||
fieldRoots = make([][]byte, 10)
|
||||
case version.Capella:
|
||||
fieldRoots = make([][]byte, 11)
|
||||
case version.Deneb:
|
||||
fieldRoots = make([][]byte, 12)
|
||||
case version.Electra:
|
||||
fieldRoots = make([][]byte, 12)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown block body version %s", version.String(blockBody.version))
|
||||
}
|
||||
|
||||
for i := range fieldRoots {
|
||||
fieldRoots[i] = make([]byte, 32)
|
||||
}
|
||||
|
||||
// Randao Reveal
|
||||
randao := blockBody.RandaoReveal()
|
||||
root, err := ssz.MerkleizeByteSliceSSZ(randao[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[0], root[:])
|
||||
|
||||
// eth1_data
|
||||
eth1 := blockBody.Eth1Data()
|
||||
root, err = eth1.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[1], root[:])
|
||||
|
||||
// graffiti
|
||||
root = blockBody.Graffiti()
|
||||
copy(fieldRoots[2], root[:])
|
||||
|
||||
// Proposer slashings
|
||||
ps := blockBody.ProposerSlashings()
|
||||
root, err = ssz.MerkleizeListSSZ(ps, params.BeaconConfig().MaxProposerSlashings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[3], root[:])
|
||||
|
||||
// Attester slashings
|
||||
as := blockBody.AttesterSlashings()
|
||||
bodyVersion := blockBody.Version()
|
||||
if bodyVersion < version.Electra {
|
||||
root, err = ssz.MerkleizeListSSZ(as, params.BeaconConfig().MaxAttesterSlashings)
|
||||
} else {
|
||||
root, err = ssz.MerkleizeListSSZ(as, params.BeaconConfig().MaxAttesterSlashingsElectra)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[4], root[:])
|
||||
|
||||
// Attestations
|
||||
att := blockBody.Attestations()
|
||||
if bodyVersion < version.Electra {
|
||||
root, err = ssz.MerkleizeListSSZ(att, params.BeaconConfig().MaxAttestations)
|
||||
} else {
|
||||
root, err = ssz.MerkleizeListSSZ(att, params.BeaconConfig().MaxAttestationsElectra)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[5], root[:])
|
||||
|
||||
// Deposits
|
||||
dep := blockBody.Deposits()
|
||||
root, err = ssz.MerkleizeListSSZ(dep, params.BeaconConfig().MaxDeposits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[6], root[:])
|
||||
|
||||
// Voluntary Exits
|
||||
ve := blockBody.VoluntaryExits()
|
||||
root, err = ssz.MerkleizeListSSZ(ve, params.BeaconConfig().MaxVoluntaryExits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[7], root[:])
|
||||
|
||||
if blockBody.version >= version.Altair {
|
||||
// Sync Aggregate
|
||||
sa, err := blockBody.SyncAggregate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err = sa.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[8], root[:])
|
||||
}
|
||||
|
||||
if blockBody.version >= version.Bellatrix {
|
||||
// Execution Payload
|
||||
ep, err := blockBody.Execution()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err = ep.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[9], root[:])
|
||||
}
|
||||
|
||||
if blockBody.version >= version.Capella {
|
||||
// BLS Changes
|
||||
bls, err := blockBody.BLSToExecutionChanges()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err = ssz.MerkleizeListSSZ(bls, params.BeaconConfig().MaxBlsToExecutionChanges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(fieldRoots[10], root[:])
|
||||
}
|
||||
|
||||
if blockBody.version >= version.Deneb {
|
||||
// KZG commitments
|
||||
roots := make([][32]byte, len(blockBody.blobKzgCommitments))
|
||||
for i, commitment := range blockBody.blobKzgCommitments {
|
||||
chunks, err := ssz.PackByChunk([][]byte{commitment})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roots[i] = htr.VectorizedSha256(chunks)[0]
|
||||
}
|
||||
commitmentsRoot, err := ssz.BitwiseMerkleize(roots, uint64(len(roots)), 4096)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(length[:8], uint64(len(roots)))
|
||||
root = ssz.MixInLength(commitmentsRoot, length)
|
||||
copy(fieldRoots[11], root[:])
|
||||
}
|
||||
|
||||
return fieldRoots, nil
|
||||
}
|
||||
147
consensus-types/blocks/proofs_test.go
Normal file
147
consensus-types/blocks/proofs_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package blocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v5/container/trie"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
)
|
||||
|
||||
func TestComputeBlockBodyFieldRoots_Phase0(t *testing.T) {
|
||||
blockBodyPhase0 := hydrateBeaconBlockBody()
|
||||
i, err := NewBeaconBlockBody(blockBodyPhase0)
|
||||
require.NoError(t, err)
|
||||
|
||||
b, ok := i.(*BeaconBlockBody)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
fieldRoots, err := ComputeBlockBodyFieldRoots(context.Background(), b)
|
||||
require.NoError(t, err)
|
||||
trie, err := trie.GenerateTrieFromItems(fieldRoots, 3)
|
||||
require.NoError(t, err)
|
||||
layers := trie.ToProto().GetLayers()
|
||||
|
||||
hash := layers[len(layers)-1].Layer[0]
|
||||
require.NoError(t, err)
|
||||
|
||||
correctHash, err := b.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, correctHash[:], hash)
|
||||
}
|
||||
|
||||
func TestComputeBlockBodyFieldRoots_Altair(t *testing.T) {
|
||||
blockBodyAltair := hydrateBeaconBlockBodyAltair()
|
||||
i, err := NewBeaconBlockBody(blockBodyAltair)
|
||||
require.NoError(t, err)
|
||||
|
||||
b, ok := i.(*BeaconBlockBody)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
fieldRoots, err := ComputeBlockBodyFieldRoots(context.Background(), b)
|
||||
require.NoError(t, err)
|
||||
trie, err := trie.GenerateTrieFromItems(fieldRoots, 4)
|
||||
require.NoError(t, err)
|
||||
layers := trie.ToProto().GetLayers()
|
||||
|
||||
hash := layers[len(layers)-1].Layer[0]
|
||||
require.NoError(t, err)
|
||||
|
||||
correctHash, err := b.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, correctHash[:], hash)
|
||||
}
|
||||
|
||||
func TestComputeBlockBodyFieldRoots_Bellatrix(t *testing.T) {
|
||||
blockBodyBellatrix := hydrateBeaconBlockBodyBellatrix()
|
||||
i, err := NewBeaconBlockBody(blockBodyBellatrix)
|
||||
require.NoError(t, err)
|
||||
|
||||
b, ok := i.(*BeaconBlockBody)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
fieldRoots, err := ComputeBlockBodyFieldRoots(context.Background(), b)
|
||||
require.NoError(t, err)
|
||||
trie, err := trie.GenerateTrieFromItems(fieldRoots, 4)
|
||||
require.NoError(t, err)
|
||||
layers := trie.ToProto().GetLayers()
|
||||
|
||||
hash := layers[len(layers)-1].Layer[0]
|
||||
require.NoError(t, err)
|
||||
|
||||
correctHash, err := b.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, correctHash[:], hash)
|
||||
}
|
||||
|
||||
func TestComputeBlockBodyFieldRoots_Capella(t *testing.T) {
|
||||
blockBodyCapella := hydrateBeaconBlockBodyCapella()
|
||||
i, err := NewBeaconBlockBody(blockBodyCapella)
|
||||
require.NoError(t, err)
|
||||
|
||||
b, ok := i.(*BeaconBlockBody)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
fieldRoots, err := ComputeBlockBodyFieldRoots(context.Background(), b)
|
||||
require.NoError(t, err)
|
||||
trie, err := trie.GenerateTrieFromItems(fieldRoots, 4)
|
||||
require.NoError(t, err)
|
||||
layers := trie.ToProto().GetLayers()
|
||||
|
||||
hash := layers[len(layers)-1].Layer[0]
|
||||
require.NoError(t, err)
|
||||
|
||||
correctHash, err := b.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, correctHash[:], hash)
|
||||
}
|
||||
|
||||
func TestComputeBlockBodyFieldRoots_Deneb(t *testing.T) {
|
||||
blockBodyDeneb := hydrateBeaconBlockBodyDeneb()
|
||||
i, err := NewBeaconBlockBody(blockBodyDeneb)
|
||||
require.NoError(t, err)
|
||||
|
||||
b, ok := i.(*BeaconBlockBody)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
fieldRoots, err := ComputeBlockBodyFieldRoots(context.Background(), b)
|
||||
require.NoError(t, err)
|
||||
trie, err := trie.GenerateTrieFromItems(fieldRoots, 4)
|
||||
require.NoError(t, err)
|
||||
layers := trie.ToProto().GetLayers()
|
||||
|
||||
hash := layers[len(layers)-1].Layer[0]
|
||||
require.NoError(t, err)
|
||||
|
||||
correctHash, err := b.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, correctHash[:], hash)
|
||||
}
|
||||
|
||||
func TestComputeBlockBodyFieldRoots_Electra(t *testing.T) {
|
||||
blockBodyElectra := hydrateBeaconBlockBodyElectra()
|
||||
i, err := NewBeaconBlockBody(blockBodyElectra)
|
||||
require.NoError(t, err)
|
||||
|
||||
b, ok := i.(*BeaconBlockBody)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
fieldRoots, err := ComputeBlockBodyFieldRoots(context.Background(), b)
|
||||
require.NoError(t, err)
|
||||
trie, err := trie.GenerateTrieFromItems(fieldRoots, 4)
|
||||
require.NoError(t, err)
|
||||
layers := trie.ToProto().GetLayers()
|
||||
|
||||
hash := layers[len(layers)-1].Layer[0]
|
||||
require.NoError(t, err)
|
||||
|
||||
correctHash, err := b.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, correctHash[:], hash)
|
||||
}
|
||||
Reference in New Issue
Block a user