mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-06 22:23:56 -05:00
**What type of PR is this?** Feature **What does this PR do? Why is it needed?** Adds data column support to backfill. **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. --------- Co-authored-by: Kasey <kasey@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Preston Van Loon <preston@pvl.dev>
126 lines
4.2 KiB
Go
126 lines
4.2 KiB
Go
package das
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/db/filesystem"
|
|
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var errIndexOutOfBounds = errors.New("sidecar.index > MAX_BLOBS_PER_BLOCK")
|
|
|
|
// cacheKey includes the slot so that we can easily iterate through the cache and compare
|
|
// slots for eviction purposes. Whether the input is the block or the sidecar, we always have
|
|
// the root+slot when interacting with the cache, so it isn't an inconvenience to use both.
|
|
type cacheKey struct {
|
|
slot primitives.Slot
|
|
root [fieldparams.RootLength]byte
|
|
}
|
|
|
|
type blobCache struct {
|
|
entries map[cacheKey]*blobCacheEntry
|
|
}
|
|
|
|
func newBlobCache() *blobCache {
|
|
return &blobCache{entries: make(map[cacheKey]*blobCacheEntry)}
|
|
}
|
|
|
|
// keyFromSidecar is a convenience method for constructing a cacheKey from a BlobSidecar value.
|
|
func keyFromSidecar(sc blocks.ROBlob) cacheKey {
|
|
return cacheKey{slot: sc.Slot(), root: sc.BlockRoot()}
|
|
}
|
|
|
|
// keyFromBlock is a convenience method for constructing a cacheKey from a ROBlock value.
|
|
func keyFromBlock(b blocks.ROBlock) cacheKey {
|
|
return cacheKey{slot: b.Block().Slot(), root: b.Root()}
|
|
}
|
|
|
|
// ensure returns the entry for the given key, creating it if it isn't already present.
|
|
func (c *blobCache) ensure(key cacheKey) *blobCacheEntry {
|
|
e, ok := c.entries[key]
|
|
if !ok {
|
|
e = &blobCacheEntry{}
|
|
c.entries[key] = e
|
|
}
|
|
return e
|
|
}
|
|
|
|
// delete removes the cache entry from the cache.
|
|
func (c *blobCache) delete(key cacheKey) {
|
|
delete(c.entries, key)
|
|
}
|
|
|
|
// blobCacheEntry holds a fixed-length cache of BlobSidecars.
|
|
type blobCacheEntry struct {
|
|
scs []*blocks.ROBlob
|
|
diskSummary filesystem.BlobStorageSummary
|
|
}
|
|
|
|
func (e *blobCacheEntry) setDiskSummary(sum filesystem.BlobStorageSummary) {
|
|
e.diskSummary = sum
|
|
}
|
|
|
|
// stash adds an item to the in-memory cache of BlobSidecars.
|
|
// Only the first BlobSidecar of a given Index will be kept in the cache.
|
|
// stash will return an error if the given blob is already in the cache, or if the Index is out of bounds.
|
|
func (e *blobCacheEntry) stash(sc *blocks.ROBlob) error {
|
|
maxBlobsPerBlock := params.BeaconConfig().MaxBlobsPerBlock(sc.Slot())
|
|
if sc.Index >= uint64(maxBlobsPerBlock) {
|
|
return errors.Wrapf(errIndexOutOfBounds, "index=%d", sc.Index)
|
|
}
|
|
if e.scs == nil {
|
|
e.scs = make([]*blocks.ROBlob, maxBlobsPerBlock)
|
|
}
|
|
if e.scs[sc.Index] != nil {
|
|
return errors.Wrapf(errDuplicateSidecar, "root=%#x, index=%d, commitment=%#x", sc.BlockRoot(), sc.Index, sc.KzgCommitment)
|
|
}
|
|
e.scs[sc.Index] = sc
|
|
return nil
|
|
}
|
|
|
|
// filter evicts sidecars that are not committed to by the block and returns custom
|
|
// errors if the cache is missing any of the commitments, or if the commitments in
|
|
// the cache do not match those found in the block. If err is nil, then all expected
|
|
// commitments were found in the cache and the sidecar slice return value can be used
|
|
// to perform a DA check against the cached sidecars.
|
|
// filter only returns blobs that need to be checked. Blobs already available on disk will be excluded.
|
|
func (e *blobCacheEntry) filter(root [32]byte, kc [][]byte) ([]blocks.ROBlob, error) {
|
|
count := len(kc)
|
|
if e.diskSummary.AllAvailable(count) {
|
|
return nil, nil
|
|
}
|
|
scs := make([]blocks.ROBlob, 0, count)
|
|
for i := uint64(0); i < uint64(count); i++ {
|
|
// We already have this blob, we don't need to write it or validate it.
|
|
if e.diskSummary.HasIndex(i) {
|
|
continue
|
|
}
|
|
// Check if e.scs has this index before accessing
|
|
var sidecar *blocks.ROBlob
|
|
if i < uint64(len(e.scs)) {
|
|
sidecar = e.scs[i]
|
|
}
|
|
|
|
if kc[i] == nil {
|
|
if sidecar != nil {
|
|
return nil, errors.Wrapf(errCommitmentMismatch, "root=%#x, index=%#x, commitment=%#x, no block commitment", root, i, sidecar.KzgCommitment)
|
|
}
|
|
continue
|
|
}
|
|
|
|
if sidecar == nil {
|
|
return nil, errors.Wrapf(errMissingSidecar, "root=%#x, index=%#x", root, i)
|
|
}
|
|
if !bytes.Equal(kc[i], sidecar.KzgCommitment) {
|
|
return nil, errors.Wrapf(errCommitmentMismatch, "root=%#x, index=%#x, commitment=%#x, block commitment=%#x", root, i, sidecar.KzgCommitment, kc[i])
|
|
}
|
|
scs = append(scs, *sidecar)
|
|
}
|
|
|
|
return scs, nil
|
|
}
|