2019-09-20 17:08:32 +00:00
|
|
|
package sync
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/hex"
|
|
|
|
"sort"
|
2020-02-04 08:31:31 +00:00
|
|
|
"sync"
|
2020-11-19 05:15:58 +00:00
|
|
|
"time"
|
2019-09-20 17:08:32 +00:00
|
|
|
|
2019-12-03 15:56:04 +00:00
|
|
|
"github.com/pkg/errors"
|
2020-01-02 08:09:28 +00:00
|
|
|
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
2020-10-14 07:55:28 +00:00
|
|
|
"github.com/prysmaticlabs/go-ssz"
|
2019-12-03 15:56:04 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
2020-10-14 07:55:28 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
2019-09-20 17:08:32 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
2020-08-20 04:50:14 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2020-06-26 14:58:47 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/rand"
|
2019-12-16 17:00:15 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/runutil"
|
2020-05-13 03:28:17 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/slotutil"
|
2019-10-10 08:44:24 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
2019-09-20 17:08:32 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2020-10-26 21:17:07 +00:00
|
|
|
"github.com/trailofbits/go-mutexasserts"
|
2019-10-10 08:44:24 +00:00
|
|
|
"go.opencensus.io/trace"
|
2019-09-20 17:08:32 +00:00
|
|
|
)
|
|
|
|
|
2020-05-13 03:28:17 +00:00
|
|
|
var processPendingBlocksPeriod = slotutil.DivideSlotBy(3 /* times per slot */)
|
2019-09-20 17:08:32 +00:00
|
|
|
|
2020-08-20 04:50:14 +00:00
|
|
|
const maxPeerRequest = 50
|
|
|
|
const numOfTries = 5
|
2020-11-19 05:15:58 +00:00
|
|
|
const maxBlocksPerSlot = 3
|
2020-08-20 04:50:14 +00:00
|
|
|
|
2019-09-20 17:08:32 +00:00
|
|
|
// processes pending blocks queue on every processPendingBlocksPeriod
|
2020-06-22 20:37:48 +00:00
|
|
|
func (s *Service) processPendingBlocksQueue() {
|
2020-06-23 12:00:29 +00:00
|
|
|
// Prevents multiple queue processing goroutines (invoked by RunEvery) from contending for data.
|
2020-02-04 08:31:31 +00:00
|
|
|
locker := new(sync.Mutex)
|
2020-06-22 20:37:48 +00:00
|
|
|
runutil.RunEvery(s.ctx, processPendingBlocksPeriod, func() {
|
2020-02-04 08:31:31 +00:00
|
|
|
locker.Lock()
|
2020-09-09 09:48:52 +00:00
|
|
|
if err := s.processPendingBlocks(s.ctx); err != nil {
|
2020-08-04 20:30:40 +00:00
|
|
|
log.WithError(err).Debug("Failed to process pending blocks")
|
2020-04-13 04:11:09 +00:00
|
|
|
}
|
2020-02-04 08:31:31 +00:00
|
|
|
locker.Unlock()
|
2019-12-16 17:00:15 +00:00
|
|
|
})
|
2019-09-20 17:08:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// processes the block tree inside the queue
|
2020-06-22 20:37:48 +00:00
|
|
|
func (s *Service) processPendingBlocks(ctx context.Context) error {
|
2019-10-10 08:44:24 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "processPendingBlocks")
|
|
|
|
defer span.End()
|
|
|
|
|
2020-06-22 20:37:48 +00:00
|
|
|
pids := s.p2p.Peers().Connected()
|
|
|
|
if err := s.validatePendingSlots(); err != nil {
|
2019-12-03 15:56:04 +00:00
|
|
|
return errors.Wrap(err, "could not validate pending slots")
|
|
|
|
}
|
2020-06-22 20:37:48 +00:00
|
|
|
slots := s.sortedPendingSlots()
|
2020-10-12 08:11:05 +00:00
|
|
|
var parentRoots [][32]byte
|
2019-09-20 17:08:32 +00:00
|
|
|
|
2019-10-10 08:44:24 +00:00
|
|
|
span.AddAttributes(
|
|
|
|
trace.Int64Attribute("numSlots", int64(len(slots))),
|
|
|
|
trace.Int64Attribute("numPeers", int64(len(pids))),
|
|
|
|
)
|
|
|
|
|
2020-06-26 14:58:47 +00:00
|
|
|
randGen := rand.NewGenerator()
|
2020-06-22 20:37:48 +00:00
|
|
|
for _, slot := range slots {
|
2019-10-10 08:44:24 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "processPendingBlocks.InnerLoop")
|
2020-06-22 20:37:48 +00:00
|
|
|
span.AddAttributes(trace.Int64Attribute("slot", int64(slot)))
|
2019-10-10 08:44:24 +00:00
|
|
|
|
2020-06-22 20:37:48 +00:00
|
|
|
s.pendingQueueLock.RLock()
|
2020-11-19 05:15:58 +00:00
|
|
|
bs := s.pendingBlocksInCache(slot)
|
2020-08-18 00:01:32 +00:00
|
|
|
// Skip if there's no block in the queue.
|
|
|
|
if len(bs) == 0 {
|
2020-06-22 20:37:48 +00:00
|
|
|
s.pendingQueueLock.RUnlock()
|
2020-02-01 22:47:51 +00:00
|
|
|
span.End()
|
2020-01-13 15:09:22 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-07-27 11:57:19 +00:00
|
|
|
s.pendingQueueLock.RUnlock()
|
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
// Loop through the pending queue and mark the potential parent blocks as seen.
|
|
|
|
for _, b := range bs {
|
|
|
|
if b == nil || b.Block == nil {
|
|
|
|
span.End()
|
|
|
|
continue
|
2020-07-27 11:57:19 +00:00
|
|
|
}
|
2019-09-20 17:08:32 +00:00
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
s.pendingQueueLock.RLock()
|
|
|
|
inPendingQueue := s.seenPendingBlocks[bytesutil.ToBytes32(b.Block.ParentRoot)]
|
|
|
|
s.pendingQueueLock.RUnlock()
|
2019-09-20 17:08:32 +00:00
|
|
|
|
2020-08-27 18:13:32 +00:00
|
|
|
blkRoot, err := b.Block.HashTreeRoot()
|
2020-08-18 00:01:32 +00:00
|
|
|
if err != nil {
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
span.End()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
parentIsBad := s.hasBadBlock(bytesutil.ToBytes32(b.Block.ParentRoot))
|
|
|
|
blockIsBad := s.hasBadBlock(blkRoot)
|
|
|
|
// Check if parent is a bad block.
|
|
|
|
if parentIsBad || blockIsBad {
|
|
|
|
// Set block as bad if its parent block is bad too.
|
|
|
|
if parentIsBad {
|
2020-08-18 01:21:10 +00:00
|
|
|
s.setBadBlock(ctx, blkRoot)
|
2020-01-17 22:43:32 +00:00
|
|
|
}
|
2020-08-18 00:01:32 +00:00
|
|
|
// Remove block from queue.
|
|
|
|
s.pendingQueueLock.Lock()
|
2020-11-19 05:15:58 +00:00
|
|
|
if err := s.deleteBlockFromPendingQueue(slot, b, blkRoot); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-18 00:01:32 +00:00
|
|
|
s.pendingQueueLock.Unlock()
|
|
|
|
span.End()
|
|
|
|
continue
|
2020-01-17 22:43:32 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
inDB := s.db.HasBlock(ctx, bytesutil.ToBytes32(b.Block.ParentRoot))
|
|
|
|
hasPeer := len(pids) != 0
|
|
|
|
|
|
|
|
// Only request for missing parent block if it's not in DB, not in pending cache
|
|
|
|
// and has peer in the peer list.
|
|
|
|
if !inPendingQueue && !inDB && hasPeer {
|
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"currentSlot": b.Block.Slot,
|
|
|
|
"parentRoot": hex.EncodeToString(bytesutil.Trunc(b.Block.ParentRoot)),
|
2020-10-12 14:41:59 +00:00
|
|
|
}).Debug("Requesting parent block")
|
2020-08-20 04:50:14 +00:00
|
|
|
parentRoots = append(parentRoots, bytesutil.ToBytes32(b.Block.ParentRoot))
|
2020-08-18 00:01:32 +00:00
|
|
|
|
|
|
|
span.End()
|
|
|
|
continue
|
2019-09-20 17:08:32 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
if !inDB {
|
|
|
|
span.End()
|
|
|
|
continue
|
|
|
|
}
|
2020-11-17 21:50:51 +00:00
|
|
|
|
|
|
|
if err := s.validateBeaconBlock(ctx, b, blkRoot); err != nil {
|
|
|
|
log.Debugf("Could not validate block from slot %d: %v", b.Block.Slot, err)
|
|
|
|
s.setBadBlock(ctx, blkRoot)
|
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
}
|
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
if err := s.chain.ReceiveBlock(ctx, b, blkRoot); err != nil {
|
|
|
|
log.Debugf("Could not process block from slot %d: %v", b.Block.Slot, err)
|
2020-08-18 01:21:10 +00:00
|
|
|
s.setBadBlock(ctx, blkRoot)
|
2020-08-18 00:01:32 +00:00
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
}
|
2019-09-20 17:08:32 +00:00
|
|
|
|
2020-11-17 21:50:51 +00:00
|
|
|
s.setSeenBlockIndexSlot(b.Block.Slot, b.Block.ProposerIndex)
|
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
// Broadcasting the block again once a node is able to process it.
|
|
|
|
if err := s.p2p.Broadcast(ctx, b); err != nil {
|
|
|
|
log.WithError(err).Debug("Failed to broadcast block")
|
|
|
|
}
|
2020-02-02 02:22:59 +00:00
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
s.pendingQueueLock.Lock()
|
2020-11-19 05:15:58 +00:00
|
|
|
if err := s.deleteBlockFromPendingQueue(slot, b, blkRoot); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-18 00:01:32 +00:00
|
|
|
s.pendingQueueLock.Unlock()
|
2019-09-20 17:08:32 +00:00
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"slot": slot,
|
|
|
|
"blockRoot": hex.EncodeToString(bytesutil.Trunc(blkRoot[:])),
|
|
|
|
}).Debug("Processed pending block and cleared it in cache")
|
2020-02-01 22:47:51 +00:00
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
span.End()
|
|
|
|
}
|
2019-09-20 17:08:32 +00:00
|
|
|
}
|
2020-02-02 01:42:29 +00:00
|
|
|
|
2020-08-20 04:50:14 +00:00
|
|
|
return s.sendBatchRootRequest(ctx, parentRoots, randGen)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) sendBatchRootRequest(ctx context.Context, roots [][32]byte, randGen *rand.Rand) error {
|
|
|
|
ctx, span := trace.StartSpan(ctx, "sendBatchRootRequest")
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
if len(roots) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
_, bestPeers := s.p2p.Peers().BestFinalized(maxPeerRequest, s.chain.FinalizedCheckpt().Epoch)
|
|
|
|
if len(bestPeers) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
roots = s.dedupRoots(roots)
|
|
|
|
// Randomly choose a peer to query from our best peers. If that peer cannot return
|
|
|
|
// all the requested blocks, we randomly select another peer.
|
|
|
|
pid := bestPeers[randGen.Int()%len(bestPeers)]
|
|
|
|
for i := 0; i < numOfTries; i++ {
|
2020-10-14 07:55:28 +00:00
|
|
|
req := types.BeaconBlockByRootsReq(roots)
|
2020-08-20 04:50:14 +00:00
|
|
|
if len(roots) > int(params.BeaconNetworkConfig().MaxRequestBlocks) {
|
|
|
|
req = roots[:params.BeaconNetworkConfig().MaxRequestBlocks]
|
|
|
|
}
|
2020-10-14 07:55:28 +00:00
|
|
|
if err := s.sendRecentBeaconBlocksRequest(ctx, &req, pid); err != nil {
|
2020-08-20 04:50:14 +00:00
|
|
|
traceutil.AnnotateError(span, err)
|
|
|
|
log.Debugf("Could not send recent block request: %v", err)
|
|
|
|
}
|
|
|
|
newRoots := make([][32]byte, 0, len(roots))
|
|
|
|
s.pendingQueueLock.RLock()
|
|
|
|
for _, rt := range roots {
|
|
|
|
if !s.seenPendingBlocks[rt] {
|
|
|
|
newRoots = append(newRoots, rt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.pendingQueueLock.RUnlock()
|
|
|
|
if len(newRoots) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Choosing a new peer with the leftover set of
|
|
|
|
// roots to request.
|
|
|
|
roots = newRoots
|
|
|
|
pid = bestPeers[randGen.Int()%len(bestPeers)]
|
|
|
|
}
|
2019-09-20 17:08:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-22 20:37:48 +00:00
|
|
|
func (s *Service) sortedPendingSlots() []uint64 {
|
|
|
|
s.pendingQueueLock.RLock()
|
|
|
|
defer s.pendingQueueLock.RUnlock()
|
2019-09-20 17:08:32 +00:00
|
|
|
|
2020-11-19 05:15:58 +00:00
|
|
|
items := s.slotToPendingBlocks.Items()
|
|
|
|
|
|
|
|
slots := make([]uint64, 0, len(items))
|
|
|
|
for k := range items {
|
|
|
|
slot := cacheKeyToSlot(k)
|
2020-06-22 20:37:48 +00:00
|
|
|
slots = append(slots, slot)
|
2019-09-20 17:08:32 +00:00
|
|
|
}
|
2020-05-29 13:50:41 +00:00
|
|
|
sort.Slice(slots, func(i, j int) bool {
|
|
|
|
return slots[i] < slots[j]
|
|
|
|
})
|
2019-11-25 05:05:51 +00:00
|
|
|
return slots
|
2019-11-21 13:24:50 +00:00
|
|
|
}
|
2019-12-03 15:56:04 +00:00
|
|
|
|
|
|
|
// validatePendingSlots validates the pending blocks
|
|
|
|
// by their slot. If they are before the current finalized
|
|
|
|
// checkpoint, these blocks are removed from the queue.
|
2020-06-22 20:37:48 +00:00
|
|
|
func (s *Service) validatePendingSlots() error {
|
|
|
|
s.pendingQueueLock.Lock()
|
|
|
|
defer s.pendingQueueLock.Unlock()
|
2019-12-03 15:56:04 +00:00
|
|
|
oldBlockRoots := make(map[[32]byte]bool)
|
|
|
|
|
2020-06-22 20:37:48 +00:00
|
|
|
finalizedEpoch := s.chain.FinalizedCheckpt().Epoch
|
2020-11-19 05:15:58 +00:00
|
|
|
if s.slotToPendingBlocks == nil {
|
|
|
|
return errors.New("slotToPendingBlocks cache can't be nil")
|
|
|
|
}
|
|
|
|
items := s.slotToPendingBlocks.Items()
|
|
|
|
for k := range items {
|
|
|
|
slot := cacheKeyToSlot(k)
|
|
|
|
blks := s.pendingBlocksInCache(slot)
|
2020-08-18 00:01:32 +00:00
|
|
|
for _, b := range blks {
|
|
|
|
epoch := helpers.SlotToEpoch(slot)
|
|
|
|
// remove all descendant blocks of old blocks
|
|
|
|
if oldBlockRoots[bytesutil.ToBytes32(b.Block.ParentRoot)] {
|
2020-08-27 18:13:32 +00:00
|
|
|
root, err := b.Block.HashTreeRoot()
|
2020-08-18 00:01:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
oldBlockRoots[root] = true
|
2020-11-19 05:15:58 +00:00
|
|
|
if err := s.deleteBlockFromPendingQueue(slot, b, root); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-18 00:01:32 +00:00
|
|
|
continue
|
2019-12-03 15:56:04 +00:00
|
|
|
}
|
2020-08-18 00:01:32 +00:00
|
|
|
// don't process old blocks
|
|
|
|
if finalizedEpoch > 0 && epoch <= finalizedEpoch {
|
2020-08-27 18:13:32 +00:00
|
|
|
blkRoot, err := b.Block.HashTreeRoot()
|
2020-08-18 00:01:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
oldBlockRoots[blkRoot] = true
|
2020-11-19 05:15:58 +00:00
|
|
|
if err := s.deleteBlockFromPendingQueue(slot, b, blkRoot); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-12-03 15:56:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-01-02 08:09:28 +00:00
|
|
|
|
2020-06-22 20:37:48 +00:00
|
|
|
func (s *Service) clearPendingSlots() {
|
|
|
|
s.pendingQueueLock.Lock()
|
|
|
|
defer s.pendingQueueLock.Unlock()
|
2020-11-19 05:15:58 +00:00
|
|
|
s.slotToPendingBlocks.Flush()
|
2020-06-22 20:37:48 +00:00
|
|
|
s.seenPendingBlocks = make(map[[32]byte]bool)
|
2020-01-02 08:09:28 +00:00
|
|
|
}
|
2020-08-18 00:01:32 +00:00
|
|
|
|
|
|
|
// Delete block from the list from the pending queue using the slot as key.
|
|
|
|
// Note: this helper is not thread safe.
|
2020-11-19 05:15:58 +00:00
|
|
|
func (s *Service) deleteBlockFromPendingQueue(slot uint64, b *ethpb.SignedBeaconBlock, r [32]byte) error {
|
2020-10-26 21:17:07 +00:00
|
|
|
mutexasserts.AssertRWMutexLocked(&s.pendingQueueLock)
|
|
|
|
|
2020-11-19 05:15:58 +00:00
|
|
|
blks := s.pendingBlocksInCache(slot)
|
|
|
|
if len(blks) == 0 {
|
|
|
|
return nil
|
2020-08-18 00:01:32 +00:00
|
|
|
}
|
2020-11-19 05:15:58 +00:00
|
|
|
|
2020-08-18 00:01:32 +00:00
|
|
|
newBlks := make([]*ethpb.SignedBeaconBlock, 0, len(blks))
|
|
|
|
for _, blk := range blks {
|
2020-08-27 13:12:49 +00:00
|
|
|
if ssz.DeepEqual(blk, b) {
|
2020-08-18 00:01:32 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
newBlks = append(newBlks, blk)
|
|
|
|
}
|
|
|
|
if len(newBlks) == 0 {
|
2020-11-19 05:15:58 +00:00
|
|
|
s.slotToPendingBlocks.Delete(slotToCacheKey(slot))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decrease exp itme in proportion to how many blocks are still in the cache for slot key.
|
|
|
|
d := pendingBlockExpTime / time.Duration(len(newBlks))
|
|
|
|
if err := s.slotToPendingBlocks.Replace(slotToCacheKey(slot), newBlks, d); err != nil {
|
|
|
|
return err
|
2020-08-18 00:01:32 +00:00
|
|
|
}
|
2020-08-18 16:21:25 +00:00
|
|
|
delete(s.seenPendingBlocks, r)
|
2020-11-19 05:15:58 +00:00
|
|
|
return nil
|
2020-08-18 00:01:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert block to the list in the pending queue using the slot as key.
|
|
|
|
// Note: this helper is not thread safe.
|
2020-11-19 05:15:58 +00:00
|
|
|
func (s *Service) insertBlockToPendingQueue(slot uint64, b *ethpb.SignedBeaconBlock, r [32]byte) error {
|
2020-10-26 21:17:07 +00:00
|
|
|
mutexasserts.AssertRWMutexLocked(&s.pendingQueueLock)
|
|
|
|
|
2020-08-18 16:21:25 +00:00
|
|
|
if s.seenPendingBlocks[r] {
|
2020-11-19 05:15:58 +00:00
|
|
|
return nil
|
2020-08-18 16:21:25 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 05:15:58 +00:00
|
|
|
if err := s.addPendingBlockToCache(b); err != nil {
|
|
|
|
return err
|
2020-08-18 00:01:32 +00:00
|
|
|
}
|
2020-11-19 05:15:58 +00:00
|
|
|
|
2020-08-18 16:21:25 +00:00
|
|
|
s.seenPendingBlocks[r] = true
|
2020-11-19 05:15:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This returns signed beacon blocks given input key from slotToPendingBlocks.
|
|
|
|
func (s *Service) pendingBlocksInCache(slot uint64) []*ethpb.SignedBeaconBlock {
|
|
|
|
k := slotToCacheKey(slot)
|
|
|
|
value, ok := s.slotToPendingBlocks.Get(k)
|
|
|
|
if !ok {
|
|
|
|
return []*ethpb.SignedBeaconBlock{}
|
|
|
|
}
|
|
|
|
blks, ok := value.([]*ethpb.SignedBeaconBlock)
|
|
|
|
if !ok {
|
|
|
|
return []*ethpb.SignedBeaconBlock{}
|
|
|
|
}
|
|
|
|
return blks
|
|
|
|
}
|
|
|
|
|
|
|
|
// This adds input signed beacon block to slotToPendingBlocks cache.
|
|
|
|
func (s *Service) addPendingBlockToCache(b *ethpb.SignedBeaconBlock) error {
|
|
|
|
if b == nil || b.Block == nil {
|
|
|
|
return errors.New("nil block")
|
|
|
|
}
|
|
|
|
|
|
|
|
blks := s.pendingBlocksInCache(b.Block.Slot)
|
|
|
|
|
|
|
|
if len(blks) >= maxBlocksPerSlot {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
blks = append(blks, b)
|
|
|
|
k := slotToCacheKey(b.Block.Slot)
|
|
|
|
s.slotToPendingBlocks.Set(k, blks, pendingBlockExpTime)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This converts input string to slot number in uint64.
|
|
|
|
func cacheKeyToSlot(s string) uint64 {
|
|
|
|
b := []byte(s)
|
|
|
|
return bytesutil.BytesToUint64BigEndian(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
// This converts input slot number to a key to be used for slotToPendingBlocks cache.
|
|
|
|
func slotToCacheKey(s uint64) string {
|
|
|
|
b := bytesutil.Uint64ToBytesBigEndian(s)
|
|
|
|
return string(b)
|
2020-08-18 00:01:32 +00:00
|
|
|
}
|