2019-04-18 01:27:55 +00:00
import options , sets ,
2019-07-07 10:12:01 +00:00
eth / [ common , bloom ] , stew / 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 ,
2019-03-19 16:30:35 +00:00
.. / vm / [ computation , message ] ,
2019-04-06 14:50:43 +00:00
.. / vm / interpreter / vm_forks ,
. / dao
2018-12-04 11:42:55 +00:00
2019-04-23 12:50:45 +00:00
proc processTransaction * ( tx : Transaction , sender : EthAddress , vmState : BaseVMState , fork : 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-04-23 12:50:45 +00:00
if fork > = FkSpurious :
vmState . touchedAccounts . incl ( vmState . blockHeader . coinbase )
2019-04-18 00:56:57 +00:00
var gasUsed = tx . gasLimit
2018-12-04 11:42:55 +00:00
2019-04-18 00:56:57 +00:00
block :
if vmState . cumulativeGasUsed + gasUsed > vmState . blockHeader . gasLimit :
debug " invalid tx: block header gasLimit reached " ,
blockGasLimit = vmState . blockHeader . gasLimit ,
gasUsed = gasUsed ,
txGasLimit = tx . gasLimit
gasUsed = 0
break
2019-03-18 05:48:32 +00:00
2019-04-18 00:56:57 +00:00
let upfrontGasCost = tx . gasLimit . u256 * tx . gasPrice . u256
var balance = vmState . readOnlyStateDb ( ) . getBalance ( sender )
if balance < upfrontGasCost : break
2019-03-18 05:48:32 +00:00
2019-04-18 00:56:57 +00:00
let recipient = tx . getRecipient ( )
let isCollision = vmState . readOnlyStateDb ( ) . hasCodeOrNonce ( recipient )
2019-04-23 12:50:45 +00:00
var computation = setupComputation ( vmState , tx , sender , recipient , fork )
2019-04-18 01:27:55 +00:00
if computation . isNil : # OOG in setupComputation
2019-04-18 00:56:57 +00:00
gasUsed = 0
break
vmState . mutateStateDB :
db . incNonce ( sender )
db . subBalance ( sender , upfrontGasCost )
2018-12-04 11:42:55 +00:00
2019-04-18 00:56:57 +00:00
if tx . isContractCreation and isCollision : break
2019-04-24 06:58:56 +00:00
execComputation ( computation )
if not computation . shouldBurnGas :
2019-04-18 00:56:57 +00:00
gasUsed = computation . refundGas ( tx , sender )
2019-03-18 05:48:32 +00:00
2019-04-18 00:56:57 +00:00
if computation . isSuicided ( vmState . blockHeader . coinbase ) :
gasUsed = 0
vmState . cumulativeGasUsed + = gasUsed
# miner fee
let txFee = gasUsed . u256 * tx . gasPrice . u256
vmState . mutateStateDB :
db . addBalance ( vmState . blockHeader . coinbase , txFee )
2018-12-12 00:32:33 +00:00
2019-04-18 01:27:55 +00:00
# EIP158 state clearing
for account in vmState . touchedAccounts :
if db . accountExists ( account ) and db . isEmptyAccount ( account ) :
2019-04-18 03:21:20 +00:00
debug " state clearing " , account
2019-04-18 01:27:55 +00:00
db . deleteAccount ( account )
2019-04-18 00:56:57 +00:00
result = gasUsed
2019-03-11 15:32:25 +00:00
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
2019-04-18 00:56:57 +00:00
proc makeReceipt ( vmState : BaseVMState , 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 :
2019-04-22 09:37:41 +00:00
result . stateRootOrStatus = hashOrStatus ( vmState . status )
2018-12-14 07:32:45 +00:00
2019-04-18 00:56:57 +00:00
result . cumulativeGasUsed = vmState . cumulativeGasUsed
2018-12-14 07:32:45 +00:00
result . logs = vmState . getAndClearLogEntries ( )
result . bloom = logsBloom ( result . logs ) . value . toByteArrayBE
2018-12-12 00:32:33 +00:00
2019-05-14 04:31:30 +00:00
func eth ( n : int ) : Uint256 {. compileTime . } =
n . u256 * pow ( 10 . u256 , 18 )
const
eth5 = 5 . eth
eth3 = 3 . eth
eth2 = 2 . eth
blockRewards : array [ Fork , Uint256 ] = [
eth5 , # FkFrontier
eth5 , # FkThawing
eth5 , # FkHomestead
eth5 , # FkDao
eth5 , # FkTangerine
eth5 , # FkSpurious
eth3 , # FkByzantium
eth2 # FkConstantinople
]
2019-03-20 03:22:37 +00:00
proc processBlock * ( chainDB : BaseChainDB , header : BlockHeader , body : BlockBody , vmState : BaseVMState ) : ValidationResult =
2019-04-06 14:50:43 +00:00
if chainDB . config . daoForkSupport and header . blockNumber = = chainDB . config . daoForkBlock :
vmState . mutateStateDB :
db . applyDAOHardFork ( )
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
2019-04-22 09:52:18 +00:00
let fork = vmState . blockNumber . toFork
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 )
2019-04-18 00:56:57 +00:00
vmState . cumulativeGasUsed = 0
2018-12-25 05:58:55 +00:00
for txIndex , tx in body . transactions :
2019-04-18 00:56:57 +00:00
var sender : EthAddress
if tx . getSender ( sender ) :
2019-04-23 12:50:45 +00:00
let gasUsed = processTransaction ( tx , sender , vmState , fork )
2018-12-12 00:32:33 +00:00
else :
2019-04-18 00:56:57 +00:00
debug " Could not get sender " , txIndex , tx
return ValidationResult . Error
2019-04-22 09:52:18 +00:00
vmState . receipts [ txIndex ] = makeReceipt ( vmState , fork )
2019-05-14 04:31:30 +00:00
let blockReward = blockRewards [ fork ]
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 :
2019-07-10 07:40:46 +00:00
var uncleReward = uncle . blockNumber . u256 + 8 . u256
uncleReward - = header . blockNumber . u256
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