diff --git a/WORKSPACE b/WORKSPACE index 651d8177c3..9c774b7358 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -253,16 +253,16 @@ filegroup( url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz", ) -consensus_spec_version = "v1.6.0-beta.2" +consensus_spec_version = "v1.6.0" load("@prysm//tools:download_spectests.bzl", "consensus_spec_tests") consensus_spec_tests( name = "consensus_spec_tests", flavors = { - "general": "sha256-oEj0MTViJHjZo32nABK36gfvSXpbwkBk/jt6Mj7pWFI=", - "minimal": "sha256-cS4NPv6IRBoCSmWomQ8OEo8IsVNW9YawUFqoRZQBUj4=", - "mainnet": "sha256-BYuLndMPAh4p13IRJgNfVakrCVL69KRrNw2tdc3ETbE=", + "general": "sha256-54hTaUNF9nLg+hRr3oHoq0yjZpW3MNiiUUuCQu6Rajk=", + "minimal": "sha256-1JHIGg3gVMjvcGYRHR5cwdDgOvX47oR/MWp6gyAeZfA=", + "mainnet": "sha256-292h3W2Ffts0YExgDTyxYe9Os7R0bZIXuAaMO8P6kl4=", }, version = consensus_spec_version, ) @@ -278,7 +278,7 @@ filegroup( visibility = ["//visibility:public"], ) """, - integrity = "sha256-MForEP9dTe0z3ZkTHjX4H6waSkSTghf3gQHPwrSCCro=", + integrity = "sha256-VzBgrEokvYSMIIXVnSA5XS9I3m9oxpvToQGxC1N5lzw=", strip_prefix = "consensus-specs-" + consensus_spec_version[1:], url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version, ) @@ -327,9 +327,9 @@ filegroup( visibility = ["//visibility:public"], ) """, - integrity = "sha256-NZr/gsQK9rBHRnznlPBiNzJpK8MPMrfUa3f+QYqn1+g=", - strip_prefix = "mainnet-978f1794eada6f85bee76e4d2d5959a5fb8e0cc5", - url = "https://github.com/eth-clients/mainnet/archive/978f1794eada6f85bee76e4d2d5959a5fb8e0cc5.tar.gz", + integrity = "sha256-+mqMXyboedVw8Yp0v+U9GDz98QoC1SZET8mjaKPX+AI=", + strip_prefix = "mainnet-980aee8893a2291d473c38f63797d5bc370fa381", + url = "https://github.com/eth-clients/mainnet/archive/980aee8893a2291d473c38f63797d5bc370fa381.tar.gz", ) http_archive( diff --git a/beacon-chain/das/availability_blobs_test.go b/beacon-chain/das/availability_blobs_test.go index ab49e74489..559eccad98 100644 --- a/beacon-chain/das/availability_blobs_test.go +++ b/beacon-chain/das/availability_blobs_test.go @@ -51,16 +51,20 @@ func Test_commitmentsToCheck(t *testing.T) { name: "commitments within da", block: func(t *testing.T) blocks.ROBlock { d := util.NewBeaconBlockFulu() - d.Block.Body.BlobKzgCommitments = commits[:maxBlobs] d.Block.Slot = fulu + 100 + mb := params.GetNetworkScheduleEntry(slots.ToEpoch(d.Block.Slot)).MaxBlobsPerBlock + d.Block.Body.BlobKzgCommitments = commits[:mb] sb, err := blocks.NewSignedBeaconBlock(d) require.NoError(t, err) rb, err := blocks.NewROBlock(sb) require.NoError(t, err) return rb }, - commits: commits[:maxBlobs], - slot: fulu + 100, + commits: func() [][]byte { + mb := params.GetNetworkScheduleEntry(slots.ToEpoch(fulu + 100)).MaxBlobsPerBlock + return commits[:mb] + }(), + slot: fulu + 100, }, { name: "commitments outside da", diff --git a/beacon-chain/p2p/broadcaster_test.go b/beacon-chain/p2p/broadcaster_test.go index 9565008318..382035842e 100644 --- a/beacon-chain/p2p/broadcaster_test.go +++ b/beacon-chain/p2p/broadcaster_test.go @@ -13,6 +13,7 @@ import ( "github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain/kzg" "github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers" "github.com/OffchainLabs/prysm/v6/beacon-chain/core/peerdas" + testDB "github.com/OffchainLabs/prysm/v6/beacon-chain/db/testing" "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/peers" "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/peers/scorers" p2ptest "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/testing" @@ -218,19 +219,30 @@ func TestService_BroadcastAttestation(t *testing.T) { func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) { const port = uint(2000) + // The DB has to be shared in all peers to avoid the + // duplicate metrics collector registration attempted. + // However, we don't care for this test. + db := testDB.SetupDB(t) + // Setup bootnode. - cfg := &Config{PingInterval: testPingInterval} + cfg := &Config{PingInterval: testPingInterval, DB: db} cfg.UDPPort = uint(port) _, pkey := createAddrAndPrivKey(t) ipAddr := net.ParseIP("127.0.0.1") genesisTime := time.Now() genesisValidatorsRoot := make([]byte, 32) + s := &Service{ cfg: cfg, genesisTime: genesisTime, genesisValidatorsRoot: genesisValidatorsRoot, custodyInfo: &custodyInfo{}, + ctx: t.Context(), + custodyInfoSet: make(chan struct{}), } + + close(s.custodyInfoSet) + bootListener, err := s.createListener(ipAddr, pkey) require.NoError(t, err) defer bootListener.Close() @@ -245,6 +257,7 @@ func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) { Discv5BootStrapAddrs: []string{bootNode.String()}, MaxPeers: 2, PingInterval: testPingInterval, + DB: db, } // Setup 2 different hosts for i := uint(1); i <= 2; i++ { @@ -259,7 +272,12 @@ func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) { genesisTime: genesisTime, genesisValidatorsRoot: genesisValidatorsRoot, custodyInfo: &custodyInfo{}, + ctx: t.Context(), + custodyInfoSet: make(chan struct{}), } + + close(s.custodyInfoSet) + listener, err := s.startDiscoveryV5(ipAddr, pkey) // Set for 2nd peer if i == 2 { @@ -711,18 +729,26 @@ func TestService_BroadcastDataColumn(t *testing.T) { // Create a host. _, pkey, ipAddr := createHost(t, port) + // Create a shared DB for the service + db := testDB.SetupDB(t) + + // Create and close the custody info channel immediately since custodyInfo is already set + custodyInfoSet := make(chan struct{}) + close(custodyInfoSet) + service := &Service{ ctx: ctx, host: p1.BHost, pubsub: p1.PubSub(), joinedTopics: map[string]*pubsub.Topic{}, - cfg: &Config{}, + cfg: &Config{DB: db}, genesisTime: time.Now(), genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), subnetsLock: make(map[uint64]*sync.RWMutex), subnetsLockLock: sync.Mutex{}, peers: peers.NewStatus(ctx, &peers.StatusConfig{ScorerParams: &scorers.Config{}}), custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSet, } // Create a listener. diff --git a/beacon-chain/p2p/discovery_test.go b/beacon-chain/p2p/discovery_test.go index cd48877690..1477e08cfb 100644 --- a/beacon-chain/p2p/discovery_test.go +++ b/beacon-chain/p2p/discovery_test.go @@ -136,20 +136,26 @@ func setNodeSubnets(localNode *enode.LocalNode, attSubnets []uint64) { } func TestCreateListener(t *testing.T) { - port := 1024 ipAddr, pkey := createAddrAndPrivKey(t) + + db := testDB.SetupDB(t) + custodyInfoSet := make(chan struct{}) + close(custodyInfoSet) + s := &Service{ + ctx: t.Context(), genesisTime: time.Now(), genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), - cfg: &Config{UDPPort: uint(port)}, + cfg: &Config{UDPPort: 2200, DB: db}, custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSet, } listener, err := s.createListener(ipAddr, pkey) require.NoError(t, err) defer listener.Close() assert.Equal(t, true, listener.Self().IP().Equal(ipAddr), "IP address is not the expected type") - assert.Equal(t, port, listener.Self().UDP(), "Incorrect port number") + assert.Equal(t, 2200, listener.Self().UDP(), "Incorrect port number") pubkey := listener.Self().Pubkey() XisSame := pkey.PublicKey.X.Cmp(pubkey.X) == 0 @@ -161,15 +167,21 @@ func TestCreateListener(t *testing.T) { } func TestStartDiscV5_DiscoverAllPeers(t *testing.T) { - port := 2000 ipAddr, pkey := createAddrAndPrivKey(t) genesisTime := time.Now() genesisValidatorsRoot := make([]byte, 32) + + db := testDB.SetupDB(t) + custodyInfoSet := make(chan struct{}) + close(custodyInfoSet) + s := &Service{ - cfg: &Config{UDPPort: uint(port), PingInterval: testPingInterval, DisableLivenessCheck: true}, + ctx: t.Context(), + cfg: &Config{UDPPort: 6000, PingInterval: testPingInterval, DisableLivenessCheck: true, DB: db}, // Use high port to reduce conflicts genesisTime: genesisTime, genesisValidatorsRoot: genesisValidatorsRoot, custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSet, } bootListener, err := s.createListener(ipAddr, pkey) require.NoError(t, err) @@ -183,19 +195,26 @@ func TestStartDiscV5_DiscoverAllPeers(t *testing.T) { var listeners []*listenerWrapper for i := 1; i <= 5; i++ { - port = 3000 + i + port := 6000 + i // Use unique high ports for peer discovery cfg := &Config{ Discv5BootStrapAddrs: []string{bootNode.String()}, UDPPort: uint(port), PingInterval: testPingInterval, DisableLivenessCheck: true, + DB: db, } ipAddr, pkey := createAddrAndPrivKey(t) + + custodyInfoSetLoop := make(chan struct{}) + close(custodyInfoSetLoop) + s = &Service{ + ctx: t.Context(), cfg: cfg, genesisTime: genesisTime, genesisValidatorsRoot: genesisValidatorsRoot, custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSetLoop, } listener, err := s.startDiscoveryV5(ipAddr, pkey) assert.NoError(t, err, "Could not start discovery for node") @@ -220,16 +239,6 @@ func TestStartDiscV5_DiscoverAllPeers(t *testing.T) { } func TestCreateLocalNode(t *testing.T) { - params.SetupTestConfigCleanup(t) - - // Set the fulu fork epoch to something other than the far future epoch. - initFuluForkEpoch := params.BeaconConfig().FuluForkEpoch - params.BeaconConfig().FuluForkEpoch = 42 - - defer func() { - params.BeaconConfig().FuluForkEpoch = initFuluForkEpoch - }() - testCases := []struct { name string cfg *Config @@ -264,11 +273,11 @@ func TestCreateLocalNode(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { - // Define ports. + // Define ports. Use unique ports since this test validates ENR content. const ( - udpPort = 2000 - tcpPort = 3000 - quicPort = 3000 + udpPort = 3100 + tcpPort = 3101 + quicPort = 3102 ) custodyRequirement := params.BeaconConfig().CustodyRequirement @@ -344,13 +353,19 @@ func TestCreateLocalNode(t *testing.T) { } func TestRebootDiscoveryListener(t *testing.T) { - port := 1024 ipAddr, pkey := createAddrAndPrivKey(t) + + db := testDB.SetupDB(t) + custodyInfoSet := make(chan struct{}) + close(custodyInfoSet) + s := &Service{ + ctx: t.Context(), genesisTime: time.Now(), genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), - cfg: &Config{UDPPort: uint(port)}, + cfg: &Config{UDPPort: 0, DB: db}, // Use 0 to let OS assign an available port custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSet, } createListener := func() (*discover.UDPv5, error) { @@ -379,11 +394,17 @@ func TestRebootDiscoveryListener(t *testing.T) { func TestMultiAddrsConversion_InvalidIPAddr(t *testing.T) { addr := net.ParseIP("invalidIP") _, pkey := createAddrAndPrivKey(t) + + custodyInfoSet := make(chan struct{}) + close(custodyInfoSet) + s := &Service{ + ctx: t.Context(), genesisTime: time.Now(), genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), cfg: &Config{}, custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSet, } node, err := s.createLocalNode(pkey, addr, 0, 0, 0) require.NoError(t, err) @@ -394,15 +415,23 @@ func TestMultiAddrsConversion_InvalidIPAddr(t *testing.T) { func TestMultiAddrConversion_OK(t *testing.T) { hook := logTest.NewGlobal() ipAddr, pkey := createAddrAndPrivKey(t) + + db := testDB.SetupDB(t) + custodyInfoSet := make(chan struct{}) + close(custodyInfoSet) + s := &Service{ + ctx: t.Context(), cfg: &Config{ - UDPPort: 2000, - TCPPort: 3000, - QUICPort: 3000, + UDPPort: 0, // Use 0 to let OS assign an available port + TCPPort: 0, + QUICPort: 0, + DB: db, }, genesisTime: time.Now(), genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSet, } listener, err := s.createListener(ipAddr, pkey) require.NoError(t, err) @@ -472,13 +501,20 @@ func TestHostIsResolved(t *testing.T) { "2001:4860:4860::8844": true, } + db := testDB.SetupDB(t) + custodyInfoSet := make(chan struct{}) + close(custodyInfoSet) + s := &Service{ + ctx: t.Context(), cfg: &Config{ HostDNS: host, + DB: db, }, genesisTime: time.Now(), genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSet, } ip, key := createAddrAndPrivKey(t) list, err := s.createListener(ip, key) @@ -540,15 +576,21 @@ func TestOutboundPeerThreshold(t *testing.T) { } func TestUDPMultiAddress(t *testing.T) { - port := 6500 ipAddr, pkey := createAddrAndPrivKey(t) genesisTime := time.Now() genesisValidatorsRoot := make([]byte, 32) + + db := testDB.SetupDB(t) + custodyInfoSet := make(chan struct{}) + close(custodyInfoSet) + s := &Service{ - cfg: &Config{UDPPort: uint(port)}, + ctx: t.Context(), + cfg: &Config{UDPPort: 2500, DB: db}, genesisTime: genesisTime, genesisValidatorsRoot: genesisValidatorsRoot, custodyInfo: &custodyInfo{}, + custodyInfoSet: custodyInfoSet, } createListener := func() (*discover.UDPv5, error) { @@ -562,7 +604,7 @@ func TestUDPMultiAddress(t *testing.T) { multiAddresses, err := s.DiscoveryAddresses() require.NoError(t, err) require.Equal(t, true, len(multiAddresses) > 0) - assert.Equal(t, true, strings.Contains(multiAddresses[0].String(), fmt.Sprintf("%d", port))) + assert.Equal(t, true, strings.Contains(multiAddresses[0].String(), fmt.Sprintf("%d", 2500))) assert.Equal(t, true, strings.Contains(multiAddresses[0].String(), "udp")) } @@ -912,7 +954,7 @@ func TestRefreshPersistentSubnets(t *testing.T) { actualPingCount++ return nil }, - cfg: &Config{UDPPort: 2000, DB: testDB.SetupDB(t)}, + cfg: &Config{UDPPort: 0, DB: testDB.SetupDB(t)}, // Use 0 to let OS assign an available port peers: p2p.Peers(), genesisTime: time.Now().Add(-time.Duration(tc.epochSinceGenesis*secondsPerEpoch) * time.Second), genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32), diff --git a/beacon-chain/p2p/service_test.go b/beacon-chain/p2p/service_test.go index df6a2789e3..4d16057e5d 100644 --- a/beacon-chain/p2p/service_test.go +++ b/beacon-chain/p2p/service_test.go @@ -58,14 +58,13 @@ func TestService_Stop_DontPanicIfDv5ListenerIsNotInited(t *testing.T) { } func TestService_Start_OnlyStartsOnce(t *testing.T) { - params.SetupTestConfigCleanup(t) hook := logTest.NewGlobal() cs := startup.NewClockSynchronizer() cfg := &Config{ - UDPPort: 2000, - TCPPort: 3000, - QUICPort: 3000, + UDPPort: 0, // Use 0 to let OS assign an available port + TCPPort: 0, + QUICPort: 0, ClockWaiter: cs, DB: testDB.SetupDB(t), } @@ -73,6 +72,7 @@ func TestService_Start_OnlyStartsOnce(t *testing.T) { require.NoError(t, err) s.dv5Listener = testp2p.NewMockListener(nil, nil) s.custodyInfo = &custodyInfo{} + close(s.custodyInfoSet) exitRoutine := make(chan bool) go func() { s.Start() @@ -111,9 +111,9 @@ func TestService_Start_NoDiscoverFlag(t *testing.T) { cs := startup.NewClockSynchronizer() cfg := &Config{ - UDPPort: 2000, - TCPPort: 3000, - QUICPort: 3000, + UDPPort: 0, // Use 0 to let OS assign an available port + TCPPort: 0, + QUICPort: 0, StateNotifier: &mock.MockStateNotifier{}, NoDiscovery: true, // <-- no s.dv5Listener is created ClockWaiter: cs, @@ -147,12 +147,11 @@ func TestService_Start_NoDiscoverFlag(t *testing.T) { func TestListenForNewNodes(t *testing.T) { const ( - port = uint(2000) + bootPort = uint(2200) // Use specific port for bootnode ENR testPollingPeriod = 1 * time.Second peerCount = 5 ) - params.SetupTestConfigCleanup(t) db := testDB.SetupDB(t) // Setup bootnode. @@ -160,7 +159,7 @@ func TestListenForNewNodes(t *testing.T) { StateNotifier: &mock.MockStateNotifier{}, PingInterval: testPingInterval, DisableLivenessCheck: true, - UDPPort: port, + UDPPort: bootPort, DB: db, } @@ -171,10 +170,13 @@ func TestListenForNewNodes(t *testing.T) { s := &Service{ cfg: cfg, + ctx: t.Context(), genesisTime: genesisTime, genesisValidatorsRoot: gvr[:], custodyInfo: &custodyInfo{}, + custodyInfoSet: make(chan struct{}), } + close(s.custodyInfoSet) bootListener, err := s.createListener(ipAddr, pkey) require.NoError(t, err) @@ -199,25 +201,29 @@ func TestListenForNewNodes(t *testing.T) { hosts := make([]host.Host, 0, peerCount) for i := uint(1); i <= peerCount; i++ { + peerPort := bootPort + i cfg = &Config{ Discv5BootStrapAddrs: []string{bootNode.String()}, PingInterval: testPingInterval, DisableLivenessCheck: true, MaxPeers: peerCount, ClockWaiter: cs, - UDPPort: port + i, - TCPPort: port + i, + UDPPort: peerPort, + TCPPort: peerPort, DB: db, } - h, pkey, ipAddr := createHost(t, port+i) + h, pkey, ipAddr := createHost(t, peerPort) s := &Service{ cfg: cfg, + ctx: t.Context(), genesisTime: genesisTime, genesisValidatorsRoot: gvr[:], custodyInfo: &custodyInfo{}, + custodyInfoSet: make(chan struct{}), } + close(s.custodyInfoSet) listener, err := s.startDiscoveryV5(ipAddr, pkey) require.NoError(t, err, "Could not start discovery for node") @@ -247,6 +253,7 @@ func TestListenForNewNodes(t *testing.T) { s, err = NewService(t.Context(), cfg) require.NoError(t, err) s.custodyInfo = &custodyInfo{} + close(s.custodyInfoSet) go s.Start() @@ -270,7 +277,6 @@ func TestListenForNewNodes(t *testing.T) { } func TestPeer_Disconnect(t *testing.T) { - params.SetupTestConfigCleanup(t) h1, _, _ := createHost(t, 5000) defer func() { if err := h1.Close(); err != nil { diff --git a/beacon-chain/p2p/subnets_test.go b/beacon-chain/p2p/subnets_test.go index 357784421f..ddd9849af0 100644 --- a/beacon-chain/p2p/subnets_test.go +++ b/beacon-chain/p2p/subnets_test.go @@ -69,10 +69,13 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) { bootNodeService := &Service{ cfg: &Config{UDPPort: 2000, TCPPort: 3000, QUICPort: 3000, DisableLivenessCheck: true, PingInterval: testPingInterval}, + ctx: ctx, genesisTime: genesisTime, genesisValidatorsRoot: params.BeaconConfig().GenesisValidatorsRoot[:], custodyInfo: &custodyInfo{}, + custodyInfoSet: make(chan struct{}), } + close(bootNodeService.custodyInfoSet) bootNodeForkDigest, err := bootNodeService.currentForkDigest() require.NoError(t, err) @@ -102,6 +105,7 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) { PingInterval: testPingInterval, DisableLivenessCheck: true, DB: db, + DataDir: t.TempDir(), // Unique data dir for each peer }) require.NoError(t, err) @@ -109,6 +113,7 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) { service.genesisTime = genesisTime service.genesisValidatorsRoot = params.BeaconConfig().GenesisValidatorsRoot[:] service.custodyInfo = &custodyInfo{} + close(service.custodyInfoSet) nodeForkDigest, err := service.currentForkDigest() require.NoError(t, err) @@ -152,6 +157,7 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) { TCPPort: 3010, QUICPort: 3010, DB: db, + DataDir: t.TempDir(), // Unique data dir for test service } service, err := NewService(t.Context(), cfg) @@ -160,6 +166,7 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) { service.genesisTime = genesisTime service.genesisValidatorsRoot = params.BeaconConfig().GenesisValidatorsRoot[:] service.custodyInfo = &custodyInfo{} + close(service.custodyInfoSet) service.Start() defer func() { diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index a46c57f87a..6d9db8ad53 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -300,6 +300,7 @@ func TestListAttestationsV2(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig() config.ElectraForkEpoch = 0 + config.FuluForkEpoch = config.FarFutureEpoch params.OverrideBeaconConfig(config) chainService := &blockchainmock.ChainService{State: bs} @@ -357,6 +358,12 @@ func TestListAttestationsV2(t *testing.T) { writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.ElectraForkEpoch = 0 + config.FuluForkEpoch = config.FarFutureEpoch + params.OverrideBeaconConfig(config) + s.ListAttestationsV2(writer, request) assert.Equal(t, http.StatusOK, writer.Code) resp := &structs.ListAttestationsResponse{} @@ -394,6 +401,186 @@ func TestListAttestationsV2(t *testing.T) { assert.Equal(t, "0x0400000000000000", a.CommitteeBits) } }) + t.Run("Post-Fulu", func(t *testing.T) { + cb1 := primitives.NewAttestationCommitteeBits() + cb1.SetBitAt(1, true) + cb2 := primitives.NewAttestationCommitteeBits() + cb2.SetBitAt(2, true) + + attFulu1 := ðpbv1alpha1.AttestationElectra{ + AggregationBits: []byte{1, 10}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 0, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot1"), 32), + }, + }, + CommitteeBits: cb1, + Signature: bytesutil.PadTo([]byte("signature1"), 96), + } + attFulu2 := ðpbv1alpha1.AttestationElectra{ + AggregationBits: []byte{1, 10}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 0, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, + }, + CommitteeBits: cb2, + Signature: bytesutil.PadTo([]byte("signature2"), 96), + } + attFulu3 := ðpbv1alpha1.AttestationElectra{ + AggregationBits: bitfield.NewBitlist(8), + Data: ðpbv1alpha1.AttestationData{ + Slot: 2, + CommitteeIndex: 0, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot3"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot3"), 32), + }, + }, + CommitteeBits: cb1, + Signature: bytesutil.PadTo([]byte("signature3"), 96), + } + attFulu4 := ðpbv1alpha1.AttestationElectra{ + AggregationBits: bitfield.NewBitlist(8), + Data: ðpbv1alpha1.AttestationData{ + Slot: 2, + CommitteeIndex: 0, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot4"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot4"), 32), + }, + }, + CommitteeBits: cb2, + Signature: bytesutil.PadTo([]byte("signature4"), 96), + } + bs, err := util.NewBeaconStateFulu() + require.NoError(t, err) + + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.ElectraForkEpoch = 0 + config.FuluForkEpoch = 0 + params.OverrideBeaconConfig(config) + + chainService := &blockchainmock.ChainService{State: bs} + s := &Server{ + AttestationsPool: attestations.NewPool(), + ChainInfoFetcher: chainService, + TimeFetcher: chainService, + } + // Added one pre electra attestation to ensure it is ignored. + require.NoError(t, s.AttestationsPool.SaveAggregatedAttestations([]ethpbv1alpha1.Att{attFulu1, attFulu2, att1})) + require.NoError(t, s.AttestationsPool.SaveUnaggregatedAttestations([]ethpbv1alpha1.Att{attFulu3, attFulu4, att3})) + + t.Run("empty request", func(t *testing.T) { + url := "http://example.com" + request := httptest.NewRequest(http.MethodGet, url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.ListAttestationsV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + resp := &structs.ListAttestationsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + + var atts []*structs.AttestationElectra + require.NoError(t, json.Unmarshal(resp.Data, &atts)) + assert.Equal(t, 4, len(atts)) + assert.Equal(t, "fulu", resp.Version) + }) + t.Run("slot request", func(t *testing.T) { + url := "http://example.com?slot=2" + request := httptest.NewRequest(http.MethodGet, url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.ListAttestationsV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + resp := &structs.ListAttestationsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + + var atts []*structs.AttestationElectra + require.NoError(t, json.Unmarshal(resp.Data, &atts)) + assert.Equal(t, 2, len(atts)) + assert.Equal(t, "fulu", resp.Version) + for _, a := range atts { + assert.Equal(t, "2", a.Data.Slot) + } + }) + t.Run("index request", func(t *testing.T) { + url := "http://example.com?committee_index=2" + request := httptest.NewRequest(http.MethodGet, url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.ListAttestationsV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + resp := &structs.ListAttestationsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + + var atts []*structs.AttestationElectra + require.NoError(t, json.Unmarshal(resp.Data, &atts)) + assert.Equal(t, 2, len(atts)) + assert.Equal(t, "fulu", resp.Version) + for _, a := range atts { + assert.Equal(t, "0x0400000000000000", a.CommitteeBits) + } + }) + t.Run("both slot + index request", func(t *testing.T) { + url := "http://example.com?slot=2&committee_index=2" + request := httptest.NewRequest(http.MethodGet, url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.ListAttestationsV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + resp := &structs.ListAttestationsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + + var atts []*structs.AttestationElectra + require.NoError(t, json.Unmarshal(resp.Data, &atts)) + assert.Equal(t, 1, len(atts)) + assert.Equal(t, "fulu", resp.Version) + for _, a := range atts { + assert.Equal(t, "2", a.Data.Slot) + assert.Equal(t, "0x0400000000000000", a.CommitteeBits) + } + }) + }) }) } @@ -494,6 +681,7 @@ func TestSubmitAttestationsV2(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig() config.ElectraForkEpoch = 0 + config.FuluForkEpoch = config.FarFutureEpoch params.OverrideBeaconConfig(config) var body bytes.Buffer @@ -574,6 +762,7 @@ func TestSubmitAttestationsV2(t *testing.T) { assert.Equal(t, true, strings.Contains(e.Failures[0].Message, "Incorrect attestation signature")) }) }) + t.Run("post-electra", func(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig() @@ -1462,6 +1651,7 @@ func TestGetAttesterSlashingsV2(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig() config.ElectraForkEpoch = 100 + config.FuluForkEpoch = config.FarFutureEpoch params.OverrideBeaconConfig(config) chainService := &blockchainmock.ChainService{State: bs} @@ -1493,6 +1683,7 @@ func TestGetAttesterSlashingsV2(t *testing.T) { require.DeepEqual(t, slashingPostElectra, ss[0]) }) + t.Run("post-electra-ok", func(t *testing.T) { bs, err := util.NewBeaconStateElectra() require.NoError(t, err) @@ -1500,6 +1691,7 @@ func TestGetAttesterSlashingsV2(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig() config.ElectraForkEpoch = 100 + config.FuluForkEpoch = config.FarFutureEpoch params.OverrideBeaconConfig(config) chainService := &blockchainmock.ChainService{State: bs} @@ -1570,6 +1762,7 @@ func TestGetAttesterSlashingsV2(t *testing.T) { params.SetupTestConfigCleanup(t) config := params.BeaconConfig() config.ElectraForkEpoch = 100 + config.FuluForkEpoch = config.FarFutureEpoch params.OverrideBeaconConfig(config) chainService := &blockchainmock.ChainService{State: bs} @@ -1596,6 +1789,83 @@ func TestGetAttesterSlashingsV2(t *testing.T) { require.NoError(t, json.Unmarshal(resp.Data, &slashings)) require.NotNil(t, slashings) require.Equal(t, 0, len(slashings)) + + t.Run("Post-Fulu", func(t *testing.T) { + t.Run("post-fulu-ok", func(t *testing.T) { + bs, err := util.NewBeaconStateFulu() + require.NoError(t, err) + + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.ElectraForkEpoch = 0 + config.FuluForkEpoch = 0 + params.OverrideBeaconConfig(config) + + chainService := &blockchainmock.ChainService{State: bs} + + s := &Server{ + ChainInfoFetcher: chainService, + TimeFetcher: chainService, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashingPostElectra}}, + } + + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp := &structs.GetAttesterSlashingsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + assert.Equal(t, "fulu", resp.Version) + + // Unmarshal resp.Data into a slice of slashings + var slashings []*structs.AttesterSlashingElectra + require.NoError(t, json.Unmarshal(resp.Data, &slashings)) + + ss, err := structs.AttesterSlashingsElectraToConsensus(slashings) + require.NoError(t, err) + + require.DeepEqual(t, slashingPostElectra, ss[0]) + }) + t.Run("no-slashings", func(t *testing.T) { + bs, err := util.NewBeaconStateFulu() + require.NoError(t, err) + + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.ElectraForkEpoch = 0 + config.FuluForkEpoch = 0 + params.OverrideBeaconConfig(config) + + chainService := &blockchainmock.ChainService{State: bs} + s := &Server{ + ChainInfoFetcher: chainService, + TimeFetcher: chainService, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{}}, + } + + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp := &structs.GetAttesterSlashingsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + assert.Equal(t, "fulu", resp.Version) + + // Unmarshal resp.Data into a slice of slashings + var slashings []*structs.AttesterSlashingElectra + require.NoError(t, json.Unmarshal(resp.Data, &slashings)) + require.NotNil(t, slashings) + require.Equal(t, 0, len(slashings)) + }) + }) }) } diff --git a/beacon-chain/rpc/eth/config/handlers_test.go b/beacon-chain/rpc/eth/config/handlers_test.go index 36ef6a0e97..86713ba815 100644 --- a/beacon-chain/rpc/eth/config/handlers_test.go +++ b/beacon-chain/rpc/eth/config/handlers_test.go @@ -568,10 +568,9 @@ func TestGetSpec(t *testing.T) { case "SYNC_MESSAGE_DUE_BPS": assert.Equal(t, "104", v) case "BLOB_SCHEDULE": - // BLOB_SCHEDULE should be an empty slice when no schedule is defined blobSchedule, ok := v.([]interface{}) assert.Equal(t, true, ok) - assert.Equal(t, 0, len(blobSchedule)) + assert.Equal(t, 2, len(blobSchedule)) default: t.Errorf("Incorrect key: %s", k) } diff --git a/changelog/james-prysm_fulu-fork-epoch.md b/changelog/james-prysm_fulu-fork-epoch.md new file mode 100644 index 0000000000..d2864ec640 --- /dev/null +++ b/changelog/james-prysm_fulu-fork-epoch.md @@ -0,0 +1,8 @@ +### Added + +- Fulu fork epoch for mainnet configurations set for December 3, 2025, 09:49:11pm UTC +- Added BPO schedules for December 9, 2025, 02:21:11pm UTC and January 7, 2026, 01:01:11am UTC + +### Changed + +- updated consensus spec to 1.6.0 from 1.6.0-beta.2 \ No newline at end of file diff --git a/config/params/fork_test.go b/config/params/fork_test.go index c31042b503..1f3fada116 100644 --- a/config/params/fork_test.go +++ b/config/params/fork_test.go @@ -130,10 +130,10 @@ func TestNextForkData(t *testing.T) { wantedEpoch: cfg.BellatrixForkEpoch, }, { - name: "after last bpo - should be far future epoch and 0x00000000", + name: "post last full fork, fulu bpo 1", currEpoch: params.LastForkEpoch() + 1, - wantedForkVersion: [4]byte(cfg.ElectraForkVersion), - wantedEpoch: cfg.ElectraForkEpoch, + wantedForkVersion: [4]byte(cfg.FuluForkVersion), + wantedEpoch: cfg.BlobSchedule[0].Epoch, }, } for _, tt := range tests { diff --git a/config/params/mainnet_config.go b/config/params/mainnet_config.go index 68a2bd958b..72131d173b 100644 --- a/config/params/mainnet_config.go +++ b/config/params/mainnet_config.go @@ -30,7 +30,7 @@ const ( // Electra Fork Epoch for mainnet config mainnetElectraForkEpoch = 364032 // May 7, 2025, 10:05:11 UTC // Fulu Fork Epoch for mainnet config - mainnetFuluForkEpoch = math.MaxUint64 // Far future / to be defined + mainnetFuluForkEpoch = 411392 // December 3, 2025, 09:49:11pm UTC ) var mainnetNetworkConfig = &NetworkConfig{ @@ -338,7 +338,16 @@ var mainnetBeaconConfig = &BeaconChainConfig{ SubnetsPerNode: 2, NodeIdBits: 256, - BlobSchedule: []BlobScheduleEntry{}, + BlobSchedule: []BlobScheduleEntry{ + { + Epoch: 412672, // December 9, 2025, 02:21:11pm UTC + MaxBlobsPerBlock: 15, + }, + { + Epoch: 419072, // January 7, 2026, 01:01:11am UTC + MaxBlobsPerBlock: 21, + }, + }, } // MainnetTestConfig provides a version of the mainnet config that has a different name diff --git a/specrefs/.ethspecify.yml b/specrefs/.ethspecify.yml index 2fd5381136..9e060d29a0 100644 --- a/specrefs/.ethspecify.yml +++ b/specrefs/.ethspecify.yml @@ -1,4 +1,4 @@ -version: v1.6.0-beta.2 +version: v1.6.0 style: full specrefs: diff --git a/specrefs/configs.yml b/specrefs/configs.yml index a23916fd4a..46b1f05d0b 100644 --- a/specrefs/configs.yml +++ b/specrefs/configs.yml @@ -108,8 +108,16 @@ search: BlobSchedule\s+\[]BlobScheduleEntry regex: true spec: | - + BLOB_SCHEDULE: tuple[frozendict[str, Any], ...] = ( + frozendict({ + "EPOCH": 412672, + "MAX_BLOBS_PER_BLOCK": 15, + }), + frozendict({ + "EPOCH": 419072, + "MAX_BLOBS_PER_BLOCK": 21, + }), ) @@ -266,8 +274,8 @@ search: FuluForkEpoch\s+primitives.Epoch regex: true spec: | - - FULU_FORK_EPOCH: Epoch = 18446744073709551615 + + FULU_FORK_EPOCH: Epoch = 411392 - name: FULU_FORK_VERSION