Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement block_to_light_client_header #14433

Merged
merged 13 commits into from
Sep 12, 2024
3 changes: 3 additions & 0 deletions beacon-chain/core/light-client/lightclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,9 @@ func BlockToLightClientHeaderCapella(ctx context.Context, block interfaces.ReadO
return nil, err
}
withdrawalsRoot, err := ComputeWithdrawalsRoot(payload)
if err != nil {
return nil, err
}

executionHeader := &v11.ExecutionPayloadHeaderCapella{
ParentHash: payload.ParentHash(),
Expand Down
130 changes: 122 additions & 8 deletions beacon-chain/core/light-client/lightclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateCapella(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella()
l := util.NewTestLightClient(t).SetupTestCapella(false)

update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState)
require.NoError(t, err)
Expand Down Expand Up @@ -48,7 +48,7 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateAltair(t *test
}

func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateDeneb(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb()
l := util.NewTestLightClient(t).SetupTestDeneb(false)

update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState)
require.NoError(t, err)
Expand All @@ -63,7 +63,7 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconStateDeneb(t *testi
require.DeepSSZEqual(t, ([][]byte)(nil), update.FinalityBranch, "Finality branch is not nil")
}
func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateCapella(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella()
l := util.NewTestLightClient(t).SetupTestCapella(false)
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, nil)
require.NoError(t, err)
require.NotNil(t, update, "update is nil")
Expand Down Expand Up @@ -116,7 +116,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateAltair(t *testin
}

func TestLightClient_NewLightClientFinalityUpdateFromBeaconStateDeneb(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb()
l := util.NewTestLightClient(t).SetupTestDeneb(false)

update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, nil)
require.NoError(t, err)
Expand Down Expand Up @@ -162,8 +162,8 @@ func TestLightClient_BlockToLightClientHeaderAltair(t *testing.T) {
require.DeepSSZEqual(t, bodyRoot[:], header.Beacon.BodyRoot, "Body root is not equal")
}

func TestLightClient_BlockToLightClientHeaderCapella(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella()
func TestLightClient_BlockToLightClientHeaderCapella_NonBlindedBeaconBlock(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(false)

header, err := lightClient.BlockToLightClientHeaderCapella(l.Ctx, l.Block)
require.NoError(t, err)
Expand Down Expand Up @@ -215,8 +215,61 @@ func TestLightClient_BlockToLightClientHeaderCapella(t *testing.T) {
require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs are not equal")
}

func TestLightClient_BlockToLightClientHeaderDeneb(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb()
func TestLightClient_BlockToLightClientHeaderCapella_BlindedBeaconBlock(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestCapella(true)

header, err := lightClient.BlockToLightClientHeaderCapella(l.Ctx, 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.ExecutionPayloadHeaderCapella{
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")

require.DeepSSZEqual(t, executionHeader, header.Execution, "Execution headers are not equal")

require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs are not equal")
}

func TestLightClient_BlockToLightClientHeaderDeneb_NonBlindedBeaconBlock(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(false)

header, err := lightClient.BlockToLightClientHeaderDeneb(l.Ctx, l.Block)
require.NoError(t, err)
Expand Down Expand Up @@ -276,4 +329,65 @@ func TestLightClient_BlockToLightClientHeaderDeneb(t *testing.T) {
require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs are not equal")
}

func TestLightClient_BlockToLightClientHeaderDeneb_BlindedBeaconBlock(t *testing.T) {
l := util.NewTestLightClient(t).SetupTestDeneb(true)

header, err := lightClient.BlockToLightClientHeaderDeneb(l.Ctx, 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)

blobGasUsed, err := payload.BlobGasUsed()
require.NoError(t, err)

excessBlobGas, err := payload.ExcessBlobGas()
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,
BlobGasUsed: blobGasUsed,
ExcessBlobGas: excessBlobGas,
}

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")

require.DeepSSZEqual(t, executionHeader, header.Execution, "Execution headers are not equal")

require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs are not equal")
}

// TODO - add finality update tests with non-nil finalized block for different versions
169 changes: 119 additions & 50 deletions testing/util/lightclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func NewTestLightClient(t *testing.T) *TestLightClient {
return &TestLightClient{T: t}
}

func (l *TestLightClient) SetupTestCapella() *TestLightClient {
func (l *TestLightClient) SetupTestCapella(blinded bool) *TestLightClient {
ctx := context.Background()

slot := primitives.Slot(params.BeaconConfig().CapellaForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1)
Expand Down Expand Up @@ -66,37 +66,71 @@ func (l *TestLightClient) SetupTestCapella() *TestLightClient {
parentRoot, err := signedParent.Block().HashTreeRoot()
require.NoError(l.T, err)

block := NewBeaconBlockCapella()
block.Block.Slot = slot
block.Block.ParentRoot = parentRoot[:]
if blinded {
block := NewBlindedBeaconBlockCapella()
block.Block.Slot = slot
block.Block.ParentRoot = parentRoot[:]

for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ {
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
}
for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ {
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
}

signedBlock, err := blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)
signedBlock, err := blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)

h, err := signedBlock.Header()
require.NoError(l.T, err)
h, err := signedBlock.Header()
require.NoError(l.T, err)

err = state.SetLatestBlockHeader(h.Header)
require.NoError(l.T, err)
stateRoot, err := state.HashTreeRoot(ctx)
require.NoError(l.T, err)
err = state.SetLatestBlockHeader(h.Header)
require.NoError(l.T, err)
stateRoot, err := state.HashTreeRoot(ctx)
require.NoError(l.T, err)

// get a new signed block so the root is updated with the new state root
block.Block.StateRoot = stateRoot[:]
signedBlock, err = blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)
// get a new signed block so the root is updated with the new state root
block.Block.StateRoot = stateRoot[:]
signedBlock, err = blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)

l.State = state
l.AttestedState = attestedState
l.AttestedHeader = attestedHeader
l.Block = signedBlock
l.Ctx = ctx
l.State = state
l.AttestedState = attestedState
l.AttestedHeader = attestedHeader
l.Block = signedBlock
l.Ctx = ctx

return l
return l
} else {
block := NewBeaconBlockCapella()
block.Block.Slot = slot
block.Block.ParentRoot = parentRoot[:]

for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ {
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
}

signedBlock, err := blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)

h, err := signedBlock.Header()
require.NoError(l.T, err)

err = state.SetLatestBlockHeader(h.Header)
require.NoError(l.T, err)
stateRoot, err := state.HashTreeRoot(ctx)
require.NoError(l.T, err)

// get a new signed block so the root is updated with the new state root
block.Block.StateRoot = stateRoot[:]
signedBlock, err = blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)

l.State = state
l.AttestedState = attestedState
l.AttestedHeader = attestedHeader
l.Block = signedBlock
l.Ctx = ctx

return l
}
}

func (l *TestLightClient) SetupTestAltair() *TestLightClient {
Expand Down Expand Up @@ -170,7 +204,7 @@ func (l *TestLightClient) SetupTestAltair() *TestLightClient {
return l
}

func (l *TestLightClient) SetupTestDeneb() *TestLightClient {
func (l *TestLightClient) SetupTestDeneb(blinded bool) *TestLightClient {
ctx := context.Background()

slot := primitives.Slot(params.BeaconConfig().DenebForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1)
Expand Down Expand Up @@ -208,37 +242,72 @@ func (l *TestLightClient) SetupTestDeneb() *TestLightClient {
parentRoot, err := signedParent.Block().HashTreeRoot()
require.NoError(l.T, err)

block := NewBeaconBlockDeneb()
block.Block.Slot = slot
block.Block.ParentRoot = parentRoot[:]
if blinded {
block := NewBlindedBeaconBlockDeneb()
block.Message.Slot = slot
block.Message.ParentRoot = parentRoot[:]

for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ {
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
}
for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ {
block.Message.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
}

signedBlock, err := blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)
signedBlock, err := blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)

h, err := signedBlock.Header()
require.NoError(l.T, err)
h, err := signedBlock.Header()
require.NoError(l.T, err)

err = state.SetLatestBlockHeader(h.Header)
require.NoError(l.T, err)
stateRoot, err := state.HashTreeRoot(ctx)
require.NoError(l.T, err)
err = state.SetLatestBlockHeader(h.Header)
require.NoError(l.T, err)
stateRoot, err := state.HashTreeRoot(ctx)
require.NoError(l.T, err)

// get a new signed block so the root is updated with the new state root
block.Block.StateRoot = stateRoot[:]
signedBlock, err = blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)
// get a new signed block so the root is updated with the new state root
block.Message.StateRoot = stateRoot[:]
signedBlock, err = blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)

l.State = state
l.AttestedState = attestedState
l.AttestedHeader = attestedHeader
l.Block = signedBlock
l.Ctx = ctx
l.State = state
l.AttestedState = attestedState
l.AttestedHeader = attestedHeader
l.Block = signedBlock
l.Ctx = ctx

return l
return l
} else {

block := NewBeaconBlockDeneb()
block.Block.Slot = slot
block.Block.ParentRoot = parentRoot[:]

for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ {
block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
}

signedBlock, err := blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)

h, err := signedBlock.Header()
require.NoError(l.T, err)

err = state.SetLatestBlockHeader(h.Header)
require.NoError(l.T, err)
stateRoot, err := state.HashTreeRoot(ctx)
require.NoError(l.T, err)

// get a new signed block so the root is updated with the new state root
block.Block.StateRoot = stateRoot[:]
signedBlock, err = blocks.NewSignedBeaconBlock(block)
require.NoError(l.T, err)

l.State = state
l.AttestedState = attestedState
l.AttestedHeader = attestedHeader
l.Block = signedBlock
l.Ctx = ctx

return l
}
}

func (l *TestLightClient) CheckAttestedHeader(update *ethpbv2.LightClientUpdate) {
Expand Down
Loading