mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-05 08:45:27 +00:00
2d6bf34175
* 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
202 lines
5.9 KiB
Nim
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]
|