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