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:
Rupam Dey
2024-08-14 18:16:07 +05:30
committed by GitHub
parent 0c6a068fd5
commit 74ddb84e0a
7 changed files with 471 additions and 0 deletions

View File

@@ -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",

View File

@@ -503,6 +503,135 @@ func hydrateBeaconBlockBody() *eth.BeaconBlockBody {
}
}
func hydrateBeaconBlockBodyAltair() *eth.BeaconBlockBodyAltair {
return &eth.BeaconBlockBodyAltair{
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
Graffiti: make([]byte, fieldparams.RootLength),
Eth1Data: &eth.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
},
SyncAggregate: &eth.SyncAggregate{
SyncCommitteeBits: make([]byte, 64),
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
},
}
}
func hydrateBeaconBlockBodyBellatrix() *eth.BeaconBlockBodyBellatrix {
return &eth.BeaconBlockBodyBellatrix{
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
Graffiti: make([]byte, fieldparams.RootLength),
Eth1Data: &eth.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
},
SyncAggregate: &eth.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 &eth.BeaconBlockBodyCapella{
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
Graffiti: make([]byte, fieldparams.RootLength),
Eth1Data: &eth.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
},
SyncAggregate: &eth.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 &eth.BeaconBlockBodyDeneb{
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
Graffiti: make([]byte, fieldparams.RootLength),
Eth1Data: &eth.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
},
SyncAggregate: &eth.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 &eth.BeaconBlockBodyElectra{
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
Graffiti: make([]byte, fieldparams.RootLength),
Eth1Data: &eth.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
},
SyncAggregate: &eth.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)

View 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
}

View 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)
}