github.com/dim4egster/coreth@v0.10.2/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/dim4egster/coreth/ethdb" 36 "github.com/dim4egster/coreth/ethdb/leveldb" 37 "github.com/dim4egster/coreth/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 138 // Les statistic 139 chtTrieNodes stat 140 bloomTrieNodes stat 141 142 // Meta- and unaccounted data 143 metadata stat 144 unaccounted stat 145 146 // Totals 147 total common.StorageSize 148 ) 149 // Inspect key-value database first. 150 for it.Next() { 151 var ( 152 key = it.Key() 153 size = common.StorageSize(len(key) + len(it.Value())) 154 ) 155 total += size 156 switch { 157 case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength): 158 headers.Add(size) 159 case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength): 160 bodies.Add(size) 161 case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength): 162 receipts.Add(size) 163 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix): 164 numHashPairings.Add(size) 165 case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength): 166 hashNumPairings.Add(size) 167 case len(key) == common.HashLength: 168 tries.Add(size) 169 case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: 170 codes.Add(size) 171 case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength): 172 txLookups.Add(size) 173 case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength): 174 accountSnaps.Add(size) 175 case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength): 176 storageSnaps.Add(size) 177 case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength): 178 preimages.Add(size) 179 case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength): 180 metadata.Add(size) 181 case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): 182 bloomBits.Add(size) 183 case bytes.HasPrefix(key, BloomBitsIndexPrefix): 184 bloomBits.Add(size) 185 case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength: 186 cliqueSnaps.Add(size) 187 case bytes.HasPrefix(key, []byte("cht-")) || 188 bytes.HasPrefix(key, []byte("chtIndexV2-")) || 189 bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie 190 chtTrieNodes.Add(size) 191 case bytes.HasPrefix(key, []byte("blt-")) || 192 bytes.HasPrefix(key, []byte("bltIndex-")) || 193 bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub 194 bloomTrieNodes.Add(size) 195 case bytes.HasPrefix(key, syncStorageTriesPrefix) && len(key) == syncStorageTriesKeyLength: 196 syncProgress.Add(size) 197 case bytes.HasPrefix(key, syncSegmentsPrefix) && len(key) == syncSegmentsKeyLength: 198 syncSegments.Add(size) 199 case bytes.HasPrefix(key, CodeToFetchPrefix) && len(key) == codeToFetchKeyLength: 200 codeToFetch.Add(size) 201 default: 202 var accounted bool 203 for _, meta := range [][]byte{ 204 databaseVersionKey, headHeaderKey, headBlockKey, 205 snapshotRootKey, snapshotBlockHashKey, snapshotGeneratorKey, 206 uncleanShutdownKey, syncRootKey, 207 } { 208 if bytes.Equal(key, meta) { 209 metadata.Add(size) 210 accounted = true 211 break 212 } 213 } 214 if !accounted { 215 unaccounted.Add(size) 216 } 217 } 218 count++ 219 if count%1000 == 0 && time.Since(logged) > 8*time.Second { 220 log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start))) 221 logged = time.Now() 222 } 223 } 224 // Display the database statistic. 225 stats := [][]string{ 226 {"Key-Value store", "Headers", headers.Size(), headers.Count()}, 227 {"Key-Value store", "Bodies", bodies.Size(), bodies.Count()}, 228 {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()}, 229 {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, 230 {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, 231 {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, 232 {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()}, 233 {"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, 234 {"Key-Value store", "Trie nodes", tries.Size(), tries.Count()}, 235 {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, 236 {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, 237 {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, 238 {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, 239 {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, 240 {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()}, 241 {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()}, 242 {"State sync", "Trie segments", syncSegments.Size(), syncSegments.Count()}, 243 {"State sync", "Storage tries to fetch", syncProgress.Size(), syncProgress.Count()}, 244 {"State sync", "Code to fetch", codeToFetch.Size(), codeToFetch.Count()}, 245 } 246 table := tablewriter.NewWriter(os.Stdout) 247 table.SetHeader([]string{"Database", "Category", "Size", "Items"}) 248 table.SetFooter([]string{"", "Total", total.String(), " "}) 249 table.AppendBulk(stats) 250 table.Render() 251 252 if unaccounted.size > 0 { 253 log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count) 254 } 255 256 return nil 257 } 258 259 // ClearPrefix removes all keys in db that begin with prefix 260 func ClearPrefix(db ethdb.KeyValueStore, prefix []byte) error { 261 it := db.NewIterator(prefix, nil) 262 defer it.Release() 263 264 batch := db.NewBatch() 265 for it.Next() { 266 key := common.CopyBytes(it.Key()) 267 if err := batch.Delete(key); err != nil { 268 return err 269 } 270 if batch.ValueSize() > ethdb.IdealBatchSize { 271 if err := batch.Write(); err != nil { 272 return err 273 } 274 batch.Reset() 275 } 276 } 277 if err := it.Error(); err != nil { 278 return err 279 } 280 return batch.Write() 281 }