mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-12 21:34:33 +00:00
general-state testsuite changes and better fork selection
- skipped the tests in allowedFailInCurrentBuild() - replaced doAssert() with check() in testFixtureIndexes() so we can see both hash values on failure - checking filename extension for JSON tests to avoid editor swap files - replaced the duplicated block values in the main net's ChainConfig with values from forkBlocks - allowed overriding the current fork in computations, because the old strategy of only looking at the block number doesn't work with JSON tests where the block number is usually 1 - explicitly pass the fork to gasCosts() and use it for conditional cost calculation - fixed a logic error in the CREATE opcode - fixed VM selection based on current fork in updateOpcodeExec() - single point of control for supported forks in tests (just one fork, at the moment) - 44 new test failures (that were probably passing for the wrong reasons)
This commit is contained in:
parent
8f0a78e52f
commit
115843487c
1142
GeneralStateTests.md
1142
GeneralStateTests.md
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
parseopt, strutils, macros, os,
|
parseopt, strutils, macros, os,
|
||||||
asyncdispatch2, eth_keys, eth_p2p, eth_common, chronicles, nimcrypto/hash
|
asyncdispatch2, eth_keys, eth_p2p, eth_common, chronicles, nimcrypto/hash,
|
||||||
|
./vm/interpreter/vm_forks
|
||||||
|
|
||||||
const
|
const
|
||||||
NimbusName* = "Nimbus"
|
NimbusName* = "Nimbus"
|
||||||
@ -175,14 +176,14 @@ proc publicChainConfig*(id: PublicNetwork): ChainConfig =
|
|||||||
of MainNet:
|
of MainNet:
|
||||||
ChainConfig(
|
ChainConfig(
|
||||||
chainId: MainNet.uint,
|
chainId: MainNet.uint,
|
||||||
homesteadBlock: 1150000.u256,
|
homesteadBlock: forkBlocks[FkHomestead],
|
||||||
daoForkBlock: 1920000.u256,
|
daoForkBlock: forkBlocks[FkDao],
|
||||||
daoForkSupport: true,
|
daoForkSupport: true,
|
||||||
eip150Block: 2463000.u256,
|
eip150Block: forkBlocks[FkTangerine],
|
||||||
eip150Hash: toDigest("2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
|
eip150Hash: toDigest("2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
|
||||||
eip155Block: 2675000.u256,
|
eip155Block: forkBlocks[FkSpurious],
|
||||||
eip158Block: 2675000.u256,
|
eip158Block: forkBlocks[FkSpurious],
|
||||||
byzantiumBlock: 4370000.u256
|
byzantiumBlock: forkBlocks[FkByzantium]
|
||||||
)
|
)
|
||||||
of RopstenNet:
|
of RopstenNet:
|
||||||
ChainConfig(
|
ChainConfig(
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
chronicles, strformat, strutils, sequtils, macros, terminal, math, tables,
|
chronicles, strformat, strutils, sequtils, macros, terminal, math, tables, options,
|
||||||
eth_common,
|
eth_common,
|
||||||
../constants, ../errors, ../validation, ../vm_state, ../vm_types,
|
../constants, ../errors, ../validation, ../vm_state, ../vm_types,
|
||||||
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
||||||
@ -17,7 +17,7 @@ import
|
|||||||
logScope:
|
logScope:
|
||||||
topics = "vm computation"
|
topics = "vm computation"
|
||||||
|
|
||||||
proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Message): BaseComputation =
|
proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Message, forkOverride=none(Fork)): BaseComputation =
|
||||||
new result
|
new result
|
||||||
result.vmState = vmState
|
result.vmState = vmState
|
||||||
result.msg = message
|
result.msg = message
|
||||||
@ -29,7 +29,12 @@ proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Me
|
|||||||
result.logEntries = @[]
|
result.logEntries = @[]
|
||||||
result.code = newCodeStream(message.code)
|
result.code = newCodeStream(message.code)
|
||||||
# result.rawOutput = "0x"
|
# result.rawOutput = "0x"
|
||||||
result.gasCosts = blockNumber.toFork.forkToSchedule
|
result.gasCosts =
|
||||||
|
if forkOverride.isSome:
|
||||||
|
forkOverride.get.forkToSchedule
|
||||||
|
else:
|
||||||
|
blockNumber.toFork.forkToSchedule
|
||||||
|
result.forkOverride = forkOverride
|
||||||
|
|
||||||
proc isOriginComputation*(c: BaseComputation): bool =
|
proc isOriginComputation*(c: BaseComputation): bool =
|
||||||
# Is this computation the computation initiated by a transaction
|
# Is this computation the computation initiated by a transaction
|
||||||
@ -209,7 +214,8 @@ proc generateChildComputation*(fork: Fork, computation: BaseComputation, childMs
|
|||||||
var childComp = newBaseComputation(
|
var childComp = newBaseComputation(
|
||||||
computation.vmState,
|
computation.vmState,
|
||||||
computation.vmState.blockHeader.blockNumber,
|
computation.vmState.blockHeader.blockNumber,
|
||||||
childMsg)
|
childMsg,
|
||||||
|
some(fork))
|
||||||
|
|
||||||
# Copy the fork op code executor proc (assumes child computation is in the same fork)
|
# Copy the fork op code executor proc (assumes child computation is in the same fork)
|
||||||
childComp.opCodeExec = computation.opCodeExec
|
childComp.opCodeExec = computation.opCodeExec
|
||||||
@ -235,9 +241,16 @@ proc addChildComputation(fork: Fork, computation: BaseComputation, child: BaseCo
|
|||||||
computation.returnData = child.output
|
computation.returnData = child.output
|
||||||
computation.children.add(child)
|
computation.children.add(child)
|
||||||
|
|
||||||
|
proc getFork*(computation: BaseComputation): Fork =
|
||||||
|
result =
|
||||||
|
if computation.forkOverride.isSome:
|
||||||
|
computation.forkOverride.get
|
||||||
|
else:
|
||||||
|
computation.vmState.blockHeader.blockNumber.toFork
|
||||||
|
|
||||||
proc applyChildComputation*(computation: BaseComputation, childMsg: Message, opCode: static[Op]): BaseComputation =
|
proc applyChildComputation*(computation: BaseComputation, childMsg: Message, opCode: static[Op]): BaseComputation =
|
||||||
## Apply the vm message childMsg as a child computation.
|
## Apply the vm message childMsg as a child computation.
|
||||||
let fork = computation.vmState.blockHeader.blockNumber.toFork
|
let fork = computation.getFork
|
||||||
result = fork.generateChildComputation(computation, childMsg, opCode)
|
result = fork.generateChildComputation(computation, childMsg, opCode)
|
||||||
fork.addChildComputation(computation, result)
|
fork.addChildComputation(computation, result)
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import
|
|||||||
# Gas Fee Schedule
|
# Gas Fee Schedule
|
||||||
# Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf
|
# Yellow Paper Appendix G - https://ethereum.github.io/yellowpaper/paper.pdf
|
||||||
type
|
type
|
||||||
GasFeeKind = enum
|
GasFeeKind* = enum
|
||||||
GasZero, # Nothing paid for operations of the set Wzero.
|
GasZero, # Nothing paid for operations of the set Wzero.
|
||||||
GasBase, # Amount of gas to pay for operations of the set Wbase.
|
GasBase, # Amount of gas to pay for operations of the set Wbase.
|
||||||
GasVeryLow, # Amount of gas to pay for operations of the set Wverylow.
|
GasVeryLow, # Amount of gas to pay for operations of the set Wverylow.
|
||||||
@ -102,11 +102,13 @@ type
|
|||||||
|
|
||||||
GasCosts* = array[Op, GasCost]
|
GasCosts* = array[Op, GasCost]
|
||||||
|
|
||||||
template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyped) =
|
template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) =
|
||||||
|
|
||||||
## Generate the gas cost for each forks and store them in a const
|
## Generate the gas cost for each forks and store them in a const
|
||||||
## named `ResultGasCostsName`
|
## named `ResultGasCostsName`
|
||||||
|
|
||||||
|
const FeeSchedule = gasFees[fork]
|
||||||
|
|
||||||
# ############### Helper functions ##############################
|
# ############### Helper functions ##############################
|
||||||
|
|
||||||
func `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength: Natural): GasInt {.inline.} =
|
func `prefix gasMemoryExpansion`(currentMemSize, memOffset, memLength: Natural): GasInt {.inline.} =
|
||||||
@ -151,7 +153,6 @@ template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyp
|
|||||||
## Computes all but 1/64th
|
## Computes all but 1/64th
|
||||||
## L(n) ≡ n − ⌊n/64⌋ - (floored(n/64))
|
## L(n) ≡ n − ⌊n/64⌋ - (floored(n/64))
|
||||||
# Introduced in EIP-150 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
# Introduced in EIP-150 - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
||||||
# TODO: deactivate it pre-EIP150
|
|
||||||
|
|
||||||
# Note: The all-but-one-64th calculation should occur after the memory expansion fee is taken
|
# Note: The all-but-one-64th calculation should occur after the memory expansion fee is taken
|
||||||
# https://github.com/ethereum/yellowpaper/pull/442
|
# https://github.com/ethereum/yellowpaper/pull/442
|
||||||
@ -291,10 +292,17 @@ template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyp
|
|||||||
gasParams.c_memLength
|
gasParams.c_memLength
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cnew_account - TODO - pre-EIP158 zero-value call consumed 25000 gas
|
# Cnew_account
|
||||||
# https://github.com/ethereum/eips/issues/158
|
if gasParams.c_isNewAccount:
|
||||||
if gasParams.c_isNewAccount and not value.isZero:
|
if fork < FkSpurious:
|
||||||
result.gasCost += static(FeeSchedule[GasNewAccount])
|
# Pre-EIP161 all account creation calls consumed 25000 gas.
|
||||||
|
result.gasCost += static(FeeSchedule[GasNewAccount])
|
||||||
|
else:
|
||||||
|
# Afterwards, only those transfering value:
|
||||||
|
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-158.md
|
||||||
|
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md
|
||||||
|
if not value.isZero:
|
||||||
|
result.gasCost += static(FeeSchedule[GasNewAccount])
|
||||||
|
|
||||||
# Cxfer
|
# Cxfer
|
||||||
if not value.isZero:
|
if not value.isZero:
|
||||||
@ -305,13 +313,16 @@ template gasCosts(FeeSchedule: GasFeeSchedule, prefix, ResultGasCostsName: untyp
|
|||||||
let cextra = result.gasCost
|
let cextra = result.gasCost
|
||||||
|
|
||||||
# Cgascap
|
# Cgascap
|
||||||
result.gasCost = if gasParams.c_gasBalance >= result.gasCost:
|
if fork >= FkTangerine:
|
||||||
min(
|
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
||||||
`prefix all_but_one_64th`(gasParams.c_gasBalance - result.gasCost),
|
result.gasCost =
|
||||||
gasParams.c_contract_gas
|
if gasParams.c_gasBalance >= result.gasCost:
|
||||||
)
|
min(
|
||||||
else:
|
`prefix all_but_one_64th`(gasParams.c_gasBalance - result.gasCost),
|
||||||
gasParams.c_contract_gas
|
gasParams.c_contract_gas
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
gasParams.c_contract_gas
|
||||||
|
|
||||||
# Ccallgas - Gas sent to the child message
|
# Ccallgas - Gas sent to the child message
|
||||||
result.gasRefund = result.gasCost
|
result.gasRefund = result.gasCost
|
||||||
@ -574,9 +585,20 @@ const
|
|||||||
TangerineGasFees = HomesteadGasFees.tangerineGasFees
|
TangerineGasFees = HomesteadGasFees.tangerineGasFees
|
||||||
SpuriousGasFees = TangerineGasFees.spuriousGasFees
|
SpuriousGasFees = TangerineGasFees.spuriousGasFees
|
||||||
|
|
||||||
gasCosts(BaseGasFees, base, BaseGasCosts)
|
gasFees*: array[Fork, GasFeeSchedule] = [
|
||||||
gasCosts(HomesteadGasFees, homestead, HomesteadGasCosts)
|
FkFrontier: BaseGasFees,
|
||||||
gasCosts(TangerineGasFees, tangerine, TangerineGasCosts)
|
FkThawing: BaseGasFees,
|
||||||
|
FkHomestead: HomesteadGasFees,
|
||||||
|
FkDao: HomesteadGasFees,
|
||||||
|
FkTangerine: TangerineGasFees,
|
||||||
|
FkSpurious: SpuriousGasFees,
|
||||||
|
FkByzantium: SpuriousGasFees, # not supported yet
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
gasCosts(FkFrontier, base, BaseGasCosts)
|
||||||
|
gasCosts(FkHomestead, homestead, HomesteadGasCosts)
|
||||||
|
gasCosts(FkTangerine, tangerine, TangerineGasCosts)
|
||||||
|
|
||||||
proc forkToSchedule*(fork: Fork): GasCosts =
|
proc forkToSchedule*(fork: Fork): GasCosts =
|
||||||
if fork < FkHomestead:
|
if fork < FkHomestead:
|
||||||
|
@ -522,7 +522,7 @@ op create, inline = false, value, startPosition, size:
|
|||||||
computation.vmState.blockHeader.rlphash, false).
|
computation.vmState.blockHeader.rlphash, false).
|
||||||
getBalance(computation.msg.sender)
|
getBalance(computation.msg.sender)
|
||||||
|
|
||||||
if senderBalance >= value:
|
if senderBalance < value:
|
||||||
debug "Computation Failure", reason = "Insufficient funds available to transfer", required = computation.msg.value, balance = senderBalance
|
debug "Computation Failure", reason = "Insufficient funds available to transfer", required = computation.msg.value, balance = senderBalance
|
||||||
push: 0
|
push: 0
|
||||||
return
|
return
|
||||||
|
@ -5,11 +5,10 @@
|
|||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import stint
|
import stint
|
||||||
|
|
||||||
type
|
type
|
||||||
Fork* = enum
|
Fork* = enum
|
||||||
# FkGenesis
|
|
||||||
FkFrontier,
|
FkFrontier,
|
||||||
FkThawing,
|
FkThawing,
|
||||||
FkHomestead,
|
FkHomestead,
|
||||||
@ -18,17 +17,16 @@ type
|
|||||||
FkSpurious,
|
FkSpurious,
|
||||||
FkByzantium
|
FkByzantium
|
||||||
|
|
||||||
UInt256Pair = tuple[a: Uint256, b: Uint256]
|
const
|
||||||
|
forkBlocks*: array[Fork, Uint256] = [
|
||||||
let forkBlocks: array[Fork, Uint256] = [
|
FkFrontier: 1.u256, # 30/07/2015 19:26:28
|
||||||
FkFrontier: 1.u256, # 30/07/2015 19:26:28
|
FkThawing: 200_000.u256, # 08/09/2015 01:33:09
|
||||||
FkThawing: 200_000.u256, # 08/09/2015 01:33:09
|
FkHomestead: 1_150_000.u256, # 14/03/2016 20:49:53
|
||||||
FkHomestead: 1_150_000.u256, # 14/03/2016 20:49:53
|
FkDao: 1_920_000.u256, # 20/07/2016 17:20:40
|
||||||
FkDao: 1_920_000.u256, # 20/07/2016 17:20:40
|
FkTangerine: 2_463_000.u256, # 18/10/2016 17:19:31
|
||||||
FkTangerine: 2_463_000.u256, # 18/10/2016 17:19:31
|
FkSpurious: 2_675_000.u256, # 22/11/2016 18:15:44
|
||||||
FkSpurious: 2_675_000.u256, # 22/11/2016 18:15:44
|
FkByzantium: 4_370_000.u256 # 16/10/2017 09:22:11
|
||||||
FkByzantium: 4_370_000.u256 # 16/10/2017 09:22:11
|
]
|
||||||
]
|
|
||||||
|
|
||||||
proc toFork*(blockNumber: UInt256): Fork =
|
proc toFork*(blockNumber: UInt256): Fork =
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ func invalidInstruction*(computation: var BaseComputation) {.inline.} =
|
|||||||
raise newException(ValueError, "Invalid instruction, received an opcode not implemented in the current fork.")
|
raise newException(ValueError, "Invalid instruction, received an opcode not implemented in the current fork.")
|
||||||
|
|
||||||
let FrontierOpDispatch {.compileTime.}: array[Op, NimNode] = block:
|
let FrontierOpDispatch {.compileTime.}: array[Op, NimNode] = block:
|
||||||
fill_enum_table_holes(Op, newIdentNode"invalidInstruction"):
|
fill_enum_table_holes(Op, newIdentNode("invalidInstruction")):
|
||||||
[
|
[
|
||||||
Stop: newIdentNode "toBeReplacedByBreak",
|
Stop: newIdentNode "toBeReplacedByBreak",
|
||||||
Add: newIdentNode "add",
|
Add: newIdentNode "add",
|
||||||
@ -235,20 +235,22 @@ proc frontierVM(computation: var BaseComputation) =
|
|||||||
|
|
||||||
proc updateOpcodeExec*(computation: var BaseComputation, fork: Fork) =
|
proc updateOpcodeExec*(computation: var BaseComputation, fork: Fork) =
|
||||||
case fork
|
case fork
|
||||||
of FkFrontier:
|
of FkFrontier..FkSpurious:
|
||||||
computation.opCodeExec = frontierVM
|
computation.opCodeExec = frontierVM
|
||||||
computation.frontierVM()
|
computation.frontierVM()
|
||||||
else:
|
else:
|
||||||
raise newException(VMError, "Unknown or not implemented fork: " & $fork)
|
raise newException(VMError, "Unknown or not implemented fork: " & $fork)
|
||||||
|
|
||||||
proc updateOpcodeExec*(computation: var BaseComputation) =
|
proc updateOpcodeExec*(computation: var BaseComputation) =
|
||||||
let fork = computation.vmState.blockHeader.blockNumber.toFork
|
let fork = computation.getFork
|
||||||
computation.updateOpcodeExec(fork)
|
computation.updateOpcodeExec(fork)
|
||||||
|
|
||||||
proc executeOpcodes*(computation: var BaseComputation) =
|
proc executeOpcodes*(computation: var BaseComputation) =
|
||||||
# TODO: Optimise getting fork and updating opCodeExec only when necessary
|
# TODO: Optimise getting fork and updating opCodeExec only when necessary
|
||||||
let fork = computation.vmState.blockHeader.blockNumber.toFork
|
let fork = computation.getFork
|
||||||
try:
|
try:
|
||||||
computation.updateOpcodeExec(fork)
|
computation.updateOpcodeExec(fork)
|
||||||
except VMError:
|
except VMError:
|
||||||
computation.error = Error(info: getCurrentExceptionMsg())
|
computation.error = Error(info: getCurrentExceptionMsg())
|
||||||
|
debug "executeOpcodes() failed", error = getCurrentExceptionMsg()
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
ranges/typedranges, sequtils, strformat, tables,
|
ranges/typedranges, sequtils, strformat, tables, options,
|
||||||
eth_common, chronicles,
|
eth_common, chronicles,
|
||||||
./constants, ./errors, ./vm/computation,
|
./constants, ./errors, ./vm/computation,
|
||||||
./transaction, ./vm_types, ./vm_state, ./block_types, ./db/[db_chain, state_db], ./utils/header,
|
./transaction, ./vm_types, ./vm_state, ./block_types, ./db/[db_chain, state_db], ./utils/header,
|
||||||
./vm/interpreter, ./utils/addresses
|
./vm/interpreter, ./vm/interpreter/gas_costs, ./utils/addresses
|
||||||
|
|
||||||
func intrinsicGas*(data: openarray[byte]): GasInt =
|
func intrinsicGas*(data: openarray[byte]): GasInt =
|
||||||
result = 21_000
|
result = 21_000
|
||||||
@ -33,7 +33,7 @@ proc validateTransaction*(vmState: BaseVMState, transaction: Transaction, sender
|
|||||||
transaction.accountNonce == readOnlyDB.getNonce(sender) and
|
transaction.accountNonce == readOnlyDB.getNonce(sender) and
|
||||||
readOnlyDB.getBalance(sender) >= gas_cost
|
readOnlyDB.getBalance(sender) >= gas_cost
|
||||||
|
|
||||||
proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: Transaction, sender: EthAddress) : BaseComputation =
|
proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: Transaction, sender: EthAddress, forkOverride=none(Fork)) : BaseComputation =
|
||||||
let message = newMessage(
|
let message = newMessage(
|
||||||
gas = transaction.gasLimit - transaction.payload.intrinsicGas,
|
gas = transaction.gasLimit - transaction.payload.intrinsicGas,
|
||||||
gasPrice = transaction.gasPrice,
|
gasPrice = transaction.gasPrice,
|
||||||
@ -45,7 +45,7 @@ proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: T
|
|||||||
options = newMessageOptions(origin = sender,
|
options = newMessageOptions(origin = sender,
|
||||||
createAddress = transaction.to))
|
createAddress = transaction.to))
|
||||||
|
|
||||||
result = newBaseComputation(vmState, header.blockNumber, message)
|
result = newBaseComputation(vmState, header.blockNumber, message, forkOverride)
|
||||||
doAssert result.isOriginComputation
|
doAssert result.isOriginComputation
|
||||||
|
|
||||||
proc execComputation*(computation: var BaseComputation): bool =
|
proc execComputation*(computation: var BaseComputation): bool =
|
||||||
@ -59,19 +59,24 @@ proc execComputation*(computation: var BaseComputation): bool =
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: BaseVMState, sender: EthAddress, useHomestead: bool = false): UInt256 =
|
proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: BaseVMState, sender: EthAddress, forkOverride=none(Fork)): UInt256 =
|
||||||
doAssert t.isContractCreation
|
doAssert t.isContractCreation
|
||||||
# TODO: clean up params
|
# TODO: clean up params
|
||||||
trace "Contract creation"
|
trace "Contract creation"
|
||||||
|
|
||||||
let gasUsed = t.payload.intrinsicGas.GasInt + (if useHomestead: 32000 else: 0)
|
let fork =
|
||||||
|
if forkOverride.isSome:
|
||||||
|
forkOverride.get
|
||||||
|
else:
|
||||||
|
vmState.blockNumber.toFork
|
||||||
|
let gasUsed = t.payload.intrinsicGas.GasInt + gasFees[fork][GasTXCreate]
|
||||||
|
|
||||||
# TODO: setupComputation refactoring
|
# TODO: setupComputation refactoring
|
||||||
let contractAddress = generateAddress(sender, t.accountNonce)
|
let contractAddress = generateAddress(sender, t.accountNonce)
|
||||||
let msg = newMessage(t.gasLimit - gasUsed, t.gasPrice, t.to, sender, t.value, @[], t.payload,
|
let msg = newMessage(t.gasLimit - gasUsed, t.gasPrice, t.to, sender, t.value, @[], t.payload,
|
||||||
options = newMessageOptions(origin = sender,
|
options = newMessageOptions(origin = sender,
|
||||||
createAddress = contractAddress))
|
createAddress = contractAddress))
|
||||||
var c = newBaseComputation(vmState, vmState.blockNumber, msg)
|
var c = newBaseComputation(vmState, vmState.blockNumber, msg, forkOverride)
|
||||||
|
|
||||||
if execComputation(c):
|
if execComputation(c):
|
||||||
db.addBalance(contractAddress, t.value)
|
db.addBalance(contractAddress, t.value)
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
tables, eth_common,
|
tables, eth_common, options,
|
||||||
./constants, json,
|
./constants, json,
|
||||||
./vm/[memory, stack, code_stream],
|
./vm/[memory, stack, code_stream],
|
||||||
./vm/interpreter/[gas_costs, opcode_values], # TODO - will be hidden at a lower layer
|
./vm/interpreter/[gas_costs, opcode_values, vm_forks], # TODO - will be hidden at a lower layer
|
||||||
./db/db_chain
|
./db/db_chain
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -60,6 +60,7 @@ type
|
|||||||
gasCosts*: GasCosts # TODO - will be hidden at a lower layer
|
gasCosts*: GasCosts # TODO - will be hidden at a lower layer
|
||||||
opCodeExec*: OpcodeExecutor
|
opCodeExec*: OpcodeExecutor
|
||||||
lastOpCodeHasRetVal*: bool
|
lastOpCodeHasRetVal*: bool
|
||||||
|
forkOverride*: Option[Fork]
|
||||||
|
|
||||||
Error* = ref object
|
Error* = ref object
|
||||||
info*: string
|
info*: string
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import
|
import
|
||||||
unittest, strformat, strutils, tables, json, ospaths, times,
|
unittest, strformat, strutils, tables, json, ospaths, times,
|
||||||
byteutils, ranges/typedranges, nimcrypto/[keccak, hash],
|
byteutils, ranges/typedranges, nimcrypto/[keccak, hash], options,
|
||||||
rlp, eth_trie/db, eth_common,
|
rlp, eth_trie/db, eth_common,
|
||||||
eth_keys,
|
eth_keys,
|
||||||
./test_helpers,
|
./test_helpers,
|
||||||
@ -23,14 +23,15 @@ suite "generalstate json tests":
|
|||||||
jsonTest("GeneralStateTests", testFixture)
|
jsonTest("GeneralStateTests", testFixture)
|
||||||
|
|
||||||
|
|
||||||
proc testFixtureIndexes(header: BlockHeader, pre: JsonNode, transaction: Transaction, sender: EthAddress, expectedHash: string) =
|
proc testFixtureIndexes(header: BlockHeader, pre: JsonNode, transaction: Transaction, sender: EthAddress, expectedHash: string, testStatusIMPL: var TestStatus, fork: Fork) =
|
||||||
var vmState = newBaseVMState(header, newBaseChainDB(newMemoryDb()))
|
var vmState = newBaseVMState(header, newBaseChainDB(newMemoryDb()))
|
||||||
vmState.mutateStateDB:
|
vmState.mutateStateDB:
|
||||||
setupStateDB(pre, db)
|
setupStateDB(pre, db)
|
||||||
|
|
||||||
defer:
|
defer:
|
||||||
#echo vmState.readOnlyStateDB.dumpAccount("c94f5374fce5edbc8e2a8697c15331677e6ebf0b")
|
#echo vmState.readOnlyStateDB.dumpAccount("c94f5374fce5edbc8e2a8697c15331677e6ebf0b")
|
||||||
doAssert "0x" & `$`(vmState.readOnlyStateDB.rootHash).toLowerAscii == expectedHash
|
let obtainedHash = "0x" & `$`(vmState.readOnlyStateDB.rootHash).toLowerAscii
|
||||||
|
check obtainedHash == expectedHash
|
||||||
|
|
||||||
if not validateTransaction(vmState, transaction, sender):
|
if not validateTransaction(vmState, transaction, sender):
|
||||||
vmState.mutateStateDB:
|
vmState.mutateStateDB:
|
||||||
@ -56,10 +57,10 @@ proc testFixtureIndexes(header: BlockHeader, pre: JsonNode, transaction: Transac
|
|||||||
# fixtures/GeneralStateTests/stTransactionTest/TransactionSendingToEmpty.json
|
# fixtures/GeneralStateTests/stTransactionTest/TransactionSendingToEmpty.json
|
||||||
#db.addBalance(generateAddress(sender, transaction.accountNonce), transaction.value)
|
#db.addBalance(generateAddress(sender, transaction.accountNonce), transaction.value)
|
||||||
|
|
||||||
let createGasUsed = applyCreateTransaction(db, transaction, vmState, sender, true)
|
let createGasUsed = applyCreateTransaction(db, transaction, vmState, sender, some(fork))
|
||||||
db.addBalance(header.coinbase, createGasUsed)
|
db.addBalance(header.coinbase, createGasUsed)
|
||||||
return
|
return
|
||||||
var computation = setupComputation(header, vmState, transaction, sender)
|
var computation = setupComputation(header, vmState, transaction, sender, some(fork))
|
||||||
|
|
||||||
vmState.mutateStateDB:
|
vmState.mutateStateDB:
|
||||||
# contract creation transaction.to == 0, so ensure happens after
|
# contract creation transaction.to == 0, so ensure happens after
|
||||||
@ -108,13 +109,16 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
|||||||
)
|
)
|
||||||
|
|
||||||
let ftrans = fixture["transaction"]
|
let ftrans = fixture["transaction"]
|
||||||
for expectation in fixture["post"]["Homestead"]:
|
for fork in supportedForks:
|
||||||
let
|
if fixture["post"].has_key(forkNames[fork]):
|
||||||
expectedHash = expectation["hash"].getStr
|
# echo "[fork: ", forkNames[fork], "]"
|
||||||
indexes = expectation["indexes"]
|
for expectation in fixture["post"][forkNames[fork]]:
|
||||||
dataIndex = indexes["data"].getInt
|
let
|
||||||
gasIndex = indexes["gas"].getInt
|
expectedHash = expectation["hash"].getStr
|
||||||
valueIndex = indexes["value"].getInt
|
indexes = expectation["indexes"]
|
||||||
let transaction = ftrans.getFixtureTransaction(dataIndex, gasIndex, valueIndex)
|
dataIndex = indexes["data"].getInt
|
||||||
let sender = ftrans.getFixtureTransactionSender
|
gasIndex = indexes["gas"].getInt
|
||||||
testFixtureIndexes(header, fixture["pre"], transaction, sender, expectedHash)
|
valueIndex = indexes["value"].getInt
|
||||||
|
let transaction = ftrans.getFixtureTransaction(dataIndex, gasIndex, valueIndex)
|
||||||
|
let sender = ftrans.getFixtureTransactionSender
|
||||||
|
testFixtureIndexes(header, fixture["pre"], transaction, sender, expectedHash, testStatusIMPL, fork)
|
||||||
|
@ -14,23 +14,36 @@ import
|
|||||||
../nimbus/vm/interpreter/[gas_costs, vm_forks],
|
../nimbus/vm/interpreter/[gas_costs, vm_forks],
|
||||||
../tests/test_generalstate_failing
|
../tests/test_generalstate_failing
|
||||||
|
|
||||||
|
const
|
||||||
|
# from https://ethereum-tests.readthedocs.io/en/latest/test_types/state_tests.html
|
||||||
|
forkNames* = {
|
||||||
|
FkFrontier: "Frontier",
|
||||||
|
FkHomestead: "Homestead",
|
||||||
|
FkTangerine: "EIP150",
|
||||||
|
FkSpurious: "EIP158",
|
||||||
|
FkByzantium: "Byzantium",
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
supportedForks* = [FkHomestead]
|
||||||
|
|
||||||
type
|
type
|
||||||
Status* {.pure.} = enum OK, Fail, Skip
|
Status* {.pure.} = enum OK, Fail, Skip
|
||||||
|
|
||||||
func slowTest*(folder: string, name: string): bool =
|
func slowTest*(folder: string, name: string): bool =
|
||||||
# TODO: add vmPerformance and loop check here
|
result =
|
||||||
result = folder == "stQuadraticComplexityTest" or
|
(folder == "vmPerformance" and "loop" in name) or
|
||||||
name in @["randomStatetest352.json", "randomStatetest1.json",
|
folder == "stQuadraticComplexityTest" or
|
||||||
"randomStatetest32.json", "randomStatetest347.json",
|
name in @["randomStatetest352.json", "randomStatetest1.json",
|
||||||
"randomStatetest393.json", "randomStatetest626.json",
|
"randomStatetest32.json", "randomStatetest347.json",
|
||||||
"CALLCODE_Bounds.json", "DELEGATECALL_Bounds3.json",
|
"randomStatetest393.json", "randomStatetest626.json",
|
||||||
"CALLCODE_Bounds4.json", "CALL_Bounds.json",
|
"CALLCODE_Bounds.json", "DELEGATECALL_Bounds3.json",
|
||||||
"DELEGATECALL_Bounds2.json", "CALL_Bounds3.json",
|
"CALLCODE_Bounds4.json", "CALL_Bounds.json",
|
||||||
"CALLCODE_Bounds2.json", "CALLCODE_Bounds3.json",
|
"DELEGATECALL_Bounds2.json", "CALL_Bounds3.json",
|
||||||
"DELEGATECALL_Bounds.json", "CALL_Bounds2a.json",
|
"CALLCODE_Bounds2.json", "CALLCODE_Bounds3.json",
|
||||||
"CALL_Bounds2.json",
|
"DELEGATECALL_Bounds.json", "CALL_Bounds2a.json",
|
||||||
"CallToNameRegistratorMemOOGAndInsufficientBalance.json",
|
"CALL_Bounds2.json",
|
||||||
"CallToNameRegistratorTooMuchMemory0.json"]
|
"CallToNameRegistratorMemOOGAndInsufficientBalance.json",
|
||||||
|
"CallToNameRegistratorTooMuchMemory0.json"]
|
||||||
|
|
||||||
func failIn32Bits(folder, name: string): bool =
|
func failIn32Bits(folder, name: string): bool =
|
||||||
return name in @[
|
return name in @[
|
||||||
@ -82,7 +95,7 @@ func failIn32Bits(folder, name: string): bool =
|
|||||||
"returndatasize_initial_zero_read.json",
|
"returndatasize_initial_zero_read.json",
|
||||||
"call_then_create_successful_then_returndatasize.json",
|
"call_then_create_successful_then_returndatasize.json",
|
||||||
"call_outsize_then_create_successful_then_returndatasize.json",
|
"call_outsize_then_create_successful_then_returndatasize.json",
|
||||||
|
|
||||||
"returndatacopy_following_create.json",
|
"returndatacopy_following_create.json",
|
||||||
"returndatacopy_following_revert_in_create.json",
|
"returndatacopy_following_revert_in_create.json",
|
||||||
"returndatacopy_following_successful_create.json",
|
"returndatacopy_following_successful_create.json",
|
||||||
@ -99,26 +112,29 @@ func allowedFailInCurrentBuild(folder, name: string): bool =
|
|||||||
return allowedFailingGeneralStateTest(folder, name)
|
return allowedFailingGeneralStateTest(folder, name)
|
||||||
|
|
||||||
func validTest*(folder: string, name: string): bool =
|
func validTest*(folder: string, name: string): bool =
|
||||||
# tests we want to skip or which segfault will be skipped here
|
# we skip tests that are slow or expected to fail for now
|
||||||
result = (folder != "vmPerformance" or "loop" notin name) and
|
result =
|
||||||
not slowTest(folder, name)
|
not slowTest(folder, name) and
|
||||||
|
not allowedFailInCurrentBuild(folder, name)
|
||||||
|
|
||||||
proc lacksHomesteadPostStates*(filename: string): bool =
|
proc lacksSupportedForks*(filename: string): bool =
|
||||||
# XXX: Until Nimbus supports Byzantine or newer forks, as opposed
|
# XXX: Until Nimbus supports Byzantine or newer forks, as opposed
|
||||||
# to Homestead, ~1k of ~2.5k GeneralStateTests won't work. Nimbus
|
# to Homestead, ~1k of ~2.5k GeneralStateTests won't work.
|
||||||
# supporting Byzantine should trigger removal of this function. A
|
|
||||||
# possible alternate approach of avoiding double-reading fixtures
|
|
||||||
# seemed less than ideal, as by the time that happens, output has
|
|
||||||
# already appeared. Compatible with non-GST fixtures. Will become
|
|
||||||
# expensive once BlockchainTests appear, so try to remove first.
|
|
||||||
let fixtures = parseJSON(readFile(filename))
|
let fixtures = parseJSON(readFile(filename))
|
||||||
var fixture: JsonNode
|
var fixture: JsonNode
|
||||||
for label, child in fixtures:
|
for label, child in fixtures:
|
||||||
fixture = child
|
fixture = child
|
||||||
break
|
break
|
||||||
|
|
||||||
return fixture.kind == JObject and fixture.has_key("transaction") and
|
# not all fixtures make a distinction between forks, so default to accepting
|
||||||
(fixture.has_key("post") and not fixture["post"].has_key("Homestead"))
|
# them all, until we find the ones that specify forks in their "post" section
|
||||||
|
result = false
|
||||||
|
if fixture.kind == JObject and fixture.has_key("transaction") and fixture.has_key("post"):
|
||||||
|
result = true
|
||||||
|
for fork in supportedForks:
|
||||||
|
if fixture["post"].has_key(forkNames[fork]):
|
||||||
|
result = false
|
||||||
|
break
|
||||||
|
|
||||||
macro jsonTest*(s: static[string], handler: untyped): untyped =
|
macro jsonTest*(s: static[string], handler: untyped): untyped =
|
||||||
let
|
let
|
||||||
@ -128,30 +144,29 @@ macro jsonTest*(s: static[string], handler: untyped): untyped =
|
|||||||
final = newIdentNode"final"
|
final = newIdentNode"final"
|
||||||
name = newIdentNode"name"
|
name = newIdentNode"name"
|
||||||
formatted = newStrLitNode"{symbol[final]} {name:<64}{$final}{'\n'}"
|
formatted = newStrLitNode"{symbol[final]} {name:<64}{$final}{'\n'}"
|
||||||
|
|
||||||
result = quote:
|
result = quote:
|
||||||
var filenames: seq[(string, string, string)] = @[]
|
var filenames: seq[(string, string, string)] = @[]
|
||||||
var status = initOrderedTable[string, OrderedTable[string, Status]]()
|
var status = initOrderedTable[string, OrderedTable[string, Status]]()
|
||||||
for filename in walkDirRec("tests" / "fixtures" / `s`):
|
for filename in walkDirRec("tests" / "fixtures" / `s`):
|
||||||
|
if not filename.endsWith(".json"):
|
||||||
|
continue
|
||||||
var (folder, name) = filename.splitPath()
|
var (folder, name) = filename.splitPath()
|
||||||
let last = folder.splitPath().tail
|
let last = folder.splitPath().tail
|
||||||
if not status.hasKey(last):
|
if not status.hasKey(last):
|
||||||
status[last] = initOrderedTable[string, Status]()
|
status[last] = initOrderedTable[string, Status]()
|
||||||
status[last][name] = Status.Skip
|
status[last][name] = Status.Skip
|
||||||
if last.validTest(name) and not filename.lacksHomesteadPostStates:
|
if last.validTest(name) and not filename.lacksSupportedForks:
|
||||||
filenames.add((filename, last, name))
|
filenames.add((filename, last, name))
|
||||||
for child in filenames:
|
for child in filenames:
|
||||||
let (filename, folder, name) = child
|
let (filename, folder, name) = child
|
||||||
|
# we set this here because exceptions might be raised in the handler:
|
||||||
|
status[folder][name] = Status.Fail
|
||||||
test filename:
|
test filename:
|
||||||
echo folder / name
|
echo folder / name
|
||||||
status[folder][name] = Status.FAIL
|
`handler`(parseJSON(readFile(filename)), `testStatusIMPL`)
|
||||||
try:
|
if `testStatusIMPL` == OK:
|
||||||
`handler`(parseJSON(readFile(filename)), `testStatusIMPL`)
|
status[folder][name] = Status.OK
|
||||||
if `testStatusIMPL` == OK:
|
|
||||||
status[folder][name] = Status.OK
|
|
||||||
except AssertionError:
|
|
||||||
status[folder][name] = Status.FAIL
|
|
||||||
if not allowedFailInCurrentBuild(folder, name):
|
|
||||||
raise
|
|
||||||
|
|
||||||
status.sort do (a: (string, OrderedTable[string, Status]),
|
status.sort do (a: (string, OrderedTable[string, Status]),
|
||||||
b: (string, OrderedTable[string, Status])) -> int: cmp(a[0], b[0])
|
b: (string, OrderedTable[string, Status])) -> int: cmp(a[0], b[0])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user