gitlab.com/flarenetwork/coreth@v0.1.1/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 "errors" 32 "fmt" 33 "os" 34 "time" 35 36 "github.com/ethereum/go-ethereum/common" 37 "github.com/ethereum/go-ethereum/ethdb" 38 "github.com/ethereum/go-ethereum/ethdb/leveldb" 39 "github.com/ethereum/go-ethereum/ethdb/memorydb" 40 "github.com/ethereum/go-ethereum/log" 41 "github.com/olekukonko/tablewriter" 42 ) 43 44 var ( 45 // errNotSupported is returned if the database doesn't support the required operation. 46 errNotSupported = errors.New("this operation is not supported") 47 ) 48 49 // nofreezedb is a database wrapper that disables freezer data retrievals. 50 type nofreezedb struct { 51 ethdb.KeyValueStore 52 } 53 54 // HasAncient returns an error as we don't have a backing chain freezer. 55 func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) { 56 return false, errNotSupported 57 } 58 59 // Ancient returns an error as we don't have a backing chain freezer. 60 func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) { 61 return nil, errNotSupported 62 } 63 64 // Ancients returns an error as we don't have a backing chain freezer. 65 func (db *nofreezedb) Ancients() (uint64, error) { 66 return 0, errNotSupported 67 } 68 69 // AncientSize returns an error as we don't have a backing chain freezer. 70 func (db *nofreezedb) AncientSize(kind string) (uint64, error) { 71 return 0, errNotSupported 72 } 73 74 // AppendAncient returns an error as we don't have a backing chain freezer. 75 func (db *nofreezedb) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error { 76 return errNotSupported 77 } 78 79 // TruncateAncients returns an error as we don't have a backing chain freezer. 80 func (db *nofreezedb) TruncateAncients(items uint64) error { 81 return errNotSupported 82 } 83 84 // Sync returns an error as we don't have a backing chain freezer. 85 func (db *nofreezedb) Sync() error { 86 return errNotSupported 87 } 88 89 // NewDatabase creates a high level database on top of a given key-value data 90 // store without a freezer moving immutable chain segments into cold storage. 91 func NewDatabase(db ethdb.KeyValueStore) ethdb.Database { 92 return &nofreezedb{ 93 KeyValueStore: db, 94 } 95 } 96 97 // NewMemoryDatabase creates an ephemeral in-memory key-value database without a 98 // freezer moving immutable chain segments into cold storage. 99 func NewMemoryDatabase() ethdb.Database { 100 return NewDatabase(memorydb.New()) 101 } 102 103 // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database 104 // with an initial starting capacity, but without a freezer moving immutable 105 // chain segments into cold storage. 106 func NewMemoryDatabaseWithCap(size int) ethdb.Database { 107 return NewDatabase(memorydb.NewWithCap(size)) 108 } 109 110 // NewLevelDBDatabase creates a persistent key-value database without a freezer 111 // moving immutable chain segments into cold storage. 112 func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { 113 db, err := leveldb.New(file, cache, handles, namespace, readonly) 114 if err != nil { 115 return nil, err 116 } 117 return NewDatabase(db), nil 118 } 119 120 type counter uint64 121 122 func (c counter) String() string { 123 return fmt.Sprintf("%d", c) 124 } 125 126 func (c counter) Percentage(current uint64) string { 127 return fmt.Sprintf("%d", current*100/uint64(c)) 128 } 129 130 // stat stores sizes and count for a parameter 131 type stat struct { 132 size common.StorageSize 133 count counter 134 } 135 136 // Add size to the stat and increase the counter by 1 137 func (s *stat) Add(size common.StorageSize) { 138 s.size += size 139 s.count++ 140 } 141 142 func (s *stat) Size() string { 143 return s.size.String() 144 } 145 146 func (s *stat) Count() string { 147 return s.count.String() 148 } 149 150 // InspectDatabase traverses the entire database and checks the size 151 // of all different categories of data. 152 func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { 153 it := db.NewIterator(keyPrefix, keyStart) 154 defer it.Release() 155 156 var ( 157 count int64 158 start = time.Now() 159 logged = time.Now() 160 161 // Key-value store statistics 162 headers stat 163 bodies stat 164 receipts stat 165 tds stat 166 numHashPairings stat 167 hashNumPairings stat 168 tries stat 169 codes stat 170 txLookups stat 171 accountSnaps stat 172 storageSnaps stat 173 preimages stat 174 bloomBits stat 175 cliqueSnaps stat 176 177 // Ancient store statistics 178 ancientHeadersSize common.StorageSize 179 ancientBodiesSize common.StorageSize 180 ancientReceiptsSize common.StorageSize 181 ancientTdsSize common.StorageSize 182 ancientHashesSize common.StorageSize 183 184 // Les statistic 185 chtTrieNodes stat 186 bloomTrieNodes stat 187 188 // Meta- and unaccounted data 189 metadata stat 190 unaccounted stat 191 192 // Totals 193 total common.StorageSize 194 ) 195 // Inspect key-value database first. 196 for it.Next() { 197 var ( 198 key = it.Key() 199 size = common.StorageSize(len(key) + len(it.Value())) 200 ) 201 total += size 202 switch { 203 case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength): 204 headers.Add(size) 205 case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength): 206 bodies.Add(size) 207 case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength): 208 receipts.Add(size) 209 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix): 210 tds.Add(size) 211 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix): 212 numHashPairings.Add(size) 213 case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength): 214 hashNumPairings.Add(size) 215 case len(key) == common.HashLength: 216 tries.Add(size) 217 case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: 218 codes.Add(size) 219 case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength): 220 txLookups.Add(size) 221 case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength): 222 accountSnaps.Add(size) 223 case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength): 224 storageSnaps.Add(size) 225 case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength): 226 preimages.Add(size) 227 case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength): 228 metadata.Add(size) 229 case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): 230 bloomBits.Add(size) 231 case bytes.HasPrefix(key, BloomBitsIndexPrefix): 232 bloomBits.Add(size) 233 case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength: 234 cliqueSnaps.Add(size) 235 case bytes.HasPrefix(key, []byte("cht-")) || 236 bytes.HasPrefix(key, []byte("chtIndexV2-")) || 237 bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie 238 chtTrieNodes.Add(size) 239 case bytes.HasPrefix(key, []byte("blt-")) || 240 bytes.HasPrefix(key, []byte("bltIndex-")) || 241 bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub 242 bloomTrieNodes.Add(size) 243 default: 244 var accounted bool 245 for _, meta := range [][]byte{ 246 databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey, 247 fastTrieProgressKey, snapshotDisabledKey, snapshotRootKey, snapshotJournalKey, 248 snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, 249 uncleanShutdownKey, badBlockKey, 250 } { 251 if bytes.Equal(key, meta) { 252 metadata.Add(size) 253 accounted = true 254 break 255 } 256 } 257 if !accounted { 258 unaccounted.Add(size) 259 } 260 } 261 count++ 262 if count%1000 == 0 && time.Since(logged) > 8*time.Second { 263 log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start))) 264 logged = time.Now() 265 } 266 } 267 // Inspect append-only file store then. 268 ancientSizes := []*common.StorageSize{&ancientHeadersSize, &ancientBodiesSize, &ancientReceiptsSize, &ancientHashesSize, &ancientTdsSize} 269 for i, category := range []string{freezerHeaderTable, freezerBodiesTable, freezerReceiptTable, freezerHashTable, freezerDifficultyTable} { 270 if size, err := db.AncientSize(category); err == nil { 271 *ancientSizes[i] += common.StorageSize(size) 272 total += common.StorageSize(size) 273 } 274 } 275 // Get number of ancient rows inside the freezer 276 ancients := counter(0) 277 if count, err := db.Ancients(); err == nil { 278 ancients = counter(count) 279 } 280 // Display the database statistic. 281 stats := [][]string{ 282 {"Key-Value store", "Headers", headers.Size(), headers.Count()}, 283 {"Key-Value store", "Bodies", bodies.Size(), bodies.Count()}, 284 {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()}, 285 {"Key-Value store", "Difficulties", tds.Size(), tds.Count()}, 286 {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, 287 {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, 288 {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, 289 {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()}, 290 {"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, 291 {"Key-Value store", "Trie nodes", tries.Size(), tries.Count()}, 292 {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, 293 {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, 294 {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, 295 {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, 296 {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, 297 {"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()}, 298 {"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()}, 299 {"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()}, 300 {"Ancient store", "Difficulties", ancientTdsSize.String(), ancients.String()}, 301 {"Ancient store", "Block number->hash", ancientHashesSize.String(), ancients.String()}, 302 {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()}, 303 {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()}, 304 } 305 table := tablewriter.NewWriter(os.Stdout) 306 table.SetHeader([]string{"Database", "Category", "Size", "Items"}) 307 table.SetFooter([]string{"", "Total", total.String(), " "}) 308 table.AppendBulk(stats) 309 table.Render() 310 311 if unaccounted.size > 0 { 312 log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count) 313 } 314 315 return nil 316 }