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