2018-12-31 03:27:02 +00:00
import options ,
2019-02-05 19:15:50 +00:00
eth / [ common , bloom ] , ranges , chronicles , nimcrypto ,
2018-12-14 07:32:45 +00:00
.. / db / [ db_chain , state_db ] ,
.. / utils , .. / constants , .. / transaction ,
.. / vm_state , .. / vm_types , .. / vm_state_transactions ,
2018-12-25 05:58:55 +00:00
.. / vm / [ computation , interpreter_dispatch , message ] ,
.. / vm / interpreter / vm_forks
2018-12-04 11:42:55 +00:00
2019-03-07 07:10:05 +00:00
proc processTransaction * ( tx : Transaction , sender : EthAddress , vmState : BaseVMState , forkOverride = none ( Fork ) ) : GasInt =
2018-12-04 11:42:55 +00:00
## Process the transaction, write the results to db.
## Returns amount of ETH to be rewarded to miner
2018-12-06 23:16:34 +00:00
trace " Sender " , sender
2019-02-27 06:11:31 +00:00
trace " txHash " , rlpHash = tx . rlpHash
2019-03-12 01:07:29 +00:00
# TODO: we have identical `fork` code in setupComputation.
# at later stage, we need to get rid of it
# and apply changes in eth_*, debug_* RPC,
# macro assembler and premix tool set.
# at every place where setupComputation and
# processTransaction are used.
2019-03-11 15:32:25 +00:00
let fork =
if forkOverride . isSome :
forkOverride . get
else :
vmState . blockNumber . toFork
2019-02-27 06:11:31 +00:00
let upfrontGasCost = tx . gasLimit . u256 * tx . gasPrice . u256
2019-03-07 07:10:05 +00:00
var balance = vmState . readOnlyStateDb ( ) . getBalance ( sender )
if balance < upfrontGasCost :
return tx . gasLimit
2018-12-04 11:42:55 +00:00
2019-03-18 05:48:32 +00:00
let recipient = tx . getRecipient ( )
let isCollision = vmState . readOnlyStateDb ( ) . hasCodeOrNonce ( recipient )
var computation = setupComputation ( vmState , tx , sender , recipient , forkOverride )
2019-03-16 15:23:15 +00:00
if computation . isNil :
return 0
2019-03-18 05:48:32 +00:00
2019-03-07 07:10:05 +00:00
vmState . mutateStateDB :
db . incNonce ( sender )
db . subBalance ( sender , upfrontGasCost )
2018-12-04 11:42:55 +00:00
2019-03-18 05:48:32 +00:00
if tx . isContractCreation and isCollision :
return tx . gasLimit
2019-03-11 15:32:25 +00:00
var snapshot = vmState . snapshot ( )
2019-03-12 01:07:29 +00:00
defer : snapshot . dispose ( )
2019-03-18 05:48:32 +00:00
2019-03-11 15:32:25 +00:00
var contractOK = true
2019-03-07 07:10:05 +00:00
result = tx . gasLimit
if execComputation ( computation ) :
if tx . isContractCreation :
2019-03-15 11:16:47 +00:00
contractOK = computation . writeContract ( fork )
2019-03-07 07:10:05 +00:00
result = computation . refundGas ( tx , sender )
2018-12-12 00:32:33 +00:00
2019-03-11 15:32:25 +00:00
if not contractOK and fork = = FkHomestead :
snapshot . revert ( )
# consume all gas
result = tx . gasLimit
else :
snapshot . commit ( )
2018-12-14 07:32:45 +00:00
type
# TODO: these types need to be removed
2019-02-05 19:15:50 +00:00
# once eth/bloom and eth/common sync'ed
Bloom = common . BloomFilter
LogsBloom = bloom . BloomFilter
2018-12-14 07:32:45 +00:00
# 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
2018-12-31 03:27:02 +00:00
proc makeReceipt ( vmState : BaseVMState , cumulativeGasUsed : GasInt , fork = FkFrontier ) : Receipt =
2018-12-14 07:32:45 +00:00
if fork < FkByzantium :
2018-12-31 03:27:02 +00:00
result . stateRootOrStatus = hashOrStatus ( vmState . accountDb . rootHash )
2018-12-14 07:32:45 +00:00
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
2018-12-12 00:32:33 +00:00
2018-12-25 05:58:55 +00:00
proc processBlock * ( chainDB : BaseChainDB , head , header : BlockHeader , body : BlockBody , vmState : BaseVMState ) : ValidationResult =
2018-12-12 00:32:33 +00:00
let blockReward = 5 . u256 * pow ( 10 . u256 , 18 ) # 5 ETH
2018-12-25 05:58:55 +00:00
if body . transactions . calcTxRoot ! = header . txRoot :
debug " Mismatched txRoot " , blockNumber = header . blockNumber
2018-12-12 00:32:33 +00:00
return ValidationResult . Error
2018-12-25 05:58:55 +00:00
if header . txRoot ! = BLANK_ROOT_HASH :
if body . transactions . len = = 0 :
debug " No transactions in body " , blockNumber = header . blockNumber
2018-12-12 00:32:33 +00:00
return ValidationResult . Error
else :
2018-12-25 05:58:55 +00:00
trace " Has transactions " , blockNumber = header . blockNumber , blockHash = header . blockHash
2018-12-12 00:32:33 +00:00
2018-12-25 12:10:04 +00:00
vmState . receipts = newSeq [ Receipt ] ( body . transactions . len )
2018-12-12 00:32:33 +00:00
var cumulativeGasUsed = GasInt ( 0 )
2018-12-25 05:58:55 +00:00
for txIndex , tx in body . transactions :
2019-03-18 07:35:52 +00:00
if cumulativeGasUsed + tx . gasLimit > header . gasLimit :
2019-03-07 08:04:32 +00:00
vmState . mutateStateDB :
2019-03-18 07:35:52 +00:00
db . addBalance ( header . coinbase , 0 . u256 )
# TODO: do we need to break or continue execution?
2018-12-12 00:32:33 +00:00
else :
2019-03-18 07:35:52 +00:00
var sender : EthAddress
if tx . getSender ( sender ) :
let gasUsed = processTransaction ( tx , sender , vmState )
cumulativeGasUsed + = gasUsed
# miner fee
let txFee = gasUsed . u256 * tx . gasPrice . u256
vmState . mutateStateDB :
db . addBalance ( header . coinbase , txFee )
else :
debug " Could not get sender " , txIndex , tx
return ValidationResult . Error
vmState . receipts [ txIndex ] = makeReceipt ( vmState , cumulativeGasUsed )
2018-12-12 00:32:33 +00:00
var mainReward = blockReward
2018-12-25 05:58:55 +00:00
if header . ommersHash ! = EMPTY_UNCLE_HASH :
let h = chainDB . persistUncles ( body . uncles )
if h ! = header . ommersHash :
2018-12-12 00:32:33 +00:00
debug " Uncle hash mismatch "
return ValidationResult . Error
2018-12-25 05:58:55 +00:00
for uncle in body . uncles :
var uncleReward = uncle . blockNumber + 8 . u256
uncleReward - = header . blockNumber
2018-12-12 00:32:33 +00:00
uncleReward = uncleReward * blockReward
uncleReward = uncleReward div 8 . u256
2019-03-07 08:04:32 +00:00
vmState . mutateStateDB :
db . addBalance ( uncle . coinbase , uncleReward )
2018-12-12 00:32:33 +00:00
mainReward + = blockReward div 32 . u256
# Reward beneficiary
2019-03-07 08:04:32 +00:00
vmState . mutateStateDB :
db . addBalance ( header . coinbase , mainReward )
2018-12-12 00:32:33 +00:00
2019-03-07 08:04:32 +00:00
let stateDb = vmState . accountDb
2018-12-25 05:58:55 +00:00
if header . stateRoot ! = stateDb . rootHash :
error " Wrong state root in block " , blockNumber = header . blockNumber , expected = header . stateRoot , actual = stateDb . rootHash , arrivedFrom = chainDB . getCanonicalHead ( ) . stateRoot
2018-12-12 00:32:33 +00:00
# this one is a show stopper until we are confident in our VM's
# compatibility with the main chain
2018-12-25 10:31:51 +00:00
return ValidationResult . Error
2018-12-12 00:32:33 +00:00
2018-12-25 05:58:55 +00:00
let bloom = createBloom ( vmState . receipts )
if header . bloom ! = bloom :
debug " wrong bloom in block " , blockNumber = header . blockNumber
return ValidationResult . Error
2018-12-12 00:32:33 +00:00
2018-12-25 05:58:55 +00:00
let receiptRoot = calcReceiptRoot ( vmState . receipts )
if header . receiptRoot ! = receiptRoot :
debug " wrong receiptRoot in block " , blockNumber = header . blockNumber , actual = receiptRoot , expected = header . receiptRoot
return ValidationResult . Error