Refactor light client functions (#14434)

* Use correct types in light client functions

* conversions

* more refactoring

* test fixes

* changelog

* error fix

* revert test changes

* revert test skip

* Update api/server/structs/conversions_lightclient.go

Co-authored-by: Rupam Dey <117000803+rupam-04@users.noreply.github.com>

* use BlockToLightClientHeader

* reviewer suggestion

* Revert "use BlockToLightClientHeader"

This reverts commit f3df56ded5.

---------

Co-authored-by: Rupam Dey <117000803+rupam-04@users.noreply.github.com>
This commit is contained in:
Radosław Kapka
2024-09-13 18:08:28 -04:00
committed by GitHub
parent ed6f69e868
commit 7ac3c01b5b
15 changed files with 546 additions and 590 deletions

View File

@@ -36,6 +36,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
- Clean up `ProposeBlock` for validator client to reduce cognitive scoring and enable further changes. - Clean up `ProposeBlock` for validator client to reduce cognitive scoring and enable further changes.
- Updated k8s-io/client-go to v0.30.4 and k8s-io/apimachinery to v0.30.4 - Updated k8s-io/client-go to v0.30.4 and k8s-io/apimachinery to v0.30.4
- Migrated tracing library from opencensus to opentelemetry for both the beacon node and validator. - Migrated tracing library from opencensus to opentelemetry for both the beacon node and validator.
- Refactored light client code to make it more readable and make future PRs easier.
### Deprecated ### Deprecated
- `--disable-grpc-gateway` flag is deprecated due to grpc gateway removal. - `--disable-grpc-gateway` flag is deprecated due to grpc gateway removal.

View File

@@ -34,6 +34,9 @@ go_library(
"//encoding/bytesutil:go_default_library", "//encoding/bytesutil:go_default_library",
"//math:go_default_library", "//math:go_default_library",
"//proto/engine/v1:go_default_library", "//proto/engine/v1:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/eth/v2:go_default_library",
"//proto/migration:go_default_library",
"//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",

View File

@@ -1,3 +1,122 @@
package structs package structs
// import (
"encoding/json"
"fmt"
"strconv"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
v1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1"
v2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v5/proto/migration"
)
func LightClientUpdateFromConsensus(update *v2.LightClientUpdate) (*LightClientUpdate, error) {
attestedHeader, err := lightClientHeaderContainerToJSON(update.AttestedHeader)
if err != nil {
return nil, errors.Wrap(err, "could not marshal attested light client header")
}
finalizedHeader, err := lightClientHeaderContainerToJSON(update.FinalizedHeader)
if err != nil {
return nil, errors.Wrap(err, "could not marshal finalized light client header")
}
return &LightClientUpdate{
AttestedHeader: attestedHeader,
NextSyncCommittee: SyncCommitteeFromConsensus(migration.V2SyncCommitteeToV1Alpha1(update.NextSyncCommittee)),
NextSyncCommitteeBranch: branchToJSON(update.NextSyncCommitteeBranch),
FinalizedHeader: finalizedHeader,
FinalityBranch: branchToJSON(update.FinalityBranch),
SyncAggregate: syncAggregateToJSON(update.SyncAggregate),
SignatureSlot: strconv.FormatUint(uint64(update.SignatureSlot), 10),
}, nil
}
func LightClientFinalityUpdateFromConsensus(update *v2.LightClientFinalityUpdate) (*LightClientFinalityUpdate, error) {
attestedHeader, err := lightClientHeaderContainerToJSON(update.AttestedHeader)
if err != nil {
return nil, errors.Wrap(err, "could not marshal attested light client header")
}
finalizedHeader, err := lightClientHeaderContainerToJSON(update.FinalizedHeader)
if err != nil {
return nil, errors.Wrap(err, "could not marshal finalized light client header")
}
return &LightClientFinalityUpdate{
AttestedHeader: attestedHeader,
FinalizedHeader: finalizedHeader,
FinalityBranch: branchToJSON(update.FinalityBranch),
SyncAggregate: syncAggregateToJSON(update.SyncAggregate),
SignatureSlot: strconv.FormatUint(uint64(update.SignatureSlot), 10),
}, nil
}
func LightClientOptimisticUpdateFromConsensus(update *v2.LightClientOptimisticUpdate) (*LightClientOptimisticUpdate, error) {
attestedHeader, err := lightClientHeaderContainerToJSON(update.AttestedHeader)
if err != nil {
return nil, errors.Wrap(err, "could not marshal attested light client header")
}
return &LightClientOptimisticUpdate{
AttestedHeader: attestedHeader,
SyncAggregate: syncAggregateToJSON(update.SyncAggregate),
SignatureSlot: strconv.FormatUint(uint64(update.SignatureSlot), 10),
}, nil
}
func branchToJSON(branchBytes [][]byte) []string {
if branchBytes == nil {
return nil
}
branch := make([]string, len(branchBytes))
for i, root := range branchBytes {
branch[i] = hexutil.Encode(root)
}
return branch
}
func syncAggregateToJSON(input *v1.SyncAggregate) *SyncAggregate {
return &SyncAggregate{
SyncCommitteeBits: hexutil.Encode(input.SyncCommitteeBits),
SyncCommitteeSignature: hexutil.Encode(input.SyncCommitteeSignature),
}
}
func lightClientHeaderContainerToJSON(container *v2.LightClientHeaderContainer) (json.RawMessage, error) {
beacon, err := container.GetBeacon()
if err != nil {
return nil, errors.Wrap(err, "could not get beacon block header")
}
var header any
switch t := (container.Header).(type) {
case *v2.LightClientHeaderContainer_HeaderAltair:
header = &LightClientHeader{Beacon: BeaconBlockHeaderFromConsensus(migration.V1HeaderToV1Alpha1(beacon))}
case *v2.LightClientHeaderContainer_HeaderCapella:
execution, err := ExecutionPayloadHeaderCapellaFromConsensus(t.HeaderCapella.Execution)
if err != nil {
return nil, err
}
header = &LightClientHeaderCapella{
Beacon: BeaconBlockHeaderFromConsensus(migration.V1HeaderToV1Alpha1(beacon)),
Execution: execution,
ExecutionBranch: branchToJSON(t.HeaderCapella.ExecutionBranch),
}
case *v2.LightClientHeaderContainer_HeaderDeneb:
execution, err := ExecutionPayloadHeaderDenebFromConsensus(t.HeaderDeneb.Execution)
if err != nil {
return nil, err
}
header = &LightClientHeaderDeneb{
Beacon: BeaconBlockHeaderFromConsensus(migration.V1HeaderToV1Alpha1(beacon)),
Execution: execution,
ExecutionBranch: branchToJSON(t.HeaderDeneb.ExecutionBranch),
}
default:
return nil, fmt.Errorf("unsupported header type %T", t)
}
return json.Marshal(header)
}

View File

@@ -96,21 +96,7 @@ type LightClientFinalityUpdateEvent struct {
Data *LightClientFinalityUpdate `json:"data"` Data *LightClientFinalityUpdate `json:"data"`
} }
type LightClientFinalityUpdate struct {
AttestedHeader *BeaconBlockHeader `json:"attested_header"`
FinalizedHeader *BeaconBlockHeader `json:"finalized_header"`
FinalityBranch []string `json:"finality_branch"`
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
SignatureSlot string `json:"signature_slot"`
}
type LightClientOptimisticUpdateEvent struct { type LightClientOptimisticUpdateEvent struct {
Version string `json:"version"` Version string `json:"version"`
Data *LightClientOptimisticUpdate `json:"data"` Data *LightClientOptimisticUpdate `json:"data"`
} }
type LightClientOptimisticUpdate struct {
AttestedHeader *BeaconBlockHeader `json:"attested_header"`
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
SignatureSlot string `json:"signature_slot"`
}

View File

@@ -24,11 +24,6 @@ type LightClientBootstrap struct {
CurrentSyncCommitteeBranch []string `json:"current_sync_committee_branch"` CurrentSyncCommitteeBranch []string `json:"current_sync_committee_branch"`
} }
type LightClientBootstrapResponse struct {
Version string `json:"version"`
Data *LightClientBootstrap `json:"data"`
}
type LightClientUpdate struct { type LightClientUpdate struct {
AttestedHeader json.RawMessage `json:"attested_header"` AttestedHeader json.RawMessage `json:"attested_header"`
NextSyncCommittee *SyncCommittee `json:"next_sync_committee,omitempty"` NextSyncCommittee *SyncCommittee `json:"next_sync_committee,omitempty"`
@@ -39,11 +34,40 @@ type LightClientUpdate struct {
SignatureSlot string `json:"signature_slot"` SignatureSlot string `json:"signature_slot"`
} }
type LightClientUpdateWithVersion struct { type LightClientFinalityUpdate struct {
AttestedHeader json.RawMessage `json:"attested_header"`
FinalizedHeader json.RawMessage `json:"finalized_header"`
FinalityBranch []string `json:"finality_branch"`
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
SignatureSlot string `json:"signature_slot"`
}
type LightClientOptimisticUpdate struct {
AttestedHeader json.RawMessage `json:"attested_header"`
SyncAggregate *SyncAggregate `json:"sync_aggregate"`
SignatureSlot string `json:"signature_slot"`
}
type LightClientBootstrapResponse struct {
Version string `json:"version"`
Data *LightClientBootstrap `json:"data"`
}
type LightClientUpdateResponse struct {
Version string `json:"version"` Version string `json:"version"`
Data *LightClientUpdate `json:"data"` Data *LightClientUpdate `json:"data"`
} }
type LightClientUpdatesByRangeResponse struct { type LightClientFinalityUpdateResponse struct {
Updates []*LightClientUpdateWithVersion `json:"updates"` Version string `json:"version"`
Data *LightClientFinalityUpdate `json:"data"`
}
type LightClientOptimisticUpdateResponse struct {
Version string `json:"version"`
Data *LightClientOptimisticUpdate `json:"data"`
}
type LightClientUpdatesByRangeResponse struct {
Updates []*LightClientUpdateResponse `json:"updates"`
} }

View File

@@ -193,7 +193,7 @@ func (s *Service) sendLightClientFinalityUpdate(ctx context.Context, signed inte
// Return the result // Return the result
result := &ethpbv2.LightClientFinalityUpdateWithVersion{ result := &ethpbv2.LightClientFinalityUpdateWithVersion{
Version: ethpbv2.Version(signed.Version()), Version: ethpbv2.Version(signed.Version()),
Data: lightclient.CreateLightClientFinalityUpdate(update), Data: update,
} }
// Send event // Send event
@@ -227,7 +227,7 @@ func (s *Service) sendLightClientOptimisticUpdate(ctx context.Context, signed in
// Return the result // Return the result
result := &ethpbv2.LightClientOptimisticUpdateWithVersion{ result := &ethpbv2.LightClientOptimisticUpdateWithVersion{
Version: ethpbv2.Version(signed.Version()), Version: ethpbv2.Version(signed.Version()),
Data: lightclient.CreateLightClientOptimisticUpdate(update), Data: update,
} }
return s.cfg.StateNotifier.StateFeed().Send(&feed.Event{ return s.cfg.StateNotifier.StateFeed().Send(&feed.Event{

View File

@@ -33,7 +33,6 @@ go_test(
"//consensus-types/blocks:go_default_library", "//consensus-types/blocks:go_default_library",
"//consensus-types/primitives:go_default_library", "//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library", "//proto/engine/v1:go_default_library",
"//proto/eth/v2:go_default_library",
"//testing/require:go_default_library", "//testing/require:go_default_library",
"//testing/util:go_default_library", "//testing/util:go_default_library",
], ],

View File

@@ -2,6 +2,7 @@ package light_client
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -20,16 +21,15 @@ import (
"github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots" "github.com/prysmaticlabs/prysm/v5/time/slots"
"context"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
) )
const ( const (
FinalityBranchNumOfLeaves = 6 FinalityBranchNumOfLeaves = 6
executionBranchNumOfLeaves = 4
) )
// CreateLightClientFinalityUpdate - implements https://github.com/ethereum/consensus-specs/blob/3d235740e5f1e641d3b160c8688f26e7dc5a1894/specs/altair/light-client/full-node.md#create_light_client_finality_update // createLightClientFinalityUpdate - implements https://github.com/ethereum/consensus-specs/blob/3d235740e5f1e641d3b160c8688f26e7dc5a1894/specs/altair/light-client/full-node.md#create_light_client_finality_update
// def create_light_client_finality_update(update: LightClientUpdate) -> LightClientFinalityUpdate: // def create_light_client_finality_update(update: LightClientUpdate) -> LightClientFinalityUpdate:
// //
// return LightClientFinalityUpdate( // return LightClientFinalityUpdate(
@@ -39,7 +39,7 @@ const (
// sync_aggregate=update.sync_aggregate, // sync_aggregate=update.sync_aggregate,
// signature_slot=update.signature_slot, // signature_slot=update.signature_slot,
// ) // )
func CreateLightClientFinalityUpdate(update *ethpbv2.LightClientUpdate) *ethpbv2.LightClientFinalityUpdate { func createLightClientFinalityUpdate(update *ethpbv2.LightClientUpdate) *ethpbv2.LightClientFinalityUpdate {
finalityUpdate := &ethpbv2.LightClientFinalityUpdate{ finalityUpdate := &ethpbv2.LightClientFinalityUpdate{
AttestedHeader: update.AttestedHeader, AttestedHeader: update.AttestedHeader,
FinalizedHeader: update.FinalizedHeader, FinalizedHeader: update.FinalizedHeader,
@@ -51,7 +51,7 @@ func CreateLightClientFinalityUpdate(update *ethpbv2.LightClientUpdate) *ethpbv2
return finalityUpdate return finalityUpdate
} }
// CreateLightClientOptimisticUpdate - implements https://github.com/ethereum/consensus-specs/blob/3d235740e5f1e641d3b160c8688f26e7dc5a1894/specs/altair/light-client/full-node.md#create_light_client_optimistic_update // createLightClientOptimisticUpdate - implements https://github.com/ethereum/consensus-specs/blob/3d235740e5f1e641d3b160c8688f26e7dc5a1894/specs/altair/light-client/full-node.md#create_light_client_optimistic_update
// def create_light_client_optimistic_update(update: LightClientUpdate) -> LightClientOptimisticUpdate: // def create_light_client_optimistic_update(update: LightClientUpdate) -> LightClientOptimisticUpdate:
// //
// return LightClientOptimisticUpdate( // return LightClientOptimisticUpdate(
@@ -59,7 +59,7 @@ func CreateLightClientFinalityUpdate(update *ethpbv2.LightClientUpdate) *ethpbv2
// sync_aggregate=update.sync_aggregate, // sync_aggregate=update.sync_aggregate,
// signature_slot=update.signature_slot, // signature_slot=update.signature_slot,
// ) // )
func CreateLightClientOptimisticUpdate(update *ethpbv2.LightClientUpdate) *ethpbv2.LightClientOptimisticUpdate { func createLightClientOptimisticUpdate(update *ethpbv2.LightClientUpdate) *ethpbv2.LightClientOptimisticUpdate {
optimisticUpdate := &ethpbv2.LightClientOptimisticUpdate{ optimisticUpdate := &ethpbv2.LightClientOptimisticUpdate{
AttestedHeader: update.AttestedHeader, AttestedHeader: update.AttestedHeader,
SyncAggregate: update.SyncAggregate, SyncAggregate: update.SyncAggregate,
@@ -69,11 +69,98 @@ func CreateLightClientOptimisticUpdate(update *ethpbv2.LightClientUpdate) *ethpb
return optimisticUpdate return optimisticUpdate
} }
func NewLightClientFinalityUpdateFromBeaconState(
ctx context.Context,
state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock,
attestedState state.BeaconState,
finalizedBlock interfaces.ReadOnlySignedBeaconBlock,
) (*ethpbv2.LightClientFinalityUpdate, error) {
update, err := NewLightClientUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock)
if err != nil {
return nil, err
}
return createLightClientFinalityUpdate(update), nil
}
func NewLightClientOptimisticUpdateFromBeaconState( func NewLightClientOptimisticUpdateFromBeaconState(
ctx context.Context, ctx context.Context,
state state.BeaconState, state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock, block interfaces.ReadOnlySignedBeaconBlock,
attestedState state.BeaconState) (*ethpbv2.LightClientUpdate, error) { attestedState state.BeaconState,
) (*ethpbv2.LightClientOptimisticUpdate, error) {
update, err := NewLightClientUpdateFromBeaconState(ctx, state, block, attestedState, nil)
if err != nil {
return nil, err
}
return createLightClientOptimisticUpdate(update), nil
}
// NewLightClientUpdateFromBeaconState implements https://github.com/ethereum/consensus-specs/blob/d70dcd9926a4bbe987f1b4e65c3e05bd029fcfb8/specs/altair/light-client/full-node.md#create_light_client_update
// def create_light_client_update(state: BeaconState,
//
// block: SignedBeaconBlock,
// attested_state: BeaconState,
// finalized_block: Optional[SignedBeaconBlock]) -> LightClientUpdate:
// assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
// assert sum(block.message.body.sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
//
// assert state.slot == state.latest_block_header.slot
// header = state.latest_block_header.copy()
// header.state_root = hash_tree_root(state)
// assert hash_tree_root(header) == hash_tree_root(block.message)
// update_signature_period = compute_sync_committee_period(compute_epoch_at_slot(block.message.slot))
//
// assert attested_state.slot == attested_state.latest_block_header.slot
// attested_header = attested_state.latest_block_header.copy()
// attested_header.state_root = hash_tree_root(attested_state)
// assert hash_tree_root(attested_header) == block.message.parent_root
// update_attested_period = compute_sync_committee_period(compute_epoch_at_slot(attested_header.slot))
//
// # `next_sync_committee` is only useful if the message is signed by the current sync committee
// if update_attested_period == update_signature_period:
// next_sync_committee = attested_state.next_sync_committee
// next_sync_committee_branch = compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX)
// else:
// next_sync_committee = SyncCommittee()
// next_sync_committee_branch = [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]
//
// # Indicate finality whenever possible
// if finalized_block is not None:
// if finalized_block.message.slot != GENESIS_SLOT:
// finalized_header = BeaconBlockHeader(
// slot=finalized_block.message.slot,
// proposer_index=finalized_block.message.proposer_index,
// parent_root=finalized_block.message.parent_root,
// state_root=finalized_block.message.state_root,
// body_root=hash_tree_root(finalized_block.message.body),
// )
// assert hash_tree_root(finalized_header) == attested_state.finalized_checkpoint.root
// else:
// assert attested_state.finalized_checkpoint.root == Bytes32()
// finalized_header = BeaconBlockHeader()
// finality_branch = compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX)
// else:
// finalized_header = BeaconBlockHeader()
// finality_branch = [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]
//
// return LightClientUpdate(
// attested_header=attested_header,
// next_sync_committee=next_sync_committee,
// next_sync_committee_branch=next_sync_committee_branch,
// finalized_header=finalized_header,
// finality_branch=finality_branch,
// sync_aggregate=block.message.body.sync_aggregate,
// signature_slot=block.message.slot,
// )
func NewLightClientUpdateFromBeaconState(
ctx context.Context,
state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock,
attestedState state.BeaconState,
finalizedBlock interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientUpdate, error) {
// assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH // assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
attestedEpoch := slots.ToEpoch(attestedState.Slot()) attestedEpoch := slots.ToEpoch(attestedState.Slot())
if attestedEpoch < params.BeaconConfig().AltairForkEpoch { if attestedEpoch < params.BeaconConfig().AltairForkEpoch {
@@ -85,7 +172,6 @@ func NewLightClientOptimisticUpdateFromBeaconState(
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get sync aggregate") return nil, errors.Wrap(err, "could not get sync aggregate")
} }
if syncAggregate.SyncCommitteeBits.Count() < params.BeaconConfig().MinSyncCommitteeParticipants { if syncAggregate.SyncCommitteeBits.Count() < params.BeaconConfig().MinSyncCommitteeParticipants {
return nil, fmt.Errorf("invalid sync committee bits count %d", syncAggregate.SyncCommitteeBits.Count()) return nil, fmt.Errorf("invalid sync committee bits count %d", syncAggregate.SyncCommitteeBits.Count())
} }
@@ -102,21 +188,21 @@ func NewLightClientOptimisticUpdateFromBeaconState(
return nil, errors.Wrap(err, "could not get state root") return nil, errors.Wrap(err, "could not get state root")
} }
header.StateRoot = stateRoot[:] header.StateRoot = stateRoot[:]
headerRoot, err := header.HashTreeRoot() headerRoot, err := header.HashTreeRoot()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get header root") return nil, errors.Wrap(err, "could not get header root")
} }
blockRoot, err := block.Block().HashTreeRoot() blockRoot, err := block.Block().HashTreeRoot()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get block root") return nil, errors.Wrap(err, "could not get block root")
} }
if headerRoot != blockRoot { if headerRoot != blockRoot {
return nil, fmt.Errorf("header root %#x not equal to block root %#x", headerRoot, blockRoot) return nil, fmt.Errorf("header root %#x not equal to block root %#x", headerRoot, blockRoot)
} }
// update_signature_period = compute_sync_committee_period(compute_epoch_at_slot(block.message.slot))
updateSignaturePeriod := slots.SyncCommitteePeriod(slots.ToEpoch(block.Block().Slot()))
// assert attested_state.slot == attested_state.latest_block_header.slot // assert attested_state.slot == attested_state.latest_block_header.slot
if attestedState.Slot() != attestedState.LatestBlockHeader().Slot { if attestedState.Slot() != attestedState.LatestBlockHeader().Slot {
return nil, fmt.Errorf("attested state slot %d not equal to attested latest block header slot %d", attestedState.Slot(), attestedState.LatestBlockHeader().Slot) return nil, fmt.Errorf("attested state slot %d not equal to attested latest block header slot %d", attestedState.Slot(), attestedState.LatestBlockHeader().Slot)
@@ -137,34 +223,32 @@ func NewLightClientOptimisticUpdateFromBeaconState(
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get attested header root") return nil, errors.Wrap(err, "could not get attested header root")
} }
if attestedHeaderRoot != block.Block().ParentRoot() { if attestedHeaderRoot != block.Block().ParentRoot() {
return nil, fmt.Errorf("attested header root %#x not equal to block parent root %#x", attestedHeaderRoot, block.Block().ParentRoot()) return nil, fmt.Errorf("attested header root %#x not equal to block parent root %#x", attestedHeaderRoot, block.Block().ParentRoot())
} }
syncAggregateResult := &ethpbv1.SyncAggregate{ // update_attested_period = compute_sync_committee_period(compute_epoch_at_slot(attested_header.slot))
SyncCommitteeBits: syncAggregate.SyncCommitteeBits, updateAttestedPeriod := slots.SyncCommitteePeriod(slots.ToEpoch(attestedHeader.Slot))
SyncCommitteeSignature: syncAggregate.SyncCommitteeSignature,
// update = LightClientUpdate()
result, err := createDefaultLightClientUpdate(block.Block().Version())
if err != nil {
return nil, errors.Wrap(err, "could not create default light client update")
} }
result := &ethpbv2.LightClientUpdate{ // update.attested_header = block_to_light_client_header(attested_block)
SyncAggregate: syncAggregateResult, blockHeader := &ethpbv1.BeaconBlockHeader{
SignatureSlot: block.Block().Slot(), Slot: attestedHeader.Slot,
ProposerIndex: attestedHeader.ProposerIndex,
ParentRoot: attestedHeader.ParentRoot,
StateRoot: attestedHeader.StateRoot,
BodyRoot: attestedHeader.BodyRoot,
} }
switch block.Block().Version() { switch block.Block().Version() {
case version.Altair, version.Bellatrix: case version.Altair, version.Bellatrix:
result.AttestedHeader = &ethpbv2.LightClientHeaderContainer{ result.AttestedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderAltair{ Header: &ethpbv2.LightClientHeaderContainer_HeaderAltair{
HeaderAltair: &ethpbv2.LightClientHeader{ HeaderAltair: &ethpbv2.LightClientHeader{Beacon: blockHeader},
Beacon: &ethpbv1.BeaconBlockHeader{
Slot: attestedHeader.Slot,
ProposerIndex: attestedHeader.ProposerIndex,
ParentRoot: attestedHeader.ParentRoot,
StateRoot: attestedHeader.StateRoot,
BodyRoot: attestedHeader.BodyRoot,
},
},
}, },
} }
case version.Capella: case version.Capella:
@@ -172,49 +256,32 @@ func NewLightClientOptimisticUpdateFromBeaconState(
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get execution payload header") return nil, errors.Wrap(err, "could not get execution payload header")
} }
executionPayloadProof, err := blocks.PayloadProof(ctx, block.Block()) executionPayloadProof, err := blocks.PayloadProof(ctx, block.Block())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get execution payload proof") return nil, errors.Wrap(err, "could not get execution payload proof")
} }
result.AttestedHeader = &ethpbv2.LightClientHeaderContainer{ result.AttestedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderCapella{ Header: &ethpbv2.LightClientHeaderContainer_HeaderCapella{
HeaderCapella: &ethpbv2.LightClientHeaderCapella{ HeaderCapella: &ethpbv2.LightClientHeaderCapella{
Beacon: &ethpbv1.BeaconBlockHeader{ Beacon: blockHeader,
Slot: attestedHeader.Slot,
ProposerIndex: attestedHeader.ProposerIndex,
ParentRoot: attestedHeader.ParentRoot,
StateRoot: attestedHeader.StateRoot,
BodyRoot: attestedHeader.BodyRoot,
},
Execution: executionPayloadHeader, Execution: executionPayloadHeader,
ExecutionBranch: executionPayloadProof, ExecutionBranch: executionPayloadProof,
}, },
}, },
} }
case version.Deneb:
case version.Deneb, version.Electra:
executionPayloadHeader, err := getExecutionPayloadHeaderDeneb(block) executionPayloadHeader, err := getExecutionPayloadHeaderDeneb(block)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get execution payload header") return nil, errors.Wrap(err, "could not get execution payload header")
} }
executionPayloadProof, err := blocks.PayloadProof(ctx, block.Block()) executionPayloadProof, err := blocks.PayloadProof(ctx, block.Block())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get execution payload proof") return nil, errors.Wrap(err, "could not get execution payload proof")
} }
result.AttestedHeader = &ethpbv2.LightClientHeaderContainer{ result.AttestedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderDeneb{ Header: &ethpbv2.LightClientHeaderContainer_HeaderDeneb{
HeaderDeneb: &ethpbv2.LightClientHeaderDeneb{ HeaderDeneb: &ethpbv2.LightClientHeaderDeneb{
Beacon: &ethpbv1.BeaconBlockHeader{ Beacon: blockHeader,
Slot: attestedHeader.Slot,
ProposerIndex: attestedHeader.ProposerIndex,
ParentRoot: attestedHeader.ParentRoot,
StateRoot: attestedHeader.StateRoot,
BodyRoot: attestedHeader.BodyRoot,
},
Execution: executionPayloadHeader, Execution: executionPayloadHeader,
ExecutionBranch: executionPayloadProof, ExecutionBranch: executionPayloadProof,
}, },
@@ -224,168 +291,198 @@ func NewLightClientOptimisticUpdateFromBeaconState(
return nil, fmt.Errorf("unsupported block version %s", version.String(block.Block().Version())) return nil, fmt.Errorf("unsupported block version %s", version.String(block.Block().Version()))
} }
return result, nil // if update_attested_period == update_signature_period
} if updateAttestedPeriod == updateSignaturePeriod {
tempNextSyncCommittee, err := attestedState.NextSyncCommittee()
if err != nil {
return nil, errors.Wrap(err, "could not get next sync committee")
}
nextSyncCommittee := &ethpbv2.SyncCommittee{
Pubkeys: tempNextSyncCommittee.Pubkeys,
AggregatePubkey: tempNextSyncCommittee.AggregatePubkey,
}
nextSyncCommitteeBranch, err := attestedState.NextSyncCommitteeProof(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not get next sync committee proof")
}
func NewLightClientFinalityUpdateFromBeaconState( // update.next_sync_committee = attested_state.next_sync_committee
ctx context.Context, result.NextSyncCommittee = nextSyncCommittee
state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock, // update.next_sync_committee_branch = NextSyncCommitteeBranch(
attestedState state.BeaconState, // compute_merkle_proof(attested_state, next_sync_committee_gindex_at_slot(attested_state.slot)))
finalizedBlock interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientUpdate, error) { result.NextSyncCommitteeBranch = nextSyncCommitteeBranch
result, err := NewLightClientOptimisticUpdateFromBeaconState(
ctx,
state,
block,
attestedState,
)
if err != nil {
return nil, err
} }
// Indicate finality whenever possible // if finalized_block is not None
var finalizedHeaderBeacon *ethpbv1.BeaconBlockHeader
var finalityBranch [][]byte
if finalizedBlock != nil && !finalizedBlock.IsNil() { if finalizedBlock != nil && !finalizedBlock.IsNil() {
// if finalized_block.message.slot != GENESIS_SLOT
if finalizedBlock.Block().Slot() != 0 { if finalizedBlock.Block().Slot() != 0 {
tempFinalizedHeader, err := finalizedBlock.Header() // update.finalized_header = block_to_light_client_header(finalized_block)
v1alpha1FinalizedHeader, err := finalizedBlock.Header()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get finalized header") return nil, errors.Wrap(err, "could not get finalized header")
} }
finalizedHeaderBeacon := migration.V1Alpha1SignedHeaderToV1(tempFinalizedHeader).GetMessage() finalizedHeader := migration.V1Alpha1SignedHeaderToV1(v1alpha1FinalizedHeader).GetMessage()
finalizedHeaderRoot, err := finalizedHeader.HashTreeRoot()
finalizedHeaderRoot, err := finalizedHeaderBeacon.HashTreeRoot()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get finalized header root") return nil, errors.Wrap(err, "could not get finalized header root")
} }
switch block.Block().Version() {
case version.Altair, version.Bellatrix:
result.FinalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderAltair{
HeaderAltair: &ethpbv2.LightClientHeader{Beacon: finalizedHeader},
},
}
case version.Capella:
executionPayloadHeader, err := getExecutionPayloadHeaderCapella(finalizedBlock)
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload header")
}
executionPayloadProof, err := blocks.PayloadProof(ctx, finalizedBlock.Block())
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload proof")
}
result.FinalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderCapella{
HeaderCapella: &ethpbv2.LightClientHeaderCapella{
Beacon: finalizedHeader,
Execution: executionPayloadHeader,
ExecutionBranch: executionPayloadProof,
},
},
}
case version.Deneb:
executionPayloadHeader, err := getExecutionPayloadHeaderDeneb(finalizedBlock)
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload header")
}
executionPayloadProof, err := blocks.PayloadProof(ctx, finalizedBlock.Block())
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload proof")
}
result.FinalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderDeneb{
HeaderDeneb: &ethpbv2.LightClientHeaderDeneb{
Beacon: finalizedHeader,
Execution: executionPayloadHeader,
ExecutionBranch: executionPayloadProof,
},
},
}
default:
return nil, fmt.Errorf("unsupported block version %s", version.String(block.Block().Version()))
}
// assert hash_tree_root(update.finalized_header.beacon) == attested_state.finalized_checkpoint.root
if finalizedHeaderRoot != bytesutil.ToBytes32(attestedState.FinalizedCheckpoint().Root) { if finalizedHeaderRoot != bytesutil.ToBytes32(attestedState.FinalizedCheckpoint().Root) {
return nil, fmt.Errorf("finalized header root %#x not equal to attested finalized checkpoint root %#x", finalizedHeaderRoot, bytesutil.ToBytes32(attestedState.FinalizedCheckpoint().Root)) return nil, fmt.Errorf(
"finalized header root %#x not equal to attested finalized checkpoint root %#x",
finalizedHeaderRoot,
bytesutil.ToBytes32(attestedState.FinalizedCheckpoint().Root),
)
} }
} else { } else {
// assert attested_state.finalized_checkpoint.root == Bytes32()
if !bytes.Equal(attestedState.FinalizedCheckpoint().Root, make([]byte, 32)) { if !bytes.Equal(attestedState.FinalizedCheckpoint().Root, make([]byte, 32)) {
return nil, fmt.Errorf("invalid finalized header root %v", attestedState.FinalizedCheckpoint().Root) return nil, fmt.Errorf("invalid finalized header root %v", attestedState.FinalizedCheckpoint().Root)
} }
finalizedHeaderBeacon = &ethpbv1.BeaconBlockHeader{
Slot: 0,
ProposerIndex: 0,
ParentRoot: make([]byte, 32),
StateRoot: make([]byte, 32),
BodyRoot: make([]byte, 32),
}
} }
var bErr error // update.finality_branch = FinalityBranch(
finalityBranch, bErr = attestedState.FinalizedRootProof(ctx) // compute_merkle_proof(attested_state, finalized_root_gindex_at_slot(attested_state.slot)))
if bErr != nil { finalityBranch, err := attestedState.FinalizedRootProof(ctx)
return nil, errors.Wrap(bErr, "could not get finalized root proof") if err != nil {
} return nil, errors.Wrap(err, "could not get finalized root proof")
} else {
finalizedHeaderBeacon = &ethpbv1.BeaconBlockHeader{
Slot: 0,
ProposerIndex: 0,
ParentRoot: make([]byte, 32),
StateRoot: make([]byte, 32),
BodyRoot: make([]byte, 32),
}
finalityBranch = make([][]byte, FinalityBranchNumOfLeaves)
for i := 0; i < FinalityBranchNumOfLeaves; i++ {
finalityBranch[i] = make([]byte, 32)
}
}
switch block.Block().Version() {
case version.Altair, version.Bellatrix:
result.FinalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderAltair{
HeaderAltair: &ethpbv2.LightClientHeader{Beacon: finalizedHeaderBeacon},
},
} }
result.FinalityBranch = finalityBranch result.FinalityBranch = finalityBranch
case version.Capella:
if finalizedBlock != nil && !finalizedBlock.IsNil() {
execution, err := getExecutionPayloadHeaderCapella(finalizedBlock)
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload header")
}
executionBranch, err := blocks.PayloadProof(ctx, finalizedBlock.Block())
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload proof")
}
result.FinalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderCapella{
HeaderCapella: &ethpbv2.LightClientHeaderCapella{
Beacon: finalizedHeaderBeacon,
Execution: execution,
ExecutionBranch: executionBranch,
},
},
}
result.FinalityBranch = finalityBranch
} else {
execution := createEmptyExecutionPayloadHeaderCapella()
executionBranch := make([][]byte, 0)
result.FinalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderCapella{
HeaderCapella: &ethpbv2.LightClientHeaderCapella{
Beacon: finalizedHeaderBeacon,
Execution: execution,
ExecutionBranch: executionBranch,
},
},
}
result.FinalityBranch = finalityBranch
}
case version.Deneb, version.Electra:
if finalizedBlock != nil && !finalizedBlock.IsNil() {
execution, err := getExecutionPayloadHeaderDeneb(finalizedBlock)
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload header")
}
executionBranch, err := blocks.PayloadProof(ctx, finalizedBlock.Block())
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload proof")
}
result.FinalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderDeneb{
HeaderDeneb: &ethpbv2.LightClientHeaderDeneb{
Beacon: finalizedHeaderBeacon,
Execution: execution,
ExecutionBranch: executionBranch,
},
},
}
result.FinalityBranch = finalityBranch
} else {
execution := createEmptyExecutionPayloadHeaderDeneb()
executionBranch := make([][]byte, 0)
result.FinalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderDeneb{
HeaderDeneb: &ethpbv2.LightClientHeaderDeneb{
Beacon: finalizedHeaderBeacon,
Execution: execution,
ExecutionBranch: executionBranch,
},
},
}
result.FinalityBranch = finalityBranch
}
default:
return nil, fmt.Errorf("unsupported block version %s", version.String(block.Block().Version()))
} }
// update.sync_aggregate = block.message.body.sync_aggregate
result.SyncAggregate = &ethpbv1.SyncAggregate{
SyncCommitteeBits: syncAggregate.SyncCommitteeBits,
SyncCommitteeSignature: syncAggregate.SyncCommitteeSignature,
}
// update.signature_slot = block.message.slot
result.SignatureSlot = block.Block().Slot()
return result, nil return result, nil
} }
func createDefaultLightClientUpdate(v int) (*ethpbv2.LightClientUpdate, error) {
syncCommitteeSize := params.BeaconConfig().SyncCommitteeSize
pubKeys := make([][]byte, syncCommitteeSize)
for i := uint64(0); i < syncCommitteeSize; i++ {
pubKeys[i] = make([]byte, fieldparams.BLSPubkeyLength)
}
nextSyncCommittee := &ethpbv2.SyncCommittee{
Pubkeys: pubKeys,
AggregatePubkey: make([]byte, fieldparams.BLSPubkeyLength),
}
nextSyncCommitteeBranch := make([][]byte, fieldparams.NextSyncCommitteeBranchDepth)
for i := 0; i < fieldparams.NextSyncCommitteeBranchDepth; i++ {
nextSyncCommitteeBranch[i] = make([]byte, fieldparams.RootLength)
}
executionBranch := make([][]byte, executionBranchNumOfLeaves)
for i := 0; i < executionBranchNumOfLeaves; i++ {
executionBranch[i] = make([]byte, 32)
}
finalizedBlockHeader := &ethpbv1.BeaconBlockHeader{
Slot: 0,
ProposerIndex: 0,
ParentRoot: make([]byte, 32),
StateRoot: make([]byte, 32),
BodyRoot: make([]byte, 32),
}
finalityBranch := make([][]byte, FinalityBranchNumOfLeaves)
for i := 0; i < FinalityBranchNumOfLeaves; i++ {
finalityBranch[i] = make([]byte, 32)
}
var finalizedHeader *ethpbv2.LightClientHeaderContainer
switch v {
case version.Altair, version.Bellatrix:
finalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderAltair{
HeaderAltair: &ethpbv2.LightClientHeader{
Beacon: finalizedBlockHeader,
},
},
}
case version.Capella:
finalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderCapella{
HeaderCapella: &ethpbv2.LightClientHeaderCapella{
Beacon: finalizedBlockHeader,
Execution: createEmptyExecutionPayloadHeaderCapella(),
ExecutionBranch: executionBranch,
},
},
}
case version.Deneb:
finalizedHeader = &ethpbv2.LightClientHeaderContainer{
Header: &ethpbv2.LightClientHeaderContainer_HeaderDeneb{
HeaderDeneb: &ethpbv2.LightClientHeaderDeneb{
Beacon: finalizedBlockHeader,
Execution: createEmptyExecutionPayloadHeaderDeneb(),
ExecutionBranch: executionBranch,
},
},
}
default:
return nil, fmt.Errorf("unsupported block version %s", version.String(v))
}
return &ethpbv2.LightClientUpdate{
NextSyncCommittee: nextSyncCommittee,
NextSyncCommitteeBranch: nextSyncCommitteeBranch,
FinalizedHeader: finalizedHeader,
FinalityBranch: finalityBranch,
}, nil
}
func createEmptyExecutionPayloadHeaderCapella() *enginev1.ExecutionPayloadHeaderCapella { func createEmptyExecutionPayloadHeaderCapella() *enginev1.ExecutionPayloadHeaderCapella {
return &enginev1.ExecutionPayloadHeaderCapella{ return &enginev1.ExecutionPayloadHeaderCapella{
ParentHash: make([]byte, 32), ParentHash: make([]byte, 32),
@@ -535,23 +632,6 @@ func getExecutionPayloadHeaderDeneb(block interfaces.ReadOnlySignedBeaconBlock)
return execution, nil return execution, nil
} }
func NewLightClientUpdateFromFinalityUpdate(update *ethpbv2.LightClientFinalityUpdate) *ethpbv2.LightClientUpdate {
return &ethpbv2.LightClientUpdate{
AttestedHeader: update.AttestedHeader,
FinalizedHeader: update.FinalizedHeader,
FinalityBranch: update.FinalityBranch,
SyncAggregate: update.SyncAggregate,
SignatureSlot: update.SignatureSlot,
}
}
func NewLightClientUpdateFromOptimisticUpdate(update *ethpbv2.LightClientOptimisticUpdate) *ethpbv2.LightClientUpdate {
return &ethpbv2.LightClientUpdate{
AttestedHeader: update.AttestedHeader,
SyncAggregate: update.SyncAggregate,
SignatureSlot: update.SignatureSlot,
}
}
func ComputeTransactionsRoot(payload interfaces.ExecutionData) ([]byte, error) { func ComputeTransactionsRoot(payload interfaces.ExecutionData) ([]byte, error) {
transactionsRoot, err := payload.TransactionsRoot() transactionsRoot, err := payload.TransactionsRoot()
@@ -590,6 +670,10 @@ func ComputeWithdrawalsRoot(payload interfaces.ExecutionData) ([]byte, error) {
} }
func BlockToLightClientHeaderAltair(block interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientHeader, error) { func BlockToLightClientHeaderAltair(block interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientHeader, error) {
if block.Version() != version.Altair {
return nil, fmt.Errorf("block version is %s instead of Altair", version.String(block.Version()))
}
parentRoot := block.Block().ParentRoot() parentRoot := block.Block().ParentRoot()
stateRoot := block.Block().StateRoot() stateRoot := block.Block().StateRoot()
bodyRoot, err := block.Block().Body().HashTreeRoot() bodyRoot, err := block.Block().Body().HashTreeRoot()
@@ -610,7 +694,7 @@ func BlockToLightClientHeaderAltair(block interfaces.ReadOnlySignedBeaconBlock)
func BlockToLightClientHeaderCapella(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientHeaderCapella, error) { func BlockToLightClientHeaderCapella(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientHeaderCapella, error) {
if block.Version() != version.Capella { if block.Version() != version.Capella {
return nil, fmt.Errorf("creating Capella light client header is not supported before Capella, invalid slot %d", block.Block().Slot()) return nil, fmt.Errorf("block version is %s instead of Capella", version.String(block.Version()))
} }
payload, err := block.Block().Body().Execution() payload, err := block.Block().Body().Execution()
@@ -671,9 +755,8 @@ func BlockToLightClientHeaderCapella(ctx context.Context, block interfaces.ReadO
} }
func BlockToLightClientHeaderDeneb(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientHeaderDeneb, error) { func BlockToLightClientHeaderDeneb(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.LightClientHeaderDeneb, error) {
epoch := slots.ToEpoch(block.Block().Slot()) if block.Version() != version.Deneb {
if epoch < params.BeaconConfig().DenebForkEpoch { return nil, fmt.Errorf("block version is %s instead of Deneb", version.String(block.Version()))
return nil, fmt.Errorf("creating Deneb light client header is not supported before Deneb, invalid slot %d", block.Block().Slot())
} }
payload, err := block.Block().Body().Execution() payload, err := block.Block().Body().Execution()

View File

@@ -10,27 +10,10 @@ import (
light_client "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" light_client "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
"github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
v2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util" "github.com/prysmaticlabs/prysm/v5/testing/util"
) )
func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateCapella(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false)
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState)
require.NoError(t, err)
require.NotNil(t, update, "update is nil")
require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.CheckSyncAggregate(update)
l.CheckAttestedHeader(update)
require.Equal(t, (*v2.LightClientHeaderContainer)(nil), update.FinalizedHeader, "Finalized header is not nil")
require.DeepSSZEqual(t, ([][]byte)(nil), update.FinalityBranch, "Finality branch is not nil")
}
func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateAltair(t *testing.T) { func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateAltair(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair() l := util.NewTestLightClient(t).SetupTestAltair()
@@ -40,11 +23,21 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateAltair(t *test
require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.CheckSyncAggregate(update) l.CheckSyncAggregate(update.SyncAggregate)
l.CheckAttestedHeader(update) l.CheckAttestedHeader(update.AttestedHeader)
}
require.Equal(t, (*v2.LightClientHeaderContainer)(nil), update.FinalizedHeader, "Finalized header is not nil") func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateCapella(t *testing.T) {
require.DeepSSZEqual(t, ([][]byte)(nil), update.FinalityBranch, "Finality branch is not nil") l := util.NewTestLightClient(t).SetupTestCapella(false)
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState)
require.NoError(t, err)
require.NotNil(t, update, "update is nil")
require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.CheckSyncAggregate(update.SyncAggregate)
l.CheckAttestedHeader(update.AttestedHeader)
} }
func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateDeneb(t *testing.T) { func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateDeneb(t *testing.T) {
@@ -56,22 +49,21 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateDeneb(t *testi
require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.CheckSyncAggregate(update) l.CheckSyncAggregate(update.SyncAggregate)
l.CheckAttestedHeader(update) l.CheckAttestedHeader(update.AttestedHeader)
require.Equal(t, (*v2.LightClientHeaderContainer)(nil), update.FinalizedHeader, "Finalized header is not nil")
require.DeepSSZEqual(t, ([][]byte)(nil), update.FinalityBranch, "Finality branch is not nil")
} }
func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateCapella(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false) func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateAltair(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair()
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, nil) update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, nil)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, update, "update is nil") require.NotNil(t, update, "update is nil")
require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.CheckSyncAggregate(update) l.CheckSyncAggregate(update.SyncAggregate)
l.CheckAttestedHeader(update) l.CheckAttestedHeader(update.AttestedHeader)
zeroHash := params.BeaconConfig().ZeroHash[:] zeroHash := params.BeaconConfig().ZeroHash[:]
require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") require.NotNil(t, update.FinalizedHeader, "Finalized header is nil")
@@ -88,17 +80,16 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateCapella(t *testi
} }
} }
func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateAltair(t *testing.T) { func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateCapella(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair() l := util.NewTestLightClient(t).SetupTestCapella(false)
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, nil) update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, nil)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, update, "update is nil") require.NotNil(t, update, "update is nil")
require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.CheckSyncAggregate(update) l.CheckSyncAggregate(update.SyncAggregate)
l.CheckAttestedHeader(update) l.CheckAttestedHeader(update.AttestedHeader)
zeroHash := params.BeaconConfig().ZeroHash[:] zeroHash := params.BeaconConfig().ZeroHash[:]
require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") require.NotNil(t, update.FinalizedHeader, "Finalized header is nil")
@@ -124,8 +115,8 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateDeneb(t *testing
require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal")
l.CheckSyncAggregate(update) l.CheckSyncAggregate(update.SyncAggregate)
l.CheckAttestedHeader(update) l.CheckAttestedHeader(update.AttestedHeader)
zeroHash := params.BeaconConfig().ZeroHash[:] zeroHash := params.BeaconConfig().ZeroHash[:]
require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") require.NotNil(t, update.FinalizedHeader, "Finalized header is nil")

View File

@@ -301,45 +301,15 @@ func (s *Server) handleStateEvents(ctx context.Context, w http.ResponseWriter, f
if !ok { if !ok {
return write(w, flusher, topicDataMismatch, event.Data, LightClientFinalityUpdateTopic) return write(w, flusher, topicDataMismatch, event.Data, LightClientFinalityUpdateTopic)
} }
update, err := structs.LightClientFinalityUpdateFromConsensus(updateData.Data)
var finalityBranch []string
for _, b := range updateData.Data.FinalityBranch {
finalityBranch = append(finalityBranch, hexutil.Encode(b))
}
attestedBeacon, err := updateData.Data.AttestedHeader.GetBeacon()
if err != nil { if err != nil {
return errors.Wrap(err, "could not get attested header") return err
} }
finalizedBeacon, err := updateData.Data.FinalizedHeader.GetBeacon() updateEvent := &structs.LightClientFinalityUpdateEvent{
if err != nil {
return errors.Wrap(err, "could not get finalized header")
}
update := &structs.LightClientFinalityUpdateEvent{
Version: version.String(int(updateData.Version)), Version: version.String(int(updateData.Version)),
Data: &structs.LightClientFinalityUpdate{ Data: update,
AttestedHeader: &structs.BeaconBlockHeader{
Slot: fmt.Sprintf("%d", attestedBeacon.Slot),
ProposerIndex: fmt.Sprintf("%d", attestedBeacon.ProposerIndex),
ParentRoot: hexutil.Encode(attestedBeacon.ParentRoot),
StateRoot: hexutil.Encode(attestedBeacon.StateRoot),
BodyRoot: hexutil.Encode(attestedBeacon.BodyRoot),
},
FinalizedHeader: &structs.BeaconBlockHeader{
Slot: fmt.Sprintf("%d", finalizedBeacon.Slot),
ProposerIndex: fmt.Sprintf("%d", finalizedBeacon.ProposerIndex),
ParentRoot: hexutil.Encode(finalizedBeacon.ParentRoot),
StateRoot: hexutil.Encode(finalizedBeacon.StateRoot),
},
FinalityBranch: finalityBranch,
SyncAggregate: &structs.SyncAggregate{
SyncCommitteeBits: hexutil.Encode(updateData.Data.SyncAggregate.SyncCommitteeBits),
SyncCommitteeSignature: hexutil.Encode(updateData.Data.SyncAggregate.SyncCommitteeSignature),
},
SignatureSlot: fmt.Sprintf("%d", updateData.Data.SignatureSlot),
},
} }
return send(w, flusher, LightClientFinalityUpdateTopic, update) return send(w, flusher, LightClientFinalityUpdateTopic, updateEvent)
case statefeed.LightClientOptimisticUpdate: case statefeed.LightClientOptimisticUpdate:
if _, ok := requestedTopics[LightClientOptimisticUpdateTopic]; !ok { if _, ok := requestedTopics[LightClientOptimisticUpdateTopic]; !ok {
return nil return nil
@@ -348,28 +318,15 @@ func (s *Server) handleStateEvents(ctx context.Context, w http.ResponseWriter, f
if !ok { if !ok {
return write(w, flusher, topicDataMismatch, event.Data, LightClientOptimisticUpdateTopic) return write(w, flusher, topicDataMismatch, event.Data, LightClientOptimisticUpdateTopic)
} }
attestedBeacon, err := updateData.Data.AttestedHeader.GetBeacon() update, err := structs.LightClientOptimisticUpdateFromConsensus(updateData.Data)
if err != nil { if err != nil {
return errors.Wrap(err, "could not get attested header") return err
} }
update := &structs.LightClientOptimisticUpdateEvent{ updateEvent := &structs.LightClientOptimisticUpdateEvent{
Version: version.String(int(updateData.Version)), Version: version.String(int(updateData.Version)),
Data: &structs.LightClientOptimisticUpdate{ Data: update,
AttestedHeader: &structs.BeaconBlockHeader{
Slot: fmt.Sprintf("%d", attestedBeacon.Slot),
ProposerIndex: fmt.Sprintf("%d", attestedBeacon.ProposerIndex),
ParentRoot: hexutil.Encode(attestedBeacon.ParentRoot),
StateRoot: hexutil.Encode(attestedBeacon.StateRoot),
BodyRoot: hexutil.Encode(attestedBeacon.BodyRoot),
},
SyncAggregate: &structs.SyncAggregate{
SyncCommitteeBits: hexutil.Encode(updateData.Data.SyncAggregate.SyncCommitteeBits),
SyncCommitteeSignature: hexutil.Encode(updateData.Data.SyncAggregate.SyncCommitteeSignature),
},
SignatureSlot: fmt.Sprintf("%d", updateData.Data.SignatureSlot),
},
} }
return send(w, flusher, LightClientOptimisticUpdateTopic, update) return send(w, flusher, LightClientOptimisticUpdateTopic, updateEvent)
case statefeed.Reorg: case statefeed.Reorg:
if _, ok := requestedTopics[ChainReorgTopic]; !ok { if _, ok := requestedTopics[ChainReorgTopic]; !ok {
return nil return nil

View File

@@ -27,9 +27,7 @@ go_library(
"//encoding/ssz:go_default_library", "//encoding/ssz:go_default_library",
"//monitoring/tracing/trace:go_default_library", "//monitoring/tracing/trace:go_default_library",
"//network/httputil:go_default_library", "//network/httputil:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/eth/v2:go_default_library", "//proto/eth/v2:go_default_library",
"//proto/migration:go_default_library",
"//runtime/version:go_default_library", "//runtime/version:go_default_library",
"//time/slots:go_default_library", "//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",

View File

@@ -118,7 +118,7 @@ func (s *Server) GetLightClientUpdatesByRange(w http.ResponseWriter, req *http.R
} }
// Populate updates // Populate updates
var updates []*structs.LightClientUpdateWithVersion var updates []*structs.LightClientUpdateResponse
for period := startPeriod; period <= endPeriod; period++ { for period := startPeriod; period <= endPeriod; period++ {
// Get the last known state of the period, // Get the last known state of the period,
// 1. We wish the block has a parent in the same period if possible // 1. We wish the block has a parent in the same period if possible
@@ -199,7 +199,7 @@ func (s *Server) GetLightClientUpdatesByRange(w http.ResponseWriter, req *http.R
} }
} }
update, err := createLightClientUpdate( update, err := newLightClientUpdateFromBeaconState(
ctx, ctx,
state, state,
block, block,
@@ -208,7 +208,7 @@ func (s *Server) GetLightClientUpdatesByRange(w http.ResponseWriter, req *http.R
) )
if err == nil { if err == nil {
updates = append(updates, &structs.LightClientUpdateWithVersion{ updates = append(updates, &structs.LightClientUpdateResponse{
Version: version.String(attestedState.Version()), Version: version.String(attestedState.Version()),
Data: update, Data: update,
}) })
@@ -225,7 +225,6 @@ func (s *Server) GetLightClientUpdatesByRange(w http.ResponseWriter, req *http.R
// GetLightClientFinalityUpdate - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/finality_update.yaml // GetLightClientFinalityUpdate - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/finality_update.yaml
func (s *Server) GetLightClientFinalityUpdate(w http.ResponseWriter, req *http.Request) { func (s *Server) GetLightClientFinalityUpdate(w http.ResponseWriter, req *http.Request) {
// Prepare
ctx, span := trace.StartSpan(req.Context(), "beacon.GetLightClientFinalityUpdate") ctx, span := trace.StartSpan(req.Context(), "beacon.GetLightClientFinalityUpdate")
defer span.End() defer span.End()
@@ -233,56 +232,48 @@ func (s *Server) GetLightClientFinalityUpdate(w http.ResponseWriter, req *http.R
minSyncCommitteeParticipants := float64(params.BeaconConfig().MinSyncCommitteeParticipants) minSyncCommitteeParticipants := float64(params.BeaconConfig().MinSyncCommitteeParticipants)
minSignatures := uint64(math.Ceil(minSyncCommitteeParticipants * 2 / 3)) minSignatures := uint64(math.Ceil(minSyncCommitteeParticipants * 2 / 3))
block, err := s.getLightClientEventBlock(ctx, minSignatures) block, err := s.suitableBlock(ctx, minSignatures)
if !shared.WriteBlockFetchError(w, block, err) { if !shared.WriteBlockFetchError(w, block, err) {
return return
} }
state, err := s.Stater.StateBySlot(ctx, block.Block().Slot()) st, err := s.Stater.StateBySlot(ctx, block.Block().Slot())
if err != nil { if err != nil {
httputil.HandleError(w, "could not get state: "+err.Error(), http.StatusInternalServerError) httputil.HandleError(w, "Could not get state: "+err.Error(), http.StatusInternalServerError)
return return
} }
// Get attested state
attestedRoot := block.Block().ParentRoot() attestedRoot := block.Block().ParentRoot()
attestedBlock, err := s.Blocker.Block(ctx, attestedRoot[:]) attestedBlock, err := s.Blocker.Block(ctx, attestedRoot[:])
if err != nil || attestedBlock == nil { if !shared.WriteBlockFetchError(w, block, errors.Wrap(err, "could not get attested block")) {
httputil.HandleError(w, "could not get attested block: "+err.Error(), http.StatusInternalServerError)
return return
} }
attestedSlot := attestedBlock.Block().Slot() attestedSlot := attestedBlock.Block().Slot()
attestedState, err := s.Stater.StateBySlot(ctx, attestedSlot) attestedState, err := s.Stater.StateBySlot(ctx, attestedSlot)
if err != nil { if err != nil {
httputil.HandleError(w, "could not get attested state: "+err.Error(), http.StatusInternalServerError) httputil.HandleError(w, "Could not get attested state: "+err.Error(), http.StatusInternalServerError)
return return
} }
// Get finalized block
var finalizedBlock interfaces.ReadOnlySignedBeaconBlock var finalizedBlock interfaces.ReadOnlySignedBeaconBlock
finalizedCheckPoint := attestedState.FinalizedCheckpoint() finalizedCheckpoint := attestedState.FinalizedCheckpoint()
if finalizedCheckPoint != nil { if finalizedCheckpoint == nil {
finalizedRoot := bytesutil.ToBytes32(finalizedCheckPoint.Root) httputil.HandleError(w, "Attested state does not have a finalized checkpoint", http.StatusInternalServerError)
finalizedBlock, err = s.Blocker.Block(ctx, finalizedRoot[:]) return
if err != nil {
finalizedBlock = nil
}
} }
finalizedRoot := bytesutil.ToBytes32(finalizedCheckpoint.Root)
update, err := newLightClientFinalityUpdateFromBeaconState( finalizedBlock, err = s.Blocker.Block(ctx, finalizedRoot[:])
ctx, if !shared.WriteBlockFetchError(w, block, errors.Wrap(err, "could not get finalized block")) {
state,
block,
attestedState,
finalizedBlock,
)
if err != nil {
httputil.HandleError(w, "could not get light client finality update: "+err.Error(), http.StatusInternalServerError)
return return
} }
response := &structs.LightClientUpdateWithVersion{ update, err := newLightClientFinalityUpdateFromBeaconState(ctx, st, block, attestedState, finalizedBlock)
if err != nil {
httputil.HandleError(w, "Could not get light client finality update: "+err.Error(), http.StatusInternalServerError)
return
}
response := &structs.LightClientFinalityUpdateResponse{
Version: version.String(attestedState.Version()), Version: version.String(attestedState.Version()),
Data: update, Data: update,
} }
@@ -292,54 +283,42 @@ func (s *Server) GetLightClientFinalityUpdate(w http.ResponseWriter, req *http.R
// GetLightClientOptimisticUpdate - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/optimistic_update.yaml // GetLightClientOptimisticUpdate - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/optimistic_update.yaml
func (s *Server) GetLightClientOptimisticUpdate(w http.ResponseWriter, req *http.Request) { func (s *Server) GetLightClientOptimisticUpdate(w http.ResponseWriter, req *http.Request) {
// Prepare
ctx, span := trace.StartSpan(req.Context(), "beacon.GetLightClientOptimisticUpdate") ctx, span := trace.StartSpan(req.Context(), "beacon.GetLightClientOptimisticUpdate")
defer span.End() defer span.End()
minSignatures := params.BeaconConfig().MinSyncCommitteeParticipants block, err := s.suitableBlock(ctx, params.BeaconConfig().MinSyncCommitteeParticipants)
block, err := s.getLightClientEventBlock(ctx, minSignatures)
if !shared.WriteBlockFetchError(w, block, err) { if !shared.WriteBlockFetchError(w, block, err) {
return return
} }
st, err := s.Stater.StateBySlot(ctx, block.Block().Slot())
state, err := s.Stater.StateBySlot(ctx, block.Block().Slot())
if err != nil { if err != nil {
httputil.HandleError(w, "could not get state: "+err.Error(), http.StatusInternalServerError) httputil.HandleError(w, "could not get state: "+err.Error(), http.StatusInternalServerError)
return return
} }
// Get attested state
attestedRoot := block.Block().ParentRoot() attestedRoot := block.Block().ParentRoot()
attestedBlock, err := s.Blocker.Block(ctx, attestedRoot[:]) attestedBlock, err := s.Blocker.Block(ctx, attestedRoot[:])
if err != nil { if err != nil {
httputil.HandleError(w, "could not get attested block: "+err.Error(), http.StatusInternalServerError) httputil.HandleError(w, "Could not get attested block: "+err.Error(), http.StatusInternalServerError)
return return
} }
if attestedBlock == nil { if attestedBlock == nil {
httputil.HandleError(w, "attested block is nil", http.StatusInternalServerError) httputil.HandleError(w, "Attested block is nil", http.StatusInternalServerError)
return return
} }
attestedSlot := attestedBlock.Block().Slot() attestedSlot := attestedBlock.Block().Slot()
attestedState, err := s.Stater.StateBySlot(ctx, attestedSlot) attestedState, err := s.Stater.StateBySlot(ctx, attestedSlot)
if err != nil { if err != nil {
httputil.HandleError(w, "could not get attested state: "+err.Error(), http.StatusInternalServerError) httputil.HandleError(w, "Could not get attested state: "+err.Error(), http.StatusInternalServerError)
return return
} }
update, err := newLightClientOptimisticUpdateFromBeaconState( update, err := newLightClientOptimisticUpdateFromBeaconState(ctx, st, block, attestedState)
ctx,
state,
block,
attestedState,
)
if err != nil { if err != nil {
httputil.HandleError(w, "could not get light client optimistic update: "+err.Error(), http.StatusInternalServerError) httputil.HandleError(w, "Could not get light client optimistic update: "+err.Error(), http.StatusInternalServerError)
return return
} }
response := &structs.LightClientUpdateWithVersion{ response := &structs.LightClientOptimisticUpdateResponse{
Version: version.String(attestedState.Version()), Version: version.String(attestedState.Version()),
Data: update, Data: update,
} }
@@ -347,17 +326,15 @@ func (s *Server) GetLightClientOptimisticUpdate(w http.ResponseWriter, req *http
httputil.WriteJson(w, response) httputil.WriteJson(w, response)
} }
// getLightClientEventBlock - returns the block that should be used for light client events, which satisfies the minimum number of signatures from sync committee // suitableBlock returns the latest block that satisfies all criteria required for creating a new update
func (s *Server) getLightClientEventBlock(ctx context.Context, minSignaturesRequired uint64) (interfaces.ReadOnlySignedBeaconBlock, error) { func (s *Server) suitableBlock(ctx context.Context, minSignaturesRequired uint64) (interfaces.ReadOnlySignedBeaconBlock, error) {
// Get the current state st, err := s.HeadFetcher.HeadState(ctx)
state, err := s.HeadFetcher.HeadState(ctx)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get head state") return nil, errors.Wrap(err, "could not get head state")
} }
// Get the block latestBlockHeader := st.LatestBlockHeader()
latestBlockHeader := *state.LatestBlockHeader() stateRoot, err := st.HashTreeRoot(ctx)
stateRoot, err := state.HashTreeRoot(ctx)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "could not get state root") return nil, errors.Wrap(err, "could not get state root")
} }
@@ -377,7 +354,7 @@ func (s *Server) getLightClientEventBlock(ctx context.Context, minSignaturesRequ
// Loop through the blocks until we find a block that satisfies minSignaturesRequired requirement // Loop through the blocks until we find a block that satisfies minSignaturesRequired requirement
var numOfSyncCommitteeSignatures uint64 var numOfSyncCommitteeSignatures uint64
if syncAggregate, err := block.Block().Body().SyncAggregate(); err == nil && syncAggregate != nil { if syncAggregate, err := block.Block().Body().SyncAggregate(); err == nil {
numOfSyncCommitteeSignatures = syncAggregate.SyncCommitteeBits.Count() numOfSyncCommitteeSignatures = syncAggregate.SyncCommitteeBits.Count()
} }
@@ -394,7 +371,7 @@ func (s *Server) getLightClientEventBlock(ctx context.Context, minSignaturesRequ
// Get the number of sync committee signatures // Get the number of sync committee signatures
numOfSyncCommitteeSignatures = 0 numOfSyncCommitteeSignatures = 0
if syncAggregate, err := block.Block().Body().SyncAggregate(); err == nil && syncAggregate != nil { if syncAggregate, err := block.Block().Body().SyncAggregate(); err == nil {
numOfSyncCommitteeSignatures = syncAggregate.SyncCommitteeBits.Count() numOfSyncCommitteeSignatures = syncAggregate.SyncCommitteeBits.Count()
} }
} }

View File

@@ -1235,7 +1235,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdateAltair(t *testing.T) {
s.GetLightClientFinalityUpdate(writer, request) s.GetLightClientFinalityUpdate(writer, request)
require.Equal(t, http.StatusOK, writer.Code) require.Equal(t, http.StatusOK, writer.Code)
var resp structs.LightClientUpdateWithVersion var resp *structs.LightClientUpdateResponse
err = json.Unmarshal(writer.Body.Bytes(), &resp) err = json.Unmarshal(writer.Body.Bytes(), &resp)
require.NoError(t, err) require.NoError(t, err)
var respHeader structs.LightClientHeader var respHeader structs.LightClientHeader
@@ -1345,7 +1345,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdateCapella(t *testing.T) {
s.GetLightClientFinalityUpdate(writer, request) s.GetLightClientFinalityUpdate(writer, request)
require.Equal(t, http.StatusOK, writer.Code) require.Equal(t, http.StatusOK, writer.Code)
var resp structs.LightClientUpdateWithVersion var resp *structs.LightClientUpdateResponse
err = json.Unmarshal(writer.Body.Bytes(), &resp) err = json.Unmarshal(writer.Body.Bytes(), &resp)
require.NoError(t, err) require.NoError(t, err)
var respHeader structs.LightClientHeader var respHeader structs.LightClientHeader
@@ -1455,7 +1455,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdateDeneb(t *testing.T) {
s.GetLightClientFinalityUpdate(writer, request) s.GetLightClientFinalityUpdate(writer, request)
require.Equal(t, http.StatusOK, writer.Code) require.Equal(t, http.StatusOK, writer.Code)
var resp structs.LightClientUpdateWithVersion var resp *structs.LightClientUpdateResponse
err = json.Unmarshal(writer.Body.Bytes(), &resp) err = json.Unmarshal(writer.Body.Bytes(), &resp)
require.NoError(t, err) require.NoError(t, err)
var respHeader structs.LightClientHeaderDeneb var respHeader structs.LightClientHeaderDeneb
@@ -1565,7 +1565,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateAltair(t *testing.T) {
s.GetLightClientOptimisticUpdate(writer, request) s.GetLightClientOptimisticUpdate(writer, request)
require.Equal(t, http.StatusOK, writer.Code) require.Equal(t, http.StatusOK, writer.Code)
var resp structs.LightClientUpdateWithVersion var resp *structs.LightClientUpdateResponse
err = json.Unmarshal(writer.Body.Bytes(), &resp) err = json.Unmarshal(writer.Body.Bytes(), &resp)
require.NoError(t, err) require.NoError(t, err)
var respHeader structs.LightClientHeader var respHeader structs.LightClientHeader
@@ -1675,7 +1675,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateCapella(t *testing.T)
s.GetLightClientOptimisticUpdate(writer, request) s.GetLightClientOptimisticUpdate(writer, request)
require.Equal(t, http.StatusOK, writer.Code) require.Equal(t, http.StatusOK, writer.Code)
var resp structs.LightClientUpdateWithVersion var resp *structs.LightClientUpdateResponse
err = json.Unmarshal(writer.Body.Bytes(), &resp) err = json.Unmarshal(writer.Body.Bytes(), &resp)
require.NoError(t, err) require.NoError(t, err)
var respHeader structs.LightClientHeaderCapella var respHeader structs.LightClientHeaderCapella
@@ -1785,7 +1785,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateDeneb(t *testing.T) {
s.GetLightClientOptimisticUpdate(writer, request) s.GetLightClientOptimisticUpdate(writer, request)
require.Equal(t, http.StatusOK, writer.Code) require.Equal(t, http.StatusOK, writer.Code)
var resp structs.LightClientUpdateWithVersion var resp *structs.LightClientUpdateResponse
err = json.Unmarshal(writer.Body.Bytes(), &resp) err = json.Unmarshal(writer.Body.Bytes(), &resp)
require.NoError(t, err) require.NoError(t, err)
var respHeader structs.LightClientHeaderDeneb var respHeader structs.LightClientHeaderDeneb
@@ -1890,7 +1890,7 @@ func TestLightClientHandler_GetLightClientEventBlock(t *testing.T) {
} }
minSignaturesRequired := uint64(100) minSignaturesRequired := uint64(100)
eventBlock, err := s.getLightClientEventBlock(ctx, minSignaturesRequired) eventBlock, err := s.suitableBlock(ctx, minSignaturesRequired)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, eventBlock) require.NotNil(t, eventBlock)
@@ -1997,7 +1997,7 @@ func TestLightClientHandler_GetLightClientEventBlock_NeedFetchParent(t *testing.
} }
minSignaturesRequired := uint64(100) minSignaturesRequired := uint64(100)
eventBlock, err := s.getLightClientEventBlock(ctx, minSignaturesRequired) eventBlock, err := s.suitableBlock(ctx, minSignaturesRequired)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, eventBlock) require.NotNil(t, eventBlock)

View File

@@ -5,7 +5,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"strconv"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -21,9 +20,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
v1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1"
v2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" v2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2"
"github.com/prysmaticlabs/prysm/v5/proto/migration"
"github.com/prysmaticlabs/prysm/v5/time/slots" "github.com/prysmaticlabs/prysm/v5/time/slots"
) )
@@ -349,128 +346,19 @@ func createLightClientBootstrapDeneb(ctx context.Context, state state.BeaconStat
return result, nil return result, nil
} }
// createLightClientUpdate - implements https://github. func newLightClientUpdateFromBeaconState(
// com/ethereum/consensus-specs/blob/d70dcd9926a4bbe987f1b4e65c3e05bd029fcfb8/specs/altair/light-client/full-node.md#create_light_client_update
// def create_light_client_update(state: BeaconState,
//
// block: SignedBeaconBlock,
// attested_state: BeaconState,
// finalized_block: Optional[SignedBeaconBlock]) -> LightClientUpdate:
// assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
// assert sum(block.message.body.sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
//
// assert state.slot == state.latest_block_header.slot
// header = state.latest_block_header.copy()
// header.state_root = hash_tree_root(state)
// assert hash_tree_root(header) == hash_tree_root(block.message)
// update_signature_period = compute_sync_committee_period(compute_epoch_at_slot(block.message.slot))
//
// assert attested_state.slot == attested_state.latest_block_header.slot
// attested_header = attested_state.latest_block_header.copy()
// attested_header.state_root = hash_tree_root(attested_state)
// assert hash_tree_root(attested_header) == block.message.parent_root
// update_attested_period = compute_sync_committee_period(compute_epoch_at_slot(attested_header.slot))
//
// # `next_sync_committee` is only useful if the message is signed by the current sync committee
// if update_attested_period == update_signature_period:
// next_sync_committee = attested_state.next_sync_committee
// next_sync_committee_branch = compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX)
// else:
// next_sync_committee = SyncCommittee()
// next_sync_committee_branch = [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]
//
// # Indicate finality whenever possible
// if finalized_block is not None:
// if finalized_block.message.slot != GENESIS_SLOT:
// finalized_header = BeaconBlockHeader(
// slot=finalized_block.message.slot,
// proposer_index=finalized_block.message.proposer_index,
// parent_root=finalized_block.message.parent_root,
// state_root=finalized_block.message.state_root,
// body_root=hash_tree_root(finalized_block.message.body),
// )
// assert hash_tree_root(finalized_header) == attested_state.finalized_checkpoint.root
// else:
// assert attested_state.finalized_checkpoint.root == Bytes32()
// finalized_header = BeaconBlockHeader()
// finality_branch = compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX)
// else:
// finalized_header = BeaconBlockHeader()
// finality_branch = [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]
//
// return LightClientUpdate(
// attested_header=attested_header,
// next_sync_committee=next_sync_committee,
// next_sync_committee_branch=next_sync_committee_branch,
// finalized_header=finalized_header,
// finality_branch=finality_branch,
// sync_aggregate=block.message.body.sync_aggregate,
// signature_slot=block.message.slot,
// )
func createLightClientUpdate(
ctx context.Context, ctx context.Context,
state state.BeaconState, state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock, block interfaces.ReadOnlySignedBeaconBlock,
attestedState state.BeaconState, attestedState state.BeaconState,
finalizedBlock interfaces.ReadOnlySignedBeaconBlock) (*structs.LightClientUpdate, error) { finalizedBlock interfaces.ReadOnlySignedBeaconBlock,
result, err := lightclient.NewLightClientFinalityUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock) ) (*structs.LightClientUpdate, error) {
result, err := lightclient.NewLightClientUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Generate next sync committee and proof return structs.LightClientUpdateFromConsensus(result)
var nextSyncCommittee *v2.SyncCommittee
var nextSyncCommitteeBranch [][]byte
// update_signature_period = compute_sync_committee_period(compute_epoch_at_slot(block.message.slot))
updateSignaturePeriod := slots.SyncCommitteePeriod(slots.ToEpoch(block.Block().Slot()))
// update_attested_period = compute_sync_committee_period(compute_epoch_at_slot(attested_header.slot))
resultAttestedHeaderBeacon, err := result.AttestedHeader.GetBeacon()
if err != nil {
return nil, errors.Wrap(err, "could not get attested header beacon")
}
updateAttestedPeriod := slots.SyncCommitteePeriod(slots.ToEpoch(resultAttestedHeaderBeacon.Slot))
if updateAttestedPeriod == updateSignaturePeriod {
tempNextSyncCommittee, err := attestedState.NextSyncCommittee()
if err != nil {
return nil, errors.Wrap(err, "could not get next sync committee")
}
nextSyncCommittee = &v2.SyncCommittee{
Pubkeys: tempNextSyncCommittee.Pubkeys,
AggregatePubkey: tempNextSyncCommittee.AggregatePubkey,
}
nextSyncCommitteeBranch, err = attestedState.NextSyncCommitteeProof(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not get next sync committee proof")
}
} else {
syncCommitteeSize := params.BeaconConfig().SyncCommitteeSize
pubKeys := make([][]byte, syncCommitteeSize)
for i := uint64(0); i < syncCommitteeSize; i++ {
pubKeys[i] = make([]byte, fieldparams.BLSPubkeyLength)
}
nextSyncCommittee = &v2.SyncCommittee{
Pubkeys: pubKeys,
AggregatePubkey: make([]byte, fieldparams.BLSPubkeyLength),
}
nextSyncCommitteeBranch = make([][]byte, fieldparams.NextSyncCommitteeBranchDepth)
for i := 0; i < fieldparams.NextSyncCommitteeBranchDepth; i++ {
nextSyncCommitteeBranch[i] = make([]byte, fieldparams.RootLength)
}
}
result.NextSyncCommittee = nextSyncCommittee
result.NextSyncCommitteeBranch = nextSyncCommitteeBranch
res, err := newLightClientUpdateToJSON(result)
if err != nil {
return nil, errors.Wrap(err, "could not convert light client update to JSON")
}
return res, nil
} }
func newLightClientFinalityUpdateFromBeaconState( func newLightClientFinalityUpdateFromBeaconState(
@@ -478,99 +366,28 @@ func newLightClientFinalityUpdateFromBeaconState(
state state.BeaconState, state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock, block interfaces.ReadOnlySignedBeaconBlock,
attestedState state.BeaconState, attestedState state.BeaconState,
finalizedBlock interfaces.ReadOnlySignedBeaconBlock) (*structs.LightClientUpdate, error) { finalizedBlock interfaces.ReadOnlySignedBeaconBlock,
) (*structs.LightClientFinalityUpdate, error) {
result, err := lightclient.NewLightClientFinalityUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock) result, err := lightclient.NewLightClientFinalityUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
res, err := newLightClientUpdateToJSON(result) return structs.LightClientFinalityUpdateFromConsensus(result)
if err != nil {
return nil, errors.Wrap(err, "could not convert light client update to JSON")
}
return res, nil
} }
func newLightClientOptimisticUpdateFromBeaconState( func newLightClientOptimisticUpdateFromBeaconState(
ctx context.Context, ctx context.Context,
state state.BeaconState, state state.BeaconState,
block interfaces.ReadOnlySignedBeaconBlock, block interfaces.ReadOnlySignedBeaconBlock,
attestedState state.BeaconState) (*structs.LightClientUpdate, error) { attestedState state.BeaconState,
) (*structs.LightClientOptimisticUpdate, error) {
result, err := lightclient.NewLightClientOptimisticUpdateFromBeaconState(ctx, state, block, attestedState) result, err := lightclient.NewLightClientOptimisticUpdateFromBeaconState(ctx, state, block, attestedState)
if err != nil { if err != nil {
return nil, err return nil, err
} }
res, err := newLightClientUpdateToJSON(result) return structs.LightClientOptimisticUpdateFromConsensus(result)
if err != nil {
return nil, errors.Wrap(err, "could not convert light client update to JSON")
}
return res, nil
}
func branchToJSON(branchBytes [][]byte) []string {
if branchBytes == nil {
return nil
}
branch := make([]string, len(branchBytes))
for i, root := range branchBytes {
branch[i] = hexutil.Encode(root)
}
return branch
}
func syncAggregateToJSON(input *v1.SyncAggregate) *structs.SyncAggregate {
if input == nil {
return nil
}
return &structs.SyncAggregate{
SyncCommitteeBits: hexutil.Encode(input.SyncCommitteeBits),
SyncCommitteeSignature: hexutil.Encode(input.SyncCommitteeSignature),
}
}
func newLightClientUpdateToJSON(input *v2.LightClientUpdate) (*structs.LightClientUpdate, error) {
if input == nil {
return nil, errors.New("input is nil")
}
var nextSyncCommittee *structs.SyncCommittee
if input.NextSyncCommittee != nil {
nextSyncCommittee = structs.SyncCommitteeFromConsensus(migration.V2SyncCommitteeToV1Alpha1(input.NextSyncCommittee))
}
var finalizedHeader *structs.BeaconBlockHeader
if input.FinalizedHeader != nil {
inputFinalizedHeaderBeacon, err := input.FinalizedHeader.GetBeacon()
if err != nil {
return nil, errors.Wrap(err, "could not get finalized header beacon")
}
finalizedHeader = structs.BeaconBlockHeaderFromConsensus(migration.V1HeaderToV1Alpha1(inputFinalizedHeaderBeacon))
}
inputAttestedHeaderBeacon, err := input.AttestedHeader.GetBeacon()
if err != nil {
return nil, errors.Wrap(err, "could not get attested header beacon")
}
attestedHeaderJson, err := json.Marshal(&structs.LightClientHeader{Beacon: structs.BeaconBlockHeaderFromConsensus(migration.V1HeaderToV1Alpha1(inputAttestedHeaderBeacon))})
if err != nil {
return nil, errors.Wrap(err, "could not convert attested header to raw message")
}
finalizedHeaderJson, err := json.Marshal(&structs.LightClientHeader{Beacon: finalizedHeader})
if err != nil {
return nil, errors.Wrap(err, "could not convert finalized header to raw message")
}
result := &structs.LightClientUpdate{
AttestedHeader: attestedHeaderJson,
NextSyncCommittee: nextSyncCommittee,
NextSyncCommitteeBranch: branchToJSON(input.NextSyncCommitteeBranch),
FinalizedHeader: finalizedHeaderJson,
FinalityBranch: branchToJSON(input.FinalityBranch),
SyncAggregate: syncAggregateToJSON(input.SyncAggregate),
SignatureSlot: strconv.FormatUint(uint64(input.SignatureSlot), 10),
}
return result, nil
} }
func IsSyncCommitteeUpdate(update *v2.LightClientUpdate) bool { func IsSyncCommitteeUpdate(update *v2.LightClientUpdate) bool {

View File

@@ -9,6 +9,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpbv1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1"
ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/require"
@@ -295,8 +296,8 @@ func (l *TestLightClient) SetupTestDeneb(blinded bool) *TestLightClient {
return l return l
} }
func (l *TestLightClient) CheckAttestedHeader(update *ethpbv2.LightClientUpdate) { func (l *TestLightClient) CheckAttestedHeader(container *ethpbv2.LightClientHeaderContainer) {
updateAttestedHeaderBeacon, err := update.AttestedHeader.GetBeacon() updateAttestedHeaderBeacon, err := container.GetBeacon()
require.NoError(l.T, err) require.NoError(l.T, err)
require.Equal(l.T, l.AttestedHeader.Slot, updateAttestedHeaderBeacon.Slot, "Attested header slot is not equal") require.Equal(l.T, l.AttestedHeader.Slot, updateAttestedHeaderBeacon.Slot, "Attested header slot is not equal")
require.Equal(l.T, l.AttestedHeader.ProposerIndex, updateAttestedHeaderBeacon.ProposerIndex, "Attested header proposer index is not equal") require.Equal(l.T, l.AttestedHeader.ProposerIndex, updateAttestedHeaderBeacon.ProposerIndex, "Attested header proposer index is not equal")
@@ -308,9 +309,9 @@ func (l *TestLightClient) CheckAttestedHeader(update *ethpbv2.LightClientUpdate)
require.DeepSSZEqual(l.T, attestedStateRoot[:], updateAttestedHeaderBeacon.StateRoot, "Attested header state root is not equal") require.DeepSSZEqual(l.T, attestedStateRoot[:], updateAttestedHeaderBeacon.StateRoot, "Attested header state root is not equal")
} }
func (l *TestLightClient) CheckSyncAggregate(update *ethpbv2.LightClientUpdate) { func (l *TestLightClient) CheckSyncAggregate(sa *ethpbv1.SyncAggregate) {
syncAggregate, err := l.Block.Block().Body().SyncAggregate() syncAggregate, err := l.Block.Block().Body().SyncAggregate()
require.NoError(l.T, err) require.NoError(l.T, err)
require.DeepSSZEqual(l.T, syncAggregate.SyncCommitteeBits, update.SyncAggregate.SyncCommitteeBits, "SyncAggregate bits is not equal") require.DeepSSZEqual(l.T, syncAggregate.SyncCommitteeBits, sa.SyncCommitteeBits, "SyncAggregate bits is not equal")
require.DeepSSZEqual(l.T, syncAggregate.SyncCommitteeSignature, update.SyncAggregate.SyncCommitteeSignature, "SyncAggregate signature is not equal") require.DeepSSZEqual(l.T, syncAggregate.SyncCommitteeSignature, sa.SyncCommitteeSignature, "SyncAggregate signature is not equal")
} }