mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-06 20:13:59 -05:00
**What type of PR is this?** Other **What does this PR do? Why is it needed?** This pull request removes `NUMBER_OF_COLUMNS` and `MAX_CELLS_IN_EXTENDED_MATRIX` configuration. **Other notes for review** Please read commit by commit, with commit messages. **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description to this PR with sufficient context for reviewers to understand this PR.
132 lines
4.6 KiB
Go
132 lines
4.6 KiB
Go
package sync
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
|
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
|
"github.com/OffchainLabs/prysm/v7/time/slots"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// processDataColumnSidecarsFromReconstruction, after a random delay, attempts to reconstruct,
|
|
// broadcast and receive missing data column sidecars for the given block root.
|
|
// https:github.com/ethereum/consensus-specs/blob/master/specs/fulu/das-core.md#reconstruction-and-cross-seeding
|
|
func (s *Service) processDataColumnSidecarsFromReconstruction(ctx context.Context, sidecar blocks.VerifiedRODataColumn) error {
|
|
key := fmt.Sprintf("%#x", sidecar.BlockRoot())
|
|
if _, err, _ := s.reconstructionSingleFlight.Do(key, func() (any, error) {
|
|
var wg sync.WaitGroup
|
|
|
|
root := sidecar.BlockRoot()
|
|
slot := sidecar.Slot()
|
|
proposerIndex := sidecar.ProposerIndex()
|
|
|
|
// Return early if reconstruction is not needed.
|
|
if !s.shouldReconstruct(root) {
|
|
return nil, nil
|
|
}
|
|
|
|
// Compute the slot start time.
|
|
slotStartTime, err := slots.StartTime(s.cfg.clock.GenesisTime(), slot)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to calculate slot start time")
|
|
}
|
|
|
|
// Randomly choose value before starting reconstruction.
|
|
timeIntoSlot := s.computeRandomDelay(slotStartTime)
|
|
broadcastTime := slotStartTime.Add(timeIntoSlot)
|
|
waitingTime := time.Until(broadcastTime)
|
|
|
|
wg.Add(1)
|
|
time.AfterFunc(waitingTime, func() {
|
|
defer wg.Done()
|
|
|
|
// Return early if the context was canceled during the waiting time.
|
|
if err := ctx.Err(); err != nil {
|
|
return
|
|
}
|
|
|
|
// Return early if reconstruction is not needed anymore.
|
|
if !s.shouldReconstruct(root) {
|
|
return
|
|
}
|
|
|
|
// Load all the stored data column sidecars for this root.
|
|
verifiedSidecars, err := s.cfg.dataColumnStorage.Get(root, nil)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to get data column sidecars")
|
|
return
|
|
}
|
|
|
|
// Reconstruct all the data column sidecars.
|
|
startTime := time.Now()
|
|
reconstructedSidecars, err := peerdas.ReconstructDataColumnSidecars(verifiedSidecars)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to reconstruct data column sidecars")
|
|
return
|
|
}
|
|
|
|
duration := time.Since(startTime)
|
|
dataColumnReconstructionHistogram.Observe(float64(duration.Milliseconds()))
|
|
dataColumnReconstructionCounter.Add(float64(len(reconstructedSidecars) - len(verifiedSidecars)))
|
|
|
|
// Retrieve indices of data column sidecars to sample.
|
|
columnIndicesToSample, err := s.columnIndicesToSample()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to get column indices to sample")
|
|
return
|
|
}
|
|
|
|
unseenIndices, err := s.broadcastAndReceiveUnseenDataColumnSidecars(ctx, slot, proposerIndex, columnIndicesToSample, reconstructedSidecars)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to broadcast and receive unseen data column sidecars")
|
|
return
|
|
}
|
|
|
|
if len(unseenIndices) > 0 {
|
|
log.WithFields(logrus.Fields{
|
|
"root": fmt.Sprintf("%#x", root),
|
|
"slot": slot,
|
|
"proposerIndex": proposerIndex,
|
|
"count": len(unseenIndices),
|
|
"indices": helpers.SortedPrettySliceFromMap(unseenIndices),
|
|
"duration": duration,
|
|
}).Debug("Reconstructed data column sidecars")
|
|
}
|
|
})
|
|
|
|
wg.Wait()
|
|
return nil, nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// shouldReconstruct returns true if we should attempt to reconstruct the data columns for the given block root.
|
|
func (s *Service) shouldReconstruct(root [fieldparams.RootLength]byte) bool {
|
|
// Get the columns we store.
|
|
storedDataColumns := s.cfg.dataColumnStorage.Summary(root)
|
|
storedColumnsCount := storedDataColumns.Count()
|
|
|
|
// Reconstruct only if we have at least the minimum number of columns to reconstruct, but not all the columns.
|
|
// (If we have not enough columns, reconstruction is impossible. If we have all the columns, reconstruction is unnecessary.)
|
|
return storedColumnsCount >= peerdas.MinimumColumnCountToReconstruct() && storedColumnsCount != fieldparams.NumberOfColumns
|
|
}
|
|
|
|
// computeRandomDelay computes a random delay duration to wait before reconstructing data column sidecars.
|
|
func (s *Service) computeRandomDelay(slotStartTime time.Time) time.Duration {
|
|
const maxReconstructionDelaySec = 2.
|
|
|
|
randFloat := s.reconstructionRandGen.Float64()
|
|
timeIntoSlot := time.Duration(maxReconstructionDelaySec * randFloat * float64(time.Second))
|
|
return timeIntoSlot
|
|
}
|