2018-12-25 10:31:51 +00:00
import eth_common , ranges , chronicles , eth_bloom , 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
proc processTransaction * ( db : var AccountStateDB , t : Transaction , sender : EthAddress , vmState : BaseVMState ) : UInt256 =
## 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
trace " txHash " , rlpHash = t . rlpHash
2018-12-04 11:42:55 +00:00
# Inct nonce:
db . setNonce ( sender , db . getNonce ( sender ) + 1 )
var transactionFailed = false
#t.dump
# TODO: combine/refactor re validate
let upfrontGasCost = t . gasLimit . u256 * t . gasPrice . u256
let upfrontCost = upfrontGasCost + t . value
var balance = db . getBalance ( sender )
if balance < upfrontCost :
if balance < = upfrontGasCost :
result = balance
balance = 0 . u256
else :
result = upfrontGasCost
balance - = upfrontGasCost
transactionFailed = true
else :
balance - = upfrontCost
db . setBalance ( sender , balance )
if transactionFailed :
return
var gasUsed = t . payload . intrinsicGas . GasInt # += 32000 appears in Homestead when contract create
if gasUsed > t . gasLimit :
2018-12-06 23:16:34 +00:00
debug " Transaction failed. Out of gas. "
2018-12-04 11:42:55 +00:00
transactionFailed = true
else :
if t . isContractCreation :
# TODO: re-derive sender in callee for cleaner interface, perhaps
return applyCreateTransaction ( db , t , vmState , sender )
else :
let code = db . getCode ( t . to )
if code . len = = 0 :
# Value transfer
2018-12-06 23:16:34 +00:00
trace " Transfer " , value = t . value , sender , to = t . to
2018-12-04 11:42:55 +00:00
db . addBalance ( t . to , t . value )
else :
# Contract call
2018-12-06 23:16:34 +00:00
trace " Contract call "
trace " Transaction " , sender , to = t . to , value = t . value , hasCode = code . len ! = 0
2018-12-04 11:42:55 +00:00
let msg = newMessage ( t . gasLimit , t . gasPrice , t . to , sender , t . value , t . payload , code . toSeq )
# TODO: Run the vm
if gasUsed > t . gasLimit :
gasUsed = t . gasLimit
var refund = ( t . gasLimit - gasUsed ) . u256 * t . gasPrice . u256
if transactionFailed :
refund + = t . value
db . addBalance ( sender , refund )
return gasUsed . u256 * t . gasPrice . u256
2018-12-12 00:32:33 +00:00
2018-12-14 07:32:45 +00:00
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
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
var stateDb = newAccountStateDB ( chainDB . db , head . stateRoot , chainDB . pruneTrie )
2018-12-12 00:32:33 +00:00
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 :
2018-12-12 00:32:33 +00:00
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
2018-12-25 05:58:55 +00:00
stateDb . addBalance ( header . coinbase , txFee )
2018-12-12 00:32:33 +00:00
else :
2018-12-25 05:58:55 +00:00
debug " Could not get sender " , txIndex , tx
2018-12-12 00:32:33 +00:00
return ValidationResult . Error
2018-12-25 05:58:55 +00:00
vmState . receipts [ txIndex ] = makeReceipt ( vmState , stateDb . rootHash , 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
2018-12-25 05:58:55 +00:00
stateDb . addBalance ( uncle . coinbase , uncleReward )
2018-12-12 00:32:33 +00:00
mainReward + = blockReward div 32 . u256
# Reward beneficiary
2018-12-25 05:58:55 +00:00
stateDb . addBalance ( header . coinbase , mainReward )
2018-12-12 00:32:33 +00:00
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