github.com/jimmyx0x/go-ethereum@v1.10.28/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 "strings" 26 "sync/atomic" 27 "time" 28 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/ethdb" 31 "github.com/ethereum/go-ethereum/ethdb/leveldb" 32 "github.com/ethereum/go-ethereum/ethdb/memorydb" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/olekukonko/tablewriter" 35 ) 36 37 // freezerdb is a database wrapper that enabled freezer data retrievals. 38 type freezerdb struct { 39 ancientRoot string 40 ethdb.KeyValueStore 41 ethdb.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 atomic.StoreUint64(&frdb.AncientStore.(*chainFreezer).threshold, old) 75 }(atomic.LoadUint64(&frdb.AncientStore.(*chainFreezer).threshold)) 76 atomic.StoreUint64(&frdb.AncientStore.(*chainFreezer).threshold, 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 ethdb.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(ethdb.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) error { 127 return errNotSupported 128 } 129 130 // TruncateTail returns an error as we don't have a backing chain freezer. 131 func (db *nofreezedb) TruncateTail(items uint64) error { 132 return 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 ethdb.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 ethdb.KeyValueStore) ethdb.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 ethdb.KeyValueStore, ancient string, namespace string, readonly bool) (ethdb.Database, error) { 201 // Create the idle freezer instance 202 frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly, freezerTableSize, chainFreezerNoSnappy) 203 if err != nil { 204 return nil, err 205 } 206 // Since the freezer can be stored separately from the user's key-value database, 207 // there's a fairly high probability that the user requests invalid combinations 208 // of the freezer and database. Ensure that we don't shoot ourselves in the foot 209 // by serving up conflicting data, leading to both datastores getting corrupted. 210 // 211 // - If both the freezer and key-value store is empty (no genesis), we just 212 // initialized a new empty freezer, so everything's fine. 213 // - If the key-value store is empty, but the freezer is not, we need to make 214 // sure the user's genesis matches the freezer. That will be checked in the 215 // blockchain, since we don't have the genesis block here (nor should we at 216 // this point care, the key-value/freezer combo is valid). 217 // - If neither the key-value store nor the freezer is empty, cross validate 218 // the genesis hashes to make sure they are compatible. If they are, also 219 // ensure that there's no gap between the freezer and subsequently leveldb. 220 // - If the key-value store is not empty, but the freezer is we might just be 221 // upgrading to the freezer release, or we might have had a small chain and 222 // not frozen anything yet. Ensure that no blocks are missing yet from the 223 // key-value store, since that would mean we already had an old freezer. 224 225 // If the genesis hash is empty, we have a new key-value store, so nothing to 226 // validate in this method. If, however, the genesis hash is not nil, compare 227 // it to the freezer content. 228 if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 { 229 if frozen, _ := frdb.Ancients(); frozen > 0 { 230 // If the freezer already contains something, ensure that the genesis blocks 231 // match, otherwise we might mix up freezers across chains and destroy both 232 // the freezer and the key-value store. 233 frgenesis, err := frdb.Ancient(chainFreezerHashTable, 0) 234 if err != nil { 235 return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err) 236 } else if !bytes.Equal(kvgenesis, frgenesis) { 237 return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis) 238 } 239 // Key-value store and freezer belong to the same network. Ensure that they 240 // are contiguous, otherwise we might end up with a non-functional freezer. 241 if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 { 242 // Subsequent header after the freezer limit is missing from the database. 243 // Reject startup if the database has a more recent head. 244 if ldbNum := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); ldbNum > frozen-1 { 245 return nil, fmt.Errorf("gap in the chain between ancients (#%d) and leveldb (#%d) ", frozen, ldbNum) 246 } 247 // Database contains only older data than the freezer, this happens if the 248 // state was wiped and reinited from an existing freezer. 249 } 250 // Otherwise, key-value store continues where the freezer left off, all is fine. 251 // We might have duplicate blocks (crash after freezer write but before key-value 252 // store deletion, but that's fine). 253 } else { 254 // If the freezer is empty, ensure nothing was moved yet from the key-value 255 // store, otherwise we'll end up missing data. We check block #1 to decide 256 // if we froze anything previously or not, but do take care of databases with 257 // only the genesis block. 258 if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) { 259 // Key-value store contains more data than the genesis block, make sure we 260 // didn't freeze anything yet. 261 if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 { 262 return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path") 263 } 264 // Block #1 is still in the database, we're allowed to init a new freezer 265 } 266 // Otherwise, the head header is still the genesis, we're allowed to init a new 267 // freezer. 268 } 269 } 270 // Freezer is consistent with the key-value database, permit combining the two 271 if !frdb.readonly { 272 frdb.wg.Add(1) 273 go func() { 274 frdb.freeze(db) 275 frdb.wg.Done() 276 }() 277 } 278 return &freezerdb{ 279 ancientRoot: ancient, 280 KeyValueStore: db, 281 AncientStore: frdb, 282 }, nil 283 } 284 285 // NewMemoryDatabase creates an ephemeral in-memory key-value database without a 286 // freezer moving immutable chain segments into cold storage. 287 func NewMemoryDatabase() ethdb.Database { 288 return NewDatabase(memorydb.New()) 289 } 290 291 // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database 292 // with an initial starting capacity, but without a freezer moving immutable 293 // chain segments into cold storage. 294 func NewMemoryDatabaseWithCap(size int) ethdb.Database { 295 return NewDatabase(memorydb.NewWithCap(size)) 296 } 297 298 // NewLevelDBDatabase creates a persistent key-value database without a freezer 299 // moving immutable chain segments into cold storage. 300 func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { 301 db, err := leveldb.New(file, cache, handles, namespace, readonly) 302 if err != nil { 303 return nil, err 304 } 305 return NewDatabase(db), nil 306 } 307 308 // NewLevelDBDatabaseWithFreezer creates a persistent key-value database with a 309 // freezer moving immutable chain segments into cold storage. The passed ancient 310 // indicates the path of root ancient directory where the chain freezer can be 311 // opened. 312 func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, ancient string, namespace string, readonly bool) (ethdb.Database, error) { 313 kvdb, err := leveldb.New(file, cache, handles, namespace, readonly) 314 if err != nil { 315 return nil, err 316 } 317 frdb, err := NewDatabaseWithFreezer(kvdb, ancient, namespace, readonly) 318 if err != nil { 319 kvdb.Close() 320 return nil, err 321 } 322 return frdb, nil 323 } 324 325 type counter uint64 326 327 func (c counter) String() string { 328 return fmt.Sprintf("%d", c) 329 } 330 331 func (c counter) Percentage(current uint64) string { 332 return fmt.Sprintf("%d", current*100/uint64(c)) 333 } 334 335 // stat stores sizes and count for a parameter 336 type stat struct { 337 size common.StorageSize 338 count counter 339 } 340 341 // Add size to the stat and increase the counter by 1 342 func (s *stat) Add(size common.StorageSize) { 343 s.size += size 344 s.count++ 345 } 346 347 func (s *stat) Size() string { 348 return s.size.String() 349 } 350 351 func (s *stat) Count() string { 352 return s.count.String() 353 } 354 355 // InspectDatabase traverses the entire database and checks the size 356 // of all different categories of data. 357 func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { 358 it := db.NewIterator(keyPrefix, keyStart) 359 defer it.Release() 360 361 var ( 362 count int64 363 start = time.Now() 364 logged = time.Now() 365 366 // Key-value store statistics 367 headers stat 368 bodies stat 369 receipts stat 370 tds stat 371 numHashPairings stat 372 hashNumPairings stat 373 tries stat 374 codes stat 375 txLookups stat 376 accountSnaps stat 377 storageSnaps stat 378 preimages stat 379 bloomBits stat 380 beaconHeaders stat 381 cliqueSnaps stat 382 383 // Les statistic 384 chtTrieNodes stat 385 bloomTrieNodes stat 386 387 // Meta- and unaccounted data 388 metadata stat 389 unaccounted stat 390 391 // Totals 392 total common.StorageSize 393 ) 394 // Inspect key-value database first. 395 for it.Next() { 396 var ( 397 key = it.Key() 398 size = common.StorageSize(len(key) + len(it.Value())) 399 ) 400 total += size 401 switch { 402 case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength): 403 headers.Add(size) 404 case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength): 405 bodies.Add(size) 406 case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength): 407 receipts.Add(size) 408 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix): 409 tds.Add(size) 410 case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix): 411 numHashPairings.Add(size) 412 case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength): 413 hashNumPairings.Add(size) 414 case len(key) == common.HashLength: 415 tries.Add(size) 416 case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: 417 codes.Add(size) 418 case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength): 419 txLookups.Add(size) 420 case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength): 421 accountSnaps.Add(size) 422 case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength): 423 storageSnaps.Add(size) 424 case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength): 425 preimages.Add(size) 426 case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength): 427 metadata.Add(size) 428 case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength): 429 metadata.Add(size) 430 case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength): 431 bloomBits.Add(size) 432 case bytes.HasPrefix(key, BloomBitsIndexPrefix): 433 bloomBits.Add(size) 434 case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8): 435 beaconHeaders.Add(size) 436 case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength: 437 cliqueSnaps.Add(size) 438 case bytes.HasPrefix(key, ChtTablePrefix) || 439 bytes.HasPrefix(key, ChtIndexTablePrefix) || 440 bytes.HasPrefix(key, ChtPrefix): // Canonical hash trie 441 chtTrieNodes.Add(size) 442 case bytes.HasPrefix(key, BloomTrieTablePrefix) || 443 bytes.HasPrefix(key, BloomTrieIndexPrefix) || 444 bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub 445 bloomTrieNodes.Add(size) 446 default: 447 var accounted bool 448 for _, meta := range [][]byte{ 449 databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, headFinalizedBlockKey, 450 lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey, 451 snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey, 452 uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey, 453 } { 454 if bytes.Equal(key, meta) { 455 metadata.Add(size) 456 accounted = true 457 break 458 } 459 } 460 if !accounted { 461 unaccounted.Add(size) 462 } 463 } 464 count++ 465 if count%1000 == 0 && time.Since(logged) > 8*time.Second { 466 log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start))) 467 logged = time.Now() 468 } 469 } 470 // Display the database statistic of key-value store. 471 stats := [][]string{ 472 {"Key-Value store", "Headers", headers.Size(), headers.Count()}, 473 {"Key-Value store", "Bodies", bodies.Size(), bodies.Count()}, 474 {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()}, 475 {"Key-Value store", "Difficulties", tds.Size(), tds.Count()}, 476 {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, 477 {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, 478 {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, 479 {"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()}, 480 {"Key-Value store", "Contract codes", codes.Size(), codes.Count()}, 481 {"Key-Value store", "Trie nodes", tries.Size(), tries.Count()}, 482 {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, 483 {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, 484 {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, 485 {"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()}, 486 {"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()}, 487 {"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()}, 488 {"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()}, 489 {"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()}, 490 } 491 // Inspect all registered append-only file store then. 492 ancients, err := inspectFreezers(db) 493 if err != nil { 494 return err 495 } 496 for _, ancient := range ancients { 497 for _, table := range ancient.sizes { 498 stats = append(stats, []string{ 499 fmt.Sprintf("Ancient store (%s)", strings.Title(ancient.name)), 500 strings.Title(table.name), 501 table.size.String(), 502 fmt.Sprintf("%d", ancient.count()), 503 }) 504 } 505 total += ancient.size() 506 } 507 table := tablewriter.NewWriter(os.Stdout) 508 table.SetHeader([]string{"Database", "Category", "Size", "Items"}) 509 table.SetFooter([]string{"", "Total", total.String(), " "}) 510 table.AppendBulk(stats) 511 table.Render() 512 513 if unaccounted.size > 0 { 514 log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count) 515 } 516 return nil 517 }