diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index 7263cd4cad..0df990b01e 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -2804,7 +2804,7 @@ func TestProcessLightClientUpdate(t *testing.T) { require.NoError(t, s.cfg.BeaconDB.SaveState(ctx, headState, [32]byte{1, 2})) require.NoError(t, s.cfg.BeaconDB.SaveHeadBlockRoot(ctx, [32]byte{1, 2})) - for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ { + for _, testVersion := range version.All()[1:] { t.Run(version.String(testVersion), func(t *testing.T) { l := util.NewTestLightClient(t, testVersion) diff --git a/beacon-chain/db/kv/lightclient.go b/beacon-chain/db/kv/lightclient.go index abf9695e14..212498507a 100644 --- a/beacon-chain/db/kv/lightclient.go +++ b/beacon-chain/db/kv/lightclient.go @@ -181,6 +181,13 @@ func decodeLightClientBootstrap(enc []byte) (interfaces.LightClientBootstrap, [] } m = bootstrap syncCommitteeHash = enc[len(ElectraKey) : len(ElectraKey)+32] + case hasFuluKey(enc): + bootstrap := ðpb.LightClientBootstrapElectra{} + if err := bootstrap.UnmarshalSSZ(enc[len(fuluKey)+32:]); err != nil { + return nil, nil, errors.Wrap(err, "could not unmarshal Electra light client bootstrap") + } + m = bootstrap + syncCommitteeHash = enc[len(fuluKey) : len(fuluKey)+32] default: return nil, nil, errors.New("decoding of saved light client bootstrap is unsupported") } @@ -296,6 +303,12 @@ func decodeLightClientUpdate(enc []byte) (interfaces.LightClientUpdate, error) { return nil, errors.Wrap(err, "could not unmarshal Electra light client update") } m = update + case hasFuluKey(enc): + update := ðpb.LightClientUpdateElectra{} + if err := update.UnmarshalSSZ(enc[len(fuluKey):]); err != nil { + return nil, errors.Wrap(err, "could not unmarshal Fulu light client update") + } + m = update default: return nil, errors.New("decoding of saved light client update is unsupported") } @@ -304,6 +317,8 @@ func decodeLightClientUpdate(enc []byte) (interfaces.LightClientUpdate, error) { func keyForLightClientUpdate(v int) ([]byte, error) { switch v { + case version.Fulu: + return fuluKey, nil case version.Electra: return ElectraKey, nil case version.Deneb: diff --git a/beacon-chain/db/kv/lightclient_test.go b/beacon-chain/db/kv/lightclient_test.go index 609471e064..cec59f2cbf 100644 --- a/beacon-chain/db/kv/lightclient_test.go +++ b/beacon-chain/db/kv/lightclient_test.go @@ -215,8 +215,7 @@ func TestStore_LightClientUpdate_CanSaveRetrieve(t *testing.T) { db := setupDB(t) ctx := t.Context() - - for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ { + for _, testVersion := range version.All()[1:] { t.Run(version.String(testVersion), func(t *testing.T) { update, err := createUpdate(t, testVersion) require.NoError(t, err) @@ -572,8 +571,7 @@ func TestStore_LightClientBootstrap_CanSaveRetrieve(t *testing.T) { require.NoError(t, err) require.IsNil(t, retrievedBootstrap) }) - - for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ { + for _, testVersion := range version.All()[1:] { t.Run(version.String(testVersion), func(t *testing.T) { bootstrap, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().VersionToForkEpochMap()[testVersion]) * uint64(params.BeaconConfig().SlotsPerEpoch))) require.NoError(t, err) diff --git a/beacon-chain/light-client/lightclient_test.go b/beacon-chain/light-client/lightclient_test.go index 3fc490a9b8..09255a26af 100644 --- a/beacon-chain/light-client/lightclient_test.go +++ b/beacon-chain/light-client/lightclient_test.go @@ -29,58 +29,22 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T) cfg.CapellaForkEpoch = 3 cfg.DenebForkEpoch = 4 cfg.ElectraForkEpoch = 5 + cfg.FuluForkEpoch = 6 params.OverrideBeaconConfig(cfg) - t.Run("Altair", func(t *testing.T) { - l := util.NewTestLightClient(t, version.Altair) + for _, testVersion := range version.All()[1:] { + t.Run(version.String(testVersion), func(t *testing.T) { + l := util.NewTestLightClient(t, testVersion) - update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, 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") + update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, 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") - l.CheckSyncAggregate(update.SyncAggregate()) - l.CheckAttestedHeader(update.AttestedHeader()) - }) - - t.Run("Capella", func(t *testing.T) { - l := util.NewTestLightClient(t, version.Capella) - - update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, 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") - - l.CheckSyncAggregate(update.SyncAggregate()) - l.CheckAttestedHeader(update.AttestedHeader()) - }) - - t.Run("Deneb", func(t *testing.T) { - l := util.NewTestLightClient(t, version.Deneb) - - update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, 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") - - l.CheckSyncAggregate(update.SyncAggregate()) - l.CheckAttestedHeader(update.AttestedHeader()) - }) - - t.Run("Electra", func(t *testing.T) { - l := util.NewTestLightClient(t, version.Electra) - - update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, 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") - - l.CheckSyncAggregate(update.SyncAggregate()) - l.CheckAttestedHeader(update.AttestedHeader()) - }) + l.CheckSyncAggregate(update.SyncAggregate()) + l.CheckAttestedHeader(update.AttestedHeader()) + }) + } } func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { @@ -91,6 +55,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { cfg.CapellaForkEpoch = 3 cfg.DenebForkEpoch = 4 cfg.ElectraForkEpoch = 5 + cfg.FuluForkEpoch = 6 params.OverrideBeaconConfig(cfg) t.Run("Altair", func(t *testing.T) { @@ -538,6 +503,157 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal") }) }) + + t.Run("Fulu", func(t *testing.T) { + t.Run("FinalizedBlock Not Nil", func(t *testing.T) { + l := util.NewTestLightClient(t, version.Fulu) + + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, 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") + + 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 := 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") + fb, err := update.FinalityBranchElectra() + require.NoError(t, err) + proof, err := l.AttestedState.FinalizedRootProof(l.Ctx) + require.NoError(t, err) + for i, leaf := range fb { + require.DeepSSZEqual(t, proof[i], leaf[:], "Leaf is not equal") + } + + // Check Execution BlockHash + payloadInterface, err := l.FinalizedBlock.Block().Body().Execution() + require.NoError(t, err) + transactionsRoot, err := payloadInterface.TransactionsRoot() + if errors.Is(err, consensustypes.ErrUnsupportedField) { + transactions, err := payloadInterface.Transactions() + require.NoError(t, err) + transactionsRootArray, err := ssz.TransactionsRoot(transactions) + require.NoError(t, err) + transactionsRoot = transactionsRootArray[:] + } else { + require.NoError(t, err) + } + withdrawalsRoot, err := payloadInterface.WithdrawalsRoot() + if errors.Is(err, consensustypes.ErrUnsupportedField) { + withdrawals, err := payloadInterface.Withdrawals() + require.NoError(t, err) + withdrawalsRootArray, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload) + require.NoError(t, err) + withdrawalsRoot = withdrawalsRootArray[:] + } else { + require.NoError(t, err) + } + execution := &v11.ExecutionPayloadHeaderDeneb{ + ParentHash: payloadInterface.ParentHash(), + FeeRecipient: payloadInterface.FeeRecipient(), + StateRoot: payloadInterface.StateRoot(), + ReceiptsRoot: payloadInterface.ReceiptsRoot(), + LogsBloom: payloadInterface.LogsBloom(), + PrevRandao: payloadInterface.PrevRandao(), + BlockNumber: payloadInterface.BlockNumber(), + GasLimit: payloadInterface.GasLimit(), + GasUsed: payloadInterface.GasUsed(), + Timestamp: payloadInterface.Timestamp(), + ExtraData: payloadInterface.ExtraData(), + BaseFeePerGas: payloadInterface.BaseFeePerGas(), + BlockHash: payloadInterface.BlockHash(), + TransactionsRoot: transactionsRoot, + WithdrawalsRoot: withdrawalsRoot, + } + 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, version.Fulu, util.WithFinalizedCheckpointInPrevFork()) + + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, 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") + + 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 := 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") + fb, err := update.FinalityBranchElectra() + require.NoError(t, err) + proof, err := l.AttestedState.FinalizedRootProof(l.Ctx) + require.NoError(t, err) + for i, leaf := range fb { + require.DeepSSZEqual(t, proof[i], leaf[:], "Leaf is not equal") + } + + // Check Execution BlockHash + payloadInterface, err := l.FinalizedBlock.Block().Body().Execution() + require.NoError(t, err) + transactionsRoot, err := payloadInterface.TransactionsRoot() + if errors.Is(err, consensustypes.ErrUnsupportedField) { + transactions, err := payloadInterface.Transactions() + require.NoError(t, err) + transactionsRootArray, err := ssz.TransactionsRoot(transactions) + require.NoError(t, err) + transactionsRoot = transactionsRootArray[:] + } else { + require.NoError(t, err) + } + withdrawalsRoot, err := payloadInterface.WithdrawalsRoot() + if errors.Is(err, consensustypes.ErrUnsupportedField) { + withdrawals, err := payloadInterface.Withdrawals() + require.NoError(t, err) + withdrawalsRootArray, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload) + require.NoError(t, err) + withdrawalsRoot = withdrawalsRootArray[:] + } else { + require.NoError(t, err) + } + execution := &v11.ExecutionPayloadHeaderDeneb{ + ParentHash: payloadInterface.ParentHash(), + FeeRecipient: payloadInterface.FeeRecipient(), + StateRoot: payloadInterface.StateRoot(), + ReceiptsRoot: payloadInterface.ReceiptsRoot(), + LogsBloom: payloadInterface.LogsBloom(), + PrevRandao: payloadInterface.PrevRandao(), + BlockNumber: payloadInterface.BlockNumber(), + GasLimit: payloadInterface.GasLimit(), + GasUsed: payloadInterface.GasUsed(), + Timestamp: payloadInterface.Timestamp(), + ExtraData: payloadInterface.ExtraData(), + BaseFeePerGas: payloadInterface.BaseFeePerGas(), + BlockHash: payloadInterface.BlockHash(), + TransactionsRoot: transactionsRoot, + WithdrawalsRoot: withdrawalsRoot, + } + updateExecution, err := update.FinalizedHeader().Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal") + }) + }) } func TestLightClient_BlockToLightClientHeader(t *testing.T) { @@ -983,6 +1099,138 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) { }) }) + t.Run("Fulu", func(t *testing.T) { + t.Run("Non-Blinded Beacon Block", func(t *testing.T) { + l := util.NewTestLightClient(t, version.Fulu) + + header, err := lightClient.BlockToLightClientHeader(l.Ctx, version.Fulu, l.Block) + require.NoError(t, err) + require.NotNil(t, header, "header is nil") + + parentRoot := l.Block.Block().ParentRoot() + stateRoot := l.Block.Block().StateRoot() + bodyRoot, err := l.Block.Block().Body().HashTreeRoot() + require.NoError(t, err) + + payload, err := l.Block.Block().Body().Execution() + require.NoError(t, err) + + transactionsRoot, err := lightClient.ComputeTransactionsRoot(payload) + require.NoError(t, err) + + withdrawalsRoot, err := lightClient.ComputeWithdrawalsRoot(payload) + require.NoError(t, err) + + blobGasUsed, err := payload.BlobGasUsed() + require.NoError(t, err) + + excessBlobGas, err := payload.ExcessBlobGas() + require.NoError(t, err) + + executionHeader := &v11.ExecutionPayloadHeaderDeneb{ + ParentHash: payload.ParentHash(), + FeeRecipient: payload.FeeRecipient(), + StateRoot: payload.StateRoot(), + ReceiptsRoot: payload.ReceiptsRoot(), + LogsBloom: payload.LogsBloom(), + PrevRandao: payload.PrevRandao(), + BlockNumber: payload.BlockNumber(), + GasLimit: payload.GasLimit(), + GasUsed: payload.GasUsed(), + Timestamp: payload.Timestamp(), + ExtraData: payload.ExtraData(), + BaseFeePerGas: payload.BaseFeePerGas(), + BlockHash: payload.BlockHash(), + TransactionsRoot: transactionsRoot, + WithdrawalsRoot: withdrawalsRoot, + BlobGasUsed: blobGasUsed, + ExcessBlobGas: excessBlobGas, + } + + executionPayloadProof, err := blocks.PayloadProof(l.Ctx, l.Block.Block()) + require.NoError(t, err) + + require.Equal(t, l.Block.Block().Slot(), header.Beacon().Slot, "Slot is not equal") + require.Equal(t, l.Block.Block().ProposerIndex(), header.Beacon().ProposerIndex, "Proposer index is not equal") + require.DeepSSZEqual(t, parentRoot[:], header.Beacon().ParentRoot, "Parent root is not equal") + require.DeepSSZEqual(t, stateRoot[:], header.Beacon().StateRoot, "State root is not equal") + require.DeepSSZEqual(t, bodyRoot[:], header.Beacon().BodyRoot, "Body root is not equal") + + headerExecution, err := header.Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal") + + headerExecutionBranch, err := header.ExecutionBranch() + require.NoError(t, err) + require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal") + }) + + t.Run("Blinded Beacon Block", func(t *testing.T) { + l := util.NewTestLightClient(t, version.Fulu, util.WithBlinded()) + + header, err := lightClient.BlockToLightClientHeader(l.Ctx, version.Fulu, l.Block) + require.NoError(t, err) + require.NotNil(t, header, "header is nil") + + parentRoot := l.Block.Block().ParentRoot() + stateRoot := l.Block.Block().StateRoot() + bodyRoot, err := l.Block.Block().Body().HashTreeRoot() + require.NoError(t, err) + + payload, err := l.Block.Block().Body().Execution() + require.NoError(t, err) + + transactionsRoot, err := payload.TransactionsRoot() + require.NoError(t, err) + + withdrawalsRoot, err := payload.WithdrawalsRoot() + require.NoError(t, err) + + blobGasUsed, err := payload.BlobGasUsed() + require.NoError(t, err) + + excessBlobGas, err := payload.ExcessBlobGas() + require.NoError(t, err) + + executionHeader := &v11.ExecutionPayloadHeaderDeneb{ + ParentHash: payload.ParentHash(), + FeeRecipient: payload.FeeRecipient(), + StateRoot: payload.StateRoot(), + ReceiptsRoot: payload.ReceiptsRoot(), + LogsBloom: payload.LogsBloom(), + PrevRandao: payload.PrevRandao(), + BlockNumber: payload.BlockNumber(), + GasLimit: payload.GasLimit(), + GasUsed: payload.GasUsed(), + Timestamp: payload.Timestamp(), + ExtraData: payload.ExtraData(), + BaseFeePerGas: payload.BaseFeePerGas(), + BlockHash: payload.BlockHash(), + TransactionsRoot: transactionsRoot, + WithdrawalsRoot: withdrawalsRoot, + BlobGasUsed: blobGasUsed, + ExcessBlobGas: excessBlobGas, + } + + executionPayloadProof, err := blocks.PayloadProof(l.Ctx, l.Block.Block()) + require.NoError(t, err) + + require.Equal(t, l.Block.Block().Slot(), header.Beacon().Slot, "Slot is not equal") + require.Equal(t, l.Block.Block().ProposerIndex(), header.Beacon().ProposerIndex, "Proposer index is not equal") + require.DeepSSZEqual(t, parentRoot[:], header.Beacon().ParentRoot, "Parent root is not equal") + require.DeepSSZEqual(t, stateRoot[:], header.Beacon().StateRoot, "State root is not equal") + require.DeepSSZEqual(t, bodyRoot[:], header.Beacon().BodyRoot, "Body root is not equal") + + headerExecution, err := header.Execution() + require.NoError(t, err) + require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal") + + headerExecutionBranch, err := header.ExecutionBranch() + require.NoError(t, err) + require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal") + }) + }) + t.Run("Capella fork with Altair block", func(t *testing.T) { l := util.NewTestLightClient(t, version.Altair) diff --git a/beacon-chain/rpc/eth/light-client/handlers_test.go b/beacon-chain/rpc/eth/light-client/handlers_test.go index 3486c49220..6e87cfb00f 100644 --- a/beacon-chain/rpc/eth/light-client/handlers_test.go +++ b/beacon-chain/rpc/eth/light-client/handlers_test.go @@ -46,7 +46,7 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { cfg.FuluForkEpoch = 5 params.OverrideBeaconConfig(cfg) - for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ { + for _, testVersion := range version.All()[1:] { t.Run(version.String(testVersion), func(t *testing.T) { l := util.NewTestLightClient(t, testVersion) @@ -131,7 +131,7 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { resp = &pb.LightClientBootstrapCapella{} case version.Deneb: resp = &pb.LightClientBootstrapDeneb{} - case version.Electra: + case version.Electra, version.Fulu: resp = &pb.LightClientBootstrapElectra{} default: t.Fatalf("Unsupported version %s", version.String(testVersion)) @@ -173,10 +173,11 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) { config.CapellaForkEpoch = 2 config.DenebForkEpoch = 3 config.ElectraForkEpoch = 4 + config.FuluForkEpoch = 5 params.OverrideBeaconConfig(config) t.Run("can save retrieve", func(t *testing.T) { - for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ { + for _, testVersion := range version.All()[1:] { t.Run(version.String(testVersion), func(t *testing.T) { slot := primitives.Slot(params.BeaconConfig().VersionToForkEpochMap()[testVersion] * primitives.Epoch(config.SlotsPerEpoch)).Add(1) @@ -252,7 +253,7 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) { resp = &pb.LightClientUpdateCapella{} case version.Deneb: resp = &pb.LightClientUpdateDeneb{} - case version.Electra: + case version.Electra, version.Fulu: resp = &pb.LightClientUpdateElectra{} default: t.Fatalf("Unsupported version %s", version.String(testVersion)) @@ -313,7 +314,7 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) { resp = &pb.LightClientUpdateCapella{} case version.Deneb: resp = &pb.LightClientUpdateDeneb{} - case version.Electra: + case version.Electra, version.Fulu: resp = &pb.LightClientUpdateElectra{} default: t.Fatalf("Unsupported version %s", version.String(testVersion)) @@ -730,7 +731,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) { require.Equal(t, http.StatusNotFound, writer.Code) }) - for testVersion := 1; testVersion < 6; testVersion++ { + for _, testVersion := range version.All()[1:] { t.Run(version.String(testVersion), func(t *testing.T) { ctx := t.Context() @@ -793,7 +794,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) { resp = &pb.LightClientFinalityUpdateCapella{} case version.Deneb: resp = &pb.LightClientFinalityUpdateDeneb{} - case version.Electra: + case version.Electra, version.Fulu: resp = &pb.LightClientFinalityUpdateElectra{} default: t.Fatalf("Unsupported version %s", version.String(testVersion)) @@ -825,7 +826,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) { require.Equal(t, http.StatusNotFound, writer.Code) }) - for testVersion := 1; testVersion < 6; testVersion++ { + for _, testVersion := range version.All()[1:] { t.Run(version.String(testVersion), func(t *testing.T) { ctx := t.Context() l := util.NewTestLightClient(t, testVersion) @@ -886,7 +887,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) { resp = &pb.LightClientOptimisticUpdateCapella{} case version.Deneb: resp = &pb.LightClientOptimisticUpdateDeneb{} - case version.Electra: + case version.Electra, version.Fulu: resp = &pb.LightClientOptimisticUpdateDeneb{} default: t.Fatalf("Unsupported version %s", version.String(testVersion)) diff --git a/changelog/bastin_update-lc-for-fulu.md b/changelog/bastin_update-lc-for-fulu.md new file mode 100644 index 0000000000..234fc9fda5 --- /dev/null +++ b/changelog/bastin_update-lc-for-fulu.md @@ -0,0 +1,3 @@ +### Added + +- add fulu support to light client processing. \ No newline at end of file diff --git a/testing/util/lightclient.go b/testing/util/lightclient.go index ddc7789bd9..58bbfce572 100644 --- a/testing/util/lightclient.go +++ b/testing/util/lightclient.go @@ -62,6 +62,8 @@ func NewTestLightClient(t *testing.T, forkVersion int, options ...LightClientOpt return l.setupTestDeneb() case version.Electra: return l.setupTestElectra() + case version.Fulu: + return l.setupTestFulu() default: l.T.Fatalf("Unsupported version %s", version.String(l.version)) return nil @@ -955,6 +957,184 @@ func (l *TestLightClient) setupTestElectra() *TestLightClient { return l } +func (l *TestLightClient) setupTestFulu() *TestLightClient { + ctx := context.Background() + + attestedSlot := primitives.Slot(uint64(params.BeaconConfig().FuluForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)).Add(1) + if l.increaseAttestedSlotBy > 0 { + attestedSlot = attestedSlot.Add(l.increaseAttestedSlotBy) + } + + signatureSlot := attestedSlot.Add(1) + if l.increaseSignatureSlotBy > 0 { + signatureSlot = signatureSlot.Add(l.increaseSignatureSlotBy) + } + + // Attested State & Block + attestedState, err := NewBeaconStateFulu() + require.NoError(l.T, err) + require.NoError(l.T, attestedState.SetSlot(attestedSlot)) + + var signedFinalizedBlock interfaces.SignedBeaconBlock + var finalizedState state.BeaconState + // Finalized checkpoint + if !l.noFinalizedCheckpoint { + var finalizedSlot primitives.Slot + + if l.finalizedCheckpointInPrevFork { + finalizedSlot = primitives.Slot(uint64(params.BeaconConfig().ElectraForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)) + if l.increaseFinalizedSlotBy > 0 { + finalizedSlot = finalizedSlot.Add(l.increaseFinalizedSlotBy) + } + + finalizedState, err = NewBeaconStateElectra() + require.NoError(l.T, err) + require.NoError(l.T, finalizedState.SetSlot(finalizedSlot)) + + finalizedBlock := NewBeaconBlockElectra() + require.NoError(l.T, err) + finalizedBlock.Block.Slot = finalizedSlot + signedFinalizedBlock, err = blocks.NewSignedBeaconBlock(finalizedBlock) + require.NoError(l.T, err) + finalizedHeader, err := signedFinalizedBlock.Header() + require.NoError(l.T, err) + require.NoError(l.T, finalizedState.SetLatestBlockHeader(finalizedHeader.Header)) + finalizedStateRoot, err := finalizedState.HashTreeRoot(ctx) + require.NoError(l.T, err) + finalizedBlock.Block.StateRoot = finalizedStateRoot[:] + signedFinalizedBlock, err = blocks.NewSignedBeaconBlock(finalizedBlock) + require.NoError(l.T, err) + } else { + finalizedSlot = primitives.Slot(uint64(params.BeaconConfig().FuluForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)) + if l.increaseFinalizedSlotBy > 0 { + finalizedSlot = finalizedSlot.Add(l.increaseFinalizedSlotBy) + } + + finalizedState, err = NewBeaconStateFulu() + require.NoError(l.T, err) + require.NoError(l.T, finalizedState.SetSlot(finalizedSlot)) + + finalizedBlock := NewBeaconBlockFulu() + require.NoError(l.T, err) + finalizedBlock.Block.Slot = finalizedSlot + signedFinalizedBlock, err = blocks.NewSignedBeaconBlock(finalizedBlock) + require.NoError(l.T, err) + finalizedHeader, err := signedFinalizedBlock.Header() + require.NoError(l.T, err) + require.NoError(l.T, finalizedState.SetLatestBlockHeader(finalizedHeader.Header)) + finalizedStateRoot, err := finalizedState.HashTreeRoot(ctx) + require.NoError(l.T, err) + finalizedBlock.Block.StateRoot = finalizedStateRoot[:] + signedFinalizedBlock, err = blocks.NewSignedBeaconBlock(finalizedBlock) + require.NoError(l.T, err) + } + + // Set the finalized checkpoint + finalizedBlockRoot, err := signedFinalizedBlock.Block().HashTreeRoot() + require.NoError(l.T, err) + finalizedCheckpoint := ðpb.Checkpoint{ + Epoch: slots.ToEpoch(finalizedSlot), + Root: finalizedBlockRoot[:], + } + require.NoError(l.T, attestedState.SetFinalizedCheckpoint(finalizedCheckpoint)) + } + + // Attested Block + attestedBlock := NewBeaconBlockFulu() + attestedBlock.Block.Slot = attestedSlot + attestedBlock.Block.ParentRoot = l.attestedParentRoot[:] + signedAttestedBlock, err := blocks.NewSignedBeaconBlock(attestedBlock) + require.NoError(l.T, err) + attestedBlockHeader, err := signedAttestedBlock.Header() + require.NoError(l.T, err) + require.NoError(l.T, attestedState.SetLatestBlockHeader(attestedBlockHeader.Header)) + attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + require.NoError(l.T, err) + attestedBlock.Block.StateRoot = attestedStateRoot[:] + signedAttestedBlock, err = blocks.NewSignedBeaconBlock(attestedBlock) + require.NoError(l.T, err) + + // Signature State & Block + signatureState, err := NewBeaconStateFulu() + require.NoError(l.T, err) + require.NoError(l.T, signatureState.SetSlot(signatureSlot)) + + var signedSignatureBlock interfaces.SignedBeaconBlock + if l.blinded { + signatureBlock := NewBlindedBeaconBlockFulu() + signatureBlock.Message.Slot = signatureSlot + attestedBlockRoot, err := signedAttestedBlock.Block().HashTreeRoot() + require.NoError(l.T, err) + signatureBlock.Message.ParentRoot = attestedBlockRoot[:] + + var trueBitNum uint64 + if l.supermajority { + trueBitNum = uint64((float64(params.BeaconConfig().SyncCommitteeSize) * 2.0 / 3.0) + 1 + float64(l.increaseActiveParticipantsBy)) + } else { + trueBitNum = params.BeaconConfig().MinSyncCommitteeParticipants + } + for i := uint64(0); i < trueBitNum; i++ { + signatureBlock.Message.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } + + signedSignatureBlock, err = blocks.NewSignedBeaconBlock(signatureBlock) + require.NoError(l.T, err) + + signatureBlockHeader, err := signedSignatureBlock.Header() + require.NoError(l.T, err) + + err = signatureState.SetLatestBlockHeader(signatureBlockHeader.Header) + require.NoError(l.T, err) + stateRoot, err := signatureState.HashTreeRoot(ctx) + require.NoError(l.T, err) + + signatureBlock.Message.StateRoot = stateRoot[:] + signedSignatureBlock, err = blocks.NewSignedBeaconBlock(signatureBlock) + require.NoError(l.T, err) + } else { + signatureBlock := NewBeaconBlockFulu() + signatureBlock.Block.Slot = signatureSlot + attestedBlockRoot, err := signedAttestedBlock.Block().HashTreeRoot() + require.NoError(l.T, err) + signatureBlock.Block.ParentRoot = attestedBlockRoot[:] + + var trueBitNum uint64 + if l.supermajority { + trueBitNum = uint64((float64(params.BeaconConfig().SyncCommitteeSize) * 2.0 / 3.0) + 1 + float64(l.increaseActiveParticipantsBy)) + } else { + trueBitNum = params.BeaconConfig().MinSyncCommitteeParticipants + } + for i := uint64(0); i < trueBitNum; i++ { + signatureBlock.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } + + signedSignatureBlock, err = blocks.NewSignedBeaconBlock(signatureBlock) + require.NoError(l.T, err) + + signatureBlockHeader, err := signedSignatureBlock.Header() + require.NoError(l.T, err) + + err = signatureState.SetLatestBlockHeader(signatureBlockHeader.Header) + require.NoError(l.T, err) + signatureStateRoot, err := signatureState.HashTreeRoot(ctx) + require.NoError(l.T, err) + + signatureBlock.Block.StateRoot = signatureStateRoot[:] + signedSignatureBlock, err = blocks.NewSignedBeaconBlock(signatureBlock) + require.NoError(l.T, err) + } + + l.State = signatureState + l.AttestedState = attestedState + l.AttestedBlock = signedAttestedBlock + l.Block = signedSignatureBlock + l.Ctx = ctx + l.FinalizedBlock = signedFinalizedBlock + l.FinalizedState = finalizedState + + return l +} + func (l *TestLightClient) CheckAttestedHeader(header interfaces.LightClientHeader) { updateAttestedHeaderBeacon := header.Beacon() testAttestedHeader, err := l.AttestedBlock.Header()