Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contract call and contract creation refactor #252

Merged
merged 12 commits into from
Feb 27, 2019
Merged
30 changes: 15 additions & 15 deletions GeneralStateTests.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,8 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
CREATE_ContractRETURNBigOffset.json Skip
+ CREATE_ContractSSTOREDuringInit.json OK
+ CREATE_ContractSuicideDuringInit.json OK
CREATE_ContractSuicideDuringInit_ThenStoreThenReturn.json Skip
CREATE_ContractSuicideDuringInit_WithValue.json Skip
+ CREATE_ContractSuicideDuringInit_ThenStoreThenReturn.json OK
+ CREATE_ContractSuicideDuringInit_WithValue.json OK
+ CREATE_ContractSuicideDuringInit_WithValueToItself.json OK
CREATE_EContractCreateEContractInInit_Tr.json Skip
+ CREATE_EContractCreateNEContractInInitOOG_Tr.json OK
Expand All @@ -397,7 +397,7 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
TransactionCollisionToEmptyButCode.json Skip
TransactionCollisionToEmptyButNonce.json Skip
```
OK: 12/30 Fail: 0/30 Skip: 18/30
OK: 14/30 Fail: 0/30 Skip: 16/30
## stDelegatecallTestHomestead
```diff
Call1024BalanceTooLow.json Skip
Expand Down Expand Up @@ -508,9 +508,9 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ contractCreationOOGdontLeaveEmptyContractViaTransaction.json OK
+ createContractViaContract.json OK
createContractViaContractOOGInitCode.json Skip
createContractViaTransactionCost53000.json Skip
+ createContractViaTransactionCost53000.json OK
```
OK: 3/5 Fail: 0/5 Skip: 2/5
OK: 4/5 Fail: 0/5 Skip: 1/5
## stInitCodeTest
```diff
+ CallContractToCreateContractAndCallItOOG.json OK
Expand All @@ -527,12 +527,12 @@ OK: 3/5 Fail: 0/5 Skip: 2/5
+ ReturnTest.json OK
+ ReturnTest2.json OK
+ StackUnderFlowContractCreation.json OK
TransactionCreateAutoSuicideContract.json Skip
+ TransactionCreateAutoSuicideContract.json OK
+ TransactionCreateRandomInitCode.json OK
+ TransactionCreateStopInInitcode.json OK
+ TransactionCreateSuicideInInitcode.json OK
```
OK: 10/18 Fail: 0/18 Skip: 8/18
OK: 11/18 Fail: 0/18 Skip: 7/18
## stLogTests
```diff
+ log0_emptyMem.json OK
Expand Down Expand Up @@ -1510,12 +1510,12 @@ OK: 315/327 Fail: 0/327 Skip: 12/327
+ randomStatetest641.json OK
+ randomStatetest642.json OK
randomStatetest643.json Skip
randomStatetest644.json Skip
randomStatetest645.json Skip
+ randomStatetest644.json OK
+ randomStatetest645.json OK
randomStatetest646.json Skip
randomStatetest647.json Skip
```
OK: 219/227 Fail: 0/227 Skip: 8/227
OK: 221/227 Fail: 0/227 Skip: 6/227
## stRecursiveCreate
```diff
recursiveCreate.json Skip
Expand Down Expand Up @@ -2093,8 +2093,8 @@ OK: 51/67 Fail: 0/67 Skip: 16/67
+ CreateTransactionReverted.json OK
+ CreateTransactionSuccess.json OK
+ EmptyTransaction.json OK
EmptyTransaction2.json Skip
EmptyTransaction3.json Skip
+ EmptyTransaction2.json OK
+ EmptyTransaction3.json OK
+ HighGasLimit.json OK
+ InternalCallHittingGasLimit.json OK
+ InternalCallHittingGasLimit2.json OK
Expand Down Expand Up @@ -2122,7 +2122,7 @@ OK: 51/67 Fail: 0/67 Skip: 16/67
+ TransactionFromCoinbaseNotEnoughFounds.json OK
+ TransactionNonceCheck.json OK
+ TransactionNonceCheck2.json OK
TransactionSendingToEmpty.json Skip
+ TransactionSendingToEmpty.json OK
+ TransactionSendingToZero.json OK
+ TransactionToAddressh160minusOne.json OK
+ TransactionToItself.json OK
Expand All @@ -2131,7 +2131,7 @@ OK: 51/67 Fail: 0/67 Skip: 16/67
+ UserTransactionZeroCost.json OK
+ UserTransactionZeroCostWithData.json OK
```
OK: 38/44 Fail: 0/44 Skip: 6/44
OK: 41/44 Fail: 0/44 Skip: 3/44
## stTransitionTest
```diff
+ createNameRegistratorPerTxsAfter.json OK
Expand Down Expand Up @@ -2520,4 +2520,4 @@ OK: 0/133 Fail: 0/133 Skip: 133/133
OK: 0/130 Fail: 0/130 Skip: 130/130

---TOTAL---
OK: 1170/2334 Fail: 0/2334 Skip: 1164/2334
OK: 1179/2334 Fail: 0/2334 Skip: 1155/2334
51 changes: 26 additions & 25 deletions nimbus/db/db_chain.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@

import
tables, sequtils, algorithm,
ranges, state_db, nimcrypto, eth/trie/[hexary, db], eth/[common, rlp], byteutils, chronicles,
../errors, ../block_types, ../utils/header, ../constants, ./storage_types
ranges, state_db, eth/trie/[hexary, db],
eth/[common, rlp], byteutils, chronicles,
../errors, ../constants, ./storage_types,
../utils

type
BaseChainDB* = ref object
db* : TrieDatabaseRef
pruneTrie*: bool

KeyType = enum
blockNumberToHash
blockHashToScore

#KeyType = enum
# blockNumberToHash
# blockHashToScore
#
TransactionKey = tuple
blockNumber: BlockNumber
index: int
Expand Down Expand Up @@ -105,7 +107,7 @@ proc persistTransactions*(self: BaseChainDB, blockNumber: BlockNumber, transacti
for idx, tx in transactions:
let
encodedTx = rlp.encode(tx).toRange
txHash = keccak256.digest(encodedTx.toOpenArray)
txHash = keccak(encodedTx.toOpenArray)
txKey: TransactionKey = (blockNumber, idx)
trie.put(rlp.encode(idx).toRange, encodedTx)
self.db.put(transactionHashToBlockKey(txHash).toOpenArray, rlp.encode(txKey))
Expand All @@ -125,7 +127,7 @@ iterator getBlockTransactionHashes(self: BaseChainDB, blockHeader: BlockHeader):
## Returns an iterable of the transaction hashes from th block specified
## by the given block header.
for encodedTx in self.getBlockTransactionData(blockHeader.txRoot):
yield keccak256.digest(encodedTx.toOpenArray)
yield keccak(encodedTx.toOpenArray)

proc getBlockBody*(self: BaseChainDB, blockHash: Hash256, output: var BlockBody): bool =
var header: BlockHeader
Expand Down Expand Up @@ -245,24 +247,24 @@ proc persistUncles*(self: BaseChainDB, uncles: openarray[BlockHeader]): Hash256
## Persists the list of uncles to the database.
## Returns the uncles hash.
let enc = rlp.encode(uncles)
result = keccak256.digest(enc)
result = keccak(enc)
self.db.put(genericHashKey(result).toOpenArray, enc)

proc persistBlockToDb*(self: BaseChainDB; blk: Block): ValidationResult =
## Persist the given block's header and uncles.
## Assumes all block transactions have been persisted already.
let newCanonicalHeaders = self.persistHeaderToDb(blk.header)
for header in newCanonicalHeaders:
var index = 0
for txHash in self.getBlockTransactionHashes(header):
self.addTransactionToCanonicalChain(txHash, header, index)
inc index

if blk.uncles.len != 0:
let ommersHash = self.persistUncles(blk.uncles)
if ommersHash != blk.header.ommersHash:
debug "ommersHash mismatch"
return ValidationResult.Error
#proc persistBlockToDb*(self: BaseChainDB; blk: Block): ValidationResult =
# ## Persist the given block's header and uncles.
# ## Assumes all block transactions have been persisted already.
# let newCanonicalHeaders = self.persistHeaderToDb(blk.header)
# for header in newCanonicalHeaders:
# var index = 0
# for txHash in self.getBlockTransactionHashes(header):
# self.addTransactionToCanonicalChain(txHash, header, index)
# inc index
#
# if blk.uncles.len != 0:
# let ommersHash = self.persistUncles(blk.uncles)
# if ommersHash != blk.header.ommersHash:
# debug "ommersHash mismatch"
# return ValidationResult.Error

# Deprecated:
proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader {.deprecated.} =
Expand All @@ -273,4 +275,3 @@ proc lookupBlockHash*(self: BaseChainDB; n: BlockNumber): Hash256 {.deprecated.}

proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; n: BlockNumber): BlockHeader {.deprecated.} =
self.getBlockHeader(n)

11 changes: 7 additions & 4 deletions nimbus/db/state_db.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import
sequtils, strformat, tables,
chronicles, eth/[common, rlp], nimcrypto, eth/trie/[hexary, db],
../constants, ../errors, ../validation,
chronicles, eth/[common, rlp], eth/trie/[hexary, db],
../constants, ../errors, ../validation, ../utils,
storage_types

logScope:
Expand Down Expand Up @@ -115,7 +115,7 @@ proc setStorage*(db: var AccountStateDB,
var
triedb = HexaryTrie(db.trie).db
# slotHash can be obtained from accountTrie.put?
slotHash = keccak256.digest(slot.toByteArrayBE)
slotHash = keccak(slot.toByteArrayBE)
triedb.put(slotHashToSlotKey(slotHash.data).toOpenArray, rlp.encode(slot))

account.storageRoot = accountTrie.rootHash
Expand Down Expand Up @@ -156,13 +156,16 @@ proc getNonce*(db: AccountStateDB, address: EthAddress): AccountNonce =
let account = db.getAccount(address)
account.nonce

proc incNonce*(db: AccountStateDB, address: EthAddress) {.inline.} =
db.setNonce(address, db.getNonce(address) + 1)

proc setCode*(db: AccountStateDB, address: EthAddress, code: ByteRange) =
var account = db.getAccount(address)
# TODO: implement JournalDB to store code and storage
# also use JournalDB to revert state trie

let
newCodeHash = keccak256.digest code.toOpenArray
newCodeHash = keccak code.toOpenArray
triedb = HexaryTrie(db.trie).db

if code.len != 0:
Expand Down
110 changes: 24 additions & 86 deletions nimbus/p2p/executor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,115 +6,57 @@ import options,
../vm/[computation, interpreter_dispatch, message],
../vm/interpreter/vm_forks

proc contractCall(t: Transaction, vmState: BaseVMState, sender: EthAddress, forkOverride=none(Fork)): UInt256 =
# TODO: this function body was copied from GST with it's comments and TODOs.
# Right now it's main purpose is to produce VM tracing when syncing block with
# contract call. At later stage, this proc together with applyCreateTransaction
# and processTransaction need to be restructured.

# TODO: replace with cachingDb or similar approach; necessary
# when calls/subcalls/etc come in, too.
proc contractCall*(tx: Transaction, vmState: BaseVMState, sender: EthAddress, forkOverride=none(Fork)): GasInt =
var db = vmState.accountDb
let storageRoot = db.getStorageRoot(t.to)

var computation = setupComputation(vmState, t, sender, forkOverride)
# contract creation transaction.to == 0, so ensure happens after
db.addBalance(t.to, t.value)

let header = vmState.blockHeader
let gasCost = t.gasLimit.u256 * t.gasPrice.u256

var computation = setupComputation(vmState, tx, sender, forkOverride)
if execComputation(computation):
let
gasRemaining = computation.gasMeter.gasRemaining.u256
gasRefunded = computation.getGasRefund().u256
gasUsed = t.gasLimit.u256 - gasRemaining
gasRemaining = computation.gasMeter.gasRemaining
gasRefunded = computation.getGasRefund()
gasUsed = tx.gasLimit - gasRemaining
gasRefund = min(gasRefunded, gasUsed div 2)
gasRefundAmount = (gasRefund + gasRemaining) * t.gasPrice.u256
gasRefundAmount = (gasRefund + gasRemaining).u256 * tx.gasPrice.u256

db.addBalance(sender, gasRefundAmount)

return (t.gasLimit.u256 - gasRemaining - gasRefund) * t.gasPrice.u256
return (tx.gasLimit - gasRemaining - gasRefund)
else:
db.subBalance(t.to, t.value)
db.addBalance(sender, t.value)
db.setStorageRoot(t.to, storageRoot)
if computation.tracingEnabled: computation.traceError()
vmState.clearLogs()
return t.gasLimit.u256 * t.gasPrice.u256

# this proc should not be here, we need to refactor
# processTransaction
proc isPrecompiles*(address: EthAddress): bool {.inline.} =
for i in 0..18:
if address[i] != 0: return
result = address[19] >= 1.byte and address[19] <= 8.byte
return tx.gasLimit

proc processTransaction*(t: Transaction, sender: EthAddress, vmState: BaseVMState): UInt256 =
proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMState): GasInt =
## Process the transaction, write the results to db.
## Returns amount of ETH to be rewarded to miner
trace "Sender", sender
trace "txHash", rlpHash = t.rlpHash
trace "txHash", rlpHash = tx.rlpHash

var db = vmState.accountDb
# 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
let upfrontGasCost = tx.gasLimit.u256 * tx.gasPrice.u256
let upfrontCost = upfrontGasCost + tx.value
var balance = db.getBalance(sender)
if balance < upfrontCost:
if balance <= upfrontGasCost:
result = balance
result = (balance div tx.gasPrice.u256).truncate(GasInt)
balance = 0.u256
else:
result = upfrontGasCost
result = tx.gasLimit
balance -= upfrontGasCost
transactionFailed = true
else:
balance -= upfrontCost
balance -= upfrontGasCost

db.incNonce(sender)
db.setBalance(sender, balance)
if transactionFailed:
return

var gasUsed = t.payload.intrinsicGas.GasInt # += 32000 appears in Homestead when contract create
if transactionFailed: return

if gasUsed > t.gasLimit:
debug "Transaction failed. Out of gas."
transactionFailed = true
# TODO: Run the vm with proper fork
if tx.isContractCreation:
result = applyCreateTransaction(tx, vmState, sender)
else:
if t.isContractCreation:
# TODO: re-derive sender in callee for cleaner interface, perhaps
return applyCreateTransaction(t, vmState, sender)

else:
let code = db.getCode(t.to)
if code.len == 0 and not isPrecompiles(t.to):
# Value transfer
trace "Transfer", value = t.value, sender, to = t.to

db.addBalance(t.to, t.value)
else:
# Contract call
trace "Contract call"
trace "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)
# TODO: Run the vm with proper fork
return contractCall(t, vmState, sender)

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
result = contractCall(tx, vmState, sender)

type
# TODO: these types need to be removed
Expand Down Expand Up @@ -167,15 +109,11 @@ proc processBlock*(chainDB: BaseChainDB, head, header: BlockHeader, body: BlockB
for txIndex, tx in body.transactions:
var sender: EthAddress
if tx.getSender(sender):
let txFee = processTransaction(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)
let gasUsed = processTransaction(tx, sender, vmState)
cumulativeGasUsed += gasUsed

# miner fee
let txFee = gasUsed.u256 * tx.gasPrice.u256
stateDb.addBalance(header.coinbase, txFee)
else:
debug "Could not get sender", txIndex, tx
Expand Down