mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-11 06:18:05 -05:00
Compare commits
12 Commits
bal-devnet
...
plus-one-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2018b647ea | ||
|
|
0352e39f64 | ||
|
|
82847da8a7 | ||
|
|
accfdd0f7c | ||
|
|
2b51e9e350 | ||
|
|
e05fec0f5f | ||
|
|
9153c5a202 | ||
|
|
26ce94e224 | ||
|
|
1a904dbae3 | ||
|
|
255ea2fac1 | ||
|
|
37417e5905 | ||
|
|
7a5f4cf122 |
@@ -31,6 +31,7 @@ go_test(
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"//time/slots/testing:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
|
||||
@@ -36,7 +36,7 @@ type ServiceOption func(*Service)
|
||||
// The retention period is specified in epochs, and must be >= MIN_EPOCHS_FOR_BLOCK_REQUESTS.
|
||||
func WithRetentionPeriod(retentionEpochs primitives.Epoch) ServiceOption {
|
||||
return func(s *Service) {
|
||||
defaultRetentionEpochs := primitives.Epoch(params.BeaconConfig().MinEpochsForBlockRequests) + 1
|
||||
defaultRetentionEpochs := primitives.Epoch(params.BeaconConfig().MinEpochsForBlockRequests)
|
||||
if retentionEpochs < defaultRetentionEpochs {
|
||||
log.WithField("userEpochs", retentionEpochs).
|
||||
WithField("minRequired", defaultRetentionEpochs).
|
||||
@@ -75,7 +75,7 @@ func New(ctx context.Context, db iface.Database, genesisTime time.Time, initSync
|
||||
p := &Service{
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
ps: pruneStartSlotFunc(primitives.Epoch(params.BeaconConfig().MinEpochsForBlockRequests) + 1), // Default retention epochs is MIN_EPOCHS_FOR_BLOCK_REQUESTS + 1 from the current slot.
|
||||
ps: pruneStartSlotFunc(primitives.Epoch(params.BeaconConfig().MinEpochsForBlockRequests)),
|
||||
done: make(chan struct{}),
|
||||
slotTicker: slots.NewSlotTicker(slots.UnsafeStartTime(genesisTime, 0), params.BeaconConfig().SecondsPerSlot),
|
||||
initSyncWaiter: initSyncWaiter,
|
||||
@@ -239,15 +239,38 @@ func (p *Service) pruneBatches(pruneUpto primitives.Slot) (int, error) {
|
||||
}
|
||||
|
||||
// pruneStartSlotFunc returns the function to determine the start slot to start pruning.
|
||||
// The pruning calculation is epoch-aligned,
|
||||
// ensuring that earliestAvailableSlot is always at an epoch boundary.
|
||||
// So that we prune epoch-wise.
|
||||
// e.g. if retentionEpochs is 3 i.e. we should keep at least 3 epochs from current slot,
|
||||
//
|
||||
// current slot is 325 (=> current epoch is 10),
|
||||
// then we should keep epoch 7 onwards (inclusive of epoch 7).
|
||||
// So we can prune up to the last slot of 6th epoch i.e. 32 x 7 - 1 = 223
|
||||
// Earliest available slot would be 224 in that case.
|
||||
func pruneStartSlotFunc(retentionEpochs primitives.Epoch) func(primitives.Slot) primitives.Slot {
|
||||
return func(current primitives.Slot) primitives.Slot {
|
||||
if retentionEpochs > slots.MaxSafeEpoch() {
|
||||
retentionEpochs = slots.MaxSafeEpoch()
|
||||
}
|
||||
offset := slots.UnsafeEpochStart(retentionEpochs)
|
||||
if offset >= current {
|
||||
|
||||
// Calculate epoch-aligned minimum required slot
|
||||
currentEpoch := slots.ToEpoch(current)
|
||||
var minRequiredEpoch primitives.Epoch
|
||||
if currentEpoch > retentionEpochs {
|
||||
minRequiredEpoch = currentEpoch - retentionEpochs
|
||||
} else {
|
||||
minRequiredEpoch = 0
|
||||
}
|
||||
|
||||
// Get the start slot of the minimum required epoch
|
||||
minRequiredSlot, err := slots.EpochStart(minRequiredEpoch)
|
||||
if err != nil || minRequiredSlot == 0 {
|
||||
return 0
|
||||
}
|
||||
return current - offset
|
||||
|
||||
// Prune up to (but not including) the minimum required slot
|
||||
// This ensures earliestAvailableSlot (pruneUpto + 1) is at the epoch boundary
|
||||
return minRequiredSlot - 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
eth "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/testing/util"
|
||||
"github.com/OffchainLabs/prysm/v6/time/slots"
|
||||
slottest "github.com/OffchainLabs/prysm/v6/time/slots/testing"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
@@ -247,12 +248,23 @@ func TestWithRetentionPeriod_EnforcesMinimum(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
beaconDB := dbtest.SetupDB(t)
|
||||
|
||||
// Get the minimum required epochs (272 + 1 = 273 for minimal)
|
||||
minRequiredEpochs := primitives.Epoch(params.BeaconConfig().MinEpochsForBlockRequests + 1)
|
||||
// Get the minimum required epochs (272 for minimal)
|
||||
minRequiredEpochs := primitives.Epoch(params.BeaconConfig().MinEpochsForBlockRequests)
|
||||
|
||||
// Use a slot that's guaranteed to be after the minimum retention period
|
||||
currentSlot := primitives.Slot(minRequiredEpochs+100) * (params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
// Calculate epoch-aligned expected prune slot
|
||||
// For epoch-aligned pruning: pruneUpto = epochStart(currentEpoch - retention) - 1
|
||||
currentEpoch := slots.ToEpoch(currentSlot)
|
||||
|
||||
// Helper function to calculate expected prune slot for a given retention
|
||||
calcExpectedPruneSlot := func(retention primitives.Epoch) primitives.Slot {
|
||||
minEpoch := currentEpoch - retention
|
||||
minSlot, _ := slots.EpochStart(minEpoch)
|
||||
return minSlot - 1
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
userRetentionEpochs primitives.Epoch
|
||||
@@ -262,19 +274,19 @@ func TestWithRetentionPeriod_EnforcesMinimum(t *testing.T) {
|
||||
{
|
||||
name: "User value below minimum - should use minimum",
|
||||
userRetentionEpochs: 2, // Way below minimum
|
||||
expectedPruneSlot: currentSlot - primitives.Slot(minRequiredEpochs)*params.BeaconConfig().SlotsPerEpoch,
|
||||
expectedPruneSlot: calcExpectedPruneSlot(minRequiredEpochs),
|
||||
description: "Should use minimum when user value is too low",
|
||||
},
|
||||
{
|
||||
name: "User value at minimum",
|
||||
userRetentionEpochs: minRequiredEpochs,
|
||||
expectedPruneSlot: currentSlot - primitives.Slot(minRequiredEpochs)*params.BeaconConfig().SlotsPerEpoch,
|
||||
expectedPruneSlot: calcExpectedPruneSlot(minRequiredEpochs),
|
||||
description: "Should use user value when at minimum",
|
||||
},
|
||||
{
|
||||
name: "User value above minimum",
|
||||
userRetentionEpochs: minRequiredEpochs + 10,
|
||||
expectedPruneSlot: currentSlot - primitives.Slot(minRequiredEpochs+10)*params.BeaconConfig().SlotsPerEpoch,
|
||||
expectedPruneSlot: calcExpectedPruneSlot(minRequiredEpochs + 10),
|
||||
description: "Should use user value when above minimum",
|
||||
},
|
||||
}
|
||||
@@ -311,6 +323,133 @@ func TestWithRetentionPeriod_EnforcesMinimum(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithRetentionPeriod_AcceptsSpecMinimum(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
config := params.MinimalSpecConfig()
|
||||
params.OverrideBeaconConfig(config)
|
||||
|
||||
ctx := t.Context()
|
||||
beaconDB := dbtest.SetupDB(t)
|
||||
|
||||
hook := logTest.NewGlobal()
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
|
||||
// The spec minimum - this SHOULD be accepted without warning
|
||||
specMinimum := primitives.Epoch(params.BeaconConfig().MinEpochsForBlockRequests)
|
||||
|
||||
// Use a slot that's guaranteed to be after the minimum retention period
|
||||
currentSlot := primitives.Slot(specMinimum+100) * (params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
mockCustody := &mockCustodyUpdater{}
|
||||
p, err := New(
|
||||
ctx,
|
||||
beaconDB,
|
||||
time.Now(),
|
||||
nil,
|
||||
nil,
|
||||
mockCustody,
|
||||
WithRetentionPeriod(specMinimum),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test the pruning calculation
|
||||
pruneUptoSlot := p.ps(currentSlot)
|
||||
|
||||
// The expected prune slot should use epoch-aligned calculation
|
||||
// pruneUpto = epochStart(currentEpoch - retention) - 1
|
||||
currentEpoch := slots.ToEpoch(currentSlot)
|
||||
minRequiredEpoch := currentEpoch - specMinimum
|
||||
minRequiredSlot, err := slots.EpochStart(minRequiredEpoch)
|
||||
require.NoError(t, err)
|
||||
expectedPruneSlot := minRequiredSlot - 1
|
||||
|
||||
assert.Equal(t, expectedPruneSlot, pruneUptoSlot,
|
||||
"Pruner should accept and use MIN_EPOCHS_FOR_BLOCK_REQUESTS without adding 1")
|
||||
|
||||
for _, entry := range hook.AllEntries() {
|
||||
if entry.Level == logrus.WarnLevel {
|
||||
t.Errorf("Unexpected warning when using spec minimum: %s", entry.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruneStartSlotFunc_EpochAlignment(t *testing.T) {
|
||||
// This test verifies that the pruning calculation is epoch-aligned.
|
||||
params.SetupTestConfigCleanup(t)
|
||||
config := params.MinimalSpecConfig()
|
||||
params.OverrideBeaconConfig(config)
|
||||
|
||||
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch // 8 for minimal config
|
||||
retentionEpochs := primitives.Epoch(3)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
currentSlot primitives.Slot
|
||||
expectedEpochAlignment bool
|
||||
expectedMinRequiredSlot primitives.Slot
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "Pruning at epoch boundary",
|
||||
currentSlot: primitives.Slot(4 * slotsPerEpoch), // Slot 32 (epoch 4, slot 0 of epoch)
|
||||
expectedEpochAlignment: true,
|
||||
expectedMinRequiredSlot: primitives.Slot(1 * slotsPerEpoch), // Epoch 1 start = slot 8
|
||||
description: "When pruning at epoch boundary, earliestAvailableSlot should be at epoch boundary",
|
||||
},
|
||||
{
|
||||
name: "Pruning at middle of epoch",
|
||||
currentSlot: primitives.Slot(4*slotsPerEpoch + 4), // Slot 36 (epoch 4, slot 4 of epoch)
|
||||
expectedEpochAlignment: true,
|
||||
expectedMinRequiredSlot: primitives.Slot(1 * slotsPerEpoch), // Epoch 1 start = slot 8
|
||||
description: "When pruning mid-epoch, earliestAvailableSlot must still be at epoch boundary",
|
||||
},
|
||||
{
|
||||
name: "Pruning at end of epoch",
|
||||
currentSlot: primitives.Slot(5*slotsPerEpoch - 1), // Slot 39 (epoch 4, last slot)
|
||||
expectedEpochAlignment: true,
|
||||
expectedMinRequiredSlot: primitives.Slot(1 * slotsPerEpoch), // Epoch 1 start = slot 8
|
||||
description: "When pruning at epoch end, earliestAvailableSlot must be at epoch boundary",
|
||||
},
|
||||
{
|
||||
name: "Pruning at various epoch positions",
|
||||
currentSlot: primitives.Slot(8*slotsPerEpoch + 5), // Slot 69 (epoch 8, slot 5 of epoch)
|
||||
expectedEpochAlignment: true,
|
||||
expectedMinRequiredSlot: primitives.Slot(5 * slotsPerEpoch), // Epoch 5 start = slot 40
|
||||
description: "EarliestAvailableSlot should always align to epoch boundary",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create the prune start slot function
|
||||
ps := pruneStartSlotFunc(retentionEpochs)
|
||||
|
||||
// Calculate pruneUpto slot
|
||||
pruneUpto := ps(tt.currentSlot)
|
||||
|
||||
// EarliestAvailableSlot is pruneUpto + 1
|
||||
earliestAvailableSlot := pruneUpto + 1
|
||||
|
||||
// Verify epoch alignment: earliestAvailableSlot should be at an epoch boundary
|
||||
if tt.expectedEpochAlignment {
|
||||
// Check if earliestAvailableSlot is at the start of an epoch
|
||||
epoch := slots.ToEpoch(earliestAvailableSlot)
|
||||
epochStartSlot, err := slots.EpochStart(epoch)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, epochStartSlot, earliestAvailableSlot,
|
||||
"%s: earliestAvailableSlot (%d) should be at epoch boundary (slot %d of epoch %d)",
|
||||
tt.description, earliestAvailableSlot, epochStartSlot, epoch)
|
||||
}
|
||||
|
||||
// Verify it matches the expected minimum required slot for custody validation
|
||||
assert.Equal(t, tt.expectedMinRequiredSlot, earliestAvailableSlot,
|
||||
"%s: earliestAvailableSlot should match custody minimum required slot",
|
||||
tt.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruner_UpdateEarliestSlotError(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
config := params.BeaconConfig()
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
"cache.go",
|
||||
"helpers.go",
|
||||
"lightclient.go",
|
||||
"log.go",
|
||||
"store.go",
|
||||
],
|
||||
importpath = "github.com/OffchainLabs/prysm/v6/beacon-chain/light-client",
|
||||
|
||||
5
beacon-chain/light-client/log.go
Normal file
5
beacon-chain/light-client/log.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package light_client
|
||||
|
||||
import "github.com/sirupsen/logrus"
|
||||
|
||||
var log = logrus.WithField("prefix", "light-client")
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v6/time/slots"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var ErrLightClientBootstrapNotFound = errors.New("light client bootstrap not found")
|
||||
|
||||
@@ -312,14 +312,14 @@ func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSign
|
||||
rob, err := blocks.NewROBlockWithRoot(block, root)
|
||||
if block.IsBlinded() {
|
||||
block, blobSidecars, err = vs.handleBlindedBlock(ctx, block)
|
||||
if errors.Is(err, builderapi.ErrBadGateway) {
|
||||
log.WithError(err).Info("Optimistically proposed block - builder relay temporarily unavailable, block may arrive over P2P")
|
||||
return ðpb.ProposeResponse{BlockRoot: root[:]}, nil
|
||||
}
|
||||
} else if block.Version() >= version.Deneb {
|
||||
blobSidecars, dataColumnSidecars, err = vs.handleUnblindedBlock(rob, req)
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, builderapi.ErrBadGateway) && block.IsBlinded() {
|
||||
log.WithError(err).Info("Optimistically proposed block - builder relay temporarily unavailable, block may arrive over P2P")
|
||||
return ðpb.ProposeResponse{BlockRoot: root[:]}, nil
|
||||
}
|
||||
return nil, status.Errorf(codes.Internal, "%s: %v", "handle block failed", err)
|
||||
}
|
||||
|
||||
|
||||
3
changelog/bastin_lc-prefix.md
Normal file
3
changelog/bastin_lc-prefix.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Ignored
|
||||
|
||||
- Add log prefix to the light-client package.
|
||||
3
changelog/james-prysm_remove-deposit-keymanager-log.md
Normal file
3
changelog/james-prysm_remove-deposit-keymanager-log.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Removed
|
||||
|
||||
- log mentioning removed flag `--show-deposit-data`
|
||||
4
changelog/satushh-plus-one-bug.md
Normal file
4
changelog/satushh-plus-one-bug.md
Normal file
@@ -0,0 +1,4 @@
|
||||
### Fixed
|
||||
|
||||
- corrected defaultRetentionEpochs in pruner
|
||||
- epoch aligned pruning: pruning should be epoch-wise. No fractional epoch pruning.
|
||||
3
changelog/ttsao_fix-optimistic-blinded-blocks.md
Normal file
3
changelog/ttsao_fix-optimistic-blinded-blocks.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Ignored
|
||||
|
||||
- Return optimistic response only when handling blinded blocks in proposer
|
||||
@@ -221,10 +221,10 @@ func TestListAccounts_LocalKeymanager(t *testing.T) {
|
||||
// Expected output format definition
|
||||
const prologLength = 4
|
||||
const accountLength = 4
|
||||
const epilogLength = 2
|
||||
const nameOffset = 1
|
||||
const keyOffset = 2
|
||||
const privkeyOffset = 3
|
||||
const epilogLength = 1
|
||||
|
||||
const keyOffset = 1
|
||||
const privkeyOffset = 2
|
||||
|
||||
// Require the output has correct number of lines
|
||||
lineCount := prologLength + accountLength*numAccounts + epilogLength
|
||||
@@ -242,7 +242,7 @@ func TestListAccounts_LocalKeymanager(t *testing.T) {
|
||||
|
||||
// Assert that account names are printed on the correct lines
|
||||
for i, accountName := range accountNames {
|
||||
lineNumber := prologLength + accountLength*i + nameOffset
|
||||
lineNumber := prologLength + accountLength*i
|
||||
accountNameFound := strings.Contains(lines[lineNumber], accountName)
|
||||
assert.Equal(t, true, accountNameFound, "Account Name %s not found on line number %d", accountName, lineNumber)
|
||||
}
|
||||
|
||||
@@ -402,10 +402,6 @@ func (km *Keymanager) ListKeymanagerAccounts(ctx context.Context, cfg keymanager
|
||||
} else {
|
||||
fmt.Printf("Showing %d validator accounts\n", numAccounts)
|
||||
}
|
||||
fmt.Println(
|
||||
au.BrightRed("View the eth1 deposit transaction data for your accounts " +
|
||||
"by running `validator accounts list --show-deposit-data`"),
|
||||
)
|
||||
|
||||
pubKeys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user