2018-09-06 20:05:22 +03:00
import .. / db / [ db_chain , state_db ] , eth_common , chronicles , .. / vm_state , .. / vm_types , .. / transaction , ranges ,
2018-09-29 15:36:42 +00:00
.. / vm / [ computation , interpreter_dispatch , message ] , .. / constants , stint , nimcrypto ,
2018-09-27 19:09:26 +00:00
.. / vm_state_transactions ,
eth_trie / memdb , eth_trie , rlp ,
sugar
2018-08-29 11:49:01 +03:00
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
2018-09-20 23:06:22 +03:00
proc processTransaction ( db : var AccountStateDB , t : Transaction , sender : EthAddress , head : BlockHeader , chainDB : BaseChainDB ) : UInt256 =
## Process the transaction, write the results to db.
## Returns amount of ETH to be rewarded to miner
2018-09-10 11:44:07 +03:00
echo " Sender: " , sender
echo " txHash: " , t . rlpHash
# Inct nonce:
db . setNonce ( sender , db . getNonce ( sender ) + 1 )
var transactionFailed = false
2018-09-27 19:09:26 +00:00
#t.dump
# TODO: combine/refactor re validate
2018-09-10 11:44:07 +03:00
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
2018-09-29 15:36:42 +00:00
var gasUsed = t . payload . intrinsicGas . GasInt # += 32000 appears in Homestead when contract create
2018-09-10 11:44:07 +03:00
if gasUsed > t . gasLimit :
echo " Transaction failed. Out of gas. "
transactionFailed = true
else :
if t . isContractCreation :
2018-09-29 15:36:42 +00:00
# TODO: re-derive sender in callee for cleaner interface, perhaps
var vmState = newBaseVMState ( head , chainDB )
return applyCreateTransaction ( db , t , head , vmState , sender )
2018-09-20 23:06:22 +03:00
2018-09-10 11:44:07 +03:00
else :
let code = db . getCode ( t . to )
if code . len = = 0 :
# Value transfer
echo " Transfer " , t . value , " from " , sender , " to " , t . to
db . addBalance ( t . to , t . value )
else :
# Contract call
echo " Contract call "
debug " Transaction " , sender , to = t . to , value = t . value , hasCode = code . len ! = 0
let msg = newMessage ( t . gasLimit , t . gasPrice , t . to , sender , t . value , t . payload , code . toSeq )
2018-09-20 23:06:22 +03:00
# TODO: Run the vm
2018-09-10 11:44:07 +03:00
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
proc calcTxRoot ( transactions : openarray [ Transaction ] ) : Hash256 =
var tr = initHexaryTrie ( trieDB ( newMemDB ( ) ) )
for i , t in transactions :
2018-09-26 14:00:52 +03:00
tr . put ( rlp . encode ( i ) . toRange , rlp . encode ( t ) . toRange )
2018-09-10 11:44:07 +03:00
return tr . rootHash
2018-08-29 11:49:01 +03:00
method persistBlocks * ( c : Chain , headers : openarray [ BlockHeader ] , bodies : openarray [ BlockBody ] ) =
# Run the VM here
assert ( headers . len = = bodies . len )
2018-09-10 11:44:07 +03:00
let blockReward = 5 . u256 * pow ( 10 . u256 , 18 ) # 5 ETH
echo " Persisting blocks: " , headers [ 0 ] . blockNumber , " - " , headers [ ^ 1 ] . blockNumber
2018-08-29 11:49:01 +03:00
for i in 0 .. < headers . len :
2018-09-10 11:44:07 +03:00
let head = c . db . getCanonicalHead ( )
assert ( head . blockNumber = = headers [ i ] . blockNumber - 1 )
var stateDb = newAccountStateDB ( c . db . db , head . stateRoot )
var gasReward = 0 . u256
assert ( bodies [ i ] . transactions . calcTxRoot = = headers [ i ] . txRoot )
2018-09-06 20:05:22 +03:00
if headers [ i ] . txRoot ! = BLANK_ROOT_HASH :
# assert(head.blockNumber == headers[i].blockNumber - 1)
let vmState = newBaseVMState ( head , c . db )
2018-09-10 11:44:07 +03:00
assert ( bodies [ i ] . transactions . len ! = 0 )
2018-09-06 20:05:22 +03:00
if bodies [ i ] . transactions . len ! = 0 :
2018-09-10 11:44:07 +03:00
echo " block: " , headers [ i ] . blockNumber
echo " h: " , headers [ i ] . blockHash
2018-09-06 20:05:22 +03:00
for t in bodies [ i ] . transactions :
var sender : EthAddress
if t . getSender ( sender ) :
2018-09-20 23:06:22 +03:00
gasReward + = processTransaction ( stateDb , t , sender , head , c . db )
2018-09-10 11:44:07 +03:00
else :
assert ( false , " Could not get sender " )
var mainReward = blockReward + gasReward
2018-09-27 19:09:26 +00:00
#echo "mainReward = ", mainReward , " with blockReward = ", blockReward, " and gasReward = ", gasReward
2018-09-06 20:05:22 +03:00
if headers [ i ] . ommersHash ! = EMPTY_UNCLE_HASH :
2018-09-10 11:44:07 +03:00
let h = c . db . persistUncles ( bodies [ i ] . uncles )
assert ( h = = headers [ i ] . ommersHash )
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 :
echo " Wrong state root in block " , headers [ i ] . blockNumber , " . Expected: " , headers [ i ] . stateRoot , " , Actual: " , stateDb . rootHash , " arrived from " , c . db . getCanonicalHead ( ) . stateRoot
assert ( headers [ i ] . stateRoot = = stateDb . rootHash )
2018-09-06 20:05:22 +03:00
discard c . db . persistHeaderToDb ( headers [ i ] )
assert ( c . db . getCanonicalHead ( ) . blockHash = = headers [ i ] . blockHash )