diff --git a/WORKSPACE b/WORKSPACE index 86304506bf9e..bcbb747b30be 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -247,7 +247,9 @@ filegroup( url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz", ) -consensus_spec_version = "v1.4.0-beta.1" +consensus_spec_test_version = "v1.4.0-beta.2-hotfix" + +consensus_spec_version = "v1.4.0-beta.2" bls_test_version = "v0.1.1" @@ -263,8 +265,8 @@ filegroup( visibility = ["//visibility:public"], ) """, - sha256 = "24399b60ce3fbeb2311952d213dc3731b6dcb0f8881b016c283de5b518d2bbba", - url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version, + sha256 = "99770a001189f66204a4ef79161c8002bcbbcbd8236f1c6479bd5b83a3c68d42", + url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_test_version, ) http_archive( @@ -279,8 +281,8 @@ filegroup( visibility = ["//visibility:public"], ) """, - sha256 = "8e656ee48d2e2ebc9cf9baedb81f27925bc625b3e3fbb2883444a08758a5884a", - url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version, + sha256 = "56763f6492ee137108271007d62feef60d8e3f1698e53dee4bc4b07e55f7326b", + url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_test_version, ) http_archive( @@ -295,8 +297,8 @@ filegroup( visibility = ["//visibility:public"], ) """, - sha256 = "8bd137da6cc57a25383bfac5bc37e31265098145278bd8002b88e24c8b4718b9", - url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version, + sha256 = "bc1cac1a991cdc7426efea14385dcf215df85ed3f0572b824ad6a1d7ca0c89ad", + url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_test_version, ) http_archive( @@ -310,7 +312,7 @@ filegroup( visibility = ["//visibility:public"], ) """, - sha256 = "2bc1edb6e4a4f86c00317c04618a90b0ca29ee1eba833d0a64dd67fdd83fdbe3", + sha256 = "c5898001aaab2a5bb38a39ff9d17a52f1f9befcc26e63752cbf556040f0c884e", strip_prefix = "consensus-specs-" + consensus_spec_version[1:], url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version, ) diff --git a/beacon-chain/blockchain/receive_blob.go b/beacon-chain/blockchain/receive_blob.go index 2817158a7850..42677f1800be 100644 --- a/beacon-chain/blockchain/receive_blob.go +++ b/beacon-chain/blockchain/receive_blob.go @@ -1,7 +1,23 @@ package blockchain +import ( + "context" + + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" +) + // SendNewBlobEvent sends a message to the BlobNotifier channel that the blob // for the blocroot `root` is ready in the database -func (s *Service) SendNewBlobEvent(root [32]byte, index uint64) { +func (s *Service) sendNewBlobEvent(root [32]byte, index uint64) { s.blobNotifiers.forRoot(root) <- index } + +// ReceiveBlob saves the blob to database and sends the new event +func (s *Service) ReceiveBlob(ctx context.Context, b *ethpb.BlobSidecar) error { + if err := s.cfg.BeaconDB.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{b}); err != nil { + return err + } + + s.sendNewBlobEvent([32]byte(b.BlockRoot), b.Index) + return nil +} diff --git a/beacon-chain/blockchain/receive_block.go b/beacon-chain/blockchain/receive_block.go index 7fd312b9e59d..be87bb182551 100644 --- a/beacon-chain/blockchain/receive_block.go +++ b/beacon-chain/blockchain/receive_block.go @@ -3,6 +3,7 @@ package blockchain import ( "bytes" "context" + "time" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed" @@ -21,7 +22,6 @@ import ( ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/attestation" "github.com/prysmaticlabs/prysm/v4/runtime/version" - "github.com/prysmaticlabs/prysm/v4/time" "github.com/prysmaticlabs/prysm/v4/time/slots" "go.opencensus.io/trace" "golang.org/x/sync/errgroup" @@ -42,7 +42,7 @@ type BlockReceiver interface { // BlobReceiver interface defines the methods of chain service for receiving new // blobs type BlobReceiver interface { - SendNewBlobEvent([32]byte, uint64) + ReceiveBlob(context.Context, *ethpb.BlobSidecar) error } // SlashingReceiver interface defines the methods of chain service for receiving validated slashing over the wire. diff --git a/beacon-chain/blockchain/testing/mock.go b/beacon-chain/blockchain/testing/mock.go index 23346cf3a78f..f39a67d075a2 100644 --- a/beacon-chain/blockchain/testing/mock.go +++ b/beacon-chain/blockchain/testing/mock.go @@ -606,10 +606,12 @@ func (s *ChainService) UnrealizedJustifiedPayloadBlockHash() [32]byte { return [32]byte{} } -// SendNewBlobEvent mocks the same method in the chain service -func (*ChainService) SendNewBlobEvent(_ [32]byte, _ uint64) {} - // BlockBeingSynced mocks the same method in the chain service func (c *ChainService) BlockBeingSynced(root [32]byte) bool { return root == c.SyncingRoot } + +// ReceiveBlob implements the same method in the chain service +func (*ChainService) ReceiveBlob(_ context.Context, _ *ethpb.BlobSidecar) error { + return nil +} diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index c407f97ea70c..67e2b3632c48 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -142,6 +142,13 @@ func ProcessRegistryUpdates(ctx context.Context, state state.BeaconState) (state return nil, errors.Wrap(err, "could not get churn limit") } + if state.Version() >= version.Deneb { + // Cap churn limit to max per epoch churn limit. New in EIP7514. + if churnLimit > params.BeaconConfig().MaxPerEpochActivationChurnLimit { + churnLimit = params.BeaconConfig().MaxPerEpochActivationChurnLimit + } + } + // Prevent churn limit cause index out of bound. if churnLimit < limit { limit = churnLimit diff --git a/beacon-chain/core/epoch/epoch_processing_test.go b/beacon-chain/core/epoch/epoch_processing_test.go index 1dddef2fecb6..bef1b042e813 100644 --- a/beacon-chain/core/epoch/epoch_processing_test.go +++ b/beacon-chain/core/epoch/epoch_processing_test.go @@ -338,6 +338,42 @@ func TestProcessRegistryUpdates_EligibleToActivate(t *testing.T) { } } +func TestProcessRegistryUpdates_EligibleToActivate_Cancun(t *testing.T) { + base := ðpb.BeaconStateDeneb{ + Slot: 5 * params.BeaconConfig().SlotsPerEpoch, + FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 6, Root: make([]byte, fieldparams.RootLength)}, + } + cfg := params.BeaconConfig() + cfg.MinPerEpochChurnLimit = 10 + cfg.ChurnLimitQuotient = 1 + params.OverrideBeaconConfig(cfg) + + for i := uint64(0); i < 10; i++ { + base.Validators = append(base.Validators, ðpb.Validator{ + ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch, + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, + ActivationEpoch: params.BeaconConfig().FarFutureEpoch, + }) + } + beaconState, err := state_native.InitializeFromProtoDeneb(base) + require.NoError(t, err) + currentEpoch := time.CurrentEpoch(beaconState) + newState, err := epoch.ProcessRegistryUpdates(context.Background(), beaconState) + require.NoError(t, err) + for i, validator := range newState.Validators() { + assert.Equal(t, currentEpoch+1, validator.ActivationEligibilityEpoch, "Could not update registry %d, unexpected activation eligibility epoch", i) + // Note: In Deneb, only validators indices before `MaxPerEpochActivationChurnLimit` should be activated. + if uint64(i) < params.BeaconConfig().MaxPerEpochActivationChurnLimit && validator.ActivationEpoch != helpers.ActivationExitEpoch(currentEpoch) { + t.Errorf("Could not update registry %d, validators failed to activate: wanted activation epoch %d, got %d", + i, helpers.ActivationExitEpoch(currentEpoch), validator.ActivationEpoch) + } + if uint64(i) >= params.BeaconConfig().MaxPerEpochActivationChurnLimit && validator.ActivationEpoch != params.BeaconConfig().FarFutureEpoch { + t.Errorf("Could not update registry %d, validators should not have been activated, wanted activation epoch: %d, got %d", + i, params.BeaconConfig().FarFutureEpoch, validator.ActivationEpoch) + } + } +} + func TestProcessRegistryUpdates_ActivationCompletes(t *testing.T) { base := ðpb.BeaconState{ Slot: 5 * params.BeaconConfig().SlotsPerEpoch, diff --git a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go index c132f227155b..5eb001ae9d78 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go @@ -49,6 +49,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { slot := primitives.Slot(1) driftGenesisTime(f, slot, 0) newRoot := indexToHash(1) + f.store.proposerBoostRoot = [32]byte{} state, blkRoot, err := prepareForkchoiceState( ctx, slot, @@ -74,6 +75,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { slot = primitives.Slot(2) driftGenesisTime(f, slot, 0) newRoot = indexToHash(2) + f.store.proposerBoostRoot = [32]byte{} state, blkRoot, err = prepareForkchoiceState( ctx, slot, @@ -101,6 +103,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { slot = primitives.Slot(3) driftGenesisTime(f, slot, 0) newRoot = indexToHash(3) + f.store.proposerBoostRoot = [32]byte{} state, blkRoot, err = prepareForkchoiceState( ctx, slot, @@ -129,6 +132,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { slot = primitives.Slot(4) driftGenesisTime(f, slot, 0) newRoot = indexToHash(4) + f.store.proposerBoostRoot = [32]byte{} state, blkRoot, err = prepareForkchoiceState( ctx, slot, @@ -335,6 +339,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { cSlot := primitives.Slot(2) driftGenesisTime(f, cSlot, 0) c := indexToHash(2) + f.store.proposerBoostRoot = [32]byte{} state, blkRoot, err := prepareForkchoiceState( ctx, cSlot, @@ -354,6 +359,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { bSlot := primitives.Slot(1) b := indexToHash(1) + f.store.proposerBoostRoot = [32]byte{} state, blkRoot, err = prepareForkchoiceState( ctx, bSlot, @@ -378,6 +384,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // A block D, building on B, is received at slot N+3. It should not be able to win without boosting. dSlot := primitives.Slot(3) d := indexToHash(3) + f.store.proposerBoostRoot = [32]byte{} state, blkRoot, err = prepareForkchoiceState( ctx, dSlot, @@ -398,6 +405,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // If the same block arrives with boosting then it becomes head: driftGenesisTime(f, dSlot, 0) d2 := indexToHash(30) + f.store.proposerBoostRoot = [32]byte{} state, blkRoot, err = prepareForkchoiceState( ctx, dSlot, diff --git a/beacon-chain/forkchoice/doubly-linked-tree/store.go b/beacon-chain/forkchoice/doubly-linked-tree/store.go index dbf89590f375..1fbb28076637 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/store.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/store.go @@ -109,7 +109,8 @@ func (s *Store) insert(ctx context.Context, secondsIntoSlot := (timeNow - s.genesisTime) % params.BeaconConfig().SecondsPerSlot currentSlot := slots.CurrentSlot(s.genesisTime) boostThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot - if currentSlot == slot && secondsIntoSlot < boostThreshold { + isFirstBlock := s.proposerBoostRoot == [32]byte{} + if currentSlot == slot && secondsIntoSlot < boostThreshold && isFirstBlock { s.proposerBoostRoot = root } diff --git a/beacon-chain/rpc/eth/beacon/config_test.go b/beacon-chain/rpc/eth/beacon/config_test.go index f5c19681567f..12c3aeb0fa61 100644 --- a/beacon-chain/rpc/eth/beacon/config_test.go +++ b/beacon-chain/rpc/eth/beacon/config_test.go @@ -141,7 +141,7 @@ func TestGetSpec(t *testing.T) { resp, err := server.GetSpec(context.Background(), &emptypb.Empty{}) require.NoError(t, err) - assert.Equal(t, 111, len(resp.Data)) + assert.Equal(t, 112, len(resp.Data)) for k, v := range resp.Data { switch k { case "CONFIG_NAME": @@ -380,6 +380,8 @@ func TestGetSpec(t *testing.T) { assert.Equal(t, "20", v) case "REORG_PARENT_WEIGHT_THRESHOLD": assert.Equal(t, "160", v) + case "MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT": + assert.Equal(t, "8", v) case "SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY": default: t.Errorf("Incorrect key: %s", k) diff --git a/beacon-chain/sync/subscriber_blob_sidecar.go b/beacon-chain/sync/subscriber_blob_sidecar.go index fd455ffe4c65..ac2ffca0e948 100644 --- a/beacon-chain/sync/subscriber_blob_sidecar.go +++ b/beacon-chain/sync/subscriber_blob_sidecar.go @@ -18,12 +18,10 @@ func (s *Service) blobSubscriber(ctx context.Context, msg proto.Message) error { s.setSeenBlobIndex(b.Message.Blob, b.Message.Index) - if err := s.cfg.beaconDB.SaveBlobSidecar(ctx, []*eth.BlobSidecar{b.Message}); err != nil { + if err := s.cfg.chain.ReceiveBlob(ctx, b.Message); err != nil { return err } - s.cfg.chain.SendNewBlobEvent([32]byte(b.Message.BlockRoot), b.Message.Index) - s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{ Type: opfeed.BlobSidecarReceived, Data: &opfeed.BlobSidecarReceivedData{ diff --git a/config/params/config.go b/config/params/config.go index 6efeed7e1683..26932ade5e63 100644 --- a/config/params/config.go +++ b/config/params/config.go @@ -213,7 +213,7 @@ type BeaconChainConfig struct { // Mev-boost circuit breaker MaxBuilderConsecutiveMissedSlots primitives.Slot // MaxBuilderConsecutiveMissedSlots defines the number of consecutive skip slot to fallback from using relay/builder to local execution engine for block construction. - MaxBuilderEpochMissedSlots primitives.Slot // MaxBuilderEpochMissedSlots is defines the number of total skip slot (per epoch rolling windows) to fallback from using relay/builder to local execution engine for block construction. + MaxBuilderEpochMissedSlots primitives.Slot // MaxBuilderEpochMissedSlots is defining the number of total skip slot (per epoch rolling windows) to fallback from using relay/builder to local execution engine for block construction. LocalBlockValueBoost uint64 // LocalBlockValueBoost is the value boost for local block construction. This is used to prioritize local block construction over relay/builder block construction. // Execution engine timeout value @@ -221,6 +221,9 @@ type BeaconChainConfig struct { // Subnet value BlobsidecarSubnetCount uint64 `yaml:"BLOB_SIDECAR_SUBNET_COUNT"` // BlobsidecarSubnetCount is the number of blobsidecar subnets used in the gossipsub protocol. + + // Values introduced in Deneb hard fork + MaxPerEpochActivationChurnLimit uint64 `yaml:"MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT" spec:"true"` // MaxPerEpochActivationChurnLimit is the maximum amount of churn allotted for validator activation. } // InitializeForkSchedule initializes the schedules forks baked into the config. diff --git a/config/params/loader_test.go b/config/params/loader_test.go index 1258c040d368..34b9694ddb9d 100644 --- a/config/params/loader_test.go +++ b/config/params/loader_test.go @@ -47,8 +47,10 @@ var placeholderFields = []string{ "SUBNETS_PER_NODE", "TTFB_TIMEOUT", "UPDATE_TIMEOUT", + "WHISK_EPOCHS_PER_SHUFFLING_PHASE", "WHISK_FORK_EPOCH", "WHISK_FORK_VERSION", + "WHISK_PROPOSER_SELECTION_GAP", } func TestPlaceholderFieldsDistinctSorted(t *testing.T) { diff --git a/config/params/mainnet_config.go b/config/params/mainnet_config.go index 269ef8dd6783..8fcfbe5b74e6 100644 --- a/config/params/mainnet_config.go +++ b/config/params/mainnet_config.go @@ -270,6 +270,8 @@ var mainnetBeaconConfig = &BeaconChainConfig{ // Subnet value BlobsidecarSubnetCount: 6, + + MaxPerEpochActivationChurnLimit: 8, } // MainnetTestConfig provides a version of the mainnet config that has a different name diff --git a/config/params/minimal_config.go b/config/params/minimal_config.go index 93b1156dba10..9b85d563ad1d 100644 --- a/config/params/minimal_config.go +++ b/config/params/minimal_config.go @@ -13,7 +13,8 @@ func MinimalSpecConfig() *BeaconChainConfig { minimalConfig.MaxCommitteesPerSlot = 4 minimalConfig.TargetCommitteeSize = 4 minimalConfig.MaxValidatorsPerCommittee = 2048 - minimalConfig.MinPerEpochChurnLimit = 4 + minimalConfig.MinPerEpochChurnLimit = 2 // Changed in EIP7514 + minimalConfig.MaxPerEpochActivationChurnLimit = 4 // New in EIP7514 minimalConfig.ChurnLimitQuotient = 32 minimalConfig.ShuffleRoundCount = 10 minimalConfig.MinGenesisActiveValidatorCount = 64 diff --git a/config/params/testdata/e2e_config.yaml b/config/params/testdata/e2e_config.yaml index 8955a5c34054..1014305d1551 100644 --- a/config/params/testdata/e2e_config.yaml +++ b/config/params/testdata/e2e_config.yaml @@ -68,8 +68,8 @@ INACTIVITY_SCORE_BIAS: 4 INACTIVITY_SCORE_RECOVERY_RATE: 16 # 2**4 * 10**9 (= 16,000,000,000) Gwei EJECTION_BALANCE: 16000000000 -# 2**2 (= 4) -MIN_PER_EPOCH_CHURN_LIMIT: 4 +# 2**1 (= 2) +MIN_PER_EPOCH_CHURN_LIMIT: 2 # [customized] scale queue churn at much lower validator counts for testing CHURN_LIMIT_QUOTIENT: 65536 diff --git a/config/params/testnet_e2e_config.go b/config/params/testnet_e2e_config.go index 326e0313aedc..035738e895b9 100644 --- a/config/params/testnet_e2e_config.go +++ b/config/params/testnet_e2e_config.go @@ -96,6 +96,9 @@ func E2EMainnetTestConfig() *BeaconChainConfig { e2eConfig.CapellaForkVersion = []byte{3, 0, 0, 254} e2eConfig.DenebForkVersion = []byte{4, 0, 0, 254} + // Deneb changes. + e2eConfig.MinPerEpochChurnLimit = 2 + e2eConfig.InitializeForkSchedule() return e2eConfig } diff --git a/proto/eth/v1/generated.ssz.go b/proto/eth/v1/generated.ssz.go index 7d23db885f95..ccfbb1cee5ea 100644 --- a/proto/eth/v1/generated.ssz.go +++ b/proto/eth/v1/generated.ssz.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 07349f3ccd696e6980796cf0b8dc149b14d1791dce8dbc553e506af598c11ebb +// Hash: 8807216586301e9517cb647c4514f4ef73f2c52b106aa5cf7706ce504fcdf511 package v1 import ( diff --git a/proto/eth/v2/generated.ssz.go b/proto/eth/v2/generated.ssz.go index 5daf1c0f1816..a3be77536bfa 100644 --- a/proto/eth/v2/generated.ssz.go +++ b/proto/eth/v2/generated.ssz.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: eaf49253855a8c44a53cb851548885777d203c0cdcd3249906ea5f9a7b6e93c6 +// Hash: c5b73fa04173f2f7ff57471820227c1c57ed2cf64771a033a8dd7661476b1534 package eth import ( diff --git a/proto/prysm/v1alpha1/blobs.proto b/proto/prysm/v1alpha1/blobs.proto index 487af0b7b933..4cdbe9f372a5 100644 --- a/proto/prysm/v1alpha1/blobs.proto +++ b/proto/prysm/v1alpha1/blobs.proto @@ -67,4 +67,4 @@ message SignedBlindedBlobSidecar { message BlobIdentifier { bytes block_root = 1 [(ethereum.eth.ext.ssz_size) = "32"]; uint64 index = 2; -} \ No newline at end of file +} diff --git a/proto/prysm/v1alpha1/generated.ssz.go b/proto/prysm/v1alpha1/generated.ssz.go index dfbbf07f3f91..fbe50f8117bd 100644 --- a/proto/prysm/v1alpha1/generated.ssz.go +++ b/proto/prysm/v1alpha1/generated.ssz.go @@ -1,5 +1,5 @@ // Code generated by fastssz. DO NOT EDIT. -// Hash: 5d81458f3b159025d0b0af981efc97e49e33f9a0e91711f2045924512ddf8f68 +// Hash: 437628ba689fb27f524848c027086209535727cf259e162754f174e54b3940cc package eth import ( diff --git a/testing/endtoend/evaluators/operations.go b/testing/endtoend/evaluators/operations.go index 8228ad31256d..de3485fe0cb1 100644 --- a/testing/endtoend/evaluators/operations.go +++ b/testing/endtoend/evaluators/operations.go @@ -29,8 +29,6 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -// churnLimit is normally 4 unless the validator set is extremely large. -var churnLimit = 4 var depositValCount = e2e.DepositCount var numOfExits = 2 @@ -39,7 +37,7 @@ var depositsInBlockStart = params.E2ETestConfig().EpochsPerEth1VotingPeriod * 2 // deposits included + finalization + MaxSeedLookahead for activation. var depositActivationStartEpoch = depositsInBlockStart + 2 + params.E2ETestConfig().MaxSeedLookahead -var depositEndEpoch = depositActivationStartEpoch + primitives.Epoch(math.Ceil(float64(depositValCount)/float64(churnLimit))) +var depositEndEpoch = depositActivationStartEpoch + primitives.Epoch(math.Ceil(float64(depositValCount)/float64(params.BeaconConfig().MinPerEpochChurnLimit))) var exitSubmissionEpoch = primitives.Epoch(7) // ProcessesDepositsInBlocks ensures the expected amount of deposits are accepted into blocks. @@ -249,8 +247,8 @@ func activatesDepositedValidators(ec *e2etypes.EvaluationContext, conns ...*grpc return fmt.Errorf("missing %d validators for post-genesis deposits", len(expected)) } - if deposits != churnLimit { - return fmt.Errorf("expected %d deposits to be processed in epoch %d, received %d", churnLimit, epoch, deposits) + if uint64(deposits) != params.BeaconConfig().MinPerEpochChurnLimit { + return fmt.Errorf("expected %d deposits to be processed in epoch %d, received %d", params.BeaconConfig().MinPerEpochChurnLimit, epoch, deposits) } if lowBalance > 0 { diff --git a/testing/spectest/general/deneb/kzg/verify_blob_kzg_proof_batch_test.go b/testing/spectest/general/deneb/kzg/verify_blob_kzg_proof_batch_test.go index bf9848b81de8..4118da6c3886 100644 --- a/testing/spectest/general/deneb/kzg/verify_blob_kzg_proof_batch_test.go +++ b/testing/spectest/general/deneb/kzg/verify_blob_kzg_proof_batch_test.go @@ -26,9 +26,9 @@ type KZGTestData struct { func TestVerifyBlobKZGProofBatch(t *testing.T) { require.NoError(t, kzgPrysm.Start()) - testFolders, testFolderPath := utils.TestFolders(t, "general", "deneb", "kzg/verify_blob_kzg_proof_batch/small") + testFolders, testFolderPath := utils.TestFolders(t, "general", "deneb", "kzg/verify_blob_kzg_proof_batch/kzg-mainnet") if len(testFolders) == 0 { - t.Fatalf("No test folders found for %s/%s/%s", "general", "deneb", "kzg/verify_blob_kzg_proof_batch/small") + t.Fatalf("No test folders found for %s/%s/%s", "general", "deneb", "kzg/verify_blob_kzg_proof_batch/kzg-mainnet") } for _, folder := range testFolders { t.Run(folder.Name(), func(t *testing.T) { diff --git a/testing/spectest/general/phase0/bls/aggregate_test.go b/testing/spectest/general/phase0/bls/aggregate_test.go index 2d719f8c3b67..d1e583d09bb2 100644 --- a/testing/spectest/general/phase0/bls/aggregate_test.go +++ b/testing/spectest/general/phase0/bls/aggregate_test.go @@ -19,9 +19,9 @@ func TestAggregate(t *testing.T) { } func testAggregate(t *testing.T) { - testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/aggregate/small") + testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/aggregate/bls") if len(testFolders) == 0 { - t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/aggregate/small") + t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/aggregate/bls") } for _, folder := range testFolders { t.Run(folder.Name(), func(t *testing.T) { diff --git a/testing/spectest/general/phase0/bls/aggregate_verify_test.go b/testing/spectest/general/phase0/bls/aggregate_verify_test.go index 2ac41eef7969..4762615efcf7 100644 --- a/testing/spectest/general/phase0/bls/aggregate_verify_test.go +++ b/testing/spectest/general/phase0/bls/aggregate_verify_test.go @@ -20,9 +20,9 @@ func TestAggregateVerify(t *testing.T) { } func testAggregateVerify(t *testing.T) { - testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/aggregate_verify/small") + testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/aggregate_verify/bls") if len(testFolders) == 0 { - t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/aggregate_verify/small") + t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/aggregate_verify/bls") } for i, folder := range testFolders { t.Run(folder.Name(), func(t *testing.T) { diff --git a/testing/spectest/general/phase0/bls/fast_aggregate_verify_test.go b/testing/spectest/general/phase0/bls/fast_aggregate_verify_test.go index fd14d543fbaa..e2a4ebfd1f05 100644 --- a/testing/spectest/general/phase0/bls/fast_aggregate_verify_test.go +++ b/testing/spectest/general/phase0/bls/fast_aggregate_verify_test.go @@ -20,9 +20,9 @@ func TestFastAggregateVerify(t *testing.T) { } func testFastAggregateVerify(t *testing.T) { - testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/fast_aggregate_verify/small") + testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/fast_aggregate_verify/bls") if len(testFolders) == 0 { - t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/fast_aggregate_verify/small") + t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/fast_aggregate_verify/bls") } for i, folder := range testFolders { t.Run(folder.Name(), func(t *testing.T) { diff --git a/testing/spectest/general/phase0/bls/sign_test.go b/testing/spectest/general/phase0/bls/sign_test.go index bb37fac88f6e..25283475a419 100644 --- a/testing/spectest/general/phase0/bls/sign_test.go +++ b/testing/spectest/general/phase0/bls/sign_test.go @@ -20,9 +20,9 @@ func TestSign(t *testing.T) { } func testSign(t *testing.T) { - testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/sign/small") + testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/sign/bls") if len(testFolders) == 0 { - t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/sign/small") + t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/sign/bls") } for i, folder := range testFolders { t.Run(folder.Name(), func(t *testing.T) { diff --git a/testing/spectest/general/phase0/bls/verify_test.go b/testing/spectest/general/phase0/bls/verify_test.go index 6ccd96cd7be2..ef1c3d76e091 100644 --- a/testing/spectest/general/phase0/bls/verify_test.go +++ b/testing/spectest/general/phase0/bls/verify_test.go @@ -19,9 +19,9 @@ func TestVerify(t *testing.T) { } func testVerify(t *testing.T) { - testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/verify/small") + testFolders, testFolderPath := utils.TestFolders(t, "general", "phase0", "bls/verify/bls") if len(testFolders) == 0 { - t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/verify/small") + t.Fatalf("No test folders found for %s/%s/%s", "general", "phase0", "bls/verify/bls") } for i, folder := range testFolders { t.Run(folder.Name(), func(t *testing.T) { diff --git a/testing/spectest/minimal/deneb/forkchoice/forkchoice_test.go b/testing/spectest/minimal/deneb/forkchoice/forkchoice_test.go index e58e7e1eca82..a5020d7a6da5 100644 --- a/testing/spectest/minimal/deneb/forkchoice/forkchoice_test.go +++ b/testing/spectest/minimal/deneb/forkchoice/forkchoice_test.go @@ -8,5 +8,6 @@ import ( ) func TestMinimal_Deneb_Forkchoice(t *testing.T) { + t.Skip("blocked by go-kzg-4844 minimal trusted setup") forkchoice.Run(t, "minimal", version.Deneb) } diff --git a/testing/spectest/shared/common/forkchoice/BUILD.bazel b/testing/spectest/shared/common/forkchoice/BUILD.bazel index 30b86a910279..9c63fec47896 100644 --- a/testing/spectest/shared/common/forkchoice/BUILD.bazel +++ b/testing/spectest/shared/common/forkchoice/BUILD.bazel @@ -13,6 +13,7 @@ go_library( visibility = ["//testing/spectest:__subpackages__"], deps = [ "//beacon-chain/blockchain:go_default_library", + "//beacon-chain/blockchain/kzg:go_default_library", "//beacon-chain/blockchain/testing:go_default_library", "//beacon-chain/cache:go_default_library", "//beacon-chain/cache/depositcache:go_default_library", diff --git a/testing/spectest/shared/common/forkchoice/builder.go b/testing/spectest/shared/common/forkchoice/builder.go index a7c7faa16a21..fbcc45081ad8 100644 --- a/testing/spectest/shared/common/forkchoice/builder.go +++ b/testing/spectest/shared/common/forkchoice/builder.go @@ -88,13 +88,17 @@ func (bb *Builder) block(t testing.TB, b interfaces.ReadOnlySignedBeaconBlock) [ // InvalidBlock receives the invalid block and notifies forkchoice. func (bb *Builder) InvalidBlock(t testing.TB, b interfaces.ReadOnlySignedBeaconBlock) { r := bb.block(t, b) - require.Equal(t, true, bb.service.ReceiveBlock(context.TODO(), b, r) != nil) + ctx, cancel := context.WithTimeout(context.TODO(), time.Duration(params.BeaconConfig().SecondsPerSlot)*time.Second) + defer cancel() + require.Equal(t, true, bb.service.ReceiveBlock(ctx, b, r) != nil) } // ValidBlock receives the valid block and notifies forkchoice. func (bb *Builder) ValidBlock(t testing.TB, b interfaces.ReadOnlySignedBeaconBlock) { r := bb.block(t, b) - require.NoError(t, bb.service.ReceiveBlock(context.TODO(), b, r)) + ctx, cancel := context.WithTimeout(context.TODO(), time.Duration(params.BeaconConfig().SecondsPerSlot)*time.Second) + defer cancel() + require.NoError(t, bb.service.ReceiveBlock(ctx, b, r)) } // PoWBlock receives the block and notifies a mocked execution engine. diff --git a/testing/spectest/shared/common/forkchoice/runner.go b/testing/spectest/shared/common/forkchoice/runner.go index 0a21493867ff..26e240e327a5 100644 --- a/testing/spectest/shared/common/forkchoice/runner.go +++ b/testing/spectest/shared/common/forkchoice/runner.go @@ -1,10 +1,13 @@ package forkchoice import ( + "context" "fmt" + "os" "path" "testing" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/snappy" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state" @@ -90,12 +93,12 @@ func runTest(t *testing.T, config string, fork int, basePath string) { if step.Tick != nil { builder.Tick(t, int64(*step.Tick)) } + var beaconBlock interfaces.ReadOnlySignedBeaconBlock if step.Block != nil { blockFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), fmt.Sprint(*step.Block, ".ssz_snappy")) require.NoError(t, err) blockSSZ, err := snappy.Decode(nil /* dst */, blockFile) require.NoError(t, err) - var beaconBlock interfaces.ReadOnlySignedBeaconBlock switch fork { case version.Phase0: beaconBlock = unmarshalSignedPhase0Block(t, blockSSZ) @@ -110,6 +113,9 @@ func runTest(t *testing.T, config string, fork int, basePath string) { default: t.Fatalf("unknown fork version: %v", fork) } + } + runBlobStep(t, step.Blobs, beaconBlock, fork, folder, testsFolderPath, step.Proofs, builder) + if beaconBlock != nil { if step.Valid != nil && !*step.Valid { builder.InvalidBlock(t, beaconBlock) } else { @@ -272,3 +278,57 @@ func unmarshalSignedDenebBlock(t *testing.T, raw []byte) interfaces.SignedBeacon require.NoError(t, err) return blk } + +func runBlobStep(t *testing.T, + blobs *string, + beaconBlock interfaces.ReadOnlySignedBeaconBlock, + fork int, + folder os.DirEntry, + testsFolderPath string, + proofs []*string, + builder *Builder, +) { + if blobs != nil && *blobs != "null" { + require.NotNil(t, beaconBlock) + require.Equal(t, true, fork >= version.Deneb) + + block := beaconBlock.Block() + root, err := block.HashTreeRoot() + require.NoError(t, err) + parentRoot := block.ParentRoot() + kzgs, err := block.Body().BlobKzgCommitments() + require.NoError(t, err) + + blobsFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), fmt.Sprint(*blobs, ".ssz_snappy")) + require.NoError(t, err) + blobsSSZ, err := snappy.Decode(nil /* dst */, blobsFile) + require.NoError(t, err) + for index := uint64(0); index*fieldparams.BlobLength < uint64(len(blobsSSZ)); index++ { + var proof []byte + if index < uint64(len(proofs)) { + proofPTR := proofs[index] + require.NotNil(t, proofPTR) + proof, err = hexutil.Decode(*proofPTR) + require.NoError(t, err) + } + + var kzg []byte + if uint64(len(kzgs)) < index { + kzg = kzgs[index] + } + blob := [fieldparams.BlobLength]byte{} + copy(blob[:], blobsSSZ[index*fieldparams.BlobLength:]) + sidecar := ðpb.BlobSidecar{ + BlockRoot: root[:], + Index: index, + Slot: block.Slot(), + BlockParentRoot: parentRoot[:], + ProposerIndex: block.ProposerIndex(), + Blob: blob[:], + KzgCommitment: kzg, + KzgProof: proof, + } + require.NoError(t, builder.service.ReceiveBlob(context.Background(), sidecar)) + } + } +} diff --git a/testing/spectest/shared/common/forkchoice/service.go b/testing/spectest/shared/common/forkchoice/service.go index 75811e9a9ab3..aa111d1a076b 100644 --- a/testing/spectest/shared/common/forkchoice/service.go +++ b/testing/spectest/shared/common/forkchoice/service.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/kzg" mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing" "github.com/prysmaticlabs/prysm/v4/beacon-chain/cache" "github.com/prysmaticlabs/prysm/v4/beacon-chain/cache/depositcache" @@ -33,8 +34,8 @@ func startChainService(t testing.TB, block interfaces.ReadOnlySignedBeaconBlock, engineMock *engineMock, ) *blockchain.Service { - db := testDB.SetupDB(t) ctx := context.Background() + db := testDB.SetupDB(t) require.NoError(t, db.SaveBlock(ctx, block)) r, err := block.Block().HashTreeRoot() require.NoError(t, err) @@ -71,6 +72,8 @@ func startChainService(t testing.TB, ) service, err := blockchain.NewService(context.Background(), opts...) require.NoError(t, err) + // force start kzg context here until Deneb fork epoch is decided + require.NoError(t, kzg.Start()) require.NoError(t, service.StartFromSavedState(st)) return service } diff --git a/testing/spectest/shared/common/forkchoice/type.go b/testing/spectest/shared/common/forkchoice/type.go index 7e7bace891c4..e1c943c711b6 100644 --- a/testing/spectest/shared/common/forkchoice/type.go +++ b/testing/spectest/shared/common/forkchoice/type.go @@ -3,6 +3,8 @@ package forkchoice type Step struct { Tick *int `json:"tick"` Block *string `json:"block"` + Blobs *string `json:"blobs"` + Proofs []*string `json:"proofs"` Valid *bool `json:"valid"` Attestation *string `json:"attestation"` AttesterSlashing *string `json:"attester_slashing"`