gitlab.com/yannislg/go-pulse@v0.0.0-20210722055913-a3e24e95638d/core/rawdb/accessors_chain.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 "encoding/binary" 22 "math/big" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/types" 26 "github.com/ethereum/go-ethereum/crypto" 27 "github.com/ethereum/go-ethereum/ethdb" 28 "github.com/ethereum/go-ethereum/log" 29 "github.com/ethereum/go-ethereum/params" 30 "github.com/ethereum/go-ethereum/rlp" 31 ) 32 33 // ReadCanonicalHash retrieves the hash assigned to a canonical block number. 34 func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash { 35 data, _ := db.Ancient(freezerHashTable, number) 36 if len(data) == 0 { 37 data, _ = db.Get(headerHashKey(number)) 38 // In the background freezer is moving data from leveldb to flatten files. 39 // So during the first check for ancient db, the data is not yet in there, 40 // but when we reach into leveldb, the data was already moved. That would 41 // result in a not found error. 42 if len(data) == 0 { 43 data, _ = db.Ancient(freezerHashTable, number) 44 } 45 } 46 if len(data) == 0 { 47 return common.Hash{} 48 } 49 return common.BytesToHash(data) 50 } 51 52 // WriteCanonicalHash stores the hash assigned to a canonical block number. 53 func WriteCanonicalHash(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 54 if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil { 55 log.Crit("Failed to store number to hash mapping", "err", err) 56 } 57 } 58 59 // DeleteCanonicalHash removes the number to hash canonical mapping. 60 func DeleteCanonicalHash(db ethdb.KeyValueWriter, number uint64) { 61 if err := db.Delete(headerHashKey(number)); err != nil { 62 log.Crit("Failed to delete number to hash mapping", "err", err) 63 } 64 } 65 66 // ReadAllHashes retrieves all the hashes assigned to blocks at a certain heights, 67 // both canonical and reorged forks included. 68 func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash { 69 prefix := headerKeyPrefix(number) 70 71 hashes := make([]common.Hash, 0, 1) 72 it := db.NewIterator(prefix, nil) 73 defer it.Release() 74 75 for it.Next() { 76 if key := it.Key(); len(key) == len(prefix)+32 { 77 hashes = append(hashes, common.BytesToHash(key[len(key)-32:])) 78 } 79 } 80 return hashes 81 } 82 83 // ReadHeaderNumber returns the header number assigned to a hash. 84 func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 { 85 data, _ := db.Get(headerNumberKey(hash)) 86 if len(data) != 8 { 87 return nil 88 } 89 number := binary.BigEndian.Uint64(data) 90 return &number 91 } 92 93 // WriteHeaderNumber stores the hash->number mapping. 94 func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 95 key := headerNumberKey(hash) 96 enc := encodeBlockNumber(number) 97 if err := db.Put(key, enc); err != nil { 98 log.Crit("Failed to store hash to number mapping", "err", err) 99 } 100 } 101 102 // DeleteHeaderNumber removes hash->number mapping. 103 func DeleteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash) { 104 if err := db.Delete(headerNumberKey(hash)); err != nil { 105 log.Crit("Failed to delete hash to number mapping", "err", err) 106 } 107 } 108 109 // ReadHeadHeaderHash retrieves the hash of the current canonical head header. 110 func ReadHeadHeaderHash(db ethdb.KeyValueReader) common.Hash { 111 data, _ := db.Get(headHeaderKey) 112 if len(data) == 0 { 113 return common.Hash{} 114 } 115 return common.BytesToHash(data) 116 } 117 118 // WriteHeadHeaderHash stores the hash of the current canonical head header. 119 func WriteHeadHeaderHash(db ethdb.KeyValueWriter, hash common.Hash) { 120 if err := db.Put(headHeaderKey, hash.Bytes()); err != nil { 121 log.Crit("Failed to store last header's hash", "err", err) 122 } 123 } 124 125 // ReadHeadBlockHash retrieves the hash of the current canonical head block. 126 func ReadHeadBlockHash(db ethdb.KeyValueReader) common.Hash { 127 data, _ := db.Get(headBlockKey) 128 if len(data) == 0 { 129 return common.Hash{} 130 } 131 return common.BytesToHash(data) 132 } 133 134 // WriteHeadBlockHash stores the head block's hash. 135 func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) { 136 if err := db.Put(headBlockKey, hash.Bytes()); err != nil { 137 log.Crit("Failed to store last block's hash", "err", err) 138 } 139 } 140 141 // ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block. 142 func ReadHeadFastBlockHash(db ethdb.KeyValueReader) common.Hash { 143 data, _ := db.Get(headFastBlockKey) 144 if len(data) == 0 { 145 return common.Hash{} 146 } 147 return common.BytesToHash(data) 148 } 149 150 // WriteHeadFastBlockHash stores the hash of the current fast-sync head block. 151 func WriteHeadFastBlockHash(db ethdb.KeyValueWriter, hash common.Hash) { 152 if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil { 153 log.Crit("Failed to store last fast block's hash", "err", err) 154 } 155 } 156 157 // ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow 158 // reporting correct numbers across restarts. 159 func ReadFastTrieProgress(db ethdb.KeyValueReader) uint64 { 160 data, _ := db.Get(fastTrieProgressKey) 161 if len(data) == 0 { 162 return 0 163 } 164 return new(big.Int).SetBytes(data).Uint64() 165 } 166 167 // WriteFastTrieProgress stores the fast sync trie process counter to support 168 // retrieving it across restarts. 169 func WriteFastTrieProgress(db ethdb.KeyValueWriter, count uint64) { 170 if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil { 171 log.Crit("Failed to store fast sync trie progress", "err", err) 172 } 173 } 174 175 // ReadHeaderRLP retrieves a block header in its raw RLP database encoding. 176 func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { 177 // First try to look up the data in ancient database. Extra hash 178 // comparison is necessary since ancient database only maintains 179 // the canonical data. 180 data, _ := db.Ancient(freezerHeaderTable, number) 181 if len(data) > 0 && crypto.Keccak256Hash(data) == hash { 182 return data 183 } 184 // Then try to look up the data in leveldb. 185 data, _ = db.Get(headerKey(number, hash)) 186 if len(data) > 0 { 187 return data 188 } 189 // In the background freezer is moving data from leveldb to flatten files. 190 // So during the first check for ancient db, the data is not yet in there, 191 // but when we reach into leveldb, the data was already moved. That would 192 // result in a not found error. 193 data, _ = db.Ancient(freezerHeaderTable, number) 194 if len(data) > 0 && crypto.Keccak256Hash(data) == hash { 195 return data 196 } 197 return nil // Can't find the data anywhere. 198 } 199 200 // HasHeader verifies the existence of a block header corresponding to the hash. 201 func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool { 202 if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash { 203 return true 204 } 205 if has, err := db.Has(headerKey(number, hash)); !has || err != nil { 206 return false 207 } 208 return true 209 } 210 211 // ReadHeader retrieves the block header corresponding to the hash. 212 func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header { 213 data := ReadHeaderRLP(db, hash, number) 214 if len(data) == 0 { 215 return nil 216 } 217 header := new(types.Header) 218 if err := rlp.Decode(bytes.NewReader(data), header); err != nil { 219 log.Error("Invalid block header RLP", "hash", hash, "err", err) 220 return nil 221 } 222 return header 223 } 224 225 // WriteHeader stores a block header into the database and also stores the hash- 226 // to-number mapping. 227 func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) { 228 var ( 229 hash = header.Hash() 230 number = header.Number.Uint64() 231 ) 232 // Write the hash -> number mapping 233 WriteHeaderNumber(db, hash, number) 234 235 // Write the encoded header 236 data, err := rlp.EncodeToBytes(header) 237 if err != nil { 238 log.Crit("Failed to RLP encode header", "err", err) 239 } 240 key := headerKey(number, hash) 241 if err := db.Put(key, data); err != nil { 242 log.Crit("Failed to store header", "err", err) 243 } 244 } 245 246 // DeleteHeader removes all block header data associated with a hash. 247 func DeleteHeader(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 248 deleteHeaderWithoutNumber(db, hash, number) 249 if err := db.Delete(headerNumberKey(hash)); err != nil { 250 log.Crit("Failed to delete hash to number mapping", "err", err) 251 } 252 } 253 254 // deleteHeaderWithoutNumber removes only the block header but does not remove 255 // the hash to number mapping. 256 func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 257 if err := db.Delete(headerKey(number, hash)); err != nil { 258 log.Crit("Failed to delete header", "err", err) 259 } 260 } 261 262 // ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. 263 func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { 264 // First try to look up the data in ancient database. Extra hash 265 // comparison is necessary since ancient database only maintains 266 // the canonical data. 267 data, _ := db.Ancient(freezerBodiesTable, number) 268 if len(data) > 0 { 269 h, _ := db.Ancient(freezerHashTable, number) 270 if common.BytesToHash(h) == hash { 271 return data 272 } 273 } 274 // Then try to look up the data in leveldb. 275 data, _ = db.Get(blockBodyKey(number, hash)) 276 if len(data) > 0 { 277 return data 278 } 279 // In the background freezer is moving data from leveldb to flatten files. 280 // So during the first check for ancient db, the data is not yet in there, 281 // but when we reach into leveldb, the data was already moved. That would 282 // result in a not found error. 283 data, _ = db.Ancient(freezerBodiesTable, number) 284 if len(data) > 0 { 285 h, _ := db.Ancient(freezerHashTable, number) 286 if common.BytesToHash(h) == hash { 287 return data 288 } 289 } 290 return nil // Can't find the data anywhere. 291 } 292 293 // WriteBodyRLP stores an RLP encoded block body into the database. 294 func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp rlp.RawValue) { 295 if err := db.Put(blockBodyKey(number, hash), rlp); err != nil { 296 log.Crit("Failed to store block body", "err", err) 297 } 298 } 299 300 // HasBody verifies the existence of a block body corresponding to the hash. 301 func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool { 302 if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash { 303 return true 304 } 305 if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil { 306 return false 307 } 308 return true 309 } 310 311 // ReadBody retrieves the block body corresponding to the hash. 312 func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body { 313 data := ReadBodyRLP(db, hash, number) 314 if len(data) == 0 { 315 return nil 316 } 317 body := new(types.Body) 318 if err := rlp.Decode(bytes.NewReader(data), body); err != nil { 319 log.Error("Invalid block body RLP", "hash", hash, "err", err) 320 return nil 321 } 322 return body 323 } 324 325 // WriteBody stores a block body into the database. 326 func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *types.Body) { 327 data, err := rlp.EncodeToBytes(body) 328 if err != nil { 329 log.Crit("Failed to RLP encode body", "err", err) 330 } 331 WriteBodyRLP(db, hash, number, data) 332 } 333 334 // DeleteBody removes all block body data associated with a hash. 335 func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 336 if err := db.Delete(blockBodyKey(number, hash)); err != nil { 337 log.Crit("Failed to delete block body", "err", err) 338 } 339 } 340 341 // ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding. 342 func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { 343 // First try to look up the data in ancient database. Extra hash 344 // comparison is necessary since ancient database only maintains 345 // the canonical data. 346 data, _ := db.Ancient(freezerDifficultyTable, number) 347 if len(data) > 0 { 348 h, _ := db.Ancient(freezerHashTable, number) 349 if common.BytesToHash(h) == hash { 350 return data 351 } 352 } 353 // Then try to look up the data in leveldb. 354 data, _ = db.Get(headerTDKey(number, hash)) 355 if len(data) > 0 { 356 return data 357 } 358 // In the background freezer is moving data from leveldb to flatten files. 359 // So during the first check for ancient db, the data is not yet in there, 360 // but when we reach into leveldb, the data was already moved. That would 361 // result in a not found error. 362 data, _ = db.Ancient(freezerDifficultyTable, number) 363 if len(data) > 0 { 364 h, _ := db.Ancient(freezerHashTable, number) 365 if common.BytesToHash(h) == hash { 366 return data 367 } 368 } 369 return nil // Can't find the data anywhere. 370 } 371 372 // ReadTd retrieves a block's total difficulty corresponding to the hash. 373 func ReadTd(db ethdb.Reader, hash common.Hash, number uint64) *big.Int { 374 data := ReadTdRLP(db, hash, number) 375 if len(data) == 0 { 376 return nil 377 } 378 td := new(big.Int) 379 if err := rlp.Decode(bytes.NewReader(data), td); err != nil { 380 log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err) 381 return nil 382 } 383 return td 384 } 385 386 // WriteTd stores the total difficulty of a block into the database. 387 func WriteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64, td *big.Int) { 388 data, err := rlp.EncodeToBytes(td) 389 if err != nil { 390 log.Crit("Failed to RLP encode block total difficulty", "err", err) 391 } 392 if err := db.Put(headerTDKey(number, hash), data); err != nil { 393 log.Crit("Failed to store block total difficulty", "err", err) 394 } 395 } 396 397 // DeleteTd removes all block total difficulty data associated with a hash. 398 func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 399 if err := db.Delete(headerTDKey(number, hash)); err != nil { 400 log.Crit("Failed to delete block total difficulty", "err", err) 401 } 402 } 403 404 // HasReceipts verifies the existence of all the transaction receipts belonging 405 // to a block. 406 func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool { 407 if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash { 408 return true 409 } 410 if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil { 411 return false 412 } 413 return true 414 } 415 416 // ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding. 417 func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { 418 // First try to look up the data in ancient database. Extra hash 419 // comparison is necessary since ancient database only maintains 420 // the canonical data. 421 data, _ := db.Ancient(freezerReceiptTable, number) 422 if len(data) > 0 { 423 h, _ := db.Ancient(freezerHashTable, number) 424 if common.BytesToHash(h) == hash { 425 return data 426 } 427 } 428 // Then try to look up the data in leveldb. 429 data, _ = db.Get(blockReceiptsKey(number, hash)) 430 if len(data) > 0 { 431 return data 432 } 433 // In the background freezer is moving data from leveldb to flatten files. 434 // So during the first check for ancient db, the data is not yet in there, 435 // but when we reach into leveldb, the data was already moved. That would 436 // result in a not found error. 437 data, _ = db.Ancient(freezerReceiptTable, number) 438 if len(data) > 0 { 439 h, _ := db.Ancient(freezerHashTable, number) 440 if common.BytesToHash(h) == hash { 441 return data 442 } 443 } 444 return nil // Can't find the data anywhere. 445 } 446 447 // ReadRawReceipts retrieves all the transaction receipts belonging to a block. 448 // The receipt metadata fields are not guaranteed to be populated, so they 449 // should not be used. Use ReadReceipts instead if the metadata is needed. 450 func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts { 451 // Retrieve the flattened receipt slice 452 data := ReadReceiptsRLP(db, hash, number) 453 if len(data) == 0 { 454 return nil 455 } 456 // Convert the receipts from their storage form to their internal representation 457 storageReceipts := []*types.ReceiptForStorage{} 458 if err := rlp.DecodeBytes(data, &storageReceipts); err != nil { 459 log.Error("Invalid receipt array RLP", "hash", hash, "err", err) 460 return nil 461 } 462 receipts := make(types.Receipts, len(storageReceipts)) 463 for i, storageReceipt := range storageReceipts { 464 receipts[i] = (*types.Receipt)(storageReceipt) 465 } 466 return receipts 467 } 468 469 // ReadReceipts retrieves all the transaction receipts belonging to a block, including 470 // its correspoinding metadata fields. If it is unable to populate these metadata 471 // fields then nil is returned. 472 // 473 // The current implementation populates these metadata fields by reading the receipts' 474 // corresponding block body, so if the block body is not found it will return nil even 475 // if the receipt itself is stored. 476 func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) types.Receipts { 477 // We're deriving many fields from the block body, retrieve beside the receipt 478 receipts := ReadRawReceipts(db, hash, number) 479 if receipts == nil { 480 return nil 481 } 482 body := ReadBody(db, hash, number) 483 if body == nil { 484 log.Error("Missing body but have receipt", "hash", hash, "number", number) 485 return nil 486 } 487 if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil { 488 log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) 489 return nil 490 } 491 return receipts 492 } 493 494 // WriteReceipts stores all the transaction receipts belonging to a block. 495 func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts types.Receipts) { 496 // Convert the receipts into their storage form and serialize them 497 storageReceipts := make([]*types.ReceiptForStorage, len(receipts)) 498 for i, receipt := range receipts { 499 storageReceipts[i] = (*types.ReceiptForStorage)(receipt) 500 } 501 bytes, err := rlp.EncodeToBytes(storageReceipts) 502 if err != nil { 503 log.Crit("Failed to encode block receipts", "err", err) 504 } 505 // Store the flattened receipt slice 506 if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil { 507 log.Crit("Failed to store block receipts", "err", err) 508 } 509 } 510 511 // DeleteReceipts removes all receipt data associated with a block hash. 512 func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 513 if err := db.Delete(blockReceiptsKey(number, hash)); err != nil { 514 log.Crit("Failed to delete block receipts", "err", err) 515 } 516 } 517 518 // ReadBlock retrieves an entire block corresponding to the hash, assembling it 519 // back from the stored header and body. If either the header or body could not 520 // be retrieved nil is returned. 521 // 522 // Note, due to concurrent download of header and block body the header and thus 523 // canonical hash can be stored in the database but the body data not (yet). 524 func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block { 525 header := ReadHeader(db, hash, number) 526 if header == nil { 527 return nil 528 } 529 body := ReadBody(db, hash, number) 530 if body == nil { 531 return nil 532 } 533 return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles) 534 } 535 536 // WriteBlock serializes a block into the database, header and body separately. 537 func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) { 538 WriteBody(db, block.Hash(), block.NumberU64(), block.Body()) 539 WriteHeader(db, block.Header()) 540 } 541 542 // WriteAncientBlock writes entire block data into ancient store and returns the total written size. 543 func WriteAncientBlock(db ethdb.AncientWriter, block *types.Block, receipts types.Receipts, td *big.Int) int { 544 // Encode all block components to RLP format. 545 headerBlob, err := rlp.EncodeToBytes(block.Header()) 546 if err != nil { 547 log.Crit("Failed to RLP encode block header", "err", err) 548 } 549 bodyBlob, err := rlp.EncodeToBytes(block.Body()) 550 if err != nil { 551 log.Crit("Failed to RLP encode body", "err", err) 552 } 553 storageReceipts := make([]*types.ReceiptForStorage, len(receipts)) 554 for i, receipt := range receipts { 555 storageReceipts[i] = (*types.ReceiptForStorage)(receipt) 556 } 557 receiptBlob, err := rlp.EncodeToBytes(storageReceipts) 558 if err != nil { 559 log.Crit("Failed to RLP encode block receipts", "err", err) 560 } 561 tdBlob, err := rlp.EncodeToBytes(td) 562 if err != nil { 563 log.Crit("Failed to RLP encode block total difficulty", "err", err) 564 } 565 // Write all blob to flatten files. 566 err = db.AppendAncient(block.NumberU64(), block.Hash().Bytes(), headerBlob, bodyBlob, receiptBlob, tdBlob) 567 if err != nil { 568 log.Crit("Failed to write block data to ancient store", "err", err) 569 } 570 return len(headerBlob) + len(bodyBlob) + len(receiptBlob) + len(tdBlob) + common.HashLength 571 } 572 573 // DeleteBlock removes all block data associated with a hash. 574 func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 575 DeleteReceipts(db, hash, number) 576 DeleteHeader(db, hash, number) 577 DeleteBody(db, hash, number) 578 DeleteTd(db, hash, number) 579 } 580 581 // DeleteBlockWithoutNumber removes all block data associated with a hash, except 582 // the hash to number mapping. 583 func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { 584 DeleteReceipts(db, hash, number) 585 deleteHeaderWithoutNumber(db, hash, number) 586 DeleteBody(db, hash, number) 587 DeleteTd(db, hash, number) 588 } 589 590 // FindCommonAncestor returns the last common ancestor of two block headers 591 func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header { 592 for bn := b.Number.Uint64(); a.Number.Uint64() > bn; { 593 a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1) 594 if a == nil { 595 return nil 596 } 597 } 598 for an := a.Number.Uint64(); an < b.Number.Uint64(); { 599 b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1) 600 if b == nil { 601 return nil 602 } 603 } 604 for a.Hash() != b.Hash() { 605 a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1) 606 if a == nil { 607 return nil 608 } 609 b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1) 610 if b == nil { 611 return nil 612 } 613 } 614 return a 615 }