github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/cache/depositcache/deposits_cache.go (about)

     1  // Package depositcache is the source of validator deposits maintained
     2  // in-memory by the beacon node – deposits processed from the
     3  // eth1 powchain are then stored in this cache to be accessed by
     4  // any other service during a beacon node's runtime.
     5  package depositcache
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/hex"
    11  	"math/big"
    12  	"sort"
    13  	"sync"
    14  
    15  	"github.com/pkg/errors"
    16  	"github.com/prometheus/client_golang/prometheus"
    17  	"github.com/prometheus/client_golang/prometheus/promauto"
    18  	dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db"
    19  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    20  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    21  	"github.com/prysmaticlabs/prysm/shared/params"
    22  	"github.com/prysmaticlabs/prysm/shared/trieutil"
    23  	"github.com/sirupsen/logrus"
    24  	"go.opencensus.io/trace"
    25  )
    26  
    27  var (
    28  	historicalDepositsCount = promauto.NewCounter(prometheus.CounterOpts{
    29  		Name: "beacondb_all_deposits",
    30  		Help: "The number of total deposits in the beaconDB in-memory database",
    31  	})
    32  )
    33  
    34  // DepositFetcher defines a struct which can retrieve deposit information from a store.
    35  type DepositFetcher interface {
    36  	AllDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit
    37  	DepositByPubkey(ctx context.Context, pubKey []byte) (*ethpb.Deposit, *big.Int)
    38  	DepositsNumberAndRootAtHeight(ctx context.Context, blockHeight *big.Int) (uint64, [32]byte)
    39  	FinalizedDeposits(ctx context.Context) *FinalizedDeposits
    40  	NonFinalizedDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit
    41  }
    42  
    43  // FinalizedDeposits stores the trie of deposits that have been included
    44  // in the beacon state up to the latest finalized checkpoint.
    45  type FinalizedDeposits struct {
    46  	Deposits        *trieutil.SparseMerkleTrie
    47  	MerkleTrieIndex int64
    48  }
    49  
    50  // DepositCache stores all in-memory deposit objects. This
    51  // stores all the deposit related data that is required by the beacon-node.
    52  type DepositCache struct {
    53  	// Beacon chain deposits in memory.
    54  	pendingDeposits   []*dbpb.DepositContainer
    55  	deposits          []*dbpb.DepositContainer
    56  	finalizedDeposits *FinalizedDeposits
    57  	depositsLock      sync.RWMutex
    58  }
    59  
    60  // New instantiates a new deposit cache
    61  func New() (*DepositCache, error) {
    62  	finalizedDepositsTrie, err := trieutil.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	// finalizedDeposits.MerkleTrieIndex is initialized to -1 because it represents the index of the last trie item.
    68  	// Inserting the first item into the trie will set the value of the index to 0.
    69  	return &DepositCache{
    70  		pendingDeposits:   []*dbpb.DepositContainer{},
    71  		deposits:          []*dbpb.DepositContainer{},
    72  		finalizedDeposits: &FinalizedDeposits{Deposits: finalizedDepositsTrie, MerkleTrieIndex: -1},
    73  	}, nil
    74  }
    75  
    76  // InsertDeposit into the database. If deposit or block number are nil
    77  // then this method does nothing.
    78  func (dc *DepositCache) InsertDeposit(ctx context.Context, d *ethpb.Deposit, blockNum uint64, index int64, depositRoot [32]byte) error {
    79  	ctx, span := trace.StartSpan(ctx, "DepositsCache.InsertDeposit")
    80  	defer span.End()
    81  	if d == nil {
    82  		log.WithFields(logrus.Fields{
    83  			"block":        blockNum,
    84  			"deposit":      d,
    85  			"index":        index,
    86  			"deposit root": hex.EncodeToString(depositRoot[:]),
    87  		}).Warn("Ignoring nil deposit insertion")
    88  		return errors.New("nil deposit inserted into the cache")
    89  	}
    90  	dc.depositsLock.Lock()
    91  	defer dc.depositsLock.Unlock()
    92  
    93  	if int(index) != len(dc.deposits) {
    94  		return errors.Errorf("wanted deposit with index %d to be inserted but received %d", len(dc.deposits), index)
    95  	}
    96  	// Keep the slice sorted on insertion in order to avoid costly sorting on retrieval.
    97  	heightIdx := sort.Search(len(dc.deposits), func(i int) bool { return dc.deposits[i].Index >= index })
    98  	newDeposits := append(
    99  		[]*dbpb.DepositContainer{{Deposit: d, Eth1BlockHeight: blockNum, DepositRoot: depositRoot[:], Index: index}},
   100  		dc.deposits[heightIdx:]...)
   101  	dc.deposits = append(dc.deposits[:heightIdx], newDeposits...)
   102  	historicalDepositsCount.Inc()
   103  	return nil
   104  }
   105  
   106  // InsertDepositContainers inserts a set of deposit containers into our deposit cache.
   107  func (dc *DepositCache) InsertDepositContainers(ctx context.Context, ctrs []*dbpb.DepositContainer) {
   108  	ctx, span := trace.StartSpan(ctx, "DepositsCache.InsertDepositContainers")
   109  	defer span.End()
   110  	dc.depositsLock.Lock()
   111  	defer dc.depositsLock.Unlock()
   112  
   113  	sort.SliceStable(ctrs, func(i int, j int) bool { return ctrs[i].Index < ctrs[j].Index })
   114  	dc.deposits = ctrs
   115  	historicalDepositsCount.Add(float64(len(ctrs)))
   116  }
   117  
   118  // InsertFinalizedDeposits inserts deposits up to eth1DepositIndex (inclusive) into the finalized deposits cache.
   119  func (dc *DepositCache) InsertFinalizedDeposits(ctx context.Context, eth1DepositIndex int64) {
   120  	ctx, span := trace.StartSpan(ctx, "DepositsCache.InsertFinalizedDeposits")
   121  	defer span.End()
   122  	dc.depositsLock.Lock()
   123  	defer dc.depositsLock.Unlock()
   124  
   125  	depositTrie := dc.finalizedDeposits.Deposits
   126  	insertIndex := int(dc.finalizedDeposits.MerkleTrieIndex + 1)
   127  	for _, d := range dc.deposits {
   128  		if d.Index <= dc.finalizedDeposits.MerkleTrieIndex {
   129  			continue
   130  		}
   131  		if d.Index > eth1DepositIndex {
   132  			break
   133  		}
   134  		depHash, err := d.Deposit.Data.HashTreeRoot()
   135  		if err != nil {
   136  			log.WithError(err).Error("Could not hash deposit data. Finalized deposit cache not updated.")
   137  			return
   138  		}
   139  		depositTrie.Insert(depHash[:], insertIndex)
   140  		insertIndex++
   141  	}
   142  
   143  	dc.finalizedDeposits = &FinalizedDeposits{
   144  		Deposits:        depositTrie,
   145  		MerkleTrieIndex: eth1DepositIndex,
   146  	}
   147  }
   148  
   149  // AllDepositContainers returns all historical deposit containers.
   150  func (dc *DepositCache) AllDepositContainers(ctx context.Context) []*dbpb.DepositContainer {
   151  	ctx, span := trace.StartSpan(ctx, "DepositsCache.AllDepositContainers")
   152  	defer span.End()
   153  	dc.depositsLock.RLock()
   154  	defer dc.depositsLock.RUnlock()
   155  
   156  	return dc.deposits
   157  }
   158  
   159  // AllDeposits returns a list of historical deposits until the given block number
   160  // (inclusive). If no block is specified then this method returns all historical deposits.
   161  func (dc *DepositCache) AllDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit {
   162  	ctx, span := trace.StartSpan(ctx, "DepositsCache.AllDeposits")
   163  	defer span.End()
   164  	dc.depositsLock.RLock()
   165  	defer dc.depositsLock.RUnlock()
   166  
   167  	var deposits []*ethpb.Deposit
   168  	for _, ctnr := range dc.deposits {
   169  		if untilBlk == nil || untilBlk.Uint64() >= ctnr.Eth1BlockHeight {
   170  			deposits = append(deposits, ctnr.Deposit)
   171  		}
   172  	}
   173  	return deposits
   174  }
   175  
   176  // DepositsNumberAndRootAtHeight returns number of deposits made up to blockheight and the
   177  // root that corresponds to the latest deposit at that blockheight.
   178  func (dc *DepositCache) DepositsNumberAndRootAtHeight(ctx context.Context, blockHeight *big.Int) (uint64, [32]byte) {
   179  	ctx, span := trace.StartSpan(ctx, "DepositsCache.DepositsNumberAndRootAtHeight")
   180  	defer span.End()
   181  	dc.depositsLock.RLock()
   182  	defer dc.depositsLock.RUnlock()
   183  	heightIdx := sort.Search(len(dc.deposits), func(i int) bool { return dc.deposits[i].Eth1BlockHeight > blockHeight.Uint64() })
   184  	// send the deposit root of the empty trie, if eth1follow distance is greater than the time of the earliest
   185  	// deposit.
   186  	if heightIdx == 0 {
   187  		return 0, [32]byte{}
   188  	}
   189  	return uint64(heightIdx), bytesutil.ToBytes32(dc.deposits[heightIdx-1].DepositRoot)
   190  }
   191  
   192  // DepositByPubkey looks through historical deposits and finds one which contains
   193  // a certain public key within its deposit data.
   194  func (dc *DepositCache) DepositByPubkey(ctx context.Context, pubKey []byte) (*ethpb.Deposit, *big.Int) {
   195  	ctx, span := trace.StartSpan(ctx, "DepositsCache.DepositByPubkey")
   196  	defer span.End()
   197  	dc.depositsLock.RLock()
   198  	defer dc.depositsLock.RUnlock()
   199  
   200  	var deposit *ethpb.Deposit
   201  	var blockNum *big.Int
   202  	for _, ctnr := range dc.deposits {
   203  		if bytes.Equal(ctnr.Deposit.Data.PublicKey, pubKey) {
   204  			deposit = ctnr.Deposit
   205  			blockNum = big.NewInt(int64(ctnr.Eth1BlockHeight))
   206  			break
   207  		}
   208  	}
   209  	return deposit, blockNum
   210  }
   211  
   212  // FinalizedDeposits returns the finalized deposits trie.
   213  func (dc *DepositCache) FinalizedDeposits(ctx context.Context) *FinalizedDeposits {
   214  	ctx, span := trace.StartSpan(ctx, "DepositsCache.FinalizedDeposits")
   215  	defer span.End()
   216  	dc.depositsLock.RLock()
   217  	defer dc.depositsLock.RUnlock()
   218  
   219  	return &FinalizedDeposits{
   220  		Deposits:        dc.finalizedDeposits.Deposits.Copy(),
   221  		MerkleTrieIndex: dc.finalizedDeposits.MerkleTrieIndex,
   222  	}
   223  }
   224  
   225  // NonFinalizedDeposits returns the list of non-finalized deposits until the given block number (inclusive).
   226  // If no block is specified then this method returns all non-finalized deposits.
   227  func (dc *DepositCache) NonFinalizedDeposits(ctx context.Context, untilBlk *big.Int) []*ethpb.Deposit {
   228  	ctx, span := trace.StartSpan(ctx, "DepositsCache.NonFinalizedDeposits")
   229  	defer span.End()
   230  	dc.depositsLock.RLock()
   231  	defer dc.depositsLock.RUnlock()
   232  
   233  	if dc.finalizedDeposits == nil {
   234  		return dc.AllDeposits(ctx, untilBlk)
   235  	}
   236  
   237  	lastFinalizedDepositIndex := dc.finalizedDeposits.MerkleTrieIndex
   238  	var deposits []*ethpb.Deposit
   239  	for _, d := range dc.deposits {
   240  		if (d.Index > lastFinalizedDepositIndex) && (untilBlk == nil || untilBlk.Uint64() >= d.Eth1BlockHeight) {
   241  			deposits = append(deposits, d.Deposit)
   242  		}
   243  	}
   244  
   245  	return deposits
   246  }
   247  
   248  // PruneProofs removes proofs from all deposits whose index is equal or less than untilDepositIndex.
   249  func (dc *DepositCache) PruneProofs(ctx context.Context, untilDepositIndex int64) error {
   250  	ctx, span := trace.StartSpan(ctx, "DepositsCache.PruneProofs")
   251  	defer span.End()
   252  	dc.depositsLock.Lock()
   253  	defer dc.depositsLock.Unlock()
   254  
   255  	if untilDepositIndex >= int64(len(dc.deposits)) {
   256  		untilDepositIndex = int64(len(dc.deposits) - 1)
   257  	}
   258  
   259  	for i := untilDepositIndex; i >= 0; i-- {
   260  		if ctx.Err() != nil {
   261  			return ctx.Err()
   262  		}
   263  		// Finding a nil proof means that all proofs up to this deposit have been already pruned.
   264  		if dc.deposits[i].Deposit.Proof == nil {
   265  			break
   266  		}
   267  		dc.deposits[i].Deposit.Proof = nil
   268  	}
   269  
   270  	return nil
   271  }