nimbus-eth1/nimbus/p2p/chain.nim
Jordan Hrycaj 2d6bf34175
Re-adjust canonical head to parent of block to be inserted (#726)
* Re-adjust canonical head to parent of block to be inserted

why:
  of the failing tests that remain to be solved, 30 of those will succeed
  if the canonical database chain head is cleverly adjusted -- yes, it
  looks like a hack, indeed.

details:
  at the moment, this hack works for the non-hive tests only and is
  triggered by a boolean argument passed on to the chain.persistBlocks()
  method.

* Use parent instead of canonical head for block to be inserted

why:
  side chains need to be inserted typically somewhere before the
  canonical head.

details:
  the previous _hack_ was unnecessary and removed, it was inspired by
  some verification in persistBlocks() which explicitly referenced the
  canonical head (which now might or might not refer to the newly inserted
  header.)

* remove unnecessary code + comment
2021-06-22 17:52:31 +01:00

202 lines
5.9 KiB
Nim

import
../chain_config,
../db/db_chain,
../genesis,
../utils,
../vm_state,
./executor,
./validate,
./validate/epoch_hash_cache,
chronicles,
eth/[common, trie/db],
nimcrypto,
stew/endians2,
stint
when not defined(release):
import ../tracer
type
# Chain's forks not always equals to EVM's forks
ChainFork = enum
Frontier,
Homestead,
DAOFork,
Tangerine,
Spurious,
Byzantium,
Constantinople,
Petersburg,
Istanbul,
MuirGlacier,
Berlin
Chain* = ref object of AbstractChainDB
db: BaseChainDB
forkIds: array[ChainFork, ForkID]
blockZeroHash: KeccakHash
cacheByEpoch: EpochHashCache
extraValidation: bool
func toChainFork(c: ChainConfig, number: BlockNumber): ChainFork =
if number >= c.berlinBlock: Berlin
elif number >= c.muirGlacierBlock: MuirGlacier
elif number >= c.istanbulBlock: Istanbul
elif number >= c.petersburgBlock: Petersburg
elif number >= c.constantinopleBlock: Constantinople
elif number >= c.byzantiumBlock: Byzantium
elif number >= c.eip158Block: Spurious
elif number >= c.eip150Block: Tangerine
elif number >= c.daoForkBlock: DAOFork
elif number >= c.homesteadBlock: Homestead
else: Frontier
func toNextFork(n: BlockNumber): uint64 =
if n == high(BlockNumber):
result = 0'u64
else:
result = n.truncate(uint64)
func getNextFork(c: ChainConfig, fork: ChainFork): uint64 =
let next: array[ChainFork, uint64] = [
0'u64,
toNextFork(c.homesteadBlock),
toNextFork(c.daoForkBlock),
toNextFork(c.eip150Block),
toNextFork(c.eip158Block),
toNextFork(c.byzantiumBlock),
toNextFork(c.constantinopleBlock),
toNextFork(c.petersburgBlock),
toNextFork(c.istanbulBlock),
toNextFork(c.muirGlacierBlock),
toNextFork(c.berlinBlock),
]
if fork == high(ChainFork):
result = 0
return
result = next[fork]
for x in fork..high(ChainFork):
if result != next[x]:
result = next[x]
break
func calculateForkId(c: ChainConfig, fork: ChainFork, prevCRC: uint32, prevFork: uint64): ForkID =
result.nextFork = c.getNextFork(fork)
if result.nextFork != prevFork:
result.crc = crc32(prevCRC, toBytesBE(prevFork))
else:
result.crc = prevCRC
func calculateForkIds(c: ChainConfig, genesisCRC: uint32): array[ChainFork, ForkID] =
var prevCRC = genesisCRC
var prevFork = c.getNextFork(Frontier)
for fork in ChainFork:
result[fork] = calculateForkId(c, fork, prevCRC, prevFork)
prevFork = result[fork].nextFork
prevCRC = result[fork].crc
proc newChain*(db: BaseChainDB, extraValidation = false): Chain =
result.new
result.db = db
if not db.config.daoForkSupport:
db.config.daoForkBlock = db.config.homesteadBlock
let g = defaultGenesisBlockForNetwork(db.networkId)
result.blockZeroHash = g.toBlock.blockHash
let genesisCRC = crc32(0, result.blockZeroHash.data)
result.forkIds = calculateForkIds(db.config, genesisCRC)
result.extraValidation = extraValidation
if extraValidation:
result.cacheByEpoch.initEpochHashCache
method genesisHash*(c: Chain): KeccakHash {.gcsafe.} =
c.blockZeroHash
method getBlockHeader*(c: Chain, b: HashOrNum, output: var BlockHeader): bool {.gcsafe.} =
case b.isHash
of true:
c.db.getBlockHeader(b.hash, output)
else:
c.db.getBlockHeader(b.number, output)
method getBestBlockHeader*(c: Chain): BlockHeader {.gcsafe.} =
c.db.getCanonicalHead()
method getSuccessorHeader*(c: Chain, h: BlockHeader, output: var BlockHeader, skip = 0'u): bool {.gcsafe.} =
let offset = 1 + skip.toBlockNumber
if h.blockNumber <= (not 0.toBlockNumber) - offset:
result = c.db.getBlockHeader(h.blockNumber + offset, output)
method getAncestorHeader*(c: Chain, h: BlockHeader, output: var BlockHeader, skip = 0'u): bool {.gcsafe.} =
let offset = 1 + skip.toBlockNumber
if h.blockNumber >= offset:
result = c.db.getBlockHeader(h.blockNumber - offset, output)
method getBlockBody*(c: Chain, blockHash: KeccakHash): BlockBodyRef =
result = nil
method persistBlocks*(c: Chain; headers: openarray[BlockHeader];
bodies: openarray[BlockBody]): ValidationResult {.gcsafe.} =
# Run the VM here
if headers.len != bodies.len:
debug "Number of headers not matching number of bodies"
return ValidationResult.Error
c.db.highestBlock = headers[^1].blockNumber
let transaction = c.db.db.beginTransaction()
defer: transaction.dispose()
trace "Persisting blocks",
fromBlock = headers[0].blockNumber,
toBlock = headers[^1].blockNumber
for i in 0 ..< headers.len:
let
head = c.db.getBlockHeader(headers[i].parentHash)
vmState = newBaseVMState(head.stateRoot, headers[i], c.db)
validationResult = processBlock(c.db, headers[i], bodies[i], vmState)
when not defined(release):
if validationResult == ValidationResult.Error and
bodies[i].transactions.calcTxRoot == headers[i].txRoot:
dumpDebuggingMetaData(c.db, headers[i], bodies[i], vmState)
warn "Validation error. Debugging metadata dumped."
if validationResult != ValidationResult.OK:
return validationResult
if c.extraValidation:
let res = validateKinship(
c.db, headers[i],
bodies[i].uncles,
checkSealOK = false, # TODO: how to checkseal from here
c.cacheByEpoch
)
if res.isErr:
debug "kinship validation error", msg = res.error
return ValidationResult.Error
discard c.db.persistHeaderToDb(headers[i])
discard c.db.persistTransactions(headers[i].blockNumber, bodies[i].transactions)
discard c.db.persistReceipts(vmState.receipts)
# update currentBlock *after* we persist it
# so the rpc return consistent result
# between eth_blockNumber and eth_syncing
c.db.currentBlock = headers[i].blockNumber
transaction.commit()
method getTrieDB*(c: Chain): TrieDatabaseRef {.gcsafe.} =
c.db.db
method getForkId*(c: Chain, n: BlockNumber): ForkID {.gcsafe.} =
# EIP 2364/2124
let fork = c.db.config.toChainFork(n)
c.forkIds[fork]