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 }