mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-06 01:05:32 +00:00
048a43b2f1
- added some useful `chronicles` defines in the top-level nim.cfg - converted some assert() calls to error codes to avoid a reported segfault with -d:release - the recoverable errors are being recovered from, in persistWorkItem() - the only case that's a show stopper raises an exception
158 lines
6.1 KiB
Nim
158 lines
6.1 KiB
Nim
import ../db/[db_chain, state_db], eth_common, chronicles, ../vm_state, ../vm_types, ../transaction, ranges,
|
|
../vm/[computation, interpreter_dispatch, message, interpreter/vm_forks], ../constants, stint, nimcrypto,
|
|
../vm_state_transactions, sugar, ../utils, eth_trie/db, ../tracer, ./executor, json,
|
|
eth_bloom, strutils
|
|
|
|
type
|
|
# TODO: these types need to be removed
|
|
# once eth_bloom and eth_common sync'ed
|
|
Bloom = eth_common.BloomFilter
|
|
LogsBloom = eth_bloom.BloomFilter
|
|
|
|
# TODO: move these three receipt procs below somewhere else more appropriate
|
|
func logsBloom(logs: openArray[Log]): LogsBloom =
|
|
for log in logs:
|
|
result.incl log.address
|
|
for topic in log.topics:
|
|
result.incl topic
|
|
|
|
func createBloom*(receipts: openArray[Receipt]): Bloom =
|
|
var bloom: LogsBloom
|
|
for receipt in receipts:
|
|
bloom.value = bloom.value or logsBloom(receipt.logs).value
|
|
result = bloom.value.toByteArrayBE
|
|
|
|
proc makeReceipt(vmState: BaseVMState, stateRoot: Hash256, cumulativeGasUsed: GasInt, fork = FkFrontier): Receipt =
|
|
if fork < FkByzantium:
|
|
# TODO: which one: vmState.blockHeader.stateRoot or stateDb.rootHash?
|
|
# currently, vmState.blockHeader.stateRoot vs stateDb.rootHash can be different
|
|
# need to wait #188 solved
|
|
result.stateRootOrStatus = hashOrStatus(stateRoot)
|
|
else:
|
|
# TODO: post byzantium fork use status instead of rootHash
|
|
let vmStatus = true # success or failure
|
|
result.stateRootOrStatus = hashOrStatus(vmStatus)
|
|
|
|
result.cumulativeGasUsed = cumulativeGasUsed
|
|
result.logs = vmState.getAndClearLogEntries()
|
|
result.bloom = logsBloom(result.logs).value.toByteArrayBE
|
|
|
|
type
|
|
Chain* = ref object of AbstractChainDB
|
|
db: BaseChainDB
|
|
|
|
proc newChain*(db: BaseChainDB): Chain =
|
|
result.new
|
|
result.db = db
|
|
|
|
method genesisHash*(c: Chain): KeccakHash =
|
|
c.db.getBlockHash(0.toBlockNumber)
|
|
|
|
method getBlockHeader*(c: Chain, b: HashOrNum, output: var BlockHeader): bool =
|
|
case b.isHash
|
|
of true:
|
|
c.db.getBlockHeader(b.hash, output)
|
|
else:
|
|
c.db.getBlockHeader(b.number, output)
|
|
|
|
method getBestBlockHeader*(c: Chain): BlockHeader =
|
|
c.db.getCanonicalHead()
|
|
|
|
method getSuccessorHeader*(c: Chain, h: BlockHeader, output: var BlockHeader): bool =
|
|
let n = h.blockNumber + 1
|
|
c.db.getBlockHeader(n, output)
|
|
|
|
method getBlockBody*(c: Chain, blockHash: KeccakHash): BlockBodyRef =
|
|
result = nil
|
|
|
|
method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarray[BlockBody]): ValidationResult =
|
|
# Run the VM here
|
|
if headers.len != bodies.len:
|
|
debug "Number of headers not matching number of bodies"
|
|
return ValidationResult.Error
|
|
|
|
let blockReward = 5.u256 * pow(10.u256, 18) # 5 ETH
|
|
|
|
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.getCanonicalHead()
|
|
var stateDb = newAccountStateDB(c.db.db, head.stateRoot, c.db.pruneTrie)
|
|
var receipts = newSeq[Receipt](bodies[i].transactions.len)
|
|
|
|
if bodies[i].transactions.calcTxRoot != headers[i].txRoot:
|
|
debug "Mismatched txRoot", i
|
|
return ValidationResult.Error
|
|
|
|
if headers[i].txRoot != BLANK_ROOT_HASH:
|
|
let vmState = newBaseVMState(head, c.db)
|
|
if bodies[i].transactions.len == 0:
|
|
debug "No transactions in body", i
|
|
return ValidationResult.Error
|
|
else:
|
|
trace "Has transactions", blockNumber = headers[i].blockNumber, blockHash = headers[i].blockHash
|
|
|
|
var cumulativeGasUsed = GasInt(0)
|
|
for txIndex, tx in bodies[i].transactions:
|
|
var sender: EthAddress
|
|
if tx.getSender(sender):
|
|
let txFee = processTransaction(stateDb, tx, sender, vmState)
|
|
|
|
# perhaps this can be altered somehow
|
|
# or processTransaction return only gasUsed
|
|
# a `div` here is ugly and possibly div by zero
|
|
let gasUsed = (txFee div tx.gasPrice.u256).truncate(GasInt)
|
|
cumulativeGasUsed += gasUsed
|
|
|
|
# miner fee
|
|
stateDb.addBalance(headers[i].coinbase, txFee)
|
|
else:
|
|
debug "Could not get sender", i, tx
|
|
return ValidationResult.Error
|
|
receipts[txIndex] = makeReceipt(vmState, stateDb.rootHash, cumulativeGasUsed)
|
|
|
|
var mainReward = blockReward
|
|
if headers[i].ommersHash != EMPTY_UNCLE_HASH:
|
|
let h = c.db.persistUncles(bodies[i].uncles)
|
|
if h != headers[i].ommersHash:
|
|
debug "Uncle hash mismatch"
|
|
return ValidationResult.Error
|
|
for u in 0 ..< bodies[i].uncles.len:
|
|
var uncleReward = bodies[i].uncles[u].blockNumber + 8.u256
|
|
uncleReward -= headers[i].blockNumber
|
|
uncleReward = uncleReward * blockReward
|
|
uncleReward = uncleReward div 8.u256
|
|
stateDb.addBalance(bodies[i].uncles[u].coinbase, uncleReward)
|
|
mainReward += blockReward div 32.u256
|
|
|
|
# Reward beneficiary
|
|
stateDb.addBalance(headers[i].coinbase, mainReward)
|
|
|
|
if headers[i].stateRoot != stateDb.rootHash:
|
|
error "Wrong state root in block", blockNumber = headers[i].blockNumber, expected = headers[i].stateRoot, actual = stateDb.rootHash, arrivedFrom = c.db.getCanonicalHead().stateRoot
|
|
# this one is a show stopper until we are confident in our VM's
|
|
# compatibility with the main chain
|
|
raise(newException(Exception, "Wrong state root in block"))
|
|
|
|
let bloom = createBloom(receipts)
|
|
if headers[i].bloom != bloom:
|
|
debug "wrong bloom in block", blockNumber = headers[i].blockNumber
|
|
assert(headers[i].bloom == bloom)
|
|
|
|
let receiptRoot = calcReceiptRoot(receipts)
|
|
if headers[i].receiptRoot != receiptRoot:
|
|
debug "wrong receiptRoot in block", blockNumber = headers[i].blockNumber, actual=receiptRoot, expected=headers[i].receiptRoot
|
|
assert(headers[i].receiptRoot == receiptRoot)
|
|
|
|
discard c.db.persistHeaderToDb(headers[i])
|
|
if c.db.getCanonicalHead().blockHash != headers[i].blockHash:
|
|
debug "Stored block header hash doesn't match declared hash"
|
|
return ValidationResult.Error
|
|
|
|
c.db.persistTransactions(headers[i].blockNumber, bodies[i].transactions)
|
|
c.db.persistReceipts(receipts)
|
|
|
|
transaction.commit()
|