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