Skip to content

Commit

Permalink
fix segmentation fault in E2E when light-client feature flag is enabl…
Browse files Browse the repository at this point in the history
…ed (#14699)

* use `blockEpoch` in `BlockToLightClientHeader`

* deps

* dont use `EmptyExecutionPayload()`

* use original logic

* changelog

* add feature flag check

* fix

* fix error messages

* update `BlockToLightClientHeader` tests

* changelog

* deps
  • Loading branch information
rupam-04 authored Dec 10, 2024
1 parent 0d810a1 commit a0791d7
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 29 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
- Added a Prometheus error counter metric for HTTP requests to track beacon node requests.
- Added a Prometheus error counter metric for SSE requests.
- Save light client updates and bootstraps in DB.
- Added more comprehensive tests for `BlockToLightClientHeader`. [PR](https://github.com/prysmaticlabs/prysm/pull/14699)

### Changed

Expand Down Expand Up @@ -112,6 +113,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
- P2P: Avoid infinite loop when looking for peers in small networks.
- Fixed another rollback bug due to a context deadline.
- Fix checkpoint sync bug on holesky. [pr](https://github.com/prysmaticlabs/prysm/pull/14689)
- Fix segmentation fault in E2E when light-client feature flag is enabled. [PR](https://github.com/prysmaticlabs/prysm/pull/14699)


### Security
Expand Down
3 changes: 2 additions & 1 deletion beacon-chain/core/light-client/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/execution:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
Expand All @@ -31,8 +30,10 @@ go_test(
deps = [
":go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
Expand Down
22 changes: 12 additions & 10 deletions beacon-chain/core/light-client/lightclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"reflect"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/execution"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
Expand Down Expand Up @@ -473,15 +472,18 @@ func BlockToLightClientHeader(
var payloadProof [][]byte

if blockEpoch < params.BeaconConfig().CapellaForkEpoch {
var ok bool

p, err := execution.EmptyExecutionPayload(version.Deneb)
if err != nil {
return nil, errors.Wrap(err, "could not get payload header")
}
payloadHeader, ok = p.(*enginev1.ExecutionPayloadHeaderDeneb)
if !ok {
return nil, errors.Wrapf(err, "payload header type %T is not %T", payloadHeader, &enginev1.ExecutionPayloadHeaderDeneb{})
payloadHeader = &enginev1.ExecutionPayloadHeaderDeneb{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
PrevRandao: make([]byte, fieldparams.RootLength),
ExtraData: make([]byte, 0),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
TransactionsRoot: make([]byte, fieldparams.RootLength),
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
}
payloadProof = emptyPayloadProof()
} else {
Expand Down
204 changes: 198 additions & 6 deletions beacon-chain/core/light-client/lightclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import (
"github.com/pkg/errors"
lightClient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensustypes "github.com/prysmaticlabs/prysm/v5/consensus-types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
v11 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
pb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
Expand Down Expand Up @@ -357,7 +359,11 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Altair", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair()

header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().AltairForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block,
)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

Expand All @@ -376,7 +382,11 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Bellatrix", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestBellatrix()

header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().BellatrixForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block,
)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

Expand All @@ -396,7 +406,11 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Non-Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false)

header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().CapellaForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block,
)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

Expand Down Expand Up @@ -453,7 +467,11 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(true)

header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().CapellaForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block,
)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

Expand Down Expand Up @@ -512,7 +530,11 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Non-Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false)

header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().DenebForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block,
)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

Expand Down Expand Up @@ -577,7 +599,11 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
t.Run("Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(true)

header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block)
header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().DenebForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block,
)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

Expand Down Expand Up @@ -771,6 +797,172 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal")
})
})

t.Run("Capella fork with Altair block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair()

header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().CapellaForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

parentRoot := l.Block.Block().ParentRoot()
stateRoot := l.Block.Block().StateRoot()
bodyRoot, err := l.Block.Block().Body().HashTreeRoot()
require.NoError(t, err)

require.Equal(t, l.Block.Block().Slot(), header.Beacon().Slot, "Slot is not equal")
require.Equal(t, l.Block.Block().ProposerIndex(), header.Beacon().ProposerIndex, "Proposer index is not equal")
require.DeepSSZEqual(t, parentRoot[:], header.Beacon().ParentRoot, "Parent root is not equal")
require.DeepSSZEqual(t, stateRoot[:], header.Beacon().StateRoot, "State root is not equal")
require.DeepSSZEqual(t, bodyRoot[:], header.Beacon().BodyRoot, "Body root is not equal")
})

t.Run("Deneb fork with Altair block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestAltair()

header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().DenebForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

parentRoot := l.Block.Block().ParentRoot()
stateRoot := l.Block.Block().StateRoot()
bodyRoot, err := l.Block.Block().Body().HashTreeRoot()
require.NoError(t, err)

require.Equal(t, l.Block.Block().Slot(), header.Beacon().Slot, "Slot is not equal")
require.Equal(t, l.Block.Block().ProposerIndex(), header.Beacon().ProposerIndex, "Proposer index is not equal")
require.DeepSSZEqual(t, parentRoot[:], header.Beacon().ParentRoot, "Parent root is not equal")
require.DeepSSZEqual(t, stateRoot[:], header.Beacon().StateRoot, "State root is not equal")
require.DeepSSZEqual(t, bodyRoot[:], header.Beacon().BodyRoot, "Body root is not equal")
})

t.Run("Deneb fork with Capella block", func(t *testing.T) {
t.Run("Non-Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false)

header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().DenebForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

parentRoot := l.Block.Block().ParentRoot()
stateRoot := l.Block.Block().StateRoot()
bodyRoot, err := l.Block.Block().Body().HashTreeRoot()
require.NoError(t, err)

payload, err := l.Block.Block().Body().Execution()
require.NoError(t, err)

transactionsRoot, err := lightClient.ComputeTransactionsRoot(payload)
require.NoError(t, err)

withdrawalsRoot, err := lightClient.ComputeWithdrawalsRoot(payload)
require.NoError(t, err)

executionHeader := &v11.ExecutionPayloadHeaderDeneb{
ParentHash: payload.ParentHash(),
FeeRecipient: payload.FeeRecipient(),
StateRoot: payload.StateRoot(),
ReceiptsRoot: payload.ReceiptsRoot(),
LogsBloom: payload.LogsBloom(),
PrevRandao: payload.PrevRandao(),
BlockNumber: payload.BlockNumber(),
GasLimit: payload.GasLimit(),
GasUsed: payload.GasUsed(),
Timestamp: payload.Timestamp(),
ExtraData: payload.ExtraData(),
BaseFeePerGas: payload.BaseFeePerGas(),
BlockHash: payload.BlockHash(),
TransactionsRoot: transactionsRoot,
WithdrawalsRoot: withdrawalsRoot,
}

executionPayloadProof, err := blocks.PayloadProof(l.Ctx, l.Block.Block())
require.NoError(t, err)

require.Equal(t, l.Block.Block().Slot(), header.Beacon().Slot, "Slot is not equal")
require.Equal(t, l.Block.Block().ProposerIndex(), header.Beacon().ProposerIndex, "Proposer index is not equal")
require.DeepSSZEqual(t, parentRoot[:], header.Beacon().ParentRoot, "Parent root is not equal")
require.DeepSSZEqual(t, stateRoot[:], header.Beacon().StateRoot, "State root is not equal")
require.DeepSSZEqual(t, bodyRoot[:], header.Beacon().BodyRoot, "Body root is not equal")

headerExecution, err := header.Execution()
require.NoError(t, err)
require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal")

headerExecutionBranch, err := header.ExecutionBranch()
require.NoError(t, err)
require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal")
})

t.Run("Blinded Beacon Block", func(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(true)

header, err := lightClient.BlockToLightClientHeader(
l.Ctx,
primitives.Slot(params.BeaconConfig().DenebForkEpoch)*params.BeaconConfig().SlotsPerEpoch,
l.Block)
require.NoError(t, err)
require.NotNil(t, header, "header is nil")

parentRoot := l.Block.Block().ParentRoot()
stateRoot := l.Block.Block().StateRoot()
bodyRoot, err := l.Block.Block().Body().HashTreeRoot()
require.NoError(t, err)

payload, err := l.Block.Block().Body().Execution()
require.NoError(t, err)

transactionsRoot, err := payload.TransactionsRoot()
require.NoError(t, err)

withdrawalsRoot, err := payload.WithdrawalsRoot()
require.NoError(t, err)

executionHeader := &v11.ExecutionPayloadHeaderDeneb{
ParentHash: payload.ParentHash(),
FeeRecipient: payload.FeeRecipient(),
StateRoot: payload.StateRoot(),
ReceiptsRoot: payload.ReceiptsRoot(),
LogsBloom: payload.LogsBloom(),
PrevRandao: payload.PrevRandao(),
BlockNumber: payload.BlockNumber(),
GasLimit: payload.GasLimit(),
GasUsed: payload.GasUsed(),
Timestamp: payload.Timestamp(),
ExtraData: payload.ExtraData(),
BaseFeePerGas: payload.BaseFeePerGas(),
BlockHash: payload.BlockHash(),
TransactionsRoot: transactionsRoot,
WithdrawalsRoot: withdrawalsRoot,
}

executionPayloadProof, err := blocks.PayloadProof(l.Ctx, l.Block.Block())
require.NoError(t, err)

require.Equal(t, l.Block.Block().Slot(), header.Beacon().Slot, "Slot is not equal")
require.Equal(t, l.Block.Block().ProposerIndex(), header.Beacon().ProposerIndex, "Proposer index is not equal")
require.DeepSSZEqual(t, parentRoot[:], header.Beacon().ParentRoot, "Parent root is not equal")
require.DeepSSZEqual(t, stateRoot[:], header.Beacon().StateRoot, "State root is not equal")
require.DeepSSZEqual(t, bodyRoot[:], header.Beacon().BodyRoot, "Body root is not equal")

headerExecution, err := header.Execution()
require.NoError(t, err)
require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal")

headerExecutionBranch, err := header.ExecutionBranch()
require.NoError(t, err)
require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal")
})
})
}

func convertArrayToSlice(arr [4][32]uint8) [][]uint8 {
Expand Down
8 changes: 4 additions & 4 deletions consensus-types/light-client/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (h *bootstrapAltair) Header() interfaces.LightClientHeader {
func (h *bootstrapAltair) SetHeader(header interfaces.LightClientHeader) error {
p, ok := (header.Proto()).(*pb.LightClientHeaderAltair)
if !ok {
return fmt.Errorf("header type %T is not %T", p, &pb.LightClientHeaderAltair{})
return fmt.Errorf("header type %T is not %T", header.Proto(), &pb.LightClientHeaderAltair{})
}
h.p.Header = p
h.header = header
Expand Down Expand Up @@ -188,7 +188,7 @@ func (h *bootstrapCapella) Header() interfaces.LightClientHeader {
func (h *bootstrapCapella) SetHeader(header interfaces.LightClientHeader) error {
p, ok := (header.Proto()).(*pb.LightClientHeaderCapella)
if !ok {
return fmt.Errorf("header type %T is not %T", p, &pb.LightClientHeaderCapella{})
return fmt.Errorf("header type %T is not %T", header.Proto(), &pb.LightClientHeaderCapella{})
}
h.p.Header = p
h.header = header
Expand Down Expand Up @@ -286,7 +286,7 @@ func (h *bootstrapDeneb) Header() interfaces.LightClientHeader {
func (h *bootstrapDeneb) SetHeader(header interfaces.LightClientHeader) error {
p, ok := (header.Proto()).(*pb.LightClientHeaderDeneb)
if !ok {
return fmt.Errorf("header type %T is not %T", p, &pb.LightClientHeaderDeneb{})
return fmt.Errorf("header type %T is not %T", header.Proto(), &pb.LightClientHeaderDeneb{})
}
h.p.Header = p
h.header = header
Expand Down Expand Up @@ -384,7 +384,7 @@ func (h *bootstrapElectra) Header() interfaces.LightClientHeader {
func (h *bootstrapElectra) SetHeader(header interfaces.LightClientHeader) error {
p, ok := (header.Proto()).(*pb.LightClientHeaderDeneb)
if !ok {
return fmt.Errorf("header type %T is not %T", p, &pb.LightClientHeaderDeneb{})
return fmt.Errorf("header type %T is not %T", header.Proto(), &pb.LightClientHeaderDeneb{})
}
h.p.Header = p
h.header = header
Expand Down
Loading

0 comments on commit a0791d7

Please sign in to comment.