github.com/EgonCoin/EgonChain@v1.10.16/core/rawdb/database.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rawdb 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "os" 24 "sync/atomic" 25 "time" 26 27 "github.com/EgonCoin/EgonChain/common" 28 "github.com/EgonCoin/EgonChain/ethdb" 29 "github.com/EgonCoin/EgonChain/ethdb/leveldb" 30 "github.com/EgonCoin/EgonChain/ethdb/memorydb" 31 "github.com/EgonCoin/EgonChain/log" 32 "github.com/olekukonko/tablewriter" 33 ) 34 35 // freezerdb is a database wrapper that enabled freezer data retrievals. 36 type freezerdb struct { 37 ethdb.KeyValueStore 38 ethdb.AncientStore 39 } 40 41 // Close implements io.Closer, closing both the fast key-value store as well as 42 // the slow ancient tables. 43 func (frdb *freezerdb) Close() error { 44 var errs []error 45 if err := frdb.AncientStore.Close(); err != nil { 46 errs = append(errs, err) 47 } 48 if err := frdb.KeyValueStore.Close(); err != nil { 49 errs = append(errs, err) 50 } 51 if len(errs) != 0 { 52 return fmt.Errorf("%v", errs) 53 } 54 return nil 55 } 56 57 // Freeze is a helper method used for external testing to trigger and block until 58 // a freeze cycle completes, without having to sleep for a minute to trigger the 59 // automatic background run. 60 func (frdb *freezerdb) Freeze(threshold uint64) error { 61 if frdb.AncientStore.(*freezer).readonly { 62 return errReadOnly 63 } 64 // Set the freezer threshold to a temporary value 65 defer func(old uint64) { 66 atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, old) 67 }(atomic.LoadUint64(&frdb.AncientStore.(*freezer).threshold)) 68 atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, threshold) 69 70 // Trigger a freeze cycle and block until it's done 71 trigger := make(chan struct{}, 1) 72 frdb.AncientStore.(*freezer).trigger <- trigger 73 <-trigger 74 return nil 75 } 76 77 // nofreezedb is a database wrapper that disables freezer data retrievals. 78 type nofreezedb struct { 79 ethdb.KeyValueStore 80 } 81 82 // HasAncient returns an error as we don't have a backing chain freezer. 83 func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) { 84 return false, errNotSupported 85 } 86 87 // Ancient returns an error as we don't have a backing chain freezer. 88 func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) { 89 return nil, errNotSupported 90 } 91 92 // AncientRange returns an error as we don't have a backing chain freezer. 93 func (db *nofreezedb) AncientRange(kind string, start, max, maxByteSize uint64) ([][]byte, error) { 94 return nil, errNotSupported 95 } 96 97 // Ancients returns an error as we don't have a backing chain freezer. 98 func (db *nofreezedb) Ancients() (uint64, error) { 99 return 0, errNotSupported 100 } 101 102 // AncientSize returns an error as we don't have a backing chain freezer. 103 func (db *nofreezedb) AncientSize(kind string) (uint64, error) { 104 return 0, errNotSupported 105 } 106 107 // ModifyAncients is not supported. 108 func (db *nofreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) { 109 return 0, errNotSupported 110 } 111 112 // TruncateAncients returns an error as we don't have a backing chain freezer. 113 func (db *nofreezedb) TruncateAncients(items uint64) error { 114 return errNotSupported 115 } 116 117 // Sync returns an error as we don't have a backing chain freezer. 118 func (db *nofreezedb) Sync() error { 119 return errNotSupported 120 } 121 122 func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReader) error) (err error) { 123 // Unlike other ancient-related methods, this method does not return 124 // errNotSupported when invoked. 125 // The reason for this is that the caller might want to do several things: 126 // 1. Check if something is in freezer, 127 // 2. If not, check leveldb. 128 // 129 // This will work, since the ancient-checks inside 'fn' will return errors, 130 // and the leveldb work will continue. 131 // 132 // If we instead were to return errNotSupported here, then the caller would 133 // have to explicitly check for that, having an extra clause to do the 134 // non-ancient operations. 135 return fn(db) 136 } 137 138 // NewDatabase creates a high level database on top of a given key-value data 139 // store without a freezer moving immutable chain segments into cold storage. 140 func NewDatabase(db ethdb.KeyValueStore) ethdb.Database { 141 return &nofreezedb{KeyValueStore: db} 142 } 143 144 // NewDatabaseWithFreezer creates a high level database on top of a given key- 145 // value data store with a freezer moving immutable chain segments into cold 146 // storage. 147 func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace string, readonly bool) (ethdb.Database, error) { 148 // Create the idle freezer instance 149 frdb, err := newFreezer(freezer, namespace, readonly, freezerTableSize, FreezerNoSnappy) 150 if err != nil { 151 return nil, err 152 } 153 // Since the freezer can be stored separately from the user's key-value database, 154 // there's a fairly high probability that the user requests invalid combinations 155 // of the freezer and database. Ensure that we don't shoot ourselves in the foot 156 // by serving up conflicting data, leading to both datastores getting corrupted. 157 // 158 // - If both the freezer and key-value store is empty (no genesis), we just 159 // initialized a new empty freezer, so everything's fine. 160 // - If the key-value store is empty, but the freezer is not, we need to make 161 // sure the user's genesis matches the freezer. That will be checked in the 162 // blockchain, since we don't have the genesis block here (nor should we at 163 // this point care, the key-value/freezer combo is valid). 164 // - If neither the key-value store nor the freezer is empty, cross validate 165 // the genesis hashes to make sure they are compatible. If they are, also 166 // ensure that there's no gap between the freezer and sunsequently leveldb. 167 // - If the key-value store is not empty, but the freezer is we might just be 168 // upgrading to the freezer release, or we might have had a small chain and 169 // not frozen anything yet. Ensure that no blocks are missing yet from the 170 // key-value store, since that would mean we already had an old freezer. 171 172 // If the genesis hash is empty, we have a new key-value store, so nothing to 173 // validate in this method. If, however, the genesis hash is not nil, compare 174 // it to the freezer content. 175 if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 { 176 if frozen, _ := frdb.Ancients(); frozen > 0 { 177 // If the freezer already contains something, ensure that the genesis blocks 178 // match, otherwise we might mix up freezers across chains and destroy both 179 // the freezer and the key-value store. 180 frgenesis, err := frdb.Ancient(freezerHashTable, 0) 181 if err != nil { 182 return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err) 183 } else if !bytes.Equal(kvgenesis, frgenesis) { 184 return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis) 185 } 186 // Key-value store and freezer belong to the same network. Ensure that they 187 // are contiguous, otherwise we might end up with a non-functional freezer. 188 if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 { 189 // Subsequent header after the freezer limit is missing from the database. 190 // Reject startup is the database has a more recent head. 191 if *ReadHeaderNumber(db, ReadHeadHeaderHash(db)) > frozen-1 { 192 return nil, fmt.Errorf("gap (#%d) in the chain between ancients and leveldb", frozen) 193 } 194 // Database contains only older data than the freezer, this happens if the 195 // state was wiped and reinited from an existing freezer. 196 } 197 // Otherwise, key-value store continues where the freezer left off, all is fine. 198 // We might have duplicate blocks (crash after freezer write but before key-value 199 // store deletion, but that's fine). 200 } else { 201 // If the freezer is empty, ensure nothing was moved yet from the key-value 202 // store, otherwise we'll end up missing data. We check block #1 to decide 203 // if we froze anything previously or not, but do take care of databases with 204 // only the genesis block. 205 if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) { 206 // Key-value store contains more data than the genesis block, make sure we 207 // didn't freeze anything yet. 208 if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 { 209 return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path") 210 } 211 // Block #1 is still in the database, we're allowed to init a new feezer 212 } 213 // Otherwise, the head header is still the genesis, we're allowed to init a new 214 // feezer. 215 } 216 } 217 // Freezer is consistent with the key-value database, permit combining the two 218 if !frdb.readonly { 219 frdb.wg.Add(1) 220 go func() { 221 frdb.freeze(db) 222 frdb.wg.Done() 223 }() 224 } 225 return &freezerdb{ 226 KeyValueStore: db, 227 AncientStore: frdb, 228 }, nil 229 } 230 231 // NewMemoryDatabase creates an ephemeral in-memory key-value database without a 232 // freezer moving immutable chain segments into cold storage. 233 func NewMemoryDatabase() ethdb.Database { 234 return NewDatabase(memorydb.New()) 235 } 236 237 // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database 238 // with an initial starting capacity, but without a freezer moving immutable 239 // chain segments into cold storage. 240 func NewMemoryDatabaseWithCap(size int) ethdb.Database { 241 return NewDatabase(memorydb.NewWithCap(size)) 242 } 243 244 // NewLevelDBDatabase creates a persistent key-value database without a freezer 245 // moving immutable chain segments into cold storage. 246 func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { 247 db, err := leveldb.New(file, cache, handles, namespace, readonly) 248 if err != nil { 249 return nil, err 250 } 251 return NewDatabase(db), nil 252 } 253 254 // NewLevelDBDatabaseWithFreezer creates a persistent key-value database with a 255 // freezer moving immutable chain segments into cold storage. 256 func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer string, namespace string, readonly bool) (ethdb.Database, error) { 257 kvdb, err := leveldb.New(file, cache, handles, namespace, readonly) 258 if err != nil { 259 return nil, err 260 } 261 frdb, err := NewDatabaseWithFreezer(kvdb, freezer, namespace, readonly) 262 if err != nil { 263 kvdb.Close() 264 return nil, err 265 } 266 return frdb, nil 267 } 268 269 type counter uint64 270 271 func (c counter) String() string { 272 return fmt.Sprintf("%d", c) 273 } 274 275 func (c counter) Percentage(current uint64) string { 276 return fmt.Sprintf("%d", current*100/uint64(c)) 277 } 278 279 // stat stores sizes and count for a parameter 280 type stat struct { 281 size common.StorageSize 282 count counter 283 } 284 285 // Add size to the stat and increase the counter by 1 286 func (s *stat) Add(size common.StorageSize) { 287 s.size += size 288 s.count++ 289 } 290 291 func (s *stat) Size() string { 292 return s.size.String() 293 } 294 295 func (s *stat) Count() string { 296 return s.count.String() 297 } 298 299 // InspectDatabase traverses the entire database and checks the size 300 // of all different categories of data. 301 func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { 302 it := db.NewIterator(keyPrefix, keyStart) 303 defer it.Release() 304 305 var ( 306 count int64 307 start = time.Now() 308 logged = time.Now() 309 310 // Key-value store statistics 311 headers stat 312 bodies stat 313 receipts stat 314 tds stat 315 numHashPairings stat 316 hashNumPairings stat 317 tries stat 318 codes stat 319 txLookups stat 320 accountSnaps stat 321 storageSnaps stat 322 preimages stat 323 bloomBits stat 324 cliqueSnaps stat 325 326 // Ancient store statistics 327 ancientHeadersSize common.StorageSize 328 ancientBodiesSize common.StorageSize 329 ancientReceiptsSize common.StorageSize 330 ancientTdsSize common.StorageSize 331 ancientHashesSize common.StorageSize 332 333 // Les statistic 334 chtTrieNodes stat 335 bloomTrieNodes stat 336 337 // Meta- and unaccounted data 338 metadata stat 339 unaccounted stat 340 341 // Totals 342 total common.StorageSize 343 ) 344 // Inspect key-value database first. 345 for it.Next() { 346 var ( 347 key = it.Key() 348 size = common.StorageSize(len(key) + len(it.Value())) 349 ) 350 total += size 351 switch { 352 case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength): 353 headers.Add(size) 354 case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength): 355 bodies.Add(size) 356 case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength): 357 receipts.Add(size) 358 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix): 359 tds.Add(size) 360 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix): 361 numHashPairings.Add(size) 362 case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength): 363 hashNumPairings.Add(size) 364 case len(key) == common.HashLength: 365 tries.Add(size) 366 case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: 367 codes.Add(size) 368 case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength): 369 txLookups.Add(size) 370 case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength): 371 accountSnaps.Add(size) 372 case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength): 373 storageSnaps.Add(size) 374 case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength): 375 preimages.Add(size) 376 case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength): 377 metadata.Add(size) 378 case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): 379 bloomBits.Add(size) 380 case bytes.HasPrefix(key, BloomBitsIndexPrefix): 381 bloomBits.Add(size) 382 case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength: 383 cliqueSnaps.Add(size) 384 case bytes.HasPrefix(key, []byte("cht-")) || 385 bytes.HasPrefix(key, []byte("chtIndexV2-")) || 386 bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie 387 chtTrieNodes.Add(size) 388 case bytes.HasPrefix(key, []byte("blt-")) || 389 bytes.HasPrefix(key, []byte("bltIndex-")) || 390 bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub 391 bloomTrieNodes.Add(size) 392 default: 393 var accounted bool 394 for _, meta := range [][]byte{ 395 databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey, 396 fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey, 397 snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, 398 uncleanShutdownKey, badBlockKey, transitionStatusKey, 399 } { 400 if bytes.Equal(key, meta) { 401 metadata.Add(size) 402 accounted = true 403 break 404 } 405 } 406 if !accounted { 407 unaccounted.Add(size) 408 } 409 } 410 count++ 411 if count%1000 == 0 && time.Since(logged) > 8*time.Second { 412 log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start))) 413 logged = time.Now() 414 } 415 } 416 // Inspect append-only file store then. 417 ancientSizes := []*common.StorageSize{&ancientHeadersSize, &ancientBodiesSize, &ancientReceiptsSize, &ancientHashesSize, &ancientTdsSize} 418 for i, category := range []string{freezerHeaderTable, freezerBodiesTable, freezerReceiptTable, freezerHashTable, freezerDifficultyTable} { 419 if size, err := db.AncientSize(category); err == nil { 420 *ancientSizes[i] += common.StorageSize(size) 421 total += common.StorageSize(size) 422 } 423 } 424 // Get number of ancient rows inside the freezer 425 ancients := counter(0) 426 if count, err := db.Ancients(); err == nil { 427 ancients = counter(count) 428 } 429 // Display the database statistic. 430 stats := [][]string{ 431 {"Key-Value store", "Headers", headers.Size(), headers.Count()}, 432 {"Key-Value store", "Bodies", bodies.Size(), bodies.Count()}, 433 {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()}, 434 {"Key-Value store", "Difficulties", tds.Size(), tds.Count()}, 435 {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, 436 {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, 437 {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, 438 {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()}, 439 {"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, 440 {"Key-Value store", "Trie nodes", tries.Size(), tries.Count()}, 441 {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, 442 {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, 443 {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, 444 {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, 445 {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, 446 {"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()}, 447 {"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()}, 448 {"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()}, 449 {"Ancient store", "Difficulties", ancientTdsSize.String(), ancients.String()}, 450 {"Ancient store", "Block number->hash", ancientHashesSize.String(), ancients.String()}, 451 {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()}, 452 {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()}, 453 } 454 table := tablewriter.NewWriter(os.Stdout) 455 table.SetHeader([]string{"Database", "Category", "Size", "Items"}) 456 table.SetFooter([]string{"", "Total", total.String(), " "}) 457 table.AppendBulk(stats) 458 table.Render() 459 460 if unaccounted.size > 0 { 461 log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count) 462 } 463 464 return nil 465 }