diff --git a/beacon-chain/core/blocks/genesis.go b/beacon-chain/core/blocks/genesis.go index 941c86347d40..f29f706c04bd 100644 --- a/beacon-chain/core/blocks/genesis.go +++ b/beacon-chain/core/blocks/genesis.go @@ -145,6 +145,41 @@ func NewGenesisBlockForState(ctx context.Context, st state.BeaconState) (interfa }, Signature: params.BeaconConfig().EmptySignature[:], }) + case *ethpb.BeaconStateDeneb: + return blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlockDeneb{ + Block: ðpb.BeaconBlockDeneb{ + ParentRoot: params.BeaconConfig().ZeroHash[:], + StateRoot: root[:], + Body: ðpb.BeaconBlockBodyDeneb{ + RandaoReveal: make([]byte, 96), + Eth1Data: ðpb.Eth1Data{ + DepositRoot: make([]byte, 32), + BlockHash: make([]byte, 32), + }, + Graffiti: make([]byte, 32), + SyncAggregate: ðpb.SyncAggregate{ + SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8), + SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength), + }, + ExecutionPayload: &enginev1.ExecutionPayloadDeneb{ // Deneb difference. + ParentHash: make([]byte, 32), + FeeRecipient: make([]byte, 20), + StateRoot: make([]byte, 32), + ReceiptsRoot: make([]byte, 32), + LogsBloom: make([]byte, 256), + PrevRandao: make([]byte, 32), + BaseFeePerGas: make([]byte, 32), + BlockHash: make([]byte, 32), + Transactions: make([][]byte, 0), + Withdrawals: make([]*enginev1.Withdrawal, 0), + ExcessDataGas: make([]byte, 32), // New in Deneb. + }, + BlsToExecutionChanges: make([]*ethpb.SignedBLSToExecutionChange, 0), + BlobKzgCommitments: make([][]byte, 0), + }, + }, + Signature: params.BeaconConfig().EmptySignature[:], + }) default: return nil, ErrUnrecognizedState } diff --git a/beacon-chain/core/deneb/BUILD.bazel b/beacon-chain/core/deneb/BUILD.bazel new file mode 100644 index 000000000000..a59159885024 --- /dev/null +++ b/beacon-chain/core/deneb/BUILD.bazel @@ -0,0 +1,30 @@ +load("@prysm//tools/go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["upgrade.go"], + importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/deneb", + visibility = ["//visibility:public"], + deps = [ + "//beacon-chain/core/time:go_default_library", + "//beacon-chain/state:go_default_library", + "//beacon-chain/state/state-native:go_default_library", + "//config/params:go_default_library", + "//proto/engine/v1:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["upgrade_test.go"], + deps = [ + ":go_default_library", + "//beacon-chain/core/time:go_default_library", + "//config/params: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", + ], +) diff --git a/beacon-chain/core/deneb/upgrade.go b/beacon-chain/core/deneb/upgrade.go new file mode 100644 index 000000000000..d98a8896e7cc --- /dev/null +++ b/beacon-chain/core/deneb/upgrade.go @@ -0,0 +1,114 @@ +package deneb + +import ( + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/state" + state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native" + "github.com/prysmaticlabs/prysm/v4/config/params" + enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" +) + +// UpgradeToDeneb updates inputs a generic state to return the version Deneb state. +func UpgradeToDeneb(state state.BeaconState) (state.BeaconState, error) { + epoch := time.CurrentEpoch(state) + + currentSyncCommittee, err := state.CurrentSyncCommittee() + if err != nil { + return nil, err + } + nextSyncCommittee, err := state.NextSyncCommittee() + if err != nil { + return nil, err + } + prevEpochParticipation, err := state.PreviousEpochParticipation() + if err != nil { + return nil, err + } + currentEpochParticipation, err := state.CurrentEpochParticipation() + if err != nil { + return nil, err + } + inactivityScores, err := state.InactivityScores() + if err != nil { + return nil, err + } + payloadHeader, err := state.LatestExecutionPayloadHeader() + if err != nil { + return nil, err + } + txRoot, err := payloadHeader.TransactionsRoot() + if err != nil { + return nil, err + } + wdRoot, err := payloadHeader.WithdrawalsRoot() + if err != nil { + return nil, err + } + wi, err := state.NextWithdrawalIndex() + if err != nil { + return nil, err + } + vi, err := state.NextWithdrawalValidatorIndex() + if err != nil { + return nil, err + } + summaries, err := state.HistoricalSummaries() + if err != nil { + return nil, err + } + + s := ðpb.BeaconStateDeneb{ + GenesisTime: state.GenesisTime(), + GenesisValidatorsRoot: state.GenesisValidatorsRoot(), + Slot: state.Slot(), + Fork: ðpb.Fork{ + PreviousVersion: state.Fork().CurrentVersion, + CurrentVersion: params.BeaconConfig().DenebForkVersion, + Epoch: epoch, + }, + LatestBlockHeader: state.LatestBlockHeader(), + BlockRoots: state.BlockRoots(), + StateRoots: state.StateRoots(), + HistoricalRoots: [][]byte{}, + Eth1Data: state.Eth1Data(), + Eth1DataVotes: state.Eth1DataVotes(), + Eth1DepositIndex: state.Eth1DepositIndex(), + Validators: state.Validators(), + Balances: state.Balances(), + RandaoMixes: state.RandaoMixes(), + Slashings: state.Slashings(), + PreviousEpochParticipation: prevEpochParticipation, + CurrentEpochParticipation: currentEpochParticipation, + JustificationBits: state.JustificationBits(), + PreviousJustifiedCheckpoint: state.PreviousJustifiedCheckpoint(), + CurrentJustifiedCheckpoint: state.CurrentJustifiedCheckpoint(), + FinalizedCheckpoint: state.FinalizedCheckpoint(), + InactivityScores: inactivityScores, + CurrentSyncCommittee: currentSyncCommittee, + NextSyncCommittee: nextSyncCommittee, + LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderDeneb{ + ParentHash: payloadHeader.ParentHash(), + FeeRecipient: payloadHeader.FeeRecipient(), + StateRoot: payloadHeader.StateRoot(), + ReceiptsRoot: payloadHeader.ReceiptsRoot(), + LogsBloom: payloadHeader.LogsBloom(), + PrevRandao: payloadHeader.PrevRandao(), + BlockNumber: payloadHeader.BlockNumber(), + GasLimit: payloadHeader.GasLimit(), + GasUsed: payloadHeader.GasUsed(), + Timestamp: payloadHeader.Timestamp(), + ExtraData: payloadHeader.ExtraData(), + BaseFeePerGas: payloadHeader.BaseFeePerGas(), + BlockHash: payloadHeader.BlockHash(), + ExcessDataGas: make([]byte, 32), + TransactionsRoot: txRoot, + WithdrawalsRoot: wdRoot, + }, + NextWithdrawalIndex: wi, + NextWithdrawalValidatorIndex: vi, + HistoricalSummaries: summaries, + } + + return state_native.InitializeFromProtoUnsafeDeneb(s) +} diff --git a/beacon-chain/core/deneb/upgrade_test.go b/beacon-chain/core/deneb/upgrade_test.go new file mode 100644 index 000000000000..689669644f9b --- /dev/null +++ b/beacon-chain/core/deneb/upgrade_test.go @@ -0,0 +1,95 @@ +package deneb_test + +import ( + "testing" + + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/deneb" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time" + "github.com/prysmaticlabs/prysm/v4/config/params" + enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/testing/require" + "github.com/prysmaticlabs/prysm/v4/testing/util" +) + +func TestUpgradeToDeneb(t *testing.T) { + st, _ := util.DeterministicGenesisStateCapella(t, params.BeaconConfig().MaxValidatorsPerCommittee) + preForkState := st.Copy() + mSt, err := deneb.UpgradeToDeneb(st) + require.NoError(t, err) + + require.Equal(t, preForkState.GenesisTime(), mSt.GenesisTime()) + require.DeepSSZEqual(t, preForkState.GenesisValidatorsRoot(), mSt.GenesisValidatorsRoot()) + require.Equal(t, preForkState.Slot(), mSt.Slot()) + require.DeepSSZEqual(t, preForkState.LatestBlockHeader(), mSt.LatestBlockHeader()) + require.DeepSSZEqual(t, preForkState.BlockRoots(), mSt.BlockRoots()) + require.DeepSSZEqual(t, preForkState.StateRoots(), mSt.StateRoots()) + require.DeepSSZEqual(t, preForkState.Eth1Data(), mSt.Eth1Data()) + require.DeepSSZEqual(t, preForkState.Eth1DataVotes(), mSt.Eth1DataVotes()) + require.DeepSSZEqual(t, preForkState.Eth1DepositIndex(), mSt.Eth1DepositIndex()) + require.DeepSSZEqual(t, preForkState.Validators(), mSt.Validators()) + require.DeepSSZEqual(t, preForkState.Balances(), mSt.Balances()) + require.DeepSSZEqual(t, preForkState.RandaoMixes(), mSt.RandaoMixes()) + require.DeepSSZEqual(t, preForkState.Slashings(), mSt.Slashings()) + require.DeepSSZEqual(t, preForkState.JustificationBits(), mSt.JustificationBits()) + require.DeepSSZEqual(t, preForkState.PreviousJustifiedCheckpoint(), mSt.PreviousJustifiedCheckpoint()) + require.DeepSSZEqual(t, preForkState.CurrentJustifiedCheckpoint(), mSt.CurrentJustifiedCheckpoint()) + require.DeepSSZEqual(t, preForkState.FinalizedCheckpoint(), mSt.FinalizedCheckpoint()) + numValidators := mSt.NumValidators() + p, err := mSt.PreviousEpochParticipation() + require.NoError(t, err) + require.DeepSSZEqual(t, make([]byte, numValidators), p) + p, err = mSt.CurrentEpochParticipation() + require.NoError(t, err) + require.DeepSSZEqual(t, make([]byte, numValidators), p) + s, err := mSt.InactivityScores() + require.NoError(t, err) + require.DeepSSZEqual(t, make([]uint64, numValidators), s) + + f := mSt.Fork() + require.DeepSSZEqual(t, ðpb.Fork{ + PreviousVersion: st.Fork().CurrentVersion, + CurrentVersion: params.BeaconConfig().DenebForkVersion, + Epoch: time.CurrentEpoch(st), + }, f) + csc, err := mSt.CurrentSyncCommittee() + require.NoError(t, err) + psc, err := preForkState.CurrentSyncCommittee() + require.NoError(t, err) + require.DeepSSZEqual(t, psc, csc) + nsc, err := mSt.NextSyncCommittee() + require.NoError(t, err) + psc, err = preForkState.NextSyncCommittee() + require.NoError(t, err) + require.DeepSSZEqual(t, psc, nsc) + + header, err := mSt.LatestExecutionPayloadHeader() + require.NoError(t, err) + protoHeader, ok := header.Proto().(*enginev1.ExecutionPayloadHeaderDeneb) + require.Equal(t, true, ok) + prevHeader, err := preForkState.LatestExecutionPayloadHeader() + require.NoError(t, err) + txRoot, err := prevHeader.TransactionsRoot() + require.NoError(t, err) + + wdRoot, err := prevHeader.WithdrawalsRoot() + require.NoError(t, err) + wanted := &enginev1.ExecutionPayloadHeaderDeneb{ + ParentHash: prevHeader.ParentHash(), + FeeRecipient: prevHeader.FeeRecipient(), + StateRoot: prevHeader.StateRoot(), + ReceiptsRoot: prevHeader.ReceiptsRoot(), + LogsBloom: prevHeader.LogsBloom(), + PrevRandao: prevHeader.PrevRandao(), + BlockNumber: prevHeader.BlockNumber(), + GasLimit: prevHeader.GasLimit(), + GasUsed: prevHeader.GasUsed(), + Timestamp: prevHeader.Timestamp(), + BaseFeePerGas: prevHeader.BaseFeePerGas(), + BlockHash: prevHeader.BlockHash(), + TransactionsRoot: txRoot, + WithdrawalsRoot: wdRoot, + ExcessDataGas: make([]byte, 32), + } + require.DeepEqual(t, wanted, protoHeader) +} diff --git a/beacon-chain/core/time/slot_epoch.go b/beacon-chain/core/time/slot_epoch.go index c41587131c2f..f49e8e99817a 100644 --- a/beacon-chain/core/time/slot_epoch.go +++ b/beacon-chain/core/time/slot_epoch.go @@ -81,6 +81,15 @@ func CanUpgradeToCapella(slot primitives.Slot) bool { return epochStart && capellaEpoch } +// CanUpgradeToDeneb returns true if the input `slot` can upgrade to Deneb. +// Spec code: +// If state.slot % SLOTS_PER_EPOCH == 0 and compute_epoch_at_slot(state.slot) == DENEB_FORK_EPOCH +func CanUpgradeToDeneb(slot primitives.Slot) bool { + epochStart := slots.IsEpochStart(slot) + DenebEpoch := slots.ToEpoch(slot) == params.BeaconConfig().DenebForkEpoch + return epochStart && DenebEpoch +} + // CanProcessEpoch checks the eligibility to process epoch. // The epoch can be processed at the end of the last slot of every epoch. // diff --git a/beacon-chain/core/time/slot_epoch_test.go b/beacon-chain/core/time/slot_epoch_test.go index dfc1da60d07d..6de4fa2857de 100644 --- a/beacon-chain/core/time/slot_epoch_test.go +++ b/beacon-chain/core/time/slot_epoch_test.go @@ -298,3 +298,38 @@ func TestCanUpgradeToCapella(t *testing.T) { }) } } + +func TestCanUpgradeToDeneb(t *testing.T) { + params.SetupTestConfigCleanup(t) + bc := params.BeaconConfig() + bc.DenebForkEpoch = 5 + params.OverrideBeaconConfig(bc) + tests := []struct { + name string + slot primitives.Slot + want bool + }{ + { + name: "not epoch start", + slot: 1, + want: false, + }, + { + name: "not deneb epoch", + slot: params.BeaconConfig().SlotsPerEpoch, + want: false, + }, + { + name: "deneb epoch", + slot: primitives.Slot(params.BeaconConfig().DenebForkEpoch) * params.BeaconConfig().SlotsPerEpoch, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := time.CanUpgradeToDeneb(tt.slot); got != tt.want { + t.Errorf("CanUpgradeToDeneb() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/beacon-chain/core/transition/BUILD.bazel b/beacon-chain/core/transition/BUILD.bazel index 7aeac6c76399..1dd0221e0e11 100644 --- a/beacon-chain/core/transition/BUILD.bazel +++ b/beacon-chain/core/transition/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//beacon-chain/core/altair:go_default_library", "//beacon-chain/core/blocks:go_default_library", "//beacon-chain/core/capella:go_default_library", + "//beacon-chain/core/deneb:go_default_library", "//beacon-chain/core/epoch:go_default_library", "//beacon-chain/core/epoch/precompute:go_default_library", "//beacon-chain/core/execution:go_default_library", diff --git a/beacon-chain/core/transition/transition.go b/beacon-chain/core/transition/transition.go index 20e6d865c6ba..739317c8bf11 100644 --- a/beacon-chain/core/transition/transition.go +++ b/beacon-chain/core/transition/transition.go @@ -12,6 +12,7 @@ import ( "github.com/prysmaticlabs/prysm/v4/beacon-chain/cache" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/capella" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/deneb" e "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/epoch" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/epoch/precompute" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/execution" @@ -269,35 +270,56 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot primitives. return nil, errors.Wrap(err, "failed to increment state slot") } - if time.CanUpgradeToAltair(state.Slot()) { - state, err = altair.UpgradeToAltair(ctx, state) - if err != nil { - tracing.AnnotateError(span, err) - return nil, err - } + state, err = UpgradeState(ctx, state) + if err != nil { + tracing.AnnotateError(span, err) + return nil, errors.Wrap(err, "failed to upgrade state") } + } - if time.CanUpgradeToBellatrix(state.Slot()) { - state, err = execution.UpgradeToBellatrix(state) - if err != nil { - tracing.AnnotateError(span, err) - return nil, err - } + if highestSlot < state.Slot() { + SkipSlotCache.Put(ctx, key, state) + } + + return state, nil +} + +// UpgradeState upgrades the state to the next version if possible. +func UpgradeState(ctx context.Context, state state.BeaconState) (state.BeaconState, error) { + ctx, span := trace.StartSpan(ctx, "core.state.UpgradeState") + defer span.End() + var err error + if time.CanUpgradeToAltair(state.Slot()) { + state, err = altair.UpgradeToAltair(ctx, state) + if err != nil { + tracing.AnnotateError(span, err) + return nil, err } + } - if time.CanUpgradeToCapella(state.Slot()) { - state, err = capella.UpgradeToCapella(state) - if err != nil { - tracing.AnnotateError(span, err) - return nil, err - } + if time.CanUpgradeToBellatrix(state.Slot()) { + state, err = execution.UpgradeToBellatrix(state) + if err != nil { + tracing.AnnotateError(span, err) + return nil, err } } - if highestSlot < state.Slot() { - SkipSlotCache.Put(ctx, key, state) + if time.CanUpgradeToCapella(state.Slot()) { + state, err = capella.UpgradeToCapella(state) + if err != nil { + tracing.AnnotateError(span, err) + return nil, err + } } + if time.CanUpgradeToDeneb(state.Slot()) { + state, err = deneb.UpgradeToDeneb(state) + if err != nil { + tracing.AnnotateError(span, err) + return nil, err + } + } return state, nil } diff --git a/beacon-chain/core/transition/transition_test.go b/beacon-chain/core/transition/transition_test.go index 3a8e5f380ea5..31fca2595234 100644 --- a/beacon-chain/core/transition/transition_test.go +++ b/beacon-chain/core/transition/transition_test.go @@ -629,6 +629,20 @@ func TestProcessSlots_ThroughBellatrixEpoch(t *testing.T) { require.Equal(t, params.BeaconConfig().SlotsPerEpoch*10, st.Slot()) } +func TestProcessSlots_ThroughDenebEpoch(t *testing.T) { + transition.SkipSlotCache.Disable() + params.SetupTestConfigCleanup(t) + conf := params.BeaconConfig() + conf.DenebForkEpoch = 5 + params.OverrideBeaconConfig(conf) + + st, _ := util.DeterministicGenesisStateCapella(t, params.BeaconConfig().MaxValidatorsPerCommittee) + st, err := transition.ProcessSlots(context.Background(), st, params.BeaconConfig().SlotsPerEpoch*10) + require.NoError(t, err) + require.Equal(t, version.Deneb, st.Version()) + require.Equal(t, params.BeaconConfig().SlotsPerEpoch*10, st.Slot()) +} + func TestProcessSlotsUsingNextSlotCache(t *testing.T) { s, _ := util.DeterministicGenesisState(t, 1) r := []byte{'a'} diff --git a/beacon-chain/state/stategen/BUILD.bazel b/beacon-chain/state/stategen/BUILD.bazel index 0220108e7264..912a3ac2398f 100644 --- a/beacon-chain/state/stategen/BUILD.bazel +++ b/beacon-chain/state/stategen/BUILD.bazel @@ -22,6 +22,7 @@ go_library( deps = [ "//beacon-chain/core/altair:go_default_library", "//beacon-chain/core/capella:go_default_library", + "//beacon-chain/core/deneb:go_default_library", "//beacon-chain/core/execution:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/time:go_default_library", diff --git a/beacon-chain/state/stategen/replay.go b/beacon-chain/state/stategen/replay.go index 2562a38f0d45..3dcd3c3b38a9 100644 --- a/beacon-chain/state/stategen/replay.go +++ b/beacon-chain/state/stategen/replay.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/capella" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/deneb" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/execution" prysmtime "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition" @@ -243,6 +244,14 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot primi return nil, err } } + + if prysmtime.CanUpgradeToDeneb(state.Slot()) { + state, err = deneb.UpgradeToDeneb(state) + if err != nil { + tracing.AnnotateError(span, err) + return nil, err + } + } } return state, nil diff --git a/testing/spectest/mainnet/deneb/fork_helper/BUILD.bazel b/testing/spectest/mainnet/deneb/fork_helper/BUILD.bazel new file mode 100644 index 000000000000..f370420a4d28 --- /dev/null +++ b/testing/spectest/mainnet/deneb/fork_helper/BUILD.bazel @@ -0,0 +1,13 @@ +load("@prysm//tools/go:def.bzl", "go_test") + +go_test( + name = "go_default_test", + size = "small", + srcs = ["upgrade_to_deneb_test.go"], + data = glob(["*.yaml"]) + [ + "@consensus_spec_tests_mainnet//:test_data", + ], + shard_count = 4, + tags = ["spectest"], + deps = ["//testing/spectest/shared/deneb/fork:go_default_library"], +) diff --git a/testing/spectest/mainnet/deneb/fork_helper/upgrade_to_deneb_test.go b/testing/spectest/mainnet/deneb/fork_helper/upgrade_to_deneb_test.go new file mode 100644 index 000000000000..fcbea13c3ac1 --- /dev/null +++ b/testing/spectest/mainnet/deneb/fork_helper/upgrade_to_deneb_test.go @@ -0,0 +1,11 @@ +package fork_helper + +import ( + "testing" + + "github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/deneb/fork" +) + +func TestMainnet_UpgradeToDeneb(t *testing.T) { + fork.RunUpgradeToDeneb(t, "mainnet") +} diff --git a/testing/spectest/minimal/deneb/fork/BUILD.bazel b/testing/spectest/minimal/deneb/fork/BUILD.bazel new file mode 100644 index 000000000000..4c72a8287759 --- /dev/null +++ b/testing/spectest/minimal/deneb/fork/BUILD.bazel @@ -0,0 +1,17 @@ +load("@prysm//tools/go:def.bzl", "go_test") + +go_test( + name = "go_default_test", + size = "small", + srcs = ["upgrade_to_deneb_test.go"], + data = glob(["*.yaml"]) + [ + "@consensus_spec_tests_minimal//:test_data", + ], + eth_network = "minimal", + shard_count = 4, + tags = [ + "minimal", + "spectest", + ], + deps = ["//testing/spectest/shared/deneb/fork:go_default_library"], +) diff --git a/testing/spectest/minimal/deneb/fork/upgrade_to_deneb_test.go b/testing/spectest/minimal/deneb/fork/upgrade_to_deneb_test.go new file mode 100644 index 000000000000..329a8ce1fb5e --- /dev/null +++ b/testing/spectest/minimal/deneb/fork/upgrade_to_deneb_test.go @@ -0,0 +1,11 @@ +package fork + +import ( + "testing" + + "github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/deneb/fork" +) + +func TestMinimal_UpgradeToDeneb(t *testing.T) { + fork.RunUpgradeToDeneb(t, "minimal") +} diff --git a/testing/spectest/shared/deneb/fork/BUILD.bazel b/testing/spectest/shared/deneb/fork/BUILD.bazel new file mode 100644 index 000000000000..e8449f7aa7f1 --- /dev/null +++ b/testing/spectest/shared/deneb/fork/BUILD.bazel @@ -0,0 +1,20 @@ +load("@prysm//tools/go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + testonly = True, + srcs = ["upgrade_to_deneb.go"], + importpath = "github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/deneb/fork", + visibility = ["//visibility:public"], + deps = [ + "//beacon-chain/core/deneb:go_default_library", + "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/state/state-native:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", + "//testing/require:go_default_library", + "//testing/spectest/utils:go_default_library", + "//testing/util:go_default_library", + "@com_github_golang_snappy//:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", + ], +) diff --git a/testing/spectest/shared/deneb/fork/upgrade_to_deneb.go b/testing/spectest/shared/deneb/fork/upgrade_to_deneb.go new file mode 100644 index 000000000000..ace29d039eec --- /dev/null +++ b/testing/spectest/shared/deneb/fork/upgrade_to_deneb.go @@ -0,0 +1,60 @@ +package fork + +import ( + "path" + "testing" + + "github.com/golang/snappy" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/deneb" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers" + state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/testing/require" + "github.com/prysmaticlabs/prysm/v4/testing/spectest/utils" + "github.com/prysmaticlabs/prysm/v4/testing/util" + "google.golang.org/protobuf/proto" +) + +// RunUpgradeToDeneb is a helper function that runs Deneb's fork spec tests. +// It unmarshals a pre- and post-state to check `UpgradeToDeneb` comply with spec implementation. +func RunUpgradeToDeneb(t *testing.T, config string) { + require.NoError(t, utils.SetConfig(t, config)) + + testFolders, testsFolderPath := utils.TestFolders(t, config, "deneb", "fork/fork/pyspec_tests") + for _, folder := range testFolders { + t.Run(folder.Name(), func(t *testing.T) { + helpers.ClearCache() + folderPath := path.Join(testsFolderPath, folder.Name()) + + preStateFile, err := util.BazelFileBytes(path.Join(folderPath, "pre.ssz_snappy")) + require.NoError(t, err) + preStateSSZ, err := snappy.Decode(nil /* dst */, preStateFile) + require.NoError(t, err, "Failed to decompress") + preStateBase := ðpb.BeaconStateCapella{} + if err := preStateBase.UnmarshalSSZ(preStateSSZ); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + preState, err := state_native.InitializeFromProtoCapella(preStateBase) + require.NoError(t, err) + postState, err := deneb.UpgradeToDeneb(preState) + require.NoError(t, err) + postStateFromFunction, err := state_native.ProtobufBeaconStateDeneb(postState.ToProtoUnsafe()) + require.NoError(t, err) + + postStateFile, err := util.BazelFileBytes(path.Join(folderPath, "post.ssz_snappy")) + require.NoError(t, err) + postStateSSZ, err := snappy.Decode(nil /* dst */, postStateFile) + require.NoError(t, err, "Failed to decompress") + postStateFromFile := ðpb.BeaconStateDeneb{} + if err := postStateFromFile.UnmarshalSSZ(postStateSSZ); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + + if !proto.Equal(postStateFromFile, postStateFromFunction) { + t.Log(postStateFromFile.LatestExecutionPayloadHeader) + t.Log(postStateFromFunction.LatestExecutionPayloadHeader) + t.Fatal("Post state does not match expected") + } + }) + } +}