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