github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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 "path/filepath" 25 "strings" 26 "time" 27 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/ethdb" 30 "github.com/ethereum/go-ethereum/ethdb/leveldb" 31 "github.com/ethereum/go-ethereum/ethdb/memorydb" 32 "github.com/ethereum/go-ethereum/ethdb/pebble" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/olekukonko/tablewriter" 35 ) 36 37 // freezerdb is a database wrapper that enables ancient chain segment freezing. 38 type freezerdb struct { 39 ethdb.KeyValueStore 40 *chainFreezer 41 42 readOnly bool 43 ancientRoot string 44 } 45 46 // AncientDatadir returns the path of root ancient directory. 47 func (frdb *freezerdb) AncientDatadir() (string, error) { 48 return frdb.ancientRoot, nil 49 } 50 51 // Close implements io.Closer, closing both the fast key-value store as well as 52 // the slow ancient tables. 53 func (frdb *freezerdb) Close() error { 54 var errs []error 55 if err := frdb.chainFreezer.Close(); err != nil { 56 errs = append(errs, err) 57 } 58 if err := frdb.KeyValueStore.Close(); err != nil { 59 errs = append(errs, err) 60 } 61 if len(errs) != 0 { 62 return fmt.Errorf("%v", errs) 63 } 64 return nil 65 } 66 67 // Freeze is a helper method used for external testing to trigger and block until 68 // a freeze cycle completes, without having to sleep for a minute to trigger the 69 // automatic background run. 70 func (frdb *freezerdb) Freeze() error { 71 if frdb.readOnly { 72 return errReadOnly 73 } 74 // Trigger a freeze cycle and block until it's done 75 trigger := make(chan struct{}, 1) 76 frdb.chainFreezer.trigger <- trigger 77 <-trigger 78 return nil 79 } 80 81 // nofreezedb is a database wrapper that disables freezer data retrievals. 82 type nofreezedb struct { 83 ethdb.KeyValueStore 84 } 85 86 // HasAncient returns an error as we don't have a backing chain freezer. 87 func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) { 88 return false, errNotSupported 89 } 90 91 // Ancient returns an error as we don't have a backing chain freezer. 92 func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) { 93 return nil, errNotSupported 94 } 95 96 // AncientRange returns an error as we don't have a backing chain freezer. 97 func (db *nofreezedb) AncientRange(kind string, start, max, maxByteSize uint64) ([][]byte, error) { 98 return nil, errNotSupported 99 } 100 101 // Ancients returns an error as we don't have a backing chain freezer. 102 func (db *nofreezedb) Ancients() (uint64, error) { 103 return 0, errNotSupported 104 } 105 106 // Tail returns an error as we don't have a backing chain freezer. 107 func (db *nofreezedb) Tail() (uint64, error) { 108 return 0, errNotSupported 109 } 110 111 // AncientSize returns an error as we don't have a backing chain freezer. 112 func (db *nofreezedb) AncientSize(kind string) (uint64, error) { 113 return 0, errNotSupported 114 } 115 116 // ModifyAncients is not supported. 117 func (db *nofreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) { 118 return 0, errNotSupported 119 } 120 121 // TruncateHead returns an error as we don't have a backing chain freezer. 122 func (db *nofreezedb) TruncateHead(items uint64) (uint64, error) { 123 return 0, errNotSupported 124 } 125 126 // TruncateTail returns an error as we don't have a backing chain freezer. 127 func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) { 128 return 0, errNotSupported 129 } 130 131 // Sync returns an error as we don't have a backing chain freezer. 132 func (db *nofreezedb) Sync() error { 133 return errNotSupported 134 } 135 136 func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) { 137 // Unlike other ancient-related methods, this method does not return 138 // errNotSupported when invoked. 139 // The reason for this is that the caller might want to do several things: 140 // 1. Check if something is in the freezer, 141 // 2. If not, check leveldb. 142 // 143 // This will work, since the ancient-checks inside 'fn' will return errors, 144 // and the leveldb work will continue. 145 // 146 // If we instead were to return errNotSupported here, then the caller would 147 // have to explicitly check for that, having an extra clause to do the 148 // non-ancient operations. 149 return fn(db) 150 } 151 152 // MigrateTable processes the entries in a given table in sequence 153 // converting them to a new format if they're of an old format. 154 func (db *nofreezedb) MigrateTable(kind string, convert convertLegacyFn) error { 155 return errNotSupported 156 } 157 158 // AncientDatadir returns an error as we don't have a backing chain freezer. 159 func (db *nofreezedb) AncientDatadir() (string, error) { 160 return "", errNotSupported 161 } 162 163 // NewDatabase creates a high level database on top of a given key-value data 164 // store without a freezer moving immutable chain segments into cold storage. 165 func NewDatabase(db ethdb.KeyValueStore) ethdb.Database { 166 return &nofreezedb{KeyValueStore: db} 167 } 168 169 // resolveChainFreezerDir is a helper function which resolves the absolute path 170 // of chain freezer by considering backward compatibility. 171 func resolveChainFreezerDir(ancient string) string { 172 // Check if the chain freezer is already present in the specified 173 // sub folder, if not then two possibilities: 174 // - chain freezer is not initialized 175 // - chain freezer exists in legacy location (root ancient folder) 176 freezer := filepath.Join(ancient, ChainFreezerName) 177 if !common.FileExist(freezer) { 178 if !common.FileExist(ancient) { 179 // The entire ancient store is not initialized, still use the sub 180 // folder for initialization. 181 } else { 182 // Ancient root is already initialized, then we hold the assumption 183 // that chain freezer is also initialized and located in root folder. 184 // In this case fallback to legacy location. 185 freezer = ancient 186 log.Info("Found legacy ancient chain path", "location", ancient) 187 } 188 } 189 return freezer 190 } 191 192 // NewDatabaseWithFreezer creates a high level database on top of a given key- 193 // value data store with a freezer moving immutable chain segments into cold 194 // storage. The passed ancient indicates the path of root ancient directory 195 // where the chain freezer can be opened. 196 func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly bool) (ethdb.Database, error) { 197 // Create the idle freezer instance. If the given ancient directory is empty, 198 // in-memory chain freezer is used (e.g. dev mode); otherwise the regular 199 // file-based freezer is created. 200 chainFreezerDir := ancient 201 if chainFreezerDir != "" { 202 chainFreezerDir = resolveChainFreezerDir(chainFreezerDir) 203 } 204 frdb, err := newChainFreezer(chainFreezerDir, namespace, readonly) 205 if err != nil { 206 printChainMetadata(db) 207 return nil, err 208 } 209 // Since the freezer can be stored separately from the user's key-value database, 210 // there's a fairly high probability that the user requests invalid combinations 211 // of the freezer and database. Ensure that we don't shoot ourselves in the foot 212 // by serving up conflicting data, leading to both datastores getting corrupted. 213 // 214 // - If both the freezer and key-value store are empty (no genesis), we just 215 // initialized a new empty freezer, so everything's fine. 216 // - If the key-value store is empty, but the freezer is not, we need to make 217 // sure the user's genesis matches the freezer. That will be checked in the 218 // blockchain, since we don't have the genesis block here (nor should we at 219 // this point care, the key-value/freezer combo is valid). 220 // - If neither the key-value store nor the freezer is empty, cross validate 221 // the genesis hashes to make sure they are compatible. If they are, also 222 // ensure that there's no gap between the freezer and subsequently leveldb. 223 // - If the key-value store is not empty, but the freezer is, we might just be 224 // upgrading to the freezer release, or we might have had a small chain and 225 // not frozen anything yet. Ensure that no blocks are missing yet from the 226 // key-value store, since that would mean we already had an old freezer. 227 228 // If the genesis hash is empty, we have a new key-value store, so nothing to 229 // validate in this method. If, however, the genesis hash is not nil, compare 230 // it to the freezer content. 231 if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 { 232 if frozen, _ := frdb.Ancients(); frozen > 0 { 233 // If the freezer already contains something, ensure that the genesis blocks 234 // match, otherwise we might mix up freezers across chains and destroy both 235 // the freezer and the key-value store. 236 frgenesis, err := frdb.Ancient(ChainFreezerHashTable, 0) 237 if err != nil { 238 printChainMetadata(db) 239 return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err) 240 } else if !bytes.Equal(kvgenesis, frgenesis) { 241 printChainMetadata(db) 242 return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis) 243 } 244 // Key-value store and freezer belong to the same network. Ensure that they 245 // are contiguous, otherwise we might end up with a non-functional freezer. 246 if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 { 247 // Subsequent header after the freezer limit is missing from the database. 248 // Reject startup if the database has a more recent head. 249 if head := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); head > frozen-1 { 250 // Find the smallest block stored in the key-value store 251 // in range of [frozen, head] 252 var number uint64 253 for number = frozen; number <= head; number++ { 254 if present, _ := db.Has(headerHashKey(number)); present { 255 break 256 } 257 } 258 // We are about to exit on error. Print database metadata before exiting 259 printChainMetadata(db) 260 return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ", 261 frozen-1, number, head) 262 } 263 // Database contains only older data than the freezer, this happens if the 264 // state was wiped and reinited from an existing freezer. 265 } 266 // Otherwise, key-value store continues where the freezer left off, all is fine. 267 // We might have duplicate blocks (crash after freezer write but before key-value 268 // store deletion, but that's fine). 269 } else { 270 // If the freezer is empty, ensure nothing was moved yet from the key-value 271 // store, otherwise we'll end up missing data. We check block #1 to decide 272 // if we froze anything previously or not, but do take care of databases with 273 // only the genesis block. 274 if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) { 275 // Key-value store contains more data than the genesis block, make sure we 276 // didn't freeze anything yet. 277 if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 { 278 printChainMetadata(db) 279 return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path") 280 } 281 // Block #1 is still in the database, we're allowed to init a new freezer 282 } 283 // Otherwise, the head header is still the genesis, we're allowed to init a new 284 // freezer. 285 } 286 } 287 // Freezer is consistent with the key-value database, permit combining the two 288 if !readonly { 289 frdb.wg.Add(1) 290 go func() { 291 frdb.freeze(db) 292 frdb.wg.Done() 293 }() 294 } 295 return &freezerdb{ 296 ancientRoot: ancient, 297 KeyValueStore: db, 298 chainFreezer: frdb, 299 }, nil 300 } 301 302 // NewMemoryDatabase creates an ephemeral in-memory key-value database without a 303 // freezer moving immutable chain segments into cold storage. 304 func NewMemoryDatabase() ethdb.Database { 305 return NewDatabase(memorydb.New()) 306 } 307 308 // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database 309 // with an initial starting capacity, but without a freezer moving immutable 310 // chain segments into cold storage. 311 func NewMemoryDatabaseWithCap(size int) ethdb.Database { 312 return NewDatabase(memorydb.NewWithCap(size)) 313 } 314 315 // NewLevelDBDatabase creates a persistent key-value database without a freezer 316 // moving immutable chain segments into cold storage. 317 func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { 318 db, err := leveldb.New(file, cache, handles, namespace, readonly) 319 if err != nil { 320 return nil, err 321 } 322 log.Info("Using LevelDB as the backing database") 323 return NewDatabase(db), nil 324 } 325 326 // NewPebbleDBDatabase creates a persistent key-value database without a freezer 327 // moving immutable chain segments into cold storage. 328 func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly, ephemeral bool) (ethdb.Database, error) { 329 db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral) 330 if err != nil { 331 return nil, err 332 } 333 return NewDatabase(db), nil 334 } 335 336 const ( 337 dbPebble = "pebble" 338 dbLeveldb = "leveldb" 339 ) 340 341 // PreexistingDatabase checks the given data directory whether a database is already 342 // instantiated at that location, and if so, returns the type of database (or the 343 // empty string). 344 func PreexistingDatabase(path string) string { 345 if _, err := os.Stat(filepath.Join(path, "CURRENT")); err != nil { 346 return "" // No pre-existing db 347 } 348 if matches, err := filepath.Glob(filepath.Join(path, "OPTIONS*")); len(matches) > 0 || err != nil { 349 if err != nil { 350 panic(err) // only possible if the pattern is malformed 351 } 352 return dbPebble 353 } 354 return dbLeveldb 355 } 356 357 // OpenOptions contains the options to apply when opening a database. 358 // OBS: If AncientsDirectory is empty, it indicates that no freezer is to be used. 359 type OpenOptions struct { 360 Type string // "leveldb" | "pebble" 361 Directory string // the datadir 362 AncientsDirectory string // the ancients-dir 363 Namespace string // the namespace for database relevant metrics 364 Cache int // the capacity(in megabytes) of the data caching 365 Handles int // number of files to be open simultaneously 366 ReadOnly bool 367 // Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of 368 // a crash is not important. This option should typically be used in tests. 369 Ephemeral bool 370 } 371 372 // openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble. 373 // 374 // type == null type != null 375 // +---------------------------------------- 376 // db is non-existent | pebble default | specified type 377 // db is existent | from db | specified type (if compatible) 378 func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) { 379 // Reject any unsupported database type 380 if len(o.Type) != 0 && o.Type != dbLeveldb && o.Type != dbPebble { 381 return nil, fmt.Errorf("unknown db.engine %v", o.Type) 382 } 383 // Retrieve any pre-existing database's type and use that or the requested one 384 // as long as there's no conflict between the two types 385 existingDb := PreexistingDatabase(o.Directory) 386 if len(existingDb) != 0 && len(o.Type) != 0 && o.Type != existingDb { 387 return nil, fmt.Errorf("db.engine choice was %v but found pre-existing %v database in specified data directory", o.Type, existingDb) 388 } 389 if o.Type == dbPebble || existingDb == dbPebble { 390 log.Info("Using pebble as the backing database") 391 return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral) 392 } 393 if o.Type == dbLeveldb || existingDb == dbLeveldb { 394 log.Info("Using leveldb as the backing database") 395 return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) 396 } 397 // No pre-existing database, no user-requested one either. Default to Pebble. 398 log.Info("Defaulting to pebble as the backing database") 399 return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral) 400 } 401 402 // Open opens both a disk-based key-value database such as leveldb or pebble, but also 403 // integrates it with a freezer database -- if the AncientDir option has been 404 // set on the provided OpenOptions. 405 // The passed o.AncientDir indicates the path of root ancient directory where 406 // the chain freezer can be opened. 407 func Open(o OpenOptions) (ethdb.Database, error) { 408 kvdb, err := openKeyValueDatabase(o) 409 if err != nil { 410 return nil, err 411 } 412 if len(o.AncientsDirectory) == 0 { 413 return kvdb, nil 414 } 415 frdb, err := NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly) 416 if err != nil { 417 kvdb.Close() 418 return nil, err 419 } 420 return frdb, nil 421 } 422 423 type counter uint64 424 425 func (c counter) String() string { 426 return fmt.Sprintf("%d", c) 427 } 428 429 func (c counter) Percentage(current uint64) string { 430 return fmt.Sprintf("%d", current*100/uint64(c)) 431 } 432 433 // stat stores sizes and count for a parameter 434 type stat struct { 435 size common.StorageSize 436 count counter 437 } 438 439 // Add size to the stat and increase the counter by 1 440 func (s *stat) Add(size common.StorageSize) { 441 s.size += size 442 s.count++ 443 } 444 445 func (s *stat) Size() string { 446 return s.size.String() 447 } 448 449 func (s *stat) Count() string { 450 return s.count.String() 451 } 452 453 // InspectDatabase traverses the entire database and checks the size 454 // of all different categories of data. 455 func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { 456 it := db.NewIterator(keyPrefix, keyStart) 457 defer it.Release() 458 459 var ( 460 count int64 461 start = time.Now() 462 logged = time.Now() 463 464 // Key-value store statistics 465 headers stat 466 bodies stat 467 receipts stat 468 tds stat 469 numHashPairings stat 470 hashNumPairings stat 471 legacyTries stat 472 stateLookups stat 473 accountTries stat 474 storageTries stat 475 codes stat 476 txLookups stat 477 accountSnaps stat 478 storageSnaps stat 479 preimages stat 480 bloomBits stat 481 beaconHeaders stat 482 cliqueSnaps stat 483 484 // Les statistic 485 chtTrieNodes stat 486 bloomTrieNodes stat 487 488 // Meta- and unaccounted data 489 metadata stat 490 unaccounted stat 491 492 // Totals 493 total common.StorageSize 494 ) 495 // Inspect key-value database first. 496 for it.Next() { 497 var ( 498 key = it.Key() 499 size = common.StorageSize(len(key) + len(it.Value())) 500 ) 501 total += size 502 switch { 503 case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength): 504 headers.Add(size) 505 case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength): 506 bodies.Add(size) 507 case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength): 508 receipts.Add(size) 509 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix): 510 tds.Add(size) 511 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix): 512 numHashPairings.Add(size) 513 case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength): 514 hashNumPairings.Add(size) 515 case IsLegacyTrieNode(key, it.Value()): 516 legacyTries.Add(size) 517 case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength: 518 stateLookups.Add(size) 519 case IsAccountTrieNode(key): 520 accountTries.Add(size) 521 case IsStorageTrieNode(key): 522 storageTries.Add(size) 523 case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: 524 codes.Add(size) 525 case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength): 526 txLookups.Add(size) 527 case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength): 528 accountSnaps.Add(size) 529 case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength): 530 storageSnaps.Add(size) 531 case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength): 532 preimages.Add(size) 533 case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength): 534 metadata.Add(size) 535 case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength): 536 metadata.Add(size) 537 case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): 538 bloomBits.Add(size) 539 case bytes.HasPrefix(key, BloomBitsIndexPrefix): 540 bloomBits.Add(size) 541 case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8): 542 beaconHeaders.Add(size) 543 case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength: 544 cliqueSnaps.Add(size) 545 case bytes.HasPrefix(key, ChtTablePrefix) || 546 bytes.HasPrefix(key, ChtIndexTablePrefix) || 547 bytes.HasPrefix(key, ChtPrefix): // Canonical hash trie 548 chtTrieNodes.Add(size) 549 case bytes.HasPrefix(key, BloomTrieTablePrefix) || 550 bytes.HasPrefix(key, BloomTrieIndexPrefix) || 551 bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub 552 bloomTrieNodes.Add(size) 553 default: 554 var accounted bool 555 for _, meta := range [][]byte{ 556 databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, headFinalizedBlockKey, 557 lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey, 558 snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, 559 uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey, 560 persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey, 561 } { 562 if bytes.Equal(key, meta) { 563 metadata.Add(size) 564 accounted = true 565 break 566 } 567 } 568 if !accounted { 569 unaccounted.Add(size) 570 } 571 } 572 count++ 573 if count%1000 == 0 && time.Since(logged) > 8*time.Second { 574 log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start))) 575 logged = time.Now() 576 } 577 } 578 // Display the database statistic of key-value store. 579 stats := [][]string{ 580 {"Key-Value store", "Headers", headers.Size(), headers.Count()}, 581 {"Key-Value store", "Bodies", bodies.Size(), bodies.Count()}, 582 {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()}, 583 {"Key-Value store", "Difficulties", tds.Size(), tds.Count()}, 584 {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, 585 {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, 586 {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, 587 {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()}, 588 {"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, 589 {"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()}, 590 {"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()}, 591 {"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()}, 592 {"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()}, 593 {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, 594 {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, 595 {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, 596 {"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()}, 597 {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, 598 {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, 599 {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()}, 600 {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()}, 601 } 602 // Inspect all registered append-only file store then. 603 ancients, err := inspectFreezers(db) 604 if err != nil { 605 return err 606 } 607 for _, ancient := range ancients { 608 for _, table := range ancient.sizes { 609 stats = append(stats, []string{ 610 fmt.Sprintf("Ancient store (%s)", strings.Title(ancient.name)), 611 strings.Title(table.name), 612 table.size.String(), 613 fmt.Sprintf("%d", ancient.count()), 614 }) 615 } 616 total += ancient.size() 617 } 618 table := tablewriter.NewWriter(os.Stdout) 619 table.SetHeader([]string{"Database", "Category", "Size", "Items"}) 620 table.SetFooter([]string{"", "Total", total.String(), " "}) 621 table.AppendBulk(stats) 622 table.Render() 623 624 if unaccounted.size > 0 { 625 log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count) 626 } 627 return nil 628 } 629 630 // printChainMetadata prints out chain metadata to stderr. 631 func printChainMetadata(db ethdb.KeyValueStore) { 632 fmt.Fprintf(os.Stderr, "Chain metadata\n") 633 for _, v := range ReadChainMetadata(db) { 634 fmt.Fprintf(os.Stderr, " %s\n", strings.Join(v, ": ")) 635 } 636 fmt.Fprintf(os.Stderr, "\n\n") 637 } 638 639 // ReadChainMetadata returns a set of key/value pairs that contains information 640 // about the database chain status. This can be used for diagnostic purposes 641 // when investigating the state of the node. 642 func ReadChainMetadata(db ethdb.KeyValueStore) [][]string { 643 pp := func(val *uint64) string { 644 if val == nil { 645 return "<nil>" 646 } 647 return fmt.Sprintf("%d (%#x)", *val, *val) 648 } 649 data := [][]string{ 650 {"databaseVersion", pp(ReadDatabaseVersion(db))}, 651 {"headBlockHash", fmt.Sprintf("%v", ReadHeadBlockHash(db))}, 652 {"headFastBlockHash", fmt.Sprintf("%v", ReadHeadFastBlockHash(db))}, 653 {"headHeaderHash", fmt.Sprintf("%v", ReadHeadHeaderHash(db))}, 654 {"lastPivotNumber", pp(ReadLastPivotNumber(db))}, 655 {"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(ReadSnapshotSyncStatus(db)))}, 656 {"snapshotDisabled", fmt.Sprintf("%v", ReadSnapshotDisabled(db))}, 657 {"snapshotJournal", fmt.Sprintf("%d bytes", len(ReadSnapshotJournal(db)))}, 658 {"snapshotRecoveryNumber", pp(ReadSnapshotRecoveryNumber(db))}, 659 {"snapshotRoot", fmt.Sprintf("%v", ReadSnapshotRoot(db))}, 660 {"txIndexTail", pp(ReadTxIndexTail(db))}, 661 } 662 if b := ReadSkeletonSyncStatus(db); b != nil { 663 data = append(data, []string{"SkeletonSyncStatus", string(b)}) 664 } 665 return data 666 }