nimbus-eth1/nimbus/db/db_chain.nim

502 lines
18 KiB
Nim
Raw Normal View History

# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
2022-12-02 04:39:12 +00:00
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
import
std/[sequtils, algorithm],
stew/[byteutils], eth/trie/[hexary, db],
eth/[common, rlp], chronicles,
2022-12-02 04:39:12 +00:00
".."/[errors, constants, utils/utils],
Redesign of BaseVMState descriptor (#923) * Redesign of BaseVMState descriptor why: BaseVMState provides an environment for executing transactions. The current descriptor also provides data that cannot generally be known within the execution environment, e.g. the total gasUsed which is available not before after all transactions have finished. Also, the BaseVMState constructor has been replaced by a constructor that does not need pre-initialised input of the account database. also: Previous constructor and some fields are provided with a deprecated annotation (producing a lot of noise.) * Replace legacy directives in production sources * Replace legacy directives in unit test sources * fix CI (missing premix update) * Remove legacy directives * chase CI problem * rebased * Re-introduce 'AccountsCache' constructor optimisation for 'BaseVmState' re-initialisation why: Constructing a new 'AccountsCache' descriptor can be avoided sometimes when the current state root is properly positioned already. Such a feature existed already as the update function 'initStateDB()' for the 'BaseChanDB' where the accounts cache was linked into this desctiptor. The function 'initStateDB()' was removed and re-implemented into the 'BaseVmState' constructor without optimisation. The old version was of restricted use as a wrong accounts cache state would unconditionally throw an exception rather than conceptually ask for a remedy. The optimised 'BaseVmState' re-initialisation has been implemented for the 'persistBlocks()' function. also: moved some test helpers to 'test/replay' folder * Remove unused & undocumented fields from Chain descriptor why: Reduces attack surface in general & improves reading the code.
2022-01-18 16:19:32 +00:00
"."/storage_types
2022-12-02 04:39:12 +00:00
export
db,
common,
errors,
constants
type
2022-12-02 04:39:12 +00:00
ChainDBRef* = distinct TrieDatabaseRef
2018-06-20 12:33:57 +00:00
TransactionKey = tuple
blockNumber: BlockNumber
index: int
2022-12-02 04:39:12 +00:00
# ------------------------------------------------------------------------------
# Public constructor/destructor
# ------------------------------------------------------------------------------
proc new*(_: type ChainDBRef, db: TrieDatabaseRef): ChainDBRef =
result = ChainDBRef(db)
# ------------------------------------------------------------------------------
# Public functions, Getters
# ------------------------------------------------------------------------------
# A pseudo getter mainly to allow smooth transition
# from old db object to new db object
template db*(db: ChainDBRef): TrieDatabaseRef =
TrieDatabaseRef(db)
2022-02-15 03:24:38 +00:00
2022-12-02 04:39:12 +00:00
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
2022-12-02 04:39:12 +00:00
proc exists*(db: ChainDBRef, hash: Hash256): bool =
db.db.contains(hash.data)
2019-09-09 05:27:17 +00:00
2022-12-02 04:39:12 +00:00
proc getBlockHeader*(db: ChainDBRef; blockHash: Hash256, output: var BlockHeader): bool =
let data = db.db.get(genericHashKey(blockHash).toOpenArray)
if data.len != 0:
try:
output = rlp.decode(data, BlockHeader)
true
except RlpError:
false
else:
false
2022-12-02 04:39:12 +00:00
proc getBlockHeader*(db: ChainDBRef, blockHash: Hash256): BlockHeader =
## Returns the requested block header as specified by block hash.
##
## Raises BlockNotFound if it is not present in the db.
2022-12-02 04:39:12 +00:00
if not db.getBlockHeader(blockHash, result):
2018-05-30 16:11:15 +00:00
raise newException(BlockNotFound, "No block with hash " & blockHash.data.toHex)
2022-12-02 04:39:12 +00:00
proc getHash(db: ChainDBRef, key: DbKey, output: var Hash256): bool {.inline.} =
let data = db.db.get(key.toOpenArray)
if data.len != 0:
output = rlp.decode(data, Hash256)
result = true
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
proc getCanonicalHead*(db: ChainDBRef): BlockHeader =
var headHash: Hash256
2022-12-02 04:39:12 +00:00
if not db.getHash(canonicalHeadHashKey(), headHash) or
not db.getBlockHeader(headHash, result):
2018-06-20 12:33:57 +00:00
raise newException(CanonicalHeadNotFound,
"No canonical head set for this chain")
2022-12-02 04:39:12 +00:00
proc getCanonicalHeaderHash*(db: ChainDBRef): Hash256 =
discard db.getHash(canonicalHeadHashKey(), result)
2022-12-02 04:39:12 +00:00
proc getBlockHash*(db: ChainDBRef, n: BlockNumber, output: var Hash256): bool {.inline.} =
## Return the block hash for the given block number.
2022-12-02 04:39:12 +00:00
db.getHash(blockNumberToHashKey(n), output)
2018-05-30 16:11:15 +00:00
2022-12-02 04:39:12 +00:00
proc getBlockHash*(db: ChainDBRef, n: BlockNumber): Hash256 {.inline.} =
## Return the block hash for the given block number.
2022-12-02 04:39:12 +00:00
if not db.getHash(blockNumberToHashKey(n), result):
raise newException(BlockNotFound, "No block hash for number " & $n)
2022-12-02 04:39:12 +00:00
proc getHeadBlockHash*(db: ChainDBRef): Hash256 =
if not db.getHash(canonicalHeadHashKey(), result):
result = Hash256()
2022-12-02 04:39:12 +00:00
proc getBlockHeader*(db: ChainDBRef; n: BlockNumber, output: var BlockHeader): bool =
## Returns the block header with the given number in the canonical chain.
var blockHash: Hash256
2022-12-02 04:39:12 +00:00
if db.getBlockHash(n, blockHash):
result = db.getBlockHeader(blockHash, output)
2022-12-02 04:39:12 +00:00
proc getBlockHeaderWithHash*(db: ChainDBRef; n: BlockNumber): Option[(BlockHeader, Hash256)] =
## Returns the block header and its hash, with the given number in the canonical chain.
## Hash is returned to avoid recomputing it
var hash: Hash256
2022-12-02 04:39:12 +00:00
if db.getBlockHash(n, hash):
# Note: this will throw if header is not present.
var header: BlockHeader
2022-12-02 04:39:12 +00:00
if db.getBlockHeader(hash, header):
return some((header, hash))
else:
# this should not happen, but if it happen lets fail laudly as this means
# something is super wrong
raiseAssert("Corrupted database. Mapping number->hash present, without header in database")
else:
return none[(BlockHeader, Hash256)]()
2022-12-02 04:39:12 +00:00
proc getBlockHeader*(db: ChainDBRef; n: BlockNumber): BlockHeader =
## Returns the block header with the given number in the canonical chain.
## Raises BlockNotFound error if the block is not in the DB.
2022-12-02 04:39:12 +00:00
db.getBlockHeader(db.getBlockHash(n))
2022-12-02 04:39:12 +00:00
proc getScore*(db: ChainDBRef; blockHash: Hash256): UInt256 =
rlp.decode(db.db.get(blockHashToScoreKey(blockHash).toOpenArray), UInt256)
2022-12-02 04:39:12 +00:00
proc setScore*(db: ChainDBRef; blockHash: Hash256, score: UInt256) =
## for testing purpose
2022-12-02 04:39:12 +00:00
db.db.put(blockHashToScoreKey(blockHash).toOpenArray, rlp.encode(score))
2022-12-02 04:39:12 +00:00
proc getTd*(db: ChainDBRef; blockHash: Hash256, td: var UInt256): bool =
let bytes = db.db.get(blockHashToScoreKey(blockHash).toOpenArray)
if bytes.len == 0: return false
try:
2022-04-08 04:54:11 +00:00
td = rlp.decode(bytes, UInt256)
except RlpError:
return false
return true
2022-12-02 04:39:12 +00:00
proc headTotalDifficulty*(db: ChainDBRef): UInt256 =
# this is actually a combination of `getHash` and `getScore`
const key = canonicalHeadHashKey()
2022-12-02 04:39:12 +00:00
let data = db.db.get(key.toOpenArray)
if data.len == 0:
return 0.u256
let blockHash = rlp.decode(data, Hash256)
2022-12-02 04:39:12 +00:00
rlp.decode(db.db.get(blockHashToScoreKey(blockHash).toOpenArray), UInt256)
2022-12-02 04:39:12 +00:00
proc getAncestorsHashes*(db: ChainDBRef, limit: UInt256, header: BlockHeader): seq[Hash256] =
2019-09-09 05:27:17 +00:00
var ancestorCount = min(header.blockNumber, limit).truncate(int)
var h = header
result = newSeq[Hash256](ancestorCount)
while ancestorCount > 0:
2022-12-02 04:39:12 +00:00
h = db.getBlockHeader(h.parentHash)
2019-09-09 05:27:17 +00:00
result[ancestorCount - 1] = h.hash
dec ancestorCount
2022-12-02 04:39:12 +00:00
iterator findNewAncestors(db: ChainDBRef; header: BlockHeader): BlockHeader =
2018-05-30 16:11:15 +00:00
## Returns the chain leading up from the given header until the first ancestor it has in
## common with our canonical chain.
var h = header
var orig: BlockHeader
2018-05-30 16:11:15 +00:00
while true:
2022-12-02 04:39:12 +00:00
if db.getBlockHeader(h.blockNumber, orig) and orig.hash == h.hash:
break
2018-06-20 12:33:57 +00:00
2018-05-30 16:11:15 +00:00
yield h
2018-06-20 12:33:57 +00:00
2018-05-30 16:11:15 +00:00
if h.parentHash == GENESIS_PARENT_HASH:
break
2018-06-20 12:33:57 +00:00
else:
2022-12-02 04:39:12 +00:00
h = db.getBlockHeader(h.parentHash)
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
proc addBlockNumberToHashLookup*(db: ChainDBRef; header: BlockHeader) =
db.db.put(blockNumberToHashKey(header.blockNumber).toOpenArray,
rlp.encode(header.hash))
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
proc persistTransactions*(db: ChainDBRef, blockNumber:
BlockNumber, transactions: openArray[Transaction]): Hash256 =
2022-12-02 04:39:12 +00:00
var trie = initHexaryTrie(db.db)
for idx, tx in transactions:
let
encodedTx = rlp.encode(tx)
txHash = keccakHash(encodedTx)
txKey: TransactionKey = (blockNumber, idx)
trie.put(rlp.encode(idx), encodedTx)
2022-12-02 04:39:12 +00:00
db.db.put(transactionHashToBlockKey(txHash).toOpenArray, rlp.encode(txKey))
2020-07-30 07:21:11 +00:00
trie.rootHash
2022-12-02 04:39:12 +00:00
proc getTransaction*(db: ChainDBRef, txRoot: Hash256, txIndex: int, res: var Transaction): bool =
var db = initHexaryTrie(db.db, txRoot)
2020-07-30 07:21:11 +00:00
let txData = db.get(rlp.encode(txIndex))
if txData.len > 0:
res = rlp.decode(txData, Transaction)
result = true
2022-12-02 04:39:12 +00:00
iterator getBlockTransactionData*(db: ChainDBRef, transactionRoot: Hash256): seq[byte] =
var transactionDb = initHexaryTrie(db.db, transactionRoot)
var transactionIdx = 0
while true:
let transactionKey = rlp.encode(transactionIdx)
if transactionKey in transactionDb:
yield transactionDb.get(transactionKey)
else:
break
inc transactionIdx
2022-12-02 04:39:12 +00:00
iterator getBlockTransactions*(db: ChainDBRef, header: BlockHeader): Transaction =
for encodedTx in db.getBlockTransactionData(header.txRoot):
2020-07-30 07:21:11 +00:00
yield rlp.decode(encodedTx, Transaction)
2022-12-02 04:39:12 +00:00
iterator getBlockTransactionHashes*(db: ChainDBRef, blockHeader: BlockHeader): Hash256 =
2018-06-20 12:33:57 +00:00
## Returns an iterable of the transaction hashes from th block specified
## by the given block header.
2022-12-02 04:39:12 +00:00
for encodedTx in db.getBlockTransactionData(blockHeader.txRoot):
yield keccakHash(encodedTx)
2022-12-02 04:39:12 +00:00
proc getTransactionCount*(chain: ChainDBRef, txRoot: Hash256): int =
var trie = initHexaryTrie(chain.db, txRoot)
var txCount = 0
while true:
let txKey = rlp.encode(txCount)
if txKey notin trie:
break
inc txCount
txCount
2022-12-02 04:39:12 +00:00
proc getUnclesCount*(db: ChainDBRef, ommersHash: Hash256): int =
if ommersHash != EMPTY_UNCLE_HASH:
2022-12-02 04:39:12 +00:00
let encodedUncles = db.db.get(genericHashKey(ommersHash).toOpenArray)
if encodedUncles.len != 0:
let r = rlpFromBytes(encodedUncles)
result = r.listLen
2022-12-02 04:39:12 +00:00
proc getUncles*(db: ChainDBRef, ommersHash: Hash256): seq[BlockHeader] =
2020-07-30 07:21:11 +00:00
if ommersHash != EMPTY_UNCLE_HASH:
2022-12-02 04:39:12 +00:00
let encodedUncles = db.db.get(genericHashKey(ommersHash).toOpenArray)
2020-07-30 07:21:11 +00:00
if encodedUncles.len != 0:
result = rlp.decode(encodedUncles, seq[BlockHeader])
2022-12-02 04:39:12 +00:00
proc getBlockBody*(db: ChainDBRef, header: BlockHeader, output: var BlockBody): bool =
result = true
output.transactions = @[]
output.uncles = @[]
2022-12-02 04:39:12 +00:00
for encodedTx in db.getBlockTransactionData(header.txRoot):
output.transactions.add(rlp.decode(encodedTx, Transaction))
if header.ommersHash != EMPTY_UNCLE_HASH:
2022-12-02 04:39:12 +00:00
let encodedUncles = db.db.get(genericHashKey(header.ommersHash).toOpenArray)
if encodedUncles.len != 0:
output.uncles = rlp.decode(encodedUncles, seq[BlockHeader])
else:
result = false
2022-12-02 04:39:12 +00:00
proc getBlockBody*(db: ChainDBRef, blockHash: Hash256, output: var BlockBody): bool =
var header: BlockHeader
2022-12-02 04:39:12 +00:00
if db.getBlockHeader(blockHash, header):
return db.getBlockBody(header, output)
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
proc getBlockBody*(db: ChainDBRef, hash: Hash256): BlockBody =
if not db.getBlockBody(hash, result):
2018-12-14 07:32:45 +00:00
raise newException(ValueError, "Error when retrieving block body")
2022-12-02 04:39:12 +00:00
proc getUncleHashes*(db: ChainDBRef, blockHashes: openArray[Hash256]): seq[Hash256] =
2019-09-09 05:27:17 +00:00
for blockHash in blockHashes:
2022-12-02 04:39:12 +00:00
var blockBody = db.getBlockBody(blockHash)
2019-09-09 05:27:17 +00:00
for uncle in blockBody.uncles:
result.add uncle.hash
2022-12-02 04:39:12 +00:00
proc getUncleHashes*(db: ChainDBRef, header: BlockHeader): seq[Hash256] =
2020-07-30 07:21:11 +00:00
if header.ommersHash != EMPTY_UNCLE_HASH:
2022-12-02 04:39:12 +00:00
let encodedUncles = db.db.get(genericHashKey(header.ommersHash).toOpenArray)
2020-07-30 07:21:11 +00:00
if encodedUncles.len != 0:
let uncles = rlp.decode(encodedUncles, seq[BlockHeader])
for x in uncles:
result.add x.hash
2022-12-02 04:39:12 +00:00
proc getTransactionKey*(db: ChainDBRef, transactionHash: Hash256): tuple[blockNumber: BlockNumber, index: int] {.inline.} =
let tx = db.db.get(transactionHashToBlockKey(transactionHash).toOpenArray)
2020-07-30 07:21:11 +00:00
if tx.len > 0:
let key = rlp.decode(tx, TransactionKey)
result = (key.blockNumber, key.index)
else:
result = (0.toBlockNumber, -1)
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
proc removeTransactionFromCanonicalChain(db: ChainDBRef, transactionHash: Hash256) {.inline.} =
2018-06-20 12:33:57 +00:00
## Removes the transaction specified by the given hash from the canonical chain.
2022-12-02 04:39:12 +00:00
db.db.del(transactionHashToBlockKey(transactionHash).toOpenArray)
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
proc setAsCanonicalChainHead(db: ChainDBRef; headerHash: Hash256): seq[BlockHeader] =
2018-06-20 12:33:57 +00:00
## Sets the header as the canonical chain HEAD.
2022-12-02 04:39:12 +00:00
let header = db.getBlockHeader(headerHash)
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
var newCanonicalHeaders = sequtils.toSeq(db.findNewAncestors(header))
2018-06-20 12:33:57 +00:00
reverse(newCanonicalHeaders)
for h in newCanonicalHeaders:
var oldHash: Hash256
2022-12-02 04:39:12 +00:00
if not db.getBlockHash(h.blockNumber, oldHash):
2018-06-20 12:33:57 +00:00
break
2022-12-02 04:39:12 +00:00
let oldHeader = db.getBlockHeader(oldHash)
for txHash in db.getBlockTransactionHashes(oldHeader):
db.removeTransactionFromCanonicalChain(txHash)
2018-06-20 12:33:57 +00:00
# TODO re-add txn to internal pending pool (only if local sender)
for h in newCanonicalHeaders:
2022-12-02 04:39:12 +00:00
db.addBlockNumberToHashLookup(h)
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
db.db.put(canonicalHeadHashKey().toOpenArray, rlp.encode(headerHash))
2018-06-20 12:33:57 +00:00
return newCanonicalHeaders
2018-05-30 16:11:15 +00:00
2022-12-02 04:39:12 +00:00
proc headerExists*(db: ChainDBRef; blockHash: Hash256): bool =
## Returns True if the header with the given block hash is in our DB.
2022-12-02 04:39:12 +00:00
db.db.contains(genericHashKey(blockHash).toOpenArray)
2022-12-02 04:39:12 +00:00
proc markCanonicalChain(db: ChainDBRef, header: BlockHeader, headerHash: Hash256): bool =
## mark this chain as canonical by adding block number to hash lookup
## down to forking point
var
currHash = headerHash
currHeader = header
2022-07-04 05:35:56 +00:00
# mark current header as canonical
let key = blockNumberToHashKey(currHeader.blockNumber)
2022-12-02 04:39:12 +00:00
db.db.put(key.toOpenArray, rlp.encode(currHash))
2022-07-04 05:35:56 +00:00
# it is a genesis block, done
if currHeader.parentHash == Hash256():
return true
# mark ancestor blocks as canonical too
currHash = currHeader.parentHash
2022-12-02 04:39:12 +00:00
if not db.getBlockHeader(currHeader.parentHash, currHeader):
2022-07-04 05:35:56 +00:00
return false
while currHash != Hash256():
let key = blockNumberToHashKey(currHeader.blockNumber)
2022-12-02 04:39:12 +00:00
let data = db.db.get(key.toOpenArray)
if data.len == 0:
# not marked, mark it
2022-12-02 04:39:12 +00:00
db.db.put(key.toOpenArray, rlp.encode(currHash))
elif rlp.decode(data, Hash256) != currHash:
# replace prev chain
2022-12-02 04:39:12 +00:00
db.db.put(key.toOpenArray, rlp.encode(currHash))
else:
# forking point, done
break
2022-07-04 05:35:56 +00:00
if currHeader.parentHash == Hash256():
break
currHash = currHeader.parentHash
2022-12-02 04:39:12 +00:00
if not db.getBlockHeader(currHeader.parentHash, currHeader):
2022-07-04 05:35:56 +00:00
return false
return true
2022-12-02 04:39:12 +00:00
proc setHead*(db: ChainDBRef, blockHash: Hash256): bool =
var header: BlockHeader
2022-12-02 04:39:12 +00:00
if not db.getBlockHeader(blockHash, header):
return false
2022-12-02 04:39:12 +00:00
if not db.markCanonicalChain(header, blockHash):
2022-07-04 05:35:56 +00:00
return false
2022-12-02 04:39:12 +00:00
db.db.put(canonicalHeadHashKey().toOpenArray, rlp.encode(blockHash))
return true
2022-12-02 04:39:12 +00:00
proc setHead*(db: ChainDBRef, header: BlockHeader, writeHeader = false): bool =
2019-01-01 03:55:40 +00:00
var headerHash = rlpHash(header)
if writeHeader:
2022-12-02 04:39:12 +00:00
db.db.put(genericHashKey(headerHash).toOpenArray, rlp.encode(header))
if not db.markCanonicalChain(header, headerHash):
2022-07-04 05:35:56 +00:00
return false
2022-12-02 04:39:12 +00:00
db.db.put(canonicalHeadHashKey().toOpenArray, rlp.encode(headerHash))
2022-07-04 05:35:56 +00:00
return true
2019-01-01 03:55:40 +00:00
2022-12-02 04:39:12 +00:00
proc persistReceipts*(db: ChainDBRef, receipts: openArray[Receipt]): Hash256 =
var trie = initHexaryTrie(db.db)
2018-12-10 12:04:34 +00:00
for idx, rec in receipts:
trie.put(rlp.encode(idx), rlp.encode(rec))
trie.rootHash
2018-12-10 12:04:34 +00:00
2022-12-02 04:39:12 +00:00
iterator getReceipts*(db: ChainDBRef; receiptRoot: Hash256): Receipt =
var receiptDb = initHexaryTrie(db.db, receiptRoot)
var receiptIdx = 0
while true:
let receiptKey = rlp.encode(receiptIdx)
if receiptKey in receiptDb:
let receiptData = receiptDb.get(receiptKey)
2018-12-10 12:04:34 +00:00
yield rlp.decode(receiptData, Receipt)
else:
break
inc receiptIdx
2022-12-02 04:39:12 +00:00
proc getReceipts*(db: ChainDBRef; receiptRoot: Hash256): seq[Receipt] =
var receipts = newSeq[Receipt]()
2022-12-02 04:39:12 +00:00
for r in db.getReceipts(receiptRoot):
receipts.add(r)
return receipts
2022-12-02 04:39:12 +00:00
proc readTerminalHash*(db: ChainDBRef; h: var Hash256): bool =
let bytes = db.db.get(terminalHashKey().toOpenArray)
if bytes.len == 0:
return false
try:
h = rlp.decode(bytes, Hash256)
except RlpError:
return false
true
2022-12-02 04:39:12 +00:00
proc writeTerminalHash*(db: ChainDBRef; h: Hash256) =
db.db.put(terminalHashKey().toOpenArray, rlp.encode(h))
2022-12-02 04:39:12 +00:00
proc currentTerminalHeader*(db: ChainDBRef; header: var BlockHeader): bool =
var terminalHash: Hash256
2022-12-02 04:39:12 +00:00
if not db.readTerminalHash(terminalHash):
return false
2022-12-02 04:39:12 +00:00
if not db.getBlockHeader(terminalHash, header):
return false
true
2022-12-02 04:39:12 +00:00
proc persistHeaderToDb*(db: ChainDBRef; header: BlockHeader, ttd: Option[DifficultyInt]): seq[BlockHeader] =
2018-06-20 12:33:57 +00:00
let isGenesis = header.parentHash == GENESIS_PARENT_HASH
let headerHash = header.blockHash
2022-12-02 04:39:12 +00:00
if not isGenesis and not db.headerExists(header.parentHash):
2018-06-20 12:33:57 +00:00
raise newException(ParentNotFound, "Cannot persist block header " &
$headerHash & " with unknown parent " & $header.parentHash)
2022-12-02 04:39:12 +00:00
db.db.put(genericHashKey(headerHash).toOpenArray, rlp.encode(header))
2018-06-20 12:33:57 +00:00
let score = if isGenesis: header.difficulty
2022-12-02 04:39:12 +00:00
else: db.getScore(header.parentHash) + header.difficulty
db.db.put(blockHashToScoreKey(headerHash).toOpenArray, rlp.encode(score))
2022-12-02 04:39:12 +00:00
db.addBlockNumberToHashLookup(header)
2018-08-01 12:50:44 +00:00
2022-04-08 04:54:11 +00:00
var headScore: UInt256
2018-06-20 12:33:57 +00:00
try:
2022-12-02 04:39:12 +00:00
headScore = db.getScore(db.getCanonicalHead().hash)
2018-06-20 12:33:57 +00:00
except CanonicalHeadNotFound:
2022-12-02 04:39:12 +00:00
return db.setAsCanonicalChainHead(headerHash)
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
if ttd.isSome:
let ttd = ttd.get()
if headScore < ttd and score >= ttd:
db.writeTerminalHash(headerHash)
if score >= ttd:
return db.setAsCanonicalChainHead(headerHash)
2022-12-02 04:39:12 +00:00
if score > headScore:
return db.setAsCanonicalChainHead(headerHash)
2018-06-20 12:33:57 +00:00
2022-12-02 04:39:12 +00:00
proc persistHeaderToDbWithoutSetHead*(db: ChainDBRef; header: BlockHeader) =
let isGenesis = header.parentHash == GENESIS_PARENT_HASH
let headerHash = header.blockHash
let score = if isGenesis: header.difficulty
2022-12-02 04:39:12 +00:00
else: db.getScore(header.parentHash) + header.difficulty
2022-12-02 04:39:12 +00:00
db.db.put(blockHashToScoreKey(headerHash).toOpenArray, rlp.encode(score))
db.db.put(genericHashKey(headerHash).toOpenArray, rlp.encode(header))
2022-12-02 04:39:12 +00:00
proc persistUncles*(db: ChainDBRef, uncles: openArray[BlockHeader]): Hash256 =
2018-06-20 12:33:57 +00:00
## Persists the list of uncles to the database.
## Returns the uncles hash.
let enc = rlp.encode(uncles)
2019-03-07 15:53:09 +00:00
result = keccakHash(enc)
2022-12-02 04:39:12 +00:00
db.db.put(genericHashKey(result).toOpenArray, enc)
2022-12-02 04:39:12 +00:00
proc safeHeaderHash*(db: ChainDBRef): Hash256 =
discard db.getHash(safeHashKey(), result)
2022-12-02 04:39:12 +00:00
proc safeHeaderHash*(db: ChainDBRef, headerHash: Hash256) =
db.db.put(safeHashKey().toOpenArray, rlp.encode(headerHash))
2022-12-02 04:39:12 +00:00
proc finalizedHeaderHash*(db: ChainDBRef): Hash256 =
discard db.getHash(finalizedHashKey(), result)
2022-12-02 04:39:12 +00:00
proc finalizedHeaderHash*(db: ChainDBRef, headerHash: Hash256) =
db.db.put(finalizedHashKey().toOpenArray, rlp.encode(headerHash))
2022-12-02 04:39:12 +00:00
proc safeHeader*(db: ChainDBRef): BlockHeader =
db.getBlockHeader(db.safeHeaderHash)
2022-12-02 04:39:12 +00:00
proc finalizedHeader*(db: ChainDBRef): BlockHeader =
db.getBlockHeader(db.finalizedHeaderHash)
2022-12-02 04:39:12 +00:00
proc haveBlockAndState*(db: ChainDBRef, headerHash: Hash256): bool =
var header: BlockHeader
2022-12-02 04:39:12 +00:00
if not db.getBlockHeader(headerHash, header):
return false
# see if stateRoot exists
2022-12-02 04:39:12 +00:00
db.exists(header.stateRoot)