github.com/MetalBlockchain/subnet-evm@v0.4.9/core/rawdb/database.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2018 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package rawdb 28 29 import ( 30 "bytes" 31 "fmt" 32 "os" 33 "time" 34 35 "github.com/MetalBlockchain/subnet-evm/ethdb" 36 "github.com/MetalBlockchain/subnet-evm/ethdb/leveldb" 37 "github.com/MetalBlockchain/subnet-evm/ethdb/memorydb" 38 "github.com/ethereum/go-ethereum/common" 39 "github.com/ethereum/go-ethereum/log" 40 "github.com/olekukonko/tablewriter" 41 ) 42 43 // nofreezedb is a database wrapper that disables freezer data retrievals. 44 type nofreezedb struct { 45 ethdb.KeyValueStore 46 } 47 48 // NewDatabase creates a high level database on top of a given key-value data 49 // store without a freezer moving immutable chain segments into cold storage. 50 func NewDatabase(db ethdb.KeyValueStore) ethdb.Database { 51 return &nofreezedb{KeyValueStore: db} 52 } 53 54 // NewMemoryDatabase creates an ephemeral in-memory key-value database without a 55 // freezer moving immutable chain segments into cold storage. 56 func NewMemoryDatabase() ethdb.Database { 57 return NewDatabase(memorydb.New()) 58 } 59 60 // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database 61 // with an initial starting capacity, but without a freezer moving immutable 62 // chain segments into cold storage. 63 func NewMemoryDatabaseWithCap(size int) ethdb.Database { 64 return NewDatabase(memorydb.NewWithCap(size)) 65 } 66 67 // NewLevelDBDatabase creates a persistent key-value database without a freezer 68 // moving immutable chain segments into cold storage. 69 func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { 70 db, err := leveldb.New(file, cache, handles, namespace, readonly) 71 if err != nil { 72 return nil, err 73 } 74 return NewDatabase(db), nil 75 } 76 77 type counter uint64 78 79 func (c counter) String() string { 80 return fmt.Sprintf("%d", c) 81 } 82 83 func (c counter) Percentage(current uint64) string { 84 return fmt.Sprintf("%d", current*100/uint64(c)) 85 } 86 87 // stat stores sizes and count for a parameter 88 type stat struct { 89 size common.StorageSize 90 count counter 91 } 92 93 // Add size to the stat and increase the counter by 1 94 func (s *stat) Add(size common.StorageSize) { 95 s.size += size 96 s.count++ 97 } 98 99 func (s *stat) Size() string { 100 return s.size.String() 101 } 102 103 func (s *stat) Count() string { 104 return s.count.String() 105 } 106 107 // InspectDatabase traverses the entire database and checks the size 108 // of all different categories of data. 109 func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { 110 it := db.NewIterator(keyPrefix, keyStart) 111 defer it.Release() 112 113 var ( 114 count int64 115 start = time.Now() 116 logged = time.Now() 117 118 // Key-value store statistics 119 headers stat 120 bodies stat 121 receipts stat 122 numHashPairings stat 123 hashNumPairings stat 124 tries stat 125 codes stat 126 txLookups stat 127 accountSnaps stat 128 storageSnaps stat 129 preimages stat 130 bloomBits stat 131 cliqueSnaps stat 132 133 // State sync statistics 134 codeToFetch stat 135 syncProgress stat 136 syncSegments stat 137 syncPerformed stat 138 139 // Les statistic 140 chtTrieNodes stat 141 bloomTrieNodes stat 142 143 // Meta- and unaccounted data 144 metadata stat 145 unaccounted stat 146 147 // Totals 148 total common.StorageSize 149 ) 150 // Inspect key-value database first. 151 for it.Next() { 152 var ( 153 key = it.Key() 154 size = common.StorageSize(len(key) + len(it.Value())) 155 ) 156 total += size 157 switch { 158 case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength): 159 headers.Add(size) 160 case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength): 161 bodies.Add(size) 162 case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength): 163 receipts.Add(size) 164 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix): 165 numHashPairings.Add(size) 166 case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength): 167 hashNumPairings.Add(size) 168 case len(key) == common.HashLength: 169 tries.Add(size) 170 case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: 171 codes.Add(size) 172 case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength): 173 txLookups.Add(size) 174 case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength): 175 accountSnaps.Add(size) 176 case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength): 177 storageSnaps.Add(size) 178 case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength): 179 preimages.Add(size) 180 case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength): 181 metadata.Add(size) 182 case bytes.HasPrefix(key, upgradeConfigPrefix) && len(key) == (len(upgradeConfigPrefix)+common.HashLength): 183 metadata.Add(size) 184 case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): 185 bloomBits.Add(size) 186 case bytes.HasPrefix(key, BloomBitsIndexPrefix): 187 bloomBits.Add(size) 188 case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength: 189 cliqueSnaps.Add(size) 190 case bytes.HasPrefix(key, []byte("cht-")) || 191 bytes.HasPrefix(key, []byte("chtIndexV2-")) || 192 bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie 193 chtTrieNodes.Add(size) 194 case bytes.HasPrefix(key, []byte("blt-")) || 195 bytes.HasPrefix(key, []byte("bltIndex-")) || 196 bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub 197 bloomTrieNodes.Add(size) 198 case bytes.HasPrefix(key, syncStorageTriesPrefix) && len(key) == syncStorageTriesKeyLength: 199 syncProgress.Add(size) 200 case bytes.HasPrefix(key, syncSegmentsPrefix) && len(key) == syncSegmentsKeyLength: 201 syncSegments.Add(size) 202 case bytes.HasPrefix(key, CodeToFetchPrefix) && len(key) == codeToFetchKeyLength: 203 codeToFetch.Add(size) 204 case bytes.HasPrefix(key, syncPerformedPrefix) && len(key) == syncPerformedKeyLength: 205 syncPerformed.Add(size) 206 default: 207 var accounted bool 208 for _, meta := range [][]byte{ 209 databaseVersionKey, headHeaderKey, headBlockKey, 210 snapshotRootKey, snapshotBlockHashKey, snapshotGeneratorKey, 211 uncleanShutdownKey, syncRootKey, txIndexTailKey, 212 } { 213 if bytes.Equal(key, meta) { 214 metadata.Add(size) 215 accounted = true 216 break 217 } 218 } 219 if !accounted { 220 unaccounted.Add(size) 221 } 222 } 223 count++ 224 if count%1000 == 0 && time.Since(logged) > 8*time.Second { 225 log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start))) 226 logged = time.Now() 227 } 228 } 229 // Display the database statistic. 230 stats := [][]string{ 231 {"Key-Value store", "Headers", headers.Size(), headers.Count()}, 232 {"Key-Value store", "Bodies", bodies.Size(), bodies.Count()}, 233 {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()}, 234 {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, 235 {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, 236 {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, 237 {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()}, 238 {"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, 239 {"Key-Value store", "Trie nodes", tries.Size(), tries.Count()}, 240 {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, 241 {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, 242 {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, 243 {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, 244 {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, 245 {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()}, 246 {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()}, 247 {"State sync", "Trie segments", syncSegments.Size(), syncSegments.Count()}, 248 {"State sync", "Storage tries to fetch", syncProgress.Size(), syncProgress.Count()}, 249 {"State sync", "Code to fetch", codeToFetch.Size(), codeToFetch.Count()}, 250 {"State sync", "Block numbers synced to", syncPerformed.Size(), syncPerformed.Count()}, 251 } 252 table := tablewriter.NewWriter(os.Stdout) 253 table.SetHeader([]string{"Database", "Category", "Size", "Items"}) 254 table.SetFooter([]string{"", "Total", total.String(), " "}) 255 table.AppendBulk(stats) 256 table.Render() 257 258 if unaccounted.size > 0 { 259 log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count) 260 } 261 262 return nil 263 } 264 265 // ClearPrefix removes all keys in db that begin with prefix 266 func ClearPrefix(db ethdb.KeyValueStore, prefix []byte) error { 267 it := db.NewIterator(prefix, nil) 268 defer it.Release() 269 270 batch := db.NewBatch() 271 for it.Next() { 272 key := common.CopyBytes(it.Key()) 273 if err := batch.Delete(key); err != nil { 274 return err 275 } 276 if batch.ValueSize() > ethdb.IdealBatchSize { 277 if err := batch.Write(); err != nil { 278 return err 279 } 280 batch.Reset() 281 } 282 } 283 if err := it.Error(); err != nil { 284 return err 285 } 286 return batch.Write() 287 }