diff --git a/beacon-chain/core/light-client/BUILD.bazel b/beacon-chain/core/light-client/BUILD.bazel index 327e1922055..ecf6b8f71a7 100644 --- a/beacon-chain/core/light-client/BUILD.bazel +++ b/beacon-chain/core/light-client/BUILD.bazel @@ -35,6 +35,7 @@ go_test( "//consensus-types/blocks:go_default_library", "//encoding/ssz:go_default_library", "//proto/engine/v1:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", "//testing/require:go_default_library", "//testing/util:go_default_library", "@com_github_pkg_errors//:go_default_library", diff --git a/beacon-chain/core/light-client/lightclient.go b/beacon-chain/core/light-client/lightclient.go index 6a822012949..f8df29ba6b6 100644 --- a/beacon-chain/core/light-client/lightclient.go +++ b/beacon-chain/core/light-client/lightclient.go @@ -159,7 +159,7 @@ func NewLightClientUpdateFromBeaconState( updateAttestedPeriod := slots.SyncCommitteePeriod(slots.ToEpoch(attestedBlock.Block().Slot())) // update = LightClientUpdate() - result, err := createDefaultLightClientUpdate(currentSlot) + result, err := CreateDefaultLightClientUpdate(currentSlot) if err != nil { return nil, errors.Wrap(err, "could not create default light client update") } @@ -243,7 +243,7 @@ func NewLightClientUpdateFromBeaconState( return result, nil } -func createDefaultLightClientUpdate(currentSlot primitives.Slot) (interfaces.LightClientUpdate, error) { +func CreateDefaultLightClientUpdate(currentSlot primitives.Slot) (interfaces.LightClientUpdate, error) { currentEpoch := slots.ToEpoch(currentSlot) syncCommitteeSize := params.BeaconConfig().SyncCommitteeSize @@ -278,24 +278,42 @@ func createDefaultLightClientUpdate(currentSlot primitives.Slot) (interfaces.Lig var m proto.Message if currentEpoch < params.BeaconConfig().CapellaForkEpoch { m = &pb.LightClientUpdateAltair{ + AttestedHeader: &pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{}, + }, NextSyncCommittee: nextSyncCommittee, NextSyncCommitteeBranch: nextSyncCommitteeBranch, FinalityBranch: finalityBranch, } } else if currentEpoch < params.BeaconConfig().DenebForkEpoch { m = &pb.LightClientUpdateCapella{ + AttestedHeader: &pb.LightClientHeaderCapella{ + Beacon: &pb.BeaconBlockHeader{}, + Execution: &enginev1.ExecutionPayloadHeaderCapella{}, + ExecutionBranch: executionBranch, + }, NextSyncCommittee: nextSyncCommittee, NextSyncCommitteeBranch: nextSyncCommitteeBranch, FinalityBranch: finalityBranch, } } else if currentEpoch < params.BeaconConfig().ElectraForkEpoch { m = &pb.LightClientUpdateDeneb{ + AttestedHeader: &pb.LightClientHeaderDeneb{ + Beacon: &pb.BeaconBlockHeader{}, + Execution: &enginev1.ExecutionPayloadHeaderDeneb{}, + ExecutionBranch: executionBranch, + }, NextSyncCommittee: nextSyncCommittee, NextSyncCommitteeBranch: nextSyncCommitteeBranch, FinalityBranch: finalityBranch, } } else { m = &pb.LightClientUpdateElectra{ + AttestedHeader: &pb.LightClientHeaderDeneb{ + Beacon: &pb.BeaconBlockHeader{}, + Execution: &enginev1.ExecutionPayloadHeaderDeneb{}, + ExecutionBranch: executionBranch, + }, NextSyncCommittee: nextSyncCommittee, NextSyncCommitteeBranch: nextSyncCommitteeBranch, FinalityBranch: finalityBranch, @@ -372,15 +390,18 @@ func BlockToLightClientHeader( var payloadProof [][]byte if blockEpoch < params.BeaconConfig().CapellaForkEpoch { - var ok bool - - p, err := execution.EmptyExecutionPayload(version.Capella) - if err != nil { - return nil, errors.Wrap(err, "could not get payload header") - } - payloadHeader, ok = p.(*enginev1.ExecutionPayloadHeaderCapella) - if !ok { - return nil, errors.Wrapf(err, "payload header type %T is not %T", payloadHeader, &enginev1.ExecutionPayloadHeaderCapella{}) + payloadHeader = &enginev1.ExecutionPayloadHeaderCapella{ + 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 { diff --git a/beacon-chain/core/light-client/lightclient_test.go b/beacon-chain/core/light-client/lightclient_test.go index eb6ab441708..7d787b1cb52 100644 --- a/beacon-chain/core/light-client/lightclient_test.go +++ b/beacon-chain/core/light-client/lightclient_test.go @@ -1,16 +1,17 @@ package light_client_test import ( + "reflect" "testing" "github.com/pkg/errors" + lightClient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" consensustypes "github.com/prysmaticlabs/prysm/v5/consensus-types" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/encoding/ssz" v11 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" - - lightClient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" + pb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/util" ) @@ -19,39 +20,39 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T) t.Run("Altair", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestAltair() - update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock) + update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock) 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") + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") - l.CheckSyncAggregate(update.SyncAggregate) - l.CheckAttestedHeader(update.AttestedHeader) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) }) t.Run("Capella", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestCapella(false) - update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock) + update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock) 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") + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") - l.CheckSyncAggregate(update.SyncAggregate) - l.CheckAttestedHeader(update.AttestedHeader) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) }) t.Run("Deneb", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestDeneb(false) - update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock) + update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock) 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") + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") - l.CheckSyncAggregate(update.SyncAggregate) - l.CheckAttestedHeader(update.AttestedHeader) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) }) } @@ -60,33 +61,33 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { l := util.NewTestLightClient(t).SetupTestAltair() t.Run("FinalizedBlock Not Nil", func(t *testing.T) { - update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) 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") + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") - l.CheckSyncAggregate(update.SyncAggregate) - l.CheckAttestedHeader(update.AttestedHeader) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) finalizedBlockHeader, err := l.FinalizedBlock.Header() require.NoError(t, err) //zeroHash := params.BeaconConfig().ZeroHash[:] - require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") - updateFinalizedHeaderBeacon, err := update.FinalizedHeader.GetBeacon() - require.NoError(t, err) + require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil") + require.Equal(t, reflect.TypeOf(update.FinalizedHeader().Proto()), reflect.TypeOf(&pb.LightClientHeaderAltair{}), "Finalized header is not Altair") + updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon() require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal") require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal") - require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") + require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch()), "Invalid finality branch leaves") finalityBranch, err := l.AttestedState.FinalizedRootProof(l.Ctx) require.NoError(t, err) - for i, leaf := range update.FinalityBranch { - require.DeepSSZEqual(t, finalityBranch[i], leaf, "Leaf is not equal") + for i, leaf := range update.FinalityBranch() { + require.DeepSSZEqual(t, finalityBranch[i], leaf[:], "Leaf is not equal") } }) }) @@ -95,30 +96,30 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { t.Run("FinalizedBlock Not Nil", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestCapella(false) - update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) 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") + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") - l.CheckSyncAggregate(update.SyncAggregate) - l.CheckAttestedHeader(update.AttestedHeader) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) finalizedBlockHeader, err := l.FinalizedBlock.Header() require.NoError(t, err) - require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") - updateFinalizedHeaderBeacon, err := update.FinalizedHeader.GetBeacon() - require.NoError(t, err) + require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil") + require.Equal(t, reflect.TypeOf(update.FinalizedHeader().Proto()), reflect.TypeOf(&pb.LightClientHeaderCapella{}), "Finalized header is not Capella") + updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon() require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal") require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal") - require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") + require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch()), "Invalid finality branch leaves") finalityBranch, err := l.AttestedState.FinalizedRootProof(l.Ctx) require.NoError(t, err) - for i, leaf := range update.FinalityBranch { - require.DeepSSZEqual(t, finalityBranch[i], leaf, "Leaf is not equal") + for i, leaf := range update.FinalityBranch() { + require.DeepSSZEqual(t, finalityBranch[i], leaf[:], "Leaf is not equal") } // Check Execution BlockHash @@ -161,35 +162,37 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { TransactionsRoot: transactionsRoot, WithdrawalsRoot: withdrawalsRoot, } - require.DeepSSZEqual(t, execution, update.FinalizedHeader.GetHeaderCapella().Execution, "Finalized Block Execution is not equal") + updateExecution, err := update.FinalizedHeader().Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal") }) t.Run("FinalizedBlock In Previous Fork", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestCapellaFinalizedBlockAltair(false) - update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) 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") + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") - l.CheckSyncAggregate(update.SyncAggregate) - l.CheckAttestedHeader(update.AttestedHeader) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) finalizedBlockHeader, err := l.FinalizedBlock.Header() require.NoError(t, err) - require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") - updateFinalizedHeaderBeacon, err := update.FinalizedHeader.GetBeacon() - require.NoError(t, err) + require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil") + require.Equal(t, reflect.TypeOf(update.FinalizedHeader().Proto()), reflect.TypeOf(&pb.LightClientHeaderCapella{}), "Finalized header is not Capella") + updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon() require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal") require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal") - require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") + require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch()), "Invalid finality branch leaves") finalityBranch, err := l.AttestedState.FinalizedRootProof(l.Ctx) require.NoError(t, err) - for i, leaf := range update.FinalityBranch { - require.DeepSSZEqual(t, finalityBranch[i], leaf, "Leaf is not equal") + for i, leaf := range update.FinalityBranch() { + require.DeepSSZEqual(t, finalityBranch[i], leaf[:], "Leaf is not equal") } }) }) @@ -199,31 +202,30 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { t.Run("FinalizedBlock Not Nil", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestDeneb(false) - update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) 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") + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") - l.CheckSyncAggregate(update.SyncAggregate) - l.CheckAttestedHeader(update.AttestedHeader) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) //zeroHash := params.BeaconConfig().ZeroHash[:] finalizedBlockHeader, err := l.FinalizedBlock.Header() require.NoError(t, err) - require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") - updateFinalizedHeaderBeacon, err := update.FinalizedHeader.GetBeacon() - require.NoError(t, err) + require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil") + updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon() require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal") require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal") - require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") + require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch()), "Invalid finality branch leaves") finalityBranch, err := l.AttestedState.FinalizedRootProof(l.Ctx) require.NoError(t, err) - for i, leaf := range update.FinalityBranch { - require.DeepSSZEqual(t, finalityBranch[i], leaf, "Leaf is not equal") + for i, leaf := range update.FinalityBranch() { + require.DeepSSZEqual(t, finalityBranch[i], leaf[:], "Leaf is not equal") } // Check Execution BlockHash @@ -266,36 +268,38 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { TransactionsRoot: transactionsRoot, WithdrawalsRoot: withdrawalsRoot, } - require.DeepSSZEqual(t, execution, update.FinalizedHeader.GetHeaderDeneb().Execution, "Finalized Block Execution is not equal") + updateExecution, err := update.FinalizedHeader().Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal") }) t.Run("FinalizedBlock In Previous Fork", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestDenebFinalizedBlockCapella(false) - update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State.Slot(), l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock) 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") + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot(), "Signature slot is not equal") - l.CheckSyncAggregate(update.SyncAggregate) - l.CheckAttestedHeader(update.AttestedHeader) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) finalizedBlockHeader, err := l.FinalizedBlock.Header() require.NoError(t, err) - require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") - updateFinalizedHeaderBeacon, err := update.FinalizedHeader.GetBeacon() - require.NoError(t, err) + require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil") + updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon() + require.Equal(t, reflect.TypeOf(update.FinalizedHeader().Proto()), reflect.TypeOf(&pb.LightClientHeaderDeneb{}), "Finalized header is not Deneb") require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal") require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal") require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal") - require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") + require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch()), "Invalid finality branch leaves") finalityBranch, err := l.AttestedState.FinalizedRootProof(l.Ctx) require.NoError(t, err) - for i, leaf := range update.FinalityBranch { - require.DeepSSZEqual(t, finalityBranch[i], leaf, "Leaf is not equal") + for i, leaf := range update.FinalityBranch() { + require.DeepSSZEqual(t, finalityBranch[i], leaf[:], "Leaf is not equal") } // Check Execution BlockHash @@ -321,7 +325,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { } else { require.NoError(t, err) } - execution := &v11.ExecutionPayloadHeaderCapella{ + execution := &v11.ExecutionPayloadHeaderDeneb{ ParentHash: payloadInterface.ParentHash(), FeeRecipient: payloadInterface.FeeRecipient(), StateRoot: payloadInterface.StateRoot(), @@ -338,7 +342,9 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { TransactionsRoot: transactionsRoot, WithdrawalsRoot: withdrawalsRoot, } - require.DeepSSZEqual(t, execution, update.FinalizedHeader.GetHeaderCapella().Execution, "Finalized Block Execution is not equal") + updateExecution, err := update.FinalizedHeader().Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal") }) }) } @@ -347,9 +353,8 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { t.Run("Altair", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestAltair() - container, err := lightClient.BlockToLightClientHeader(l.Block) + header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block) require.NoError(t, err) - header := container.GetHeaderAltair() require.NotNil(t, header, "header is nil") parentRoot := l.Block.Block().ParentRoot() @@ -357,19 +362,18 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { 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") + 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("Bellatrix", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestBellatrix() - container, err := lightClient.BlockToLightClientHeader(l.Block) + header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block) require.NoError(t, err) - header := container.GetHeaderAltair() require.NotNil(t, header, "header is nil") parentRoot := l.Block.Block().ParentRoot() @@ -377,20 +381,19 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { 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") + 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("Capella", func(t *testing.T) { t.Run("Non-Blinded Beacon Block", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestCapella(false) - container, err := lightClient.BlockToLightClientHeader(l.Block) + header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block) require.NoError(t, err) - header := container.GetHeaderCapella() require.NotNil(t, header, "header is nil") parentRoot := l.Block.Block().ParentRoot() @@ -428,23 +431,26 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { 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.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") + headerExecution, err := header.Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal") - require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs 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) - container, err := lightClient.BlockToLightClientHeader(l.Block) + header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block) require.NoError(t, err) - header := container.GetHeaderCapella() require.NotNil(t, header, "header is nil") parentRoot := l.Block.Block().ParentRoot() @@ -482,15 +488,19 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { 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.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") + headerExecution, err := header.Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal") - require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs are not equal") + headerExecutionBranch, err := header.ExecutionBranch() + require.NoError(t, err) + require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal") }) }) @@ -498,9 +508,8 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { t.Run("Non-Blinded Beacon Block", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestDeneb(false) - container, err := lightClient.BlockToLightClientHeader(l.Block) + header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block) require.NoError(t, err) - header := container.GetHeaderDeneb() require.NotNil(t, header, "header is nil") parentRoot := l.Block.Block().ParentRoot() @@ -546,23 +555,26 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { 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.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") + headerExecution, err := header.Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal") - require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs 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).SetupTestDeneb(true) - container, err := lightClient.BlockToLightClientHeader(l.Block) + header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block) require.NoError(t, err) - header := container.GetHeaderDeneb() require.NotNil(t, header, "header is nil") parentRoot := l.Block.Block().ParentRoot() @@ -608,15 +620,19 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { 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.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") + headerExecution, err := header.Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal") - require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs are not equal") + headerExecutionBranch, err := header.ExecutionBranch() + require.NoError(t, err) + require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal") }) }) @@ -624,9 +640,8 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { t.Run("Non-Blinded Beacon Block", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestElectra(false) - container, err := lightClient.BlockToLightClientHeader(l.Block) + header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block) require.NoError(t, err) - header := container.GetHeaderDeneb() require.NotNil(t, header, "header is nil") parentRoot := l.Block.Block().ParentRoot() @@ -672,23 +687,26 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { 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.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") + headerExecution, err := header.Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal") - require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs 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).SetupTestElectra(true) - container, err := lightClient.BlockToLightClientHeader(l.Block) + header, err := lightClient.BlockToLightClientHeader(l.Ctx, l.State.Slot(), l.Block) require.NoError(t, err) - header := container.GetHeaderDeneb() require.NotNil(t, header, "header is nil") parentRoot := l.Block.Block().ParentRoot() @@ -734,15 +752,27 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { 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.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") + headerExecution, err := header.Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal") - require.DeepSSZEqual(t, executionPayloadProof, header.ExecutionBranch, "Execution payload proofs 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 { + slice := make([][]uint8, len(arr)) + for i := range arr { + slice[i] = arr[i][:] + } + return slice +} diff --git a/beacon-chain/db/kv/BUILD.bazel b/beacon-chain/db/kv/BUILD.bazel index 0a95e714aeb..714bfb1f26d 100644 --- a/beacon-chain/db/kv/BUILD.bazel +++ b/beacon-chain/db/kv/BUILD.bazel @@ -112,18 +112,18 @@ go_test( "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", "//consensus-types/interfaces:go_default_library", + "//consensus-types/light-client:go_default_library", "//consensus-types/primitives:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/dbval:go_default_library", "//proto/engine/v1:go_default_library", - "//proto/eth/v1:go_default_library", - "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/testing:go_default_library", "//runtime/version:go_default_library", "//testing/assert:go_default_library", "//testing/require:go_default_library", "//testing/util:go_default_library", + "//time/slots:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_golang_snappy//:go_default_library", "@com_github_pkg_errors//:go_default_library", diff --git a/beacon-chain/db/kv/lightclient.go b/beacon-chain/db/kv/lightclient.go index d7bbc179681..f4cf94668d3 100644 --- a/beacon-chain/db/kv/lightclient.go +++ b/beacon-chain/db/kv/lightclient.go @@ -96,6 +96,7 @@ func encodeLightClientUpdate(update interfaces.LightClientUpdate) ([]byte, error return nil, errors.Wrap(err, "could not marshal light client update") } fullEnc := make([]byte, len(key)+len(enc)) + copy(fullEnc, key) copy(fullEnc[len(key):], enc) return snappy.Encode(nil, fullEnc), nil } diff --git a/beacon-chain/db/kv/lightclient_test.go b/beacon-chain/db/kv/lightclient_test.go index dd2df2acac8..fc20d8c27a0 100644 --- a/beacon-chain/db/kv/lightclient_test.go +++ b/beacon-chain/db/kv/lightclient_test.go @@ -2,221 +2,218 @@ package kv import ( "context" + "fmt" + "math/rand" "testing" + fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" + "github.com/prysmaticlabs/prysm/v5/config/params" + "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" + light_client "github.com/prysmaticlabs/prysm/v5/consensus-types/light-client" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" - ethpbv1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" - ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" + pb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/require" + "github.com/prysmaticlabs/prysm/v5/time/slots" + "google.golang.org/protobuf/proto" ) +func createUpdate(t *testing.T, v int) (interfaces.LightClientUpdate, error) { + config := params.BeaconConfig() + var slot primitives.Slot + var header interfaces.LightClientHeader + var err error + + sampleRoot := make([]byte, 32) + for i := 0; i < 32; i++ { + sampleRoot[i] = byte(i) + } + + sampleExecutionBranch := make([][]byte, fieldparams.ExecutionBranchDepth) + for i := 0; i < 4; i++ { + sampleExecutionBranch[i] = make([]byte, 32) + for j := 0; j < 32; j++ { + sampleExecutionBranch[i][j] = byte(i + j) + } + } + + switch v { + case version.Altair: + slot = primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1, + ProposerIndex: primitives.ValidatorIndex(rand.Int()), + ParentRoot: sampleRoot, + StateRoot: sampleRoot, + BodyRoot: sampleRoot, + }, + }) + require.NoError(t, err) + case version.Capella: + slot = primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderCapella{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1, + ProposerIndex: primitives.ValidatorIndex(rand.Int()), + ParentRoot: sampleRoot, + StateRoot: sampleRoot, + BodyRoot: sampleRoot, + }, + Execution: &enginev1.ExecutionPayloadHeaderCapella{ + 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), + }, + ExecutionBranch: sampleExecutionBranch, + }) + require.NoError(t, err) + case version.Deneb: + slot = primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderDeneb{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1, + ProposerIndex: primitives.ValidatorIndex(rand.Int()), + ParentRoot: sampleRoot, + StateRoot: sampleRoot, + BodyRoot: sampleRoot, + }, + Execution: &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), + }, + ExecutionBranch: sampleExecutionBranch, + }) + require.NoError(t, err) + case version.Electra: + slot = primitives.Slot(config.ElectraForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderDeneb{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1, + ProposerIndex: primitives.ValidatorIndex(rand.Int()), + ParentRoot: sampleRoot, + StateRoot: sampleRoot, + BodyRoot: sampleRoot, + }, + Execution: &enginev1.ExecutionPayloadHeaderElectra{ + 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), + }, + ExecutionBranch: sampleExecutionBranch, + }) + require.NoError(t, err) + default: + return nil, fmt.Errorf("unsupported version %s", version.String(v)) + } + + update, err := createDefaultLightClientUpdate(slot) + require.NoError(t, err) + update.SetSignatureSlot(slot - 1) + syncCommitteeBits := make([]byte, 64) + syncCommitteeSignature := make([]byte, 96) + update.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: syncCommitteeBits, + SyncCommitteeSignature: syncCommitteeSignature, + }) + + require.NoError(t, update.SetAttestedHeader(header)) + require.NoError(t, update.SetFinalizedHeader(header)) + + return update, nil +} + func TestStore_LightClientUpdate_CanSaveRetrieveAltair(t *testing.T) { db := setupDB(t) ctx := context.Background() - update := ðpbv2.LightClientUpdate{ - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{ - Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 1, - ParentRoot: []byte{1, 1, 1}, - StateRoot: []byte{1, 1, 1}, - BodyRoot: []byte{1, 1, 1}, - }, - }, - }, - }, - NextSyncCommittee: ðpbv2.SyncCommittee{ - Pubkeys: nil, - AggregatePubkey: nil, - }, - NextSyncCommitteeBranch: nil, - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{ - Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 1, - ParentRoot: []byte{1, 1, 1}, - StateRoot: []byte{1, 1, 1}, - BodyRoot: []byte{1, 1, 1}, - }, - }, - }, - }, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - } + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) period := uint64(1) - err := db.SaveLightClientUpdate(ctx, period, ðpbv2.LightClientUpdateWithVersion{ - Version: version.Altair, - Data: update, - }) + + err = db.SaveLightClientUpdate(ctx, period, update) require.NoError(t, err) retrievedUpdate, err := db.LightClientUpdate(ctx, period) require.NoError(t, err) - require.DeepEqual(t, update, retrievedUpdate.Data, "retrieved update does not match saved update") + require.DeepEqual(t, update, retrievedUpdate, "retrieved update does not match saved update") } func TestStore_LightClientUpdate_CanSaveRetrieveCapella(t *testing.T) { db := setupDB(t) ctx := context.Background() - update := ðpbv2.LightClientUpdate{ - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderCapella{ - HeaderCapella: ðpbv2.LightClientHeaderCapella{ - Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 1, - ParentRoot: []byte{1, 1, 1}, - StateRoot: []byte{1, 1, 1}, - BodyRoot: []byte{1, 1, 1}, - }, - Execution: &enginev1.ExecutionPayloadHeaderCapella{ - FeeRecipient: []byte{1, 2, 3}, - }, - ExecutionBranch: [][]byte{{1, 2, 3}, {4, 5, 6}}, - }, - }, - }, - NextSyncCommittee: ðpbv2.SyncCommittee{ - Pubkeys: nil, - AggregatePubkey: nil, - }, - NextSyncCommitteeBranch: nil, - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderCapella{ - HeaderCapella: ðpbv2.LightClientHeaderCapella{ - Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 1, - ParentRoot: []byte{1, 1, 1}, - StateRoot: []byte{1, 1, 1}, - BodyRoot: []byte{1, 1, 1}, - }, - Execution: nil, - ExecutionBranch: nil, - }, - }, - }, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - } + update, err := createUpdate(t, version.Capella) + require.NoError(t, err) period := uint64(1) - err := db.SaveLightClientUpdate(ctx, period, ðpbv2.LightClientUpdateWithVersion{ - Version: version.Capella, - Data: update, - }) + err = db.SaveLightClientUpdate(ctx, period, update) require.NoError(t, err) retrievedUpdate, err := db.LightClientUpdate(ctx, period) require.NoError(t, err) - require.DeepEqual(t, update, retrievedUpdate.Data, "retrieved update does not match saved update") + require.DeepEqual(t, update, retrievedUpdate, "retrieved update does not match saved update") } func TestStore_LightClientUpdate_CanSaveRetrieveDeneb(t *testing.T) { db := setupDB(t) ctx := context.Background() - update := ðpbv2.LightClientUpdate{ - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderDeneb{ - HeaderDeneb: ðpbv2.LightClientHeaderDeneb{ - Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 1, - ParentRoot: []byte{1, 1, 1}, - StateRoot: []byte{1, 1, 1}, - BodyRoot: []byte{1, 1, 1}, - }, - Execution: &enginev1.ExecutionPayloadHeaderDeneb{ - FeeRecipient: []byte{1, 2, 3}, - }, - ExecutionBranch: [][]byte{{1, 2, 3}, {4, 5, 6}}, - }, - }, - }, - NextSyncCommittee: ðpbv2.SyncCommittee{ - Pubkeys: nil, - AggregatePubkey: nil, - }, - NextSyncCommitteeBranch: nil, - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderDeneb{ - HeaderDeneb: ðpbv2.LightClientHeaderDeneb{ - Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 1, - ParentRoot: []byte{1, 1, 1}, - StateRoot: []byte{1, 1, 1}, - BodyRoot: []byte{1, 1, 1}, - }, - Execution: nil, - ExecutionBranch: nil, - }, - }, - }, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - } + update, err := createUpdate(t, version.Deneb) + require.NoError(t, err) period := uint64(1) - err := db.SaveLightClientUpdate(ctx, period, ðpbv2.LightClientUpdateWithVersion{ - Version: version.Deneb, - Data: update, - }) + err = db.SaveLightClientUpdate(ctx, period, update) require.NoError(t, err) retrievedUpdate, err := db.LightClientUpdate(ctx, period) require.NoError(t, err) - require.DeepEqual(t, update, retrievedUpdate.Data, "retrieved update does not match saved update") + require.DeepEqual(t, update, retrievedUpdate, "retrieved update does not match saved update") +} + +func TestStore_LightClientUpdate_CanSaveRetrieveElectra(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + update, err := createUpdate(t, version.Electra) + require.NoError(t, err) + period := uint64(1) + err = db.SaveLightClientUpdate(ctx, period, update) + require.NoError(t, err) + + retrievedUpdate, err := db.LightClientUpdate(ctx, period) + require.NoError(t, err) + require.DeepEqual(t, update, retrievedUpdate, "retrieved update does not match saved update") } func TestStore_LightClientUpdates_canRetrieveRange(t *testing.T) { db := setupDB(t) ctx := context.Background() - updates := []*ethpbv2.LightClientUpdateWithVersion{ - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 8, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 9, - }, - }, + updates := make([]interfaces.LightClientUpdate, 0, 3) + for i := 1; i <= 3; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) } for i, update := range updates { @@ -229,7 +226,7 @@ func TestStore_LightClientUpdates_canRetrieveRange(t *testing.T) { require.NoError(t, err) require.Equal(t, len(updates), len(retrievedUpdatesMap), "retrieved updates do not match saved updates") for i, update := range updates { - require.Equal(t, update.Data.SignatureSlot, retrievedUpdatesMap[uint64(i+1)].Data.SignatureSlot, "retrieved update does not match saved update") + require.DeepEqual(t, update, retrievedUpdatesMap[uint64(i+1)], "retrieved update does not match saved update") } } @@ -237,43 +234,11 @@ func TestStore_LightClientUpdates_canRetrieveRange(t *testing.T) { func TestStore_LightClientUpdate_EndPeriodSmallerThanStartPeriod(t *testing.T) { db := setupDB(t) ctx := context.Background() - updates := []*ethpbv2.LightClientUpdateWithVersion{ - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 8, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 9, - }, - }, + updates := make([]interfaces.LightClientUpdate, 0, 3) + for i := 1; i <= 3; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) } for i, update := range updates { @@ -292,43 +257,11 @@ func TestStore_LightClientUpdate_EndPeriodSmallerThanStartPeriod(t *testing.T) { func TestStore_LightClientUpdate_EndPeriodEqualToStartPeriod(t *testing.T) { db := setupDB(t) ctx := context.Background() - updates := []*ethpbv2.LightClientUpdateWithVersion{ - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 8, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 9, - }, - }, + updates := make([]interfaces.LightClientUpdate, 0, 3) + for i := 1; i <= 3; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) } for i, update := range updates { @@ -340,53 +273,21 @@ func TestStore_LightClientUpdate_EndPeriodEqualToStartPeriod(t *testing.T) { retrievedUpdates, err := db.LightClientUpdates(ctx, 2, 2) require.NoError(t, err) require.Equal(t, 1, len(retrievedUpdates)) - require.Equal(t, updates[1].Data.SignatureSlot, retrievedUpdates[2].Data.SignatureSlot, "retrieved update does not match saved update") + require.DeepEqual(t, updates[1], retrievedUpdates[2], "retrieved update does not match saved update") } func TestStore_LightClientUpdate_StartPeriodBeforeFirstUpdate(t *testing.T) { db := setupDB(t) ctx := context.Background() - updates := []*ethpbv2.LightClientUpdateWithVersion{ - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 8, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 9, - }, - }, + updates := make([]interfaces.LightClientUpdate, 0, 3) + for i := 1; i <= 3; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) } for i, update := range updates { - err := db.SaveLightClientUpdate(ctx, uint64(i+2), update) + err := db.SaveLightClientUpdate(ctx, uint64(i+1), update) require.NoError(t, err) } @@ -395,50 +296,18 @@ func TestStore_LightClientUpdate_StartPeriodBeforeFirstUpdate(t *testing.T) { require.NoError(t, err) require.Equal(t, 3, len(retrievedUpdates)) for i, update := range updates { - require.Equal(t, update.Data.SignatureSlot, retrievedUpdates[uint64(i+2)].Data.SignatureSlot, "retrieved update does not match saved update") + require.DeepEqual(t, update, retrievedUpdates[uint64(i+1)], "retrieved update does not match saved update") } } func TestStore_LightClientUpdate_EndPeriodAfterLastUpdate(t *testing.T) { db := setupDB(t) ctx := context.Background() - updates := []*ethpbv2.LightClientUpdateWithVersion{ - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 8, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 9, - }, - }, + updates := make([]interfaces.LightClientUpdate, 0, 3) + for i := 1; i <= 3; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) } for i, update := range updates { @@ -451,50 +320,18 @@ func TestStore_LightClientUpdate_EndPeriodAfterLastUpdate(t *testing.T) { require.NoError(t, err) require.Equal(t, 3, len(retrievedUpdates)) for i, update := range updates { - require.Equal(t, update.Data.SignatureSlot, retrievedUpdates[uint64(i+1)].Data.SignatureSlot, "retrieved update does not match saved update") + require.DeepEqual(t, update, retrievedUpdates[uint64(i+1)], "retrieved update does not match saved update") } } func TestStore_LightClientUpdate_PartialUpdates(t *testing.T) { db := setupDB(t) ctx := context.Background() - updates := []*ethpbv2.LightClientUpdateWithVersion{ - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 8, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 9, - }, - }, + updates := make([]interfaces.LightClientUpdate, 0, 3) + for i := 1; i <= 3; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) } for i, update := range updates { @@ -507,100 +344,53 @@ func TestStore_LightClientUpdate_PartialUpdates(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, len(retrievedUpdates)) for i, update := range updates[:2] { - require.Equal(t, update.Data.SignatureSlot, retrievedUpdates[uint64(i+1)].Data.SignatureSlot, "retrieved update does not match saved update") + require.DeepEqual(t, update, retrievedUpdates[uint64(i+1)], "retrieved update does not match saved update") } } func TestStore_LightClientUpdate_MissingPeriods_SimpleData(t *testing.T) { db := setupDB(t) ctx := context.Background() - updates := []*ethpbv2.LightClientUpdateWithVersion{ - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 7, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 8, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 11, - }, - }, - { - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: 12, - }, - }, + updates := make([]interfaces.LightClientUpdate, 0, 4) + for i := 1; i <= 4; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) } - for _, update := range updates { - err := db.SaveLightClientUpdate(ctx, uint64(update.Data.SignatureSlot), update) + for i, update := range updates { + if i == 1 || i == 2 { + continue + } + err := db.SaveLightClientUpdate(ctx, uint64(i+1), update) require.NoError(t, err) } // Retrieve the updates - retrievedUpdates, err := db.LightClientUpdates(ctx, 7, 12) + retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 4) require.NoError(t, err) - require.Equal(t, 4, len(retrievedUpdates)) - for _, update := range updates { - require.Equal(t, update.Data.SignatureSlot, retrievedUpdates[uint64(update.Data.SignatureSlot)].Data.SignatureSlot, "retrieved update does not match saved update") - } + require.Equal(t, 2, len(retrievedUpdates)) + require.DeepEqual(t, updates[0], retrievedUpdates[uint64(1)], "retrieved update does not match saved update") + require.DeepEqual(t, updates[3], retrievedUpdates[uint64(4)], "retrieved update does not match saved update") // Retrieve the updates from the middle - retrievedUpdates, err = db.LightClientUpdates(ctx, 8, 12) + retrievedUpdates, err = db.LightClientUpdates(ctx, 2, 4) require.NoError(t, err) - require.Equal(t, 3, len(retrievedUpdates)) - require.Equal(t, updates[1].Data.SignatureSlot, retrievedUpdates[8].Data.SignatureSlot, "retrieved update does not match saved update") - require.Equal(t, updates[2].Data.SignatureSlot, retrievedUpdates[11].Data.SignatureSlot, "retrieved update does not match saved update") - require.Equal(t, updates[3].Data.SignatureSlot, retrievedUpdates[12].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, 1, len(retrievedUpdates)) + require.DeepEqual(t, updates[3], retrievedUpdates[4], "retrieved update does not match saved update") // Retrieve the updates from after the missing period - retrievedUpdates, err = db.LightClientUpdates(ctx, 11, 12) + retrievedUpdates, err = db.LightClientUpdates(ctx, 4, 4) require.NoError(t, err) - require.Equal(t, 2, len(retrievedUpdates)) - require.Equal(t, updates[2].Data.SignatureSlot, retrievedUpdates[11].Data.SignatureSlot, "retrieved update does not match saved update") - require.Equal(t, updates[3].Data.SignatureSlot, retrievedUpdates[12].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, 1, len(retrievedUpdates)) + require.DeepEqual(t, updates[3], retrievedUpdates[4], "retrieved update does not match saved update") //retrieve the updates from before the missing period to after the missing period - retrievedUpdates, err = db.LightClientUpdates(ctx, 3, 15) + retrievedUpdates, err = db.LightClientUpdates(ctx, 0, 6) require.NoError(t, err) - require.Equal(t, 4, len(retrievedUpdates)) - require.Equal(t, updates[0].Data.SignatureSlot, retrievedUpdates[7].Data.SignatureSlot, "retrieved update does not match saved update") - require.Equal(t, updates[1].Data.SignatureSlot, retrievedUpdates[8].Data.SignatureSlot, "retrieved update does not match saved update") - require.Equal(t, updates[2].Data.SignatureSlot, retrievedUpdates[11].Data.SignatureSlot, "retrieved update does not match saved update") - require.Equal(t, updates[3].Data.SignatureSlot, retrievedUpdates[12].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, 2, len(retrievedUpdates)) + require.DeepEqual(t, updates[0], retrievedUpdates[uint64(1)], "retrieved update does not match saved update") + require.DeepEqual(t, updates[3], retrievedUpdates[uint64(4)], "retrieved update does not match saved update") } func TestStore_LightClientUpdate_EmptyDB(t *testing.T) { @@ -613,177 +403,107 @@ func TestStore_LightClientUpdate_EmptyDB(t *testing.T) { require.Equal(t, 0, len(retrievedUpdates)) } -func TestStore_LightClientUpdate_MissingPeriodsAtTheEnd_SimpleData(t *testing.T) { +func TestStore_LightClientUpdate_RetrieveMissingPeriodDistributed(t *testing.T) { db := setupDB(t) ctx := context.Background() - - for i := 1; i < 4; i++ { - update := ðpbv2.LightClientUpdateWithVersion{ - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: primitives.Slot(uint64(i)), - }, - } - err := db.SaveLightClientUpdate(ctx, uint64(i), update) + updates := make([]interfaces.LightClientUpdate, 0, 5) + for i := 1; i <= 5; i++ { + update, err := createUpdate(t, version.Altair) require.NoError(t, err) + updates = append(updates, update) } - for i := 7; i < 10; i++ { - update := ðpbv2.LightClientUpdateWithVersion{ - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: primitives.Slot(uint64(i)), - }, + + for i, update := range updates { + if i == 1 || i == 3 { + continue } - err := db.SaveLightClientUpdate(ctx, uint64(i), update) + err := db.SaveLightClientUpdate(ctx, uint64(i+1), update) require.NoError(t, err) } - // Retrieve the updates from 1 to 5 - retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 5) + // Retrieve the updates + retrievedUpdates, err := db.LightClientUpdates(ctx, 0, 7) require.NoError(t, err) require.Equal(t, 3, len(retrievedUpdates)) - require.Equal(t, primitives.Slot(1), retrievedUpdates[1].Data.SignatureSlot, "retrieved update does not match saved update") - require.Equal(t, primitives.Slot(2), retrievedUpdates[2].Data.SignatureSlot, "retrieved update does not match saved update") - require.Equal(t, primitives.Slot(3), retrievedUpdates[3].Data.SignatureSlot, "retrieved update does not match saved update") - -} - -func setupLightClientTestDB(t *testing.T) (*Store, context.Context) { - db := setupDB(t) - ctx := context.Background() - - for i := 10; i < 101; i++ { // 10 to 100 - update := ðpbv2.LightClientUpdateWithVersion{ - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: primitives.Slot(uint64(i)), - }, - } - err := db.SaveLightClientUpdate(ctx, uint64(i), update) - require.NoError(t, err) - } - - for i := 110; i < 201; i++ { // 110 to 200 - update := ðpbv2.LightClientUpdateWithVersion{ - Version: 1, - Data: ðpbv2.LightClientUpdate{ - AttestedHeader: nil, - NextSyncCommittee: nil, - NextSyncCommitteeBranch: nil, - FinalizedHeader: nil, - FinalityBranch: nil, - SyncAggregate: nil, - SignatureSlot: primitives.Slot(uint64(i)), - }, - } - err := db.SaveLightClientUpdate(ctx, uint64(i), update) - require.NoError(t, err) - } - - return db, ctx + require.DeepEqual(t, updates[0], retrievedUpdates[uint64(1)], "retrieved update does not match saved update") + require.DeepEqual(t, updates[2], retrievedUpdates[uint64(3)], "retrieved update does not match saved update") + require.DeepEqual(t, updates[4], retrievedUpdates[uint64(5)], "retrieved update does not match saved update") } -func TestStore_LightClientUpdate_MissingPeriodsInTheMiddleDistributed(t *testing.T) { - db, ctx := setupLightClientTestDB(t) +func createDefaultLightClientUpdate(currentSlot primitives.Slot) (interfaces.LightClientUpdate, error) { + currentEpoch := slots.ToEpoch(currentSlot) - // Retrieve the updates - should fail because of missing periods in the middle - retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 300) - require.NoError(t, err) - require.Equal(t, 91*2, len(retrievedUpdates)) - for i := 10; i < 101; i++ { - require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + syncCommitteeSize := params.BeaconConfig().SyncCommitteeSize + pubKeys := make([][]byte, syncCommitteeSize) + for i := uint64(0); i < syncCommitteeSize; i++ { + pubKeys[i] = make([]byte, fieldparams.BLSPubkeyLength) } - for i := 110; i < 201; i++ { - require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + nextSyncCommittee := &pb.SyncCommittee{ + Pubkeys: pubKeys, + AggregatePubkey: make([]byte, fieldparams.BLSPubkeyLength), } -} - -func TestStore_LightClientUpdate_RetrieveValidRangeFromStart(t *testing.T) { - db, ctx := setupLightClientTestDB(t) - - // retrieve 1 to 100 - should work because all periods are present after the firstPeriodInDB > startPeriod - retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 100) - require.NoError(t, err) - require.Equal(t, 91, len(retrievedUpdates)) - for i := 10; i < 101; i++ { - require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + var nextSyncCommitteeBranch [][]byte + if currentEpoch >= params.BeaconConfig().ElectraForkEpoch { + nextSyncCommitteeBranch = make([][]byte, fieldparams.SyncCommitteeBranchDepthElectra) + } else { + nextSyncCommitteeBranch = make([][]byte, fieldparams.SyncCommitteeBranchDepth) } -} - -func TestStore_LightClientUpdate_RetrieveValidRangeInTheMiddle(t *testing.T) { - db, ctx := setupLightClientTestDB(t) - - // retrieve 110 to 200 - should work because all periods are present - retrievedUpdates, err := db.LightClientUpdates(ctx, 110, 200) - require.NoError(t, err) - require.Equal(t, 91, len(retrievedUpdates)) - for i := 110; i < 201; i++ { - require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + for i := 0; i < len(nextSyncCommitteeBranch); i++ { + nextSyncCommitteeBranch[i] = make([]byte, fieldparams.RootLength) } -} - -func TestStore_LightClientUpdate_MissingPeriodInTheMiddleConcentrated(t *testing.T) { - db, ctx := setupLightClientTestDB(t) - // retrieve 100 to 200 - retrievedUpdates, err := db.LightClientUpdates(ctx, 100, 200) - require.NoError(t, err) - require.Equal(t, 92, len(retrievedUpdates)) - require.Equal(t, primitives.Slot(100), retrievedUpdates[100].Data.SignatureSlot, "retrieved update does not match saved update") - for i := 110; i < 201; i++ { - require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + executionBranch := make([][]byte, fieldparams.ExecutionBranchDepth) + for i := 0; i < fieldparams.ExecutionBranchDepth; i++ { + executionBranch[i] = make([]byte, 32) } -} - -func TestStore_LightClientUpdate_MissingPeriodsAtTheEnd(t *testing.T) { - db, ctx := setupLightClientTestDB(t) - - // retrieve 10 to 109 - retrievedUpdates, err := db.LightClientUpdates(ctx, 10, 109) - require.NoError(t, err) - require.Equal(t, 91, len(retrievedUpdates)) - for i := 10; i < 101; i++ { - require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + finalityBranch := make([][]byte, fieldparams.FinalityBranchDepth) + for i := 0; i < fieldparams.FinalityBranchDepth; i++ { + finalityBranch[i] = make([]byte, 32) } -} - -func TestStore_LightClientUpdate_MissingPeriodsAtTheBeginning(t *testing.T) { - db, ctx := setupLightClientTestDB(t) - // retrieve 105 to 200 - retrievedUpdates, err := db.LightClientUpdates(ctx, 105, 200) - require.NoError(t, err) - require.Equal(t, 91, len(retrievedUpdates)) - for i := 110; i < 201; i++ { - require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + var m proto.Message + if currentEpoch < params.BeaconConfig().CapellaForkEpoch { + m = &pb.LightClientUpdateAltair{ + AttestedHeader: &pb.LightClientHeaderAltair{}, + NextSyncCommittee: nextSyncCommittee, + NextSyncCommitteeBranch: nextSyncCommitteeBranch, + FinalityBranch: finalityBranch, + } + } else if currentEpoch < params.BeaconConfig().DenebForkEpoch { + m = &pb.LightClientUpdateCapella{ + AttestedHeader: &pb.LightClientHeaderCapella{ + Beacon: &pb.BeaconBlockHeader{}, + Execution: &enginev1.ExecutionPayloadHeaderCapella{}, + ExecutionBranch: executionBranch, + }, + NextSyncCommittee: nextSyncCommittee, + NextSyncCommitteeBranch: nextSyncCommitteeBranch, + FinalityBranch: finalityBranch, + } + } else if currentEpoch < params.BeaconConfig().ElectraForkEpoch { + m = &pb.LightClientUpdateDeneb{ + AttestedHeader: &pb.LightClientHeaderDeneb{ + Beacon: &pb.BeaconBlockHeader{}, + Execution: &enginev1.ExecutionPayloadHeaderDeneb{}, + ExecutionBranch: executionBranch, + }, + NextSyncCommittee: nextSyncCommittee, + NextSyncCommitteeBranch: nextSyncCommitteeBranch, + FinalityBranch: finalityBranch, + } + } else { + m = &pb.LightClientUpdateElectra{ + AttestedHeader: &pb.LightClientHeaderDeneb{ + Beacon: &pb.BeaconBlockHeader{}, + Execution: &enginev1.ExecutionPayloadHeaderDeneb{}, + ExecutionBranch: executionBranch, + }, + NextSyncCommittee: nextSyncCommittee, + NextSyncCommitteeBranch: nextSyncCommitteeBranch, + FinalityBranch: finalityBranch, + } } -} - -func TestStore_LightClientUpdate_StartPeriodGreaterThanLastPeriod(t *testing.T) { - db, ctx := setupLightClientTestDB(t) - - // retrieve 300 to 400 - retrievedUpdates, err := db.LightClientUpdates(ctx, 300, 400) - require.NoError(t, err) - require.Equal(t, 0, len(retrievedUpdates)) + return light_client.NewWrappedUpdate(m) } diff --git a/beacon-chain/rpc/eth/light-client/BUILD.bazel b/beacon-chain/rpc/eth/light-client/BUILD.bazel index d0fa6cb7a80..f4bf65404a2 100644 --- a/beacon-chain/rpc/eth/light-client/BUILD.bazel +++ b/beacon-chain/rpc/eth/light-client/BUILD.bazel @@ -44,16 +44,18 @@ go_test( "//beacon-chain/blockchain/testing:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/light-client:go_default_library", + "//beacon-chain/db/testing:go_default_library", "//beacon-chain/rpc/testutil:go_default_library", "//beacon-chain/state:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", "//consensus-types/interfaces:go_default_library", + "//consensus-types/light-client:go_default_library", "//consensus-types/primitives:go_default_library", - "//proto/eth/v1:go_default_library", - "//proto/eth/v2:go_default_library", + "//proto/engine/v1:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//runtime/version:go_default_library", "//testing/assert:go_default_library", "//testing/require:go_default_library", "//testing/util:go_default_library", diff --git a/beacon-chain/rpc/eth/light-client/handlers.go b/beacon-chain/rpc/eth/light-client/handlers.go index 3bc6909486d..91746c5db13 100644 --- a/beacon-chain/rpc/eth/light-client/handlers.go +++ b/beacon-chain/rpc/eth/light-client/handlers.go @@ -11,10 +11,8 @@ import ( "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server/structs" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/shared" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" - types "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace" "github.com/prysmaticlabs/prysm/v5/network/httputil" "github.com/prysmaticlabs/prysm/v5/runtime/version" @@ -117,109 +115,34 @@ func (s *Server) GetLightClientUpdatesByRange(w http.ResponseWriter, req *http.R endPeriod = maxSlot / slotsPerPeriod } - // Populate updates - var updates []*structs.LightClientUpdateResponse - for period := startPeriod; period <= endPeriod; period++ { - // Get the last known state of the period, - // 1. We wish the block has a parent in the same period if possible - // 2. We wish the block has a state in the same period - lastSlotInPeriod := period*slotsPerPeriod + slotsPerPeriod - 1 - if lastSlotInPeriod > maxSlot { - lastSlotInPeriod = maxSlot - } - firstSlotInPeriod := period * slotsPerPeriod - - // Let's not use the first slot in the period, otherwise the attested header will be in previous period - firstSlotInPeriod++ - - var state state.BeaconState - var block interfaces.ReadOnlySignedBeaconBlock - for slot := lastSlotInPeriod; slot >= firstSlotInPeriod; slot-- { - state, err = s.Stater.StateBySlot(ctx, types.Slot(slot)) - if err != nil { - continue - } - - // Get the block - latestBlockHeader := state.LatestBlockHeader() - latestStateRoot, err := state.HashTreeRoot(ctx) - if err != nil { - continue - } - latestBlockHeader.StateRoot = latestStateRoot[:] - blockRoot, err := latestBlockHeader.HashTreeRoot() - if err != nil { - continue - } - - block, err = s.Blocker.Block(ctx, blockRoot[:]) - if err != nil || block == nil { - continue - } - - syncAggregate, err := block.Block().Body().SyncAggregate() - if err != nil || syncAggregate == nil { - continue - } - - if syncAggregate.SyncCommitteeBits.Count()*3 < config.SyncCommitteeSize*2 { - // Not enough votes - continue - } - - break - } + // get updates + updatesMap, err := s.BeaconDB.LightClientUpdates(ctx, startPeriod, endPeriod) + if err != nil { + httputil.HandleError(w, "Could not get light client updates from DB: "+err.Error(), http.StatusInternalServerError) + return + } - if block == nil { - // No valid block found for the period - continue - } + updates := make([]*structs.LightClientUpdateResponse, 0, len(updatesMap)) - // Get attested state - attestedRoot := block.Block().ParentRoot() - attestedBlock, err := s.Blocker.Block(ctx, attestedRoot[:]) - if err != nil || attestedBlock == nil { - continue + for i := startPeriod; i <= endPeriod; i++ { + update, ok := updatesMap[i] + if !ok { + // Only return the first contiguous range of updates + break } - attestedSlot := attestedBlock.Block().Slot() - attestedState, err := s.Stater.StateBySlot(ctx, attestedSlot) + updateJson, err := structs.LightClientUpdateFromConsensus(update) if err != nil { - continue + httputil.HandleError(w, "Could not convert light client update: "+err.Error(), http.StatusInternalServerError) + return } - // Get finalized block - var finalizedBlock interfaces.ReadOnlySignedBeaconBlock - finalizedCheckPoint := attestedState.FinalizedCheckpoint() - if finalizedCheckPoint != nil { - finalizedRoot := bytesutil.ToBytes32(finalizedCheckPoint.Root) - finalizedBlock, err = s.Blocker.Block(ctx, finalizedRoot[:]) - if err != nil { - finalizedBlock = nil - } + updateResponse := &structs.LightClientUpdateResponse{ + Version: version.String(update.Version()), + Data: updateJson, } - update, err := newLightClientUpdateFromBeaconState( - ctx, - s.ChainInfoFetcher.CurrentSlot(), - state, - block, - attestedState, - attestedBlock, - finalizedBlock, - ) - - if err == nil { - updates = append(updates, &structs.LightClientUpdateResponse{ - Version: version.String(attestedState.Version()), - Data: update, - }) - } - } - - if len(updates) == 0 { - httputil.HandleError(w, "no updates found", http.StatusNotFound) - return + updates = append(updates, updateResponse) } httputil.WriteJson(w, updates) diff --git a/beacon-chain/rpc/eth/light-client/handlers_test.go b/beacon-chain/rpc/eth/light-client/handlers_test.go index 342ea679ee3..449daa20ec9 100644 --- a/beacon-chain/rpc/eth/light-client/handlers_test.go +++ b/beacon-chain/rpc/eth/light-client/handlers_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "math/rand" "net/http" "net/http/httptest" "strconv" @@ -14,13 +15,19 @@ import ( "github.com/prysmaticlabs/prysm/v5/api/server/structs" mock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" + lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" + dbtesting "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/testutil" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" + fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" + light_client "github.com/prysmaticlabs/prysm/v5/consensus-types/light-client" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" + pb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/util" ) @@ -34,12 +41,14 @@ func TestLightClientHandler_GetLightClientBootstrap_Altair(t *testing.T) { mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot: l.State, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(stateRoot[:])) @@ -74,12 +83,14 @@ func TestLightClientHandler_GetLightClientBootstrap_Bellatrix(t *testing.T) { mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot: l.State, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(stateRoot[:])) @@ -114,12 +125,14 @@ func TestLightClientHandler_GetLightClientBootstrap_Capella(t *testing.T) { mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot: l.State, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(stateRoot[:])) @@ -154,12 +167,14 @@ func TestLightClientHandler_GetLightClientBootstrap_Deneb(t *testing.T) { mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot: l.State, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(stateRoot[:])) @@ -194,12 +209,14 @@ func TestLightClientHandler_GetLightClientBootstrap_Electra(t *testing.T) { mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot: l.State, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(stateRoot[:])) @@ -225,90 +242,37 @@ func TestLightClientHandler_GetLightClientBootstrap_Electra(t *testing.T) { require.NotNil(t, resp.Data.CurrentSyncCommitteeBranch) } +// GetLightClientByRange tests + func TestLightClientHandler_GetLightClientUpdatesByRangeAltair(t *testing.T) { helpers.ClearCache() ctx := context.Background() - config := params.BeaconConfig() - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateAltair() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) - - parent := util.NewBeaconBlockAltair() - parent.Block.Slot = slot.Sub(1) - - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header - - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 0 + params.OverrideBeaconConfig(config) - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) st, err := util.NewBeaconStateAltair() require.NoError(t, err) err = st.SetSlot(slot) require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + db := dbtesting.SetupDB(t) - block := util.NewBeaconBlockAltair() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } - - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) - - h, err := signedBlock.Header() - require.NoError(t, err) - - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(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) + update, err := createUpdate(t, version.Altair) require.NoError(t, err) - - root, err := block.Block.HashTreeRoot() + err = db.SaveLightClientUpdate(ctx, updatePeriod, update) require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} + mockChainService := &mock.ChainService{State: st} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, HeadFetcher: mockChainService, + BeaconDB: db, } startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) @@ -322,99 +286,42 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeAltair(t *testing.T) { var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - var respHeader structs.LightClientHeader - err = json.Unmarshal(resp.Updates[0].Data.AttestedHeader, &respHeader) - require.NoError(t, err) require.Equal(t, 1, len(resp.Updates)) require.Equal(t, "altair", resp.Updates[0].Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[0].Data) } func TestLightClientHandler_GetLightClientUpdatesByRangeCapella(t *testing.T) { helpers.ClearCache() ctx := context.Background() + params.SetupTestConfigCleanup(t) config := params.BeaconConfig() + config.AltairForkEpoch = 0 + config.CapellaForkEpoch = 1 + params.OverrideBeaconConfig(config) slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) - - parent := util.NewBeaconBlockCapella() - parent.Block.Slot = slot.Sub(1) - - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header - - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) - - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - st, err := util.NewBeaconStateCapella() require.NoError(t, err) err = st.SetSlot(slot) require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + db := dbtesting.SetupDB(t) - block := util.NewBeaconBlockCapella() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } - - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) - - h, err := signedBlock.Header() - require.NoError(t, err) - - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) + update, err := createUpdate(t, version.Capella) require.NoError(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) + err = db.SaveLightClientUpdate(ctx, updatePeriod, update) require.NoError(t, err) - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) - - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} + mockChainService := &mock.ChainService{State: st} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, HeadFetcher: mockChainService, + BeaconDB: db, } startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) @@ -428,99 +335,42 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeCapella(t *testing.T) { var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - var respHeader structs.LightClientHeaderCapella - err = json.Unmarshal(resp.Updates[0].Data.AttestedHeader, &respHeader) - require.NoError(t, err) require.Equal(t, 1, len(resp.Updates)) require.Equal(t, "capella", resp.Updates[0].Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[0].Data) } func TestLightClientHandler_GetLightClientUpdatesByRangeDeneb(t *testing.T) { helpers.ClearCache() ctx := context.Background() + params.SetupTestConfigCleanup(t) config := params.BeaconConfig() + config.AltairForkEpoch = 0 + config.CapellaForkEpoch = 1 + config.DenebForkEpoch = 2 + params.OverrideBeaconConfig(config) slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateDeneb() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) - - parent := util.NewBeaconBlockDeneb() - parent.Block.Slot = slot.Sub(1) - - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header - - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) - - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - st, err := util.NewBeaconStateDeneb() require.NoError(t, err) err = st.SetSlot(slot) require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + db := dbtesting.SetupDB(t) - block := util.NewBeaconBlockDeneb() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } - - signedBlock, err := blocks.NewSignedBeaconBlock(block) + update, err := createUpdate(t, version.Deneb) require.NoError(t, err) - - h, err := signedBlock.Header() + err = db.SaveLightClientUpdate(ctx, updatePeriod, update) require.NoError(t, err) - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(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(t, err) - - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) - - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} + mockChainService := &mock.ChainService{State: st} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, HeadFetcher: mockChainService, + BeaconDB: db, } startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) @@ -534,103 +384,53 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeDeneb(t *testing.T) { var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - var respHeader structs.LightClientHeaderDeneb - err = json.Unmarshal(resp.Updates[0].Data.AttestedHeader, &respHeader) - require.NoError(t, err) require.Equal(t, 1, len(resp.Updates)) require.Equal(t, "deneb", resp.Updates[0].Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[0].Data) } -func TestLightClientHandler_GetLightClientUpdatesByRange_TooBigInputCountAltair(t *testing.T) { +func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleAltair(t *testing.T) { helpers.ClearCache() ctx := context.Background() + params.SetupTestConfigCleanup(t) config := params.BeaconConfig() + config.AltairForkEpoch = 0 + config.EpochsPerSyncCommitteePeriod = 1 + params.OverrideBeaconConfig(config) slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateAltair() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) - - parent := util.NewBeaconBlockAltair() - parent.Block.Slot = slot.Sub(1) - - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header - - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) - - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - st, err := util.NewBeaconStateAltair() require.NoError(t, err) - err = st.SetSlot(slot) + headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 2 periods + err = st.SetSlot(headSlot) require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + db := dbtesting.SetupDB(t) - block := util.NewBeaconBlockAltair() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + updates := make([]interfaces.LightClientUpdate, 0) + for i := 1; i <= 2; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) } - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) - - h, err := signedBlock.Header() - require.NoError(t, err) - - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(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(t, err) - - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) - - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, + for _, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + require.NoError(t, err) + updatePeriod++ } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} + + mockChainService := &mock.ChainService{State: st} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, HeadFetcher: mockChainService, + BeaconDB: db, } - startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - count := 129 // config.MaxRequestLightClientUpdates is 128 - url := fmt.Sprintf("http://foo.com/?count=%d&start_period=%d", count, startPeriod) + startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) request := httptest.NewRequest("GET", url, nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} @@ -641,103 +441,116 @@ func TestLightClientHandler_GetLightClientUpdatesByRange_TooBigInputCountAltair( var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - var respHeader structs.LightClientHeader - err = json.Unmarshal(resp.Updates[0].Data.AttestedHeader, &respHeader) - require.NoError(t, err) - require.Equal(t, 1, len(resp.Updates)) // Even with big count input, the response is still the max available period, which is 1 in test case. - require.Equal(t, "altair", resp.Updates[0].Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + require.Equal(t, "altair", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } } -func TestLightClientHandler_GetLightClientUpdatesByRange_TooBigInputCountCapella(t *testing.T) { +func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleCapella(t *testing.T) { helpers.ClearCache() ctx := context.Background() + params.SetupTestConfigCleanup(t) config := params.BeaconConfig() + config.AltairForkEpoch = 0 + config.CapellaForkEpoch = 1 + config.EpochsPerSyncCommitteePeriod = 1 + params.OverrideBeaconConfig(config) slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateCapella() + st, err := util.NewBeaconStateAltair() require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) + headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 2 periods + err = st.SetSlot(headSlot) require.NoError(t, err) - parent := util.NewBeaconBlockCapella() - parent.Block.Slot = slot.Sub(1) + db := dbtesting.SetupDB(t) - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + updates := make([]interfaces.LightClientUpdate, 0) + for i := 0; i < 2; i++ { + update, err := createUpdate(t, version.Capella) + require.NoError(t, err) + updates = append(updates, update) + } - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + for _, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + require.NoError(t, err) + updatePeriod++ + } - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - st, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + s.GetLightClientUpdatesByRange(writer, request) - parentRoot, err := signedParent.Block().HashTreeRoot() + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - - block := util.NewBeaconBlockCapella() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] - - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + require.Equal(t, "capella", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) } +} - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) - - h, err := signedBlock.Header() - require.NoError(t, err) +func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleDeneb(t *testing.T) { + helpers.ClearCache() + ctx := context.Background() + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 0 + config.CapellaForkEpoch = 1 + config.DenebForkEpoch = 2 + config.EpochsPerSyncCommitteePeriod = 1 + params.OverrideBeaconConfig(config) + slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - err = st.SetLatestBlockHeader(h.Header) + st, err := util.NewBeaconStateAltair() require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) + headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) + err = st.SetSlot(headSlot) require.NoError(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(t, err) + db := dbtesting.SetupDB(t) - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, + updates := make([]interfaces.LightClientUpdate, 0) + for i := 0; i < 2; i++ { + update, err := createUpdate(t, version.Deneb) + require.NoError(t, err) + updates = append(updates, update) } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} + + for _, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + require.NoError(t, err) + updatePeriod++ + } + mockChainService := &mock.ChainService{State: st} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, HeadFetcher: mockChainService, + BeaconDB: db, } - startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - count := 129 // config.MaxRequestLightClientUpdates is 128 - url := fmt.Sprintf("http://foo.com/?count=%d&start_period=%d", count, startPeriod) + startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) request := httptest.NewRequest("GET", url, nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} @@ -748,103 +561,60 @@ func TestLightClientHandler_GetLightClientUpdatesByRange_TooBigInputCountCapella var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - var respHeader structs.LightClientHeaderCapella - err = json.Unmarshal(resp.Updates[0].Data.AttestedHeader, &respHeader) - require.NoError(t, err) - require.Equal(t, 1, len(resp.Updates)) // Even with big count input, the response is still the max available period, which is 1 in test case. - require.Equal(t, "capella", resp.Updates[0].Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + require.Equal(t, "deneb", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } } -func TestLightClientHandler_GetLightClientUpdatesByRange_TooBigInputCountDeneb(t *testing.T) { +func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleForksAltairCapella(t *testing.T) { helpers.ClearCache() ctx := context.Background() + params.SetupTestConfigCleanup(t) config := params.BeaconConfig() - slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + config.AltairForkEpoch = 0 + config.CapellaForkEpoch = 1 + config.EpochsPerSyncCommitteePeriod = 1 + params.OverrideBeaconConfig(config) + slotCapella := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + slotAltair := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateDeneb() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) - - parent := util.NewBeaconBlockDeneb() - parent.Block.Slot = slot.Sub(1) - - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header - - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) - - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) - - st, err := util.NewBeaconStateDeneb() + st, err := util.NewBeaconStateAltair() require.NoError(t, err) - err = st.SetSlot(slot) + headSlot := slotCapella.Add(1) + err = st.SetSlot(headSlot) require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + db := dbtesting.SetupDB(t) - block := util.NewBeaconBlockDeneb() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + updates := make([]interfaces.LightClientUpdate, 2) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } + updatePeriod := slotAltair.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - signedBlock, err := blocks.NewSignedBeaconBlock(block) + updates[0], err = createUpdate(t, version.Altair) require.NoError(t, err) - h, err := signedBlock.Header() + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[0]) require.NoError(t, err) - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(t, err) + updatePeriod = slotCapella.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - // get a new signed block so the root is updated with the new state root - block.Block.StateRoot = stateRoot[:] - signedBlock, err = blocks.NewSignedBeaconBlock(block) + updates[1], err = createUpdate(t, version.Capella) require.NoError(t, err) - root, err := block.Block.HashTreeRoot() + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[1]) require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} + mockChainService := &mock.ChainService{State: st} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, HeadFetcher: mockChainService, + BeaconDB: db, } - startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - count := 129 // config.MaxRequestLightClientUpdates is 128 - url := fmt.Sprintf("http://foo.com/?count=%d&start_period=%d", count, startPeriod) + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) request := httptest.NewRequest("GET", url, nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} @@ -855,104 +625,129 @@ func TestLightClientHandler_GetLightClientUpdatesByRange_TooBigInputCountDeneb(t var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - var respHeader structs.LightClientHeaderDeneb - err = json.Unmarshal(resp.Updates[0].Data.AttestedHeader, &respHeader) - require.NoError(t, err) - require.Equal(t, 1, len(resp.Updates)) // Even with big count input, the response is still the max available period, which is 1 in test case. - require.Equal(t, "deneb", resp.Updates[0].Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + if i < 1 { + require.Equal(t, "altair", resp.Updates[i].Version) + } else { + require.Equal(t, "capella", resp.Updates[i].Version) + } + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } } -// TODO - check for not having any blocks from the min period, and startPeriod being too early -func TestLightClientHandler_GetLightClientUpdatesByRange_TooEarlyPeriodAltair(t *testing.T) { +func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleForksCapellaDeneb(t *testing.T) { helpers.ClearCache() ctx := context.Background() + params.SetupTestConfigCleanup(t) config := params.BeaconConfig() - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + config.AltairForkEpoch = 0 + config.CapellaForkEpoch = 1 + config.DenebForkEpoch = 2 + config.EpochsPerSyncCommitteePeriod = 1 + params.OverrideBeaconConfig(config) + slotDeneb := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + slotCapella := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateAltair() + st, err := util.NewBeaconStateAltair() require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) + headSlot := slotDeneb.Add(1) + err = st.SetSlot(headSlot) require.NoError(t, err) - parent := util.NewBeaconBlockAltair() - parent.Block.Slot = slot.Sub(1) + db := dbtesting.SetupDB(t) - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 2) - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + updatePeriod := slotCapella.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + updates[0], err = createUpdate(t, version.Capella) require.NoError(t, err) - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[0]) require.NoError(t, err) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + updatePeriod = slotDeneb.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - parentRoot, err := signedParent.Block().HashTreeRoot() + updates[1], err = createUpdate(t, version.Deneb) require.NoError(t, err) - block := util.NewBeaconBlockAltair() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[1]) + require.NoError(t, err) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, } + startPeriod := 1 + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + s.GetLightClientUpdatesByRange(writer, request) - h, err := signedBlock.Header() + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + if i < 1 { + require.Equal(t, "capella", resp.Updates[i].Version) + } else { + require.Equal(t, "deneb", resp.Updates[i].Version) + } + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } +} - err = st.SetLatestBlockHeader(h.Header) +func TestLightClientHandler_GetLightClientUpdatesByRangeCountBiggerThanLimit(t *testing.T) { + helpers.ClearCache() + ctx := context.Background() + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 0 + config.EpochsPerSyncCommitteePeriod = 1 + config.MaxRequestLightClientUpdates = 2 + params.OverrideBeaconConfig(config) + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + + st, err := util.NewBeaconStateAltair() require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) + headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods + err = st.SetSlot(headSlot) require.NoError(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(t, err) + db := dbtesting.SetupDB(t) - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 3) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + + for i := 0; i < 3; i++ { + + updates[i], err = createUpdate(t, version.Altair) + require.NoError(t, err) + + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[i]) + require.NoError(t, err) + + updatePeriod++ } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} + + mockChainService := &mock.ChainService{State: st} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, HeadFetcher: mockChainService, + BeaconDB: db, } - startPeriod := 1 // very early period before Altair fork - count := 1 - url := fmt.Sprintf("http://foo.com/?count=%d&start_period=%d", count, startPeriod) + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=4&start_period=%d", startPeriod) request := httptest.NewRequest("GET", url, nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} @@ -963,104 +758,111 @@ func TestLightClientHandler_GetLightClientUpdatesByRange_TooEarlyPeriodAltair(t var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - var respHeader structs.LightClientHeader - err = json.Unmarshal(resp.Updates[0].Data.AttestedHeader, &respHeader) - require.NoError(t, err) - require.Equal(t, 1, len(resp.Updates)) - require.Equal(t, "altair", resp.Updates[0].Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + if i < 2 { + require.Equal(t, "altair", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } + } } -// TODO - same as above -func TestLightClientHandler_GetLightClientUpdatesByRange_TooBigCountAltair(t *testing.T) { +func TestLightClientHandler_GetLightClientUpdatesByRangeCountBiggerThanMax(t *testing.T) { helpers.ClearCache() ctx := context.Background() + params.SetupTestConfigCleanup(t) config := params.BeaconConfig() + config.AltairForkEpoch = 0 + config.EpochsPerSyncCommitteePeriod = 1 + config.MaxRequestLightClientUpdates = 2 + params.OverrideBeaconConfig(config) slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateAltair() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) + st, err := util.NewBeaconStateAltair() require.NoError(t, err) - - parent := util.NewBeaconBlockAltair() - parent.Block.Slot = slot.Sub(1) - - signedParent, err := blocks.NewSignedBeaconBlock(parent) + headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods + err = st.SetSlot(headSlot) require.NoError(t, err) - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + db := dbtesting.SetupDB(t) - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 3) - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + for i := 0; i < 3; i++ { + updates[i], err = createUpdate(t, version.Altair) + require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[i]) + require.NoError(t, err) - block := util.NewBeaconBlockAltair() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + updatePeriod++ + } - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, } + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=10&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + s.GetLightClientUpdatesByRange(writer, request) - h, err := signedBlock.Header() + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + if i < 2 { + require.Equal(t, "altair", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } + } +} - err = st.SetLatestBlockHeader(h.Header) +func TestLightClientHandler_GetLightClientUpdatesByRangeStartPeriodBeforeAltair(t *testing.T) { + helpers.ClearCache() + ctx := context.Background() + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 1 + config.EpochsPerSyncCommitteePeriod = 1 + params.OverrideBeaconConfig(config) + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + + st, err := util.NewBeaconStateAltair() require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) + headSlot := slot.Add(1) + err = st.SetSlot(headSlot) require.NoError(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) + db := dbtesting.SetupDB(t) + + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + + update, err := createUpdate(t, version.Altair) require.NoError(t, err) - root, err := block.Block.HashTreeRoot() + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} + mockChainService := &mock.ChainService{State: st} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, HeadFetcher: mockChainService, + BeaconDB: db, } - startPeriod := 1 // very early period before Altair fork - count := 10 // This is big count as we only have one period in test case. - url := fmt.Sprintf("http://foo.com/?count=%d&start_period=%d", count, startPeriod) + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=2&start_period=%d", startPeriod) request := httptest.NewRequest("GET", url, nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} @@ -1071,112 +873,123 @@ func TestLightClientHandler_GetLightClientUpdatesByRange_TooBigCountAltair(t *te var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - var respHeader structs.LightClientHeader - err = json.Unmarshal(resp.Updates[0].Data.AttestedHeader, &respHeader) - require.NoError(t, err) require.Equal(t, 1, len(resp.Updates)) + require.Equal(t, "altair", resp.Updates[0].Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[0].Data) + } -func TestLightClientHandler_GetLightClientUpdatesByRange_BeforeAltair(t *testing.T) { +func TestLightClientHandler_GetLightClientUpdatesByRangeMissingUpdates(t *testing.T) { helpers.ClearCache() ctx := context.Background() + params.SetupTestConfigCleanup(t) config := params.BeaconConfig() - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Sub(1) + config.AltairForkEpoch = 0 + config.EpochsPerSyncCommitteePeriod = 1 + params.OverrideBeaconConfig(config) + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateCapella() + st, err := util.NewBeaconStateAltair() require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) + headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods + err = st.SetSlot(headSlot) require.NoError(t, err) - parent := util.NewBeaconBlockCapella() - parent.Block.Slot = slot.Sub(1) + t.Run("missing update in the middle", func(t *testing.T) { + db := dbtesting.SetupDB(t) - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 3) - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + for i := 0; i < 3; i++ { + if i == 1 { // skip this update + updatePeriod++ + continue + } - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + updates[i], err = createUpdate(t, version.Altair) + require.NoError(t, err) - st, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[i]) + require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + updatePeriod++ + } - block := util.NewBeaconBlockCapella() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=10&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } + s.GetLightClientUpdatesByRange(writer, request) - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 1, len(resp.Updates)) + require.Equal(t, "altair", resp.Updates[0].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(updates[0]) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[0].Data) + }) - h, err := signedBlock.Header() - require.NoError(t, err) + t.Run("missing update at the beginning", func(t *testing.T) { + db := dbtesting.SetupDB(t) - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 3) - // 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(t, err) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) + for i := 0; i < 3; i++ { + if i == 0 { // skip this update + updatePeriod++ + continue + } - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st} - s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - } - startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - count := 1 - url := fmt.Sprintf("http://foo.com/?count=%d&start_period=%d", count, startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + updates[i], err = createUpdate(t, version.Altair) + require.NoError(t, err) - s.GetLightClientUpdatesByRange(writer, request) + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[i]) + require.NoError(t, err) + + updatePeriod++ + } + + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=10&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetLightClientUpdatesByRange(writer, request) + + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 0, len(resp.Updates)) + }) - require.Equal(t, http.StatusNotFound, writer.Code) } +// // TestLightClientHandler_GetLightClientFinalityUpdate tests + func TestLightClientHandler_GetLightClientFinalityUpdateAltair(t *testing.T) { helpers.ClearCache() ctx := context.Background() @@ -1188,7 +1001,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdateAltair(t *testing.T) { err = attestedState.SetSlot(slot.Sub(1)) require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ Epoch: config.AltairForkEpoch - 10, Root: make([]byte, 32), })) @@ -1261,13 +1074,15 @@ func TestLightClientHandler_GetLightClientFinalityUpdateAltair(t *testing.T) { mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ root: true, }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot.Sub(1): attestedState, slot: st, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com", nil) writer := httptest.NewRecorder() @@ -1298,7 +1113,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdateCapella(t *testing.T) { err = attestedState.SetSlot(slot.Sub(1)) require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ Epoch: config.AltairForkEpoch - 10, Root: make([]byte, 32), })) @@ -1371,13 +1186,15 @@ func TestLightClientHandler_GetLightClientFinalityUpdateCapella(t *testing.T) { mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ root: true, }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot.Sub(1): attestedState, slot: st, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com", nil) writer := httptest.NewRecorder() @@ -1408,7 +1225,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdateDeneb(t *testing.T) { err = attestedState.SetSlot(slot.Sub(1)) require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ Epoch: config.AltairForkEpoch - 10, Root: make([]byte, 32), })) @@ -1481,13 +1298,15 @@ func TestLightClientHandler_GetLightClientFinalityUpdateDeneb(t *testing.T) { mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ root: true, }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot.Sub(1): attestedState, slot: st, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com", nil) writer := httptest.NewRecorder() @@ -1518,7 +1337,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateAltair(t *testing.T) { err = attestedState.SetSlot(slot.Sub(1)) require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ Epoch: config.AltairForkEpoch - 10, Root: make([]byte, 32), })) @@ -1591,13 +1410,15 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateAltair(t *testing.T) { mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ root: true, }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot.Sub(1): attestedState, slot: st, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com", nil) writer := httptest.NewRecorder() @@ -1628,7 +1449,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateCapella(t *testing.T) err = attestedState.SetSlot(slot.Sub(1)) require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ Epoch: config.AltairForkEpoch - 10, Root: make([]byte, 32), })) @@ -1701,13 +1522,15 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateCapella(t *testing.T) mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ root: true, }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot.Sub(1): attestedState, slot: st, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com", nil) writer := httptest.NewRecorder() @@ -1738,7 +1561,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateDeneb(t *testing.T) { err = attestedState.SetSlot(slot.Sub(1)) require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ Epoch: config.AltairForkEpoch - 10, Root: make([]byte, 32), })) @@ -1811,13 +1634,15 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateDeneb(t *testing.T) { mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ root: true, }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot.Sub(1): attestedState, slot: st, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } request := httptest.NewRequest("GET", "http://foo.com", nil) writer := httptest.NewRecorder() @@ -1848,7 +1673,7 @@ func TestLightClientHandler_GetLightClientEventBlock(t *testing.T) { err = attestedState.SetSlot(slot.Sub(1)) require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ Epoch: config.AltairForkEpoch - 10, Root: make([]byte, 32), })) @@ -1921,13 +1746,15 @@ func TestLightClientHandler_GetLightClientEventBlock(t *testing.T) { mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ root: true, }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot.Sub(1): attestedState, slot: st, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } minSignaturesRequired := uint64(100) @@ -1952,7 +1779,7 @@ func TestLightClientHandler_GetLightClientEventBlock_NeedFetchParent(t *testing. err = attestedState.SetSlot(slot.Sub(1)) require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ Epoch: config.AltairForkEpoch - 10, Root: make([]byte, 32), })) @@ -2028,13 +1855,15 @@ func TestLightClientHandler_GetLightClientEventBlock_NeedFetchParent(t *testing. mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ root: true, }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ slot.Sub(1): attestedState, slot: st, }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, } minSignaturesRequired := uint64(100) @@ -2047,3 +1876,133 @@ func TestLightClientHandler_GetLightClientEventBlock_NeedFetchParent(t *testing. require.Equal(t, true, syncAggregate.SyncCommitteeBits.Count() >= minSignaturesRequired) require.Equal(t, slot-1, eventBlock.Block().Slot()) } + +func createUpdate(t *testing.T, v int) (interfaces.LightClientUpdate, error) { + config := params.BeaconConfig() + var slot primitives.Slot + var header interfaces.LightClientHeader + var err error + + sampleRoot := make([]byte, 32) + for i := 0; i < 32; i++ { + sampleRoot[i] = byte(i) + } + + sampleExecutionBranch := make([][]byte, fieldparams.ExecutionBranchDepth) + for i := 0; i < 4; i++ { + sampleExecutionBranch[i] = make([]byte, 32) + for j := 0; j < 32; j++ { + sampleExecutionBranch[i][j] = byte(i + j) + } + } + + switch v { + case version.Altair: + slot = primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1, + ProposerIndex: primitives.ValidatorIndex(rand.Int()), + ParentRoot: sampleRoot, + StateRoot: sampleRoot, + BodyRoot: sampleRoot, + }, + }) + require.NoError(t, err) + case version.Capella: + slot = primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderCapella{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1, + ProposerIndex: primitives.ValidatorIndex(rand.Int()), + ParentRoot: sampleRoot, + StateRoot: sampleRoot, + BodyRoot: sampleRoot, + }, + Execution: &enginev1.ExecutionPayloadHeaderCapella{ + 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), + }, + ExecutionBranch: sampleExecutionBranch, + }) + require.NoError(t, err) + case version.Deneb: + slot = primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderDeneb{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1, + ProposerIndex: primitives.ValidatorIndex(rand.Int()), + ParentRoot: sampleRoot, + StateRoot: sampleRoot, + BodyRoot: sampleRoot, + }, + Execution: &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), + }, + ExecutionBranch: sampleExecutionBranch, + }) + require.NoError(t, err) + case version.Electra: + slot = primitives.Slot(config.ElectraForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderDeneb{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1, + ProposerIndex: primitives.ValidatorIndex(rand.Int()), + ParentRoot: sampleRoot, + StateRoot: sampleRoot, + BodyRoot: sampleRoot, + }, + Execution: &enginev1.ExecutionPayloadHeaderElectra{ + 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), + }, + ExecutionBranch: sampleExecutionBranch, + }) + require.NoError(t, err) + default: + return nil, fmt.Errorf("unsupported version %s", version.String(v)) + } + + update, err := lightclient.CreateDefaultLightClientUpdate(slot) + require.NoError(t, err) + update.SetSignatureSlot(slot - 1) + syncCommitteeBits := make([]byte, 64) + syncCommitteeSignature := make([]byte, 96) + update.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: syncCommitteeBits, + SyncCommitteeSignature: syncCommitteeSignature, + }) + + require.NoError(t, update.SetAttestedHeader(header)) + require.NoError(t, update.SetFinalizedHeader(header)) + + return update, nil +} diff --git a/beacon-chain/rpc/eth/light-client/helpers.go b/beacon-chain/rpc/eth/light-client/helpers.go index f1f6cbdf7af..4f6dee400b3 100644 --- a/beacon-chain/rpc/eth/light-client/helpers.go +++ b/beacon-chain/rpc/eth/light-client/helpers.go @@ -99,23 +99,6 @@ func createLightClientBootstrap( return result, nil } -func newLightClientUpdateFromBeaconState( - ctx context.Context, - currentSlot primitives.Slot, - state state.BeaconState, - block interfaces.ReadOnlySignedBeaconBlock, - attestedState state.BeaconState, - attestedBlock interfaces.ReadOnlySignedBeaconBlock, - finalizedBlock interfaces.ReadOnlySignedBeaconBlock, -) (*structs.LightClientUpdate, error) { - result, err := lightclient.NewLightClientUpdateFromBeaconState(ctx, currentSlot, state, block, attestedState, attestedBlock, finalizedBlock) - if err != nil { - return nil, err - } - - return structs.LightClientUpdateFromConsensus(result) -} - func newLightClientFinalityUpdateFromBeaconState( ctx context.Context, currentSlot primitives.Slot, diff --git a/beacon-chain/rpc/eth/light-client/helpers_test.go b/beacon-chain/rpc/eth/light-client/helpers_test.go index 9b27204fc22..62594af80c3 100644 --- a/beacon-chain/rpc/eth/light-client/helpers_test.go +++ b/beacon-chain/rpc/eth/light-client/helpers_test.go @@ -1,616 +1,677 @@ package lightclient import ( + "strings" "testing" lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" - fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" - ethpbv1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" - ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" + "github.com/prysmaticlabs/prysm/v5/config/params" + light_client "github.com/prysmaticlabs/prysm/v5/consensus-types/light-client" + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + pb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/assert" ) // When the update has relevant sync committee func createNonEmptySyncCommitteeBranch() [][]byte { res := make([][]byte, fieldparams.SyncCommitteeBranchDepth) - res[0] = []byte("xyz") + res[0] = []byte(strings.Repeat("x", 32)) + for i := 1; i < len(res); i++ { + res[i] = make([]byte, fieldparams.RootLength) + } return res } // When the update has finality func createNonEmptyFinalityBranch() [][]byte { - res := make([][]byte, lightclient.FinalityBranchNumOfLeaves) - res[0] = []byte("xyz") + res := make([][]byte, fieldparams.FinalityBranchDepth) + res[0] = []byte(strings.Repeat("x", 32)) + for i := 1; i < fieldparams.FinalityBranchDepth; i++ { + res[i] = make([]byte, 32) + } return res } func TestIsBetterUpdate(t *testing.T) { - testCases := []struct { - name string - oldUpdate *ethpbv2.LightClientUpdate - newUpdate *ethpbv2.LightClientUpdate - expectedResult bool - }{ - { - name: "new has supermajority but old doesn't", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b11111100, 0b1}, // [0,0,1,1,1,1,1,1] - }, - }, - expectedResult: true, - }, - { - name: "old has supermajority but new doesn't", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b11111100, 0b1}, // [0,0,1,1,1,1,1,1] - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] - }, - }, - expectedResult: false, - }, - { - name: "new doesn't have supermajority and newNumActiveParticipants is greater than oldNumActiveParticipants", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] - }, - }, - expectedResult: true, - }, - { - name: "new doesn't have supermajority and newNumActiveParticipants is lesser than oldNumActiveParticipants", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - }, - expectedResult: false, - }, - { - name: "new has relevant sync committee but old doesn't", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: make([][]byte, fieldparams.SyncCommitteeBranchDepth), - SignatureSlot: 9999, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000001, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 1000000, - }, - expectedResult: true, - }, - { - name: "old has relevant sync committee but new doesn't", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000001, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 1000000, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: make([][]byte, fieldparams.SyncCommitteeBranchDepth), - SignatureSlot: 9999, - }, - expectedResult: false, - }, - { - name: "new has finality but old doesn't", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: make([][]byte, lightclient.FinalityBranchNumOfLeaves), - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - }, - expectedResult: true, - }, - { - name: "old has finality but new doesn't", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: make([][]byte, lightclient.FinalityBranchNumOfLeaves), - }, - expectedResult: false, - }, - { - name: "new has finality and sync committee finality both but old doesn't have sync committee finality", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 999999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 999999, - }}, - }, - }, - }, - expectedResult: true, - }, - { - name: "new has finality but doesn't have sync committee finality and old has sync committee finality", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 999999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 999999, - }}, - }, - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - expectedResult: false, - }, - { - name: "new has more active participants than old", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,1,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - expectedResult: true, - }, - { - name: "new has less active participants than old", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,1,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - expectedResult: false, - }, - { - name: "new's attested header's slot is lesser than old's attested header's slot", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 999999, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - expectedResult: true, - }, - { - name: "new's attested header's slot is greater than old's attested header's slot", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 999999, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - expectedResult: false, - }, - { - name: "none of the above conditions are met and new signature's slot is less than old signature's slot", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9998, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - expectedResult: true, - }, - { - name: "none of the above conditions are met and new signature's slot is greater than old signature's slot", - oldUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9998, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - newUpdate: ðpbv2.LightClientUpdate{ - SyncAggregate: ðpbv1.SyncAggregate{ - SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] - }, - AttestedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 1000000, - }}, - }, - }, - NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), - SignatureSlot: 9999, - FinalityBranch: createNonEmptyFinalityBranch(), - FinalizedHeader: ðpbv2.LightClientHeaderContainer{ - Header: ðpbv2.LightClientHeaderContainer_HeaderAltair{ - HeaderAltair: ðpbv2.LightClientHeader{Beacon: ðpbv1.BeaconBlockHeader{ - Slot: 9999, - }}, - }, - }, - }, - expectedResult: false, - }, - } - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - result, err := IsBetterUpdate(testCase.newUpdate, testCase.oldUpdate) - assert.NoError(t, err) - assert.Equal(t, testCase.expectedResult, result) + config := params.BeaconConfig() + + t.Run("new has supermajority but old doesn't", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] }) - } + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b11111100, 0b1}, // [0,0,1,1,1,1,1,1] + }) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, true, result) + }) + + t.Run("old has supermajority but new doesn't", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b11111100, 0b1}, // [0,0,1,1,1,1,1,1] + }) + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] + }) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, false, result) + }) + + t.Run("new doesn't have supermajority and newNumActiveParticipants is greater than oldNumActiveParticipants", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] + }) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, true, result) + }) + + t.Run("new doesn't have supermajority and newNumActiveParticipants is lesser than oldNumActiveParticipants", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] + }) + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, false, result) + }) + + t.Run("new has relevant sync committee but old doesn't", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(9999) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000001, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(1000000) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, true, result) + }) + + t.Run("old has relevant sync committee but new doesn't", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000001, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(1000000) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(9999) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, false, result) + }) + + t.Run("new has finality but old doesn't", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(9999) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(9999) + err = newUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, true, result) + }) + + t.Run("old has finality but new doesn't", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(9999) + err = oldUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(9999) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, false, result) + }) + + t.Run("new has finality and sync committee finality both but old doesn't have sync committee finality", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + err = oldUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(9999) + oldFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetFinalizedHeader(oldFinalizedHeader) + assert.NoError(t, err) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(999999) + err = newUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + newFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 999999, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetFinalizedHeader(newFinalizedHeader) + assert.NoError(t, err) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, true, result) + }) + + t.Run("new has finality but doesn't have sync committee finality and old has sync committee finality", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + err = oldUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(999999) + oldFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 999999, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetFinalizedHeader(oldFinalizedHeader) + assert.NoError(t, err) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(9999) + err = newUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + newFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetFinalizedHeader(newFinalizedHeader) + assert.NoError(t, err) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, false, result) + }) + + t.Run("new has more active participants than old", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,1,1,1,1,1,0,0] + }) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, true, result) + }) + + t.Run("new has less active participants than old", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,1,1,1,1,1,0,0] + }) + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, false, result) + }) + + t.Run("new's attested header's slot is lesser than old's attested header's slot", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + err = oldUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(9999) + oldFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetFinalizedHeader(oldFinalizedHeader) + assert.NoError(t, err) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 999999, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(9999) + err = newUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + newFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetFinalizedHeader(newFinalizedHeader) + assert.NoError(t, err) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, true, result) + }) + + t.Run("new's attested header's slot is greater than old's attested header's slot", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 999999, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + err = oldUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(9999) + oldFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetFinalizedHeader(oldFinalizedHeader) + assert.NoError(t, err) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(9999) + err = newUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + newFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetFinalizedHeader(newFinalizedHeader) + assert.NoError(t, err) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, false, result) + }) + + t.Run("none of the above conditions are met and new signature's slot is less than old signature's slot", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + err = oldUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(9999) + oldFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetFinalizedHeader(oldFinalizedHeader) + assert.NoError(t, err) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(9998) + err = newUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + newFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetFinalizedHeader(newFinalizedHeader) + assert.NoError(t, err) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, true, result) + }) + + t.Run("none of the above conditions are met and new signature's slot is greater than old signature's slot", func(t *testing.T) { + oldUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)) + assert.NoError(t, err) + newUpdate, err := lightclient.CreateDefaultLightClientUpdate(primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(2)) + assert.NoError(t, err) + + oldUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + oldAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetAttestedHeader(oldAttestedHeader) + assert.NoError(t, err) + err = oldUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + err = oldUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + oldUpdate.SetSignatureSlot(9998) + oldFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = oldUpdate.SetFinalizedHeader(oldFinalizedHeader) + assert.NoError(t, err) + + newUpdate.SetSyncAggregate(&pb.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }) + newAttestedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 1000000, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetAttestedHeader(newAttestedHeader) + assert.NoError(t, err) + err = newUpdate.SetNextSyncCommitteeBranch(createNonEmptySyncCommitteeBranch()) + assert.NoError(t, err) + newUpdate.SetSignatureSlot(9999) + err = newUpdate.SetFinalityBranch(createNonEmptyFinalityBranch()) + assert.NoError(t, err) + newFinalizedHeader, err := light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{ + Beacon: &pb.BeaconBlockHeader{ + Slot: 9999, + }, + }) + assert.NoError(t, err) + err = newUpdate.SetFinalizedHeader(newFinalizedHeader) + assert.NoError(t, err) + + result, err := IsBetterUpdate(newUpdate, oldUpdate) + assert.NoError(t, err) + assert.Equal(t, false, result) + }) } diff --git a/testing/util/lightclient.go b/testing/util/lightclient.go index a79e7f8f72d..2edb8390a7a 100644 --- a/testing/util/lightclient.go +++ b/testing/util/lightclient.go @@ -14,9 +14,8 @@ import ( "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/ssz" v11 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" - ethpbv1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" - ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + pb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/require" ) @@ -47,14 +46,14 @@ func (l *TestLightClient) SetupTestCapella(blinded bool) *TestLightClient { finalizedBlock, err := blocks.NewSignedBeaconBlock(NewBeaconBlockCapella()) require.NoError(l.T, err) - finalizedBlock.SetSlot(1) + finalizedBlock.SetSlot(primitives.Slot(params.BeaconConfig().CapellaForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch))) finalizedHeader, err := finalizedBlock.Header() require.NoError(l.T, err) finalizedRoot, err := finalizedHeader.Header.HashTreeRoot() require.NoError(l.T, err) require.NoError(l.T, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ - Epoch: params.BeaconConfig().CapellaForkEpoch - 10, + Epoch: params.BeaconConfig().CapellaForkEpoch, Root: finalizedRoot[:], })) @@ -441,14 +440,14 @@ func (l *TestLightClient) SetupTestDeneb(blinded bool) *TestLightClient { finalizedBlock, err := blocks.NewSignedBeaconBlock(NewBeaconBlockDeneb()) require.NoError(l.T, err) - finalizedBlock.SetSlot(1) + finalizedBlock.SetSlot(primitives.Slot(params.BeaconConfig().DenebForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch))) finalizedHeader, err := finalizedBlock.Header() require.NoError(l.T, err) finalizedRoot, err := finalizedHeader.Header.HashTreeRoot() require.NoError(l.T, err) require.NoError(l.T, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ - Epoch: params.BeaconConfig().DenebForkEpoch - 10, + Epoch: params.BeaconConfig().DenebForkEpoch, Root: finalizedRoot[:], })) @@ -665,14 +664,14 @@ func (l *TestLightClient) SetupTestDenebFinalizedBlockCapella(blinded bool) *Tes finalizedBlock, err := blocks.NewSignedBeaconBlock(NewBeaconBlockCapella()) require.NoError(l.T, err) - finalizedBlock.SetSlot(1) + finalizedBlock.SetSlot(primitives.Slot(params.BeaconConfig().DenebForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Sub(15)) finalizedHeader, err := finalizedBlock.Header() require.NoError(l.T, err) finalizedRoot, err := finalizedHeader.Header.HashTreeRoot() require.NoError(l.T, err) require.NoError(l.T, attestedState.SetFinalizedCheckpoint(ðpb.Checkpoint{ - Epoch: params.BeaconConfig().CapellaForkEpoch - 10, + Epoch: params.BeaconConfig().DenebForkEpoch - 1, Root: finalizedRoot[:], })) @@ -765,9 +764,8 @@ func (l *TestLightClient) SetupTestDenebFinalizedBlockCapella(blinded bool) *Tes return l } -func (l *TestLightClient) CheckAttestedHeader(container *ethpbv2.LightClientHeaderContainer) { - updateAttestedHeaderBeacon, err := container.GetBeacon() - require.NoError(l.T, err) +func (l *TestLightClient) CheckAttestedHeader(header interfaces.LightClientHeader) { + updateAttestedHeaderBeacon := header.Beacon() testAttestedHeader, err := l.AttestedBlock.Header() require.NoError(l.T, err) require.Equal(l.T, l.AttestedBlock.Block().Slot(), updateAttestedHeaderBeacon.Slot, "Attested block slot is not equal") @@ -820,16 +818,16 @@ func (l *TestLightClient) CheckAttestedHeader(container *ethpbv2.LightClientHead WithdrawalsRoot: withdrawalsRoot, } - updateAttestedHeaderExecution, err := container.GetExecutionHeaderCapella() + updateAttestedHeaderExecution, err := header.Execution() require.NoError(l.T, err) - require.DeepSSZEqual(l.T, execution, updateAttestedHeaderExecution, "Attested Block Execution is not equal") + require.DeepSSZEqual(l.T, execution, updateAttestedHeaderExecution.Proto(), "Attested Block Execution is not equal") executionPayloadProof, err := blocks.PayloadProof(l.Ctx, l.AttestedBlock.Block()) require.NoError(l.T, err) - updateAttestedHeaderExecutionBranch, err := container.GetExecutionBranch() + updateAttestedHeaderExecutionBranch, err := header.ExecutionBranch() require.NoError(l.T, err) for i, leaf := range updateAttestedHeaderExecutionBranch { - require.DeepSSZEqual(l.T, executionPayloadProof[i], leaf, "Leaf is not equal") + require.DeepSSZEqual(l.T, executionPayloadProof[i], leaf[:], "Leaf is not equal") } } @@ -874,21 +872,21 @@ func (l *TestLightClient) CheckAttestedHeader(container *ethpbv2.LightClientHead WithdrawalsRoot: withdrawalsRoot, } - updateAttestedHeaderExecution, err := container.GetExecutionHeaderDeneb() + updateAttestedHeaderExecution, err := header.Execution() require.NoError(l.T, err) - require.DeepSSZEqual(l.T, execution, updateAttestedHeaderExecution, "Attested Block Execution is not equal") + require.DeepSSZEqual(l.T, execution, updateAttestedHeaderExecution.Proto(), "Attested Block Execution is not equal") executionPayloadProof, err := blocks.PayloadProof(l.Ctx, l.AttestedBlock.Block()) require.NoError(l.T, err) - updateAttestedHeaderExecutionBranch, err := container.GetExecutionBranch() + updateAttestedHeaderExecutionBranch, err := header.ExecutionBranch() require.NoError(l.T, err) for i, leaf := range updateAttestedHeaderExecutionBranch { - require.DeepSSZEqual(l.T, executionPayloadProof[i], leaf, "Leaf is not equal") + require.DeepSSZEqual(l.T, executionPayloadProof[i], leaf[:], "Leaf is not equal") } } } -func (l *TestLightClient) CheckSyncAggregate(sa *ethpbv1.SyncAggregate) { +func (l *TestLightClient) CheckSyncAggregate(sa *pb.SyncAggregate) { syncAggregate, err := l.Block.Block().Body().SyncAggregate() require.NoError(l.T, err) require.DeepSSZEqual(l.T, syncAggregate.SyncCommitteeBits, sa.SyncCommitteeBits, "SyncAggregate bits is not equal")