1st gas refactoring - handle different gas costs in Ethereum forks (#37)
* Start by renaming the fork by chronological order * initial commit - groundwork: support different gasCosts depending on VMs * Running VMTests with new gas config - only push32AndSuicide changed * Introduce newNimbusVM that (temporarily) abstract over Frontier and Tangerine VM creation * Fix Gas - push32AndSuicide repassing and expXY_success passing - fixes #34 * case sensitive imports (thank you auto-completion) fix Travis on Linux * Add pre and post EIP-150 tests * Nimify gas identifiers that supports multiple forks
This commit is contained in:
parent
762b0b24d2
commit
0b6fbbad67
|
@ -116,7 +116,7 @@ VMTests
|
|||
+ expPowerOf2_64.json OK
|
||||
+ expPowerOf2_8.json OK
|
||||
- expXY.json Fail
|
||||
- expXY_success.json Fail
|
||||
+ expXY_success.json OK
|
||||
+ fibbonacci_unrolled.json OK
|
||||
+ mod0.json OK
|
||||
+ mod1.json OK
|
||||
|
@ -198,7 +198,7 @@ VMTests
|
|||
+ sub3.json OK
|
||||
+ sub4.json OK
|
||||
```
|
||||
OK: 188/195 Fail: 6/195 Skip: 1/195
|
||||
OK: 189/195 Fail: 5/195 Skip: 1/195
|
||||
## vmBitwiseLogicOperation
|
||||
```diff
|
||||
+ and0.json OK
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import
|
||||
tables, stint,
|
||||
./logging, ./constants, ./errors, ./validation, ./utils/hexadecimal, ./vm/base, ./db/db_chain,
|
||||
./utils/header, ./vm/forks/frontier/vm
|
||||
./utils/header, ./vm/forks/f20150730_frontier/frontier_vm
|
||||
|
||||
type
|
||||
Chain* = ref object
|
||||
|
@ -74,8 +74,11 @@ proc fromGenesis*(
|
|||
# TODO
|
||||
# chainDB.persistBlockToDB(result.getBlock)
|
||||
|
||||
|
||||
proc getVMClassForBlockNumber*(chain: Chain, blockNumber: UInt256): VMKind =
|
||||
## Returns the VM class for the given block number
|
||||
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
|
||||
# TODO - Refactoring: redundant with constants.nim `toFork`
|
||||
# TODO should the return value be a typedesc?
|
||||
|
||||
# TODO: validate_block_number
|
||||
|
@ -86,9 +89,12 @@ proc getVMClassForBlockNumber*(chain: Chain, blockNumber: UInt256): VMKind =
|
|||
|
||||
raise newException(ValueError, "VM not found for block #" & $blockNumber) # TODO: VMNotFound exception
|
||||
|
||||
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
|
||||
|
||||
proc getVM*(chain: Chain, header: BlockHeader = nil): VM =
|
||||
## Returns the VM instance for the given block number
|
||||
|
||||
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
|
||||
# TODO - Refactoring: redundant with constants.nim `toFork`
|
||||
# shadowing input param
|
||||
let header = if header.isNil: chain.header
|
||||
else: header
|
||||
|
|
|
@ -8,7 +8,12 @@
|
|||
import
|
||||
strformat, strutils, sequtils, tables, macros, stint, terminal,
|
||||
constants, errors, utils/hexadecimal, utils_numeric, validation, vm_state, logging, opcode_values, types,
|
||||
vm / [code_stream, gas_meter, memory, message, stack]
|
||||
vm / [code_stream, gas_meter, memory, message, stack],
|
||||
|
||||
# TODO further refactoring of gas cost
|
||||
vm/forks/gas_costs,
|
||||
vm/forks/f20150730_frontier/frontier_vm_state,
|
||||
vm/forks/f20161018_tangerine_whistle/tangerine_vm_state
|
||||
|
||||
proc memoryGasCost*(sizeInBytes: UInt256): UInt256 =
|
||||
let
|
||||
|
@ -20,7 +25,11 @@ proc memoryGasCost*(sizeInBytes: UInt256): UInt256 =
|
|||
|
||||
#const VARIABLE_GAS_COST_OPS* = {Op.Exp}
|
||||
|
||||
proc newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputation =
|
||||
method newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputation {.base.}=
|
||||
raise newException(ValueError, "Must be implemented by subclasses")
|
||||
|
||||
# TODO refactor that
|
||||
method newBaseComputation*(vmState: FrontierVMState, message: Message): BaseComputation =
|
||||
new(result)
|
||||
result.vmState = vmState
|
||||
result.msg = message
|
||||
|
@ -32,6 +41,21 @@ proc newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputatio
|
|||
result.logEntries = @[]
|
||||
result.code = newCodeStreamFromUnescaped(message.code) # TODO: what is the best repr
|
||||
result.rawOutput = "0x"
|
||||
result.gasCosts = BaseGasCosts
|
||||
|
||||
method newBaseComputation*(vmState: TangerineVMState, message: Message): BaseComputation =
|
||||
new(result)
|
||||
result.vmState = vmState
|
||||
result.msg = message
|
||||
result.memory = Memory()
|
||||
result.stack = newStack()
|
||||
result.gasMeter = newGasMeter(message.gas)
|
||||
result.children = @[]
|
||||
result.accountsToDelete = initTable[string, string]()
|
||||
result.logEntries = @[]
|
||||
result.code = newCodeStreamFromUnescaped(message.code) # TODO: what is the best repr
|
||||
result.rawOutput = "0x"
|
||||
result.gasCosts = TangerineGasCosts
|
||||
|
||||
method logger*(computation: BaseComputation): Logger =
|
||||
logging.getLogger("vm.computation.BaseComputation")
|
||||
|
|
|
@ -129,38 +129,38 @@ let
|
|||
ZERO_HASH32* = repeat("\x00", 20)
|
||||
STACK_DEPTH_LIMIT* = 1024
|
||||
|
||||
GAS_NULL* = 0.u256
|
||||
GAS_ZERO* = 0.u256
|
||||
GAS_BASE* = 2.u256
|
||||
GAS_VERY_LOW* = 3.u256
|
||||
GAS_LOW* = 5.u256
|
||||
GAS_MID* = 8.u256
|
||||
GAS_HIGH* = 10.u256
|
||||
GAS_EXT_CODE* = 20.u256
|
||||
GAS_BALANCE* = 20.u256
|
||||
GAS_SLOAD* = 200.u256 # TODO: pre eip150
|
||||
GAS_JUMP_DEST* = 1.u256
|
||||
GAS_SSET* = 20_000.u256
|
||||
GAS_SRESET* = 5_000.u256
|
||||
GAS_EXT_CODE_COST* = 700.u256
|
||||
GAS_COINBASE* = 20.u256
|
||||
GAS_SLOAD_COST* = 20.u256
|
||||
GAS_SELF_DESTRUCT_COST* = 0.u256
|
||||
GAS_IN_HANDLER* = 0.u256 # to be calculated in handler
|
||||
# GAS_NULL* = 0.u256
|
||||
# GAS_ZERO* = 0.u256
|
||||
# GAS_BASE* = 2.u256
|
||||
# GAS_VERY_LOW* = 3.u256
|
||||
# GAS_LOW* = 5.u256
|
||||
# GAS_MID* = 8.u256
|
||||
# GAS_HIGH* = 10.u256
|
||||
# GAS_EXT_CODE* = 20.u256 # TODO: this is pre-eip150, see also GAS_EXT_CODE_COST
|
||||
# GAS_BALANCE* = 20.u256 # TODO: this is pre-eip150, see also GAS_COST_BALANCE
|
||||
# GAS_SLOAD* = 200.u256 # TODO: pre eip150
|
||||
# GAS_JUMP_DEST* = 1.u256
|
||||
# GAS_SSET* = 20_000.u256
|
||||
# GAS_SRESET* = 5_000.u256
|
||||
# GAS_EXT_CODE_COST* = 700.u256
|
||||
# GAS_COINBASE* = 20.u256
|
||||
# GAS_SLOAD_COST* = 20.u256
|
||||
# GAS_SELF_DESTRUCT_COST* = 0.u256
|
||||
# GAS_IN_HANDLER* = 0.u256 # to be calculated in handler
|
||||
REFUND_SCLEAR* = 15_000.u256
|
||||
|
||||
GAS_SELF_DESTRUCT* = 0.u256
|
||||
GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.u256
|
||||
# GAS_SELF_DESTRUCT* = 0.u256
|
||||
# GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.u256
|
||||
GAS_CREATE* = 32_000.u256
|
||||
GAS_CALL* = 40.u256
|
||||
# GAS_CALL* = 40.u256
|
||||
GAS_CALL_VALUE* = 9_000.u256
|
||||
GAS_CALL_STIPEND* = 2_300.u256
|
||||
GAS_NEW_ACCOUNT* = 25_000.u256
|
||||
|
||||
GAS_COST_BALANCE* = 400.u256
|
||||
# GAS_COST_BALANCE* = 400.u256 # TODO: this is post-eip150, see also GAS_BALANCE
|
||||
|
||||
GAS_EXP* = 10.u256
|
||||
GAS_EXP_BYTE* = 10.u256
|
||||
# GAS_EXP* = 10.u256
|
||||
# GAS_EXP_BYTE* = 10.u256
|
||||
GAS_MEMORY* = 3.u256
|
||||
GAS_TX_CREATE* = 32_000.u256
|
||||
GAS_TX_DATA_ZERO* = 4.u256
|
||||
|
@ -169,7 +169,7 @@ let
|
|||
GAS_LOG* = 375.u256
|
||||
GAS_LOG_DATA* = 8.u256
|
||||
GAS_LOG_TOPIC* = 375.u256
|
||||
GAS_SHA3* = 30.u256
|
||||
# GAS_SHA3* = 30.u256
|
||||
GAS_SHA3_WORD* = 6.u256
|
||||
GAS_COPY* = 3.u256
|
||||
GAS_BLOCK_HASH* = 20.u256
|
||||
|
@ -252,6 +252,8 @@ proc contains*(ab: UInt256Pair, v: UInt256): bool =
|
|||
return v >= ab[0] and v <= ab[1]
|
||||
|
||||
proc toFork*(blockNumber: UInt256): Fork =
|
||||
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
|
||||
# TODO - Refactoring: redundant with `chain.nim` getVM
|
||||
result = fkUnknown
|
||||
let one = u256(1)
|
||||
if blockNumber in u256(0)..FORK_ICEAGE_BLKNUM - one: result = fkFrontier
|
||||
|
|
|
@ -90,9 +90,9 @@ proc exp*(computation: var BaseComputation) =
|
|||
# Exponentiation
|
||||
let (base, exponent) = computation.stack.popInt(2)
|
||||
|
||||
var gasCost = GAS_EXP_BYTE.u256
|
||||
var gasCost = computation.gasCosts[GasExp]
|
||||
if not exponent.isZero:
|
||||
gasCost += GAS_EXP_BYTE * (one(Uint256) + log256(exponent))
|
||||
gasCost += gasCost * (one(Uint256) + log256(exponent))
|
||||
computation.gasMeter.consumeGas(gasCost, reason="EXP: exponent bytes")
|
||||
|
||||
let res = if base.isZero: 0.u256 # 0^0 is 0 in py-evm
|
||||
|
|
|
@ -13,6 +13,8 @@ import
|
|||
stint
|
||||
|
||||
type
|
||||
# TODO most of these are for gas handling
|
||||
|
||||
BaseCall* = ref object of Opcode
|
||||
|
||||
Call* = ref object of BaseCall
|
||||
|
@ -27,12 +29,12 @@ type
|
|||
|
||||
DelegateCallEIP150* = ref object of DelegateCall
|
||||
|
||||
CallEIP161* = ref object of CallEIP150
|
||||
CallEIP161* = ref object of CallEIP150 # TODO: Refactoring - put that in VM forks
|
||||
|
||||
# Byzantium
|
||||
StaticCall* = ref object of CallEIP161
|
||||
StaticCall* = ref object of CallEIP161 # TODO: Refactoring - put that in VM forks
|
||||
|
||||
CallByzantium* = ref object of CallEIP161
|
||||
CallByzantium* = ref object of CallEIP161 # TODO: Refactoring - put that in VM forks
|
||||
|
||||
using
|
||||
computation: var BaseComputation
|
||||
|
@ -50,7 +52,7 @@ method callParams*(call: BaseCall, computation): (UInt256, UInt256, string, stri
|
|||
raise newException(NotImplementedError, "Must be implemented subclasses")
|
||||
|
||||
method runLogic*(call: BaseCall, computation) =
|
||||
computation.gasMeter.consumeGas(call.gasCost(computation), reason = $call.kind)
|
||||
computation.gasMeter.consumeGas(computation.gasCosts[call.gasCost(computation)], reason = $call.kind) # TODO: Refactoring call gas costs
|
||||
let (gas, value, to, sender,
|
||||
codeAddress,
|
||||
memoryInputStartPosition, memoryInputSize,
|
||||
|
|
|
@ -32,7 +32,7 @@ proc sstore*(computation) =
|
|||
let gasRefund = if isCurrentlyEmpty or not isGoingToBeEmpty: 0.u256 else: REFUND_SCLEAR
|
||||
let gasCost = if isCurrentlyEmpty and not isGoingToBeEmpty: GAS_SSET else: GAS_SRESET
|
||||
|
||||
computation.gasMeter.consumeGas(gasCost, &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} ({currentValue})")
|
||||
computation.gasMeter.consumeGas(computation.gasCosts[gasCost], &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} ({currentValue})")
|
||||
|
||||
if gasRefund > 0: computation.gasMeter.refundGas(gasRefund)
|
||||
|
||||
|
|
|
@ -20,15 +20,15 @@ using
|
|||
type
|
||||
Create* = ref object of Opcode
|
||||
|
||||
CreateEIP150* = ref object of Create
|
||||
CreateEIP150* = ref object of Create # TODO: Refactoring - put that in VM forks
|
||||
|
||||
CreateByzantium* = ref object of CreateEIP150
|
||||
CreateByzantium* = ref object of CreateEIP150 # TODO: Refactoring - put that in VM forks
|
||||
|
||||
method maxChildGasModifier(create: Create, gas: UInt256): UInt256 {.base.} =
|
||||
gas
|
||||
|
||||
method runLogic*(create: Create, computation) =
|
||||
computation.gasMeter.consumeGas(create.gasCost(computation), reason = $create.kind)
|
||||
computation.gasMeter.consumeGas(computation.gasCosts[create.gasCost(computation)], reason = $create.kind) # TODO: Refactoring create gas costs
|
||||
let (value, startPosition, size) = computation.stack.popInt(3)
|
||||
computation.extendMemory(startPosition, size)
|
||||
|
||||
|
|
|
@ -7,35 +7,36 @@
|
|||
|
||||
import
|
||||
strformat, strutils, sequtils, macros,
|
||||
constants, logging, errors, types, opcode_values, computation, vm/stack, stint
|
||||
constants, logging, errors, types, opcode_values, computation, vm/stack, stint,
|
||||
./types
|
||||
|
||||
template run*(opcode: Opcode, computation: var BaseComputation) =
|
||||
# Hook for performing the actual VM execution
|
||||
# opcode.consumeGas(computation)
|
||||
computation.gasMeter.consumeGas(opcode.gasCost(computation), reason = $opcode.kind)
|
||||
computation.gasMeter.consumeGas(computation.gasCosts[opcode.gasCost(computation)], reason = $opcode.kind) # TODO: further refactoring of gas costs
|
||||
opcode.runLogic(computation)
|
||||
|
||||
method logger*(opcode: Opcode): Logger =
|
||||
logging.getLogger(&"vm.opcode.{opcode.kind}")
|
||||
|
||||
method gasCost*(opcode: Opcode, computation: var BaseComputation): UInt256 =
|
||||
method gasCost*(opcode: Opcode, computation: var BaseComputation): GasCostKind =
|
||||
#if opcode.kind in VARIABLE_GAS_COST_OPS:
|
||||
# opcode.gasCostHandler(computation)
|
||||
#else:
|
||||
opcode.gasCostConstant
|
||||
opcode.gasCostKind
|
||||
|
||||
template newOpcode*(kind: Op, gasCost: UInt256, logic: proc(computation: var BaseComputation)): Opcode =
|
||||
Opcode(kind: kind, gasCostConstant: gasCost, runLogic: logic)
|
||||
Opcode(kind: kind, gasCostKind: gasCost, runLogic: logic)
|
||||
|
||||
template newOpcode*(kind: Op, gasHandler: proc(computation: var BaseComputation): UInt256, logic: proc(computation: var BaseComputation)): Opcode =
|
||||
Opcode(kind: kind, gasCostHandler: gasHandler, runLogic: logic)
|
||||
|
||||
method `$`*(opcode: Opcode): string =
|
||||
let gasCost = $opcode.gasCostConstant
|
||||
let gasCost = $opcode.gasCostKind
|
||||
# if opcode.kind in VARIABLE_GAS_COST_OPS:
|
||||
# "variable"
|
||||
# else:
|
||||
# $opcode.gasCostConstant
|
||||
# $opcode.gasCostKind
|
||||
&"{opcode.kind}(0x{opcode.kind.int.toHex(2)}: {gasCost})"
|
||||
|
||||
macro initOpcodes*(spec: untyped): untyped =
|
||||
|
@ -68,7 +69,7 @@ macro initOpcodes*(spec: untyped): untyped =
|
|||
`value`[`op`] = Opcode(kind: `op`, gasCostHandler: `gasCost`, runLogic: `handler`)
|
||||
else:
|
||||
quote:
|
||||
`value`[`op`] = Opcode(kind: `op`, gasCostConstant: `gasCost`, runLogic: `handler`)
|
||||
`value`[`op`] = Opcode(kind: `op`, gasCostKind: `gasCost`, runLogic: `handler`)
|
||||
result[1].add(opcode)
|
||||
|
||||
result[1].add(value)
|
||||
|
|
|
@ -9,103 +9,104 @@ import
|
|||
strformat, strutils, tables, macros,
|
||||
constants, stint, errors, logging, vm_state,
|
||||
vm / [gas_meter, stack, code_stream, memory, message, value], db / db_chain, computation, opcode, opcode_values, utils / [header, address],
|
||||
logic / [arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops]
|
||||
logic / [arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops],
|
||||
./types
|
||||
|
||||
var OPCODE_TABLE* = initOpcodes:
|
||||
# arithmetic
|
||||
Op.Add: GAS_VERY_LOW add
|
||||
Op.Mul: GAS_LOW mul
|
||||
Op.Sub: GAS_VERY_LOW sub
|
||||
Op.Div: GAS_LOW divide
|
||||
Op.SDiv: GAS_LOW sdiv
|
||||
Op.Mod: GAS_LOW modulo
|
||||
Op.SMod: GAS_LOW smod
|
||||
Op.AddMod: GAS_MID addmod
|
||||
Op.MulMod: GAS_MID mulmod
|
||||
Op.Exp: GAS_ZERO arithmetic.exp
|
||||
Op.SignExtend: GAS_LOW signextend
|
||||
Op.Add: GasVeryLow add
|
||||
Op.Mul: GasLow mul
|
||||
Op.Sub: GasVeryLow sub
|
||||
Op.Div: GasLow divide
|
||||
Op.SDiv: GasLow sdiv
|
||||
Op.Mod: GasLow modulo
|
||||
Op.SMod: GasLow smod
|
||||
Op.AddMod: GasMid addmod
|
||||
Op.MulMod: GasMid mulmod
|
||||
Op.Exp: GasInHandler arithmetic.exp
|
||||
Op.SignExtend: GasLow signextend
|
||||
|
||||
|
||||
# comparison
|
||||
Op.Lt: GAS_VERY_LOW lt
|
||||
Op.Gt: GAS_VERY_LOW gt
|
||||
Op.SLt: GAS_VERY_LOW slt
|
||||
Op.SGt: GAS_VERY_LOW sgt
|
||||
Op.Eq: GAS_VERY_LOW eq
|
||||
Op.IsZero: GAS_VERY_LOW iszero
|
||||
Op.And: GAS_VERY_LOW andOp
|
||||
Op.Or: GAS_VERY_LOW orOp
|
||||
Op.Xor: GAS_VERY_LOW xorOp
|
||||
Op.Not: GAS_VERY_LOW notOp
|
||||
Op.Byte: GAS_VERY_LOW byteOp
|
||||
Op.Lt: GasVeryLow lt
|
||||
Op.Gt: GasVeryLow gt
|
||||
Op.SLt: GasVeryLow slt
|
||||
Op.SGt: GasVeryLow sgt
|
||||
Op.Eq: GasVeryLow eq
|
||||
Op.IsZero: GasVeryLow iszero
|
||||
Op.And: GasVeryLow andOp
|
||||
Op.Or: GasVeryLow orOp
|
||||
Op.Xor: GasVeryLow xorOp
|
||||
Op.Not: GasVeryLow notOp
|
||||
Op.Byte: GasVeryLow byteOp
|
||||
|
||||
|
||||
# sha3
|
||||
Op.SHA3: GAS_SHA3 sha3op
|
||||
Op.SHA3: GasSHA3 sha3op
|
||||
|
||||
|
||||
# context
|
||||
Op.Address: GAS_BASE context.address
|
||||
Op.Balance: GAS_COST_BALANCE balance
|
||||
Op.Origin: GAS_BASE origin
|
||||
Op.Caller: GAS_BASE caller
|
||||
Op.CallValue: GAS_BASE callValue
|
||||
Op.CallDataLoad: GAS_VERY_LOW callDataLoad
|
||||
Op.CallDataSize: GAS_BASE callDataSize
|
||||
Op.CallDataCopy: GAS_BASE callDataCopy
|
||||
Op.CodeSize: GAS_BASE codesize
|
||||
Op.CodeCopy: GAS_BASE codecopy
|
||||
Op.ExtCodeSize: GAS_EXT_CODE_COST extCodeSize
|
||||
Op.ExtCodeCopy: GAS_EXT_CODE_COST extCodeCopy
|
||||
Op.Address: GasBase context.address
|
||||
Op.Balance: GasBalance balance
|
||||
Op.Origin: GasBase origin
|
||||
Op.Caller: GasBase caller
|
||||
Op.CallValue: GasBase callValue
|
||||
Op.CallDataLoad: GasVeryLow callDataLoad
|
||||
Op.CallDataSize: GasBase callDataSize
|
||||
Op.CallDataCopy: GasBase callDataCopy
|
||||
Op.CodeSize: GasBase codesize
|
||||
Op.CodeCopy: GasBase codecopy
|
||||
Op.ExtCodeSize: GasExtCode extCodeSize
|
||||
Op.ExtCodeCopy: GasExtCode extCodeCopy
|
||||
|
||||
|
||||
# block
|
||||
Op.Blockhash: GAS_BASE block_ops.blockhash
|
||||
Op.Coinbase: GAS_COINBASE coinbase
|
||||
Op.Timestamp: GAS_BASE timestamp
|
||||
Op.Number: GAS_BASE number
|
||||
Op.Difficulty: GAS_BASE difficulty
|
||||
Op.GasLimit: GAS_BASE gaslimit
|
||||
Op.Blockhash: GasBase block_ops.blockhash
|
||||
Op.Coinbase: GasCoinbase coinbase
|
||||
Op.Timestamp: GasBase timestamp
|
||||
Op.Number: GasBase number
|
||||
Op.Difficulty: GasBase difficulty
|
||||
Op.GasLimit: GasBase gaslimit
|
||||
|
||||
|
||||
# stack
|
||||
Op.Pop: GAS_BASE stack_ops.pop
|
||||
1..32 Op.PushXX: GAS_VERY_LOW pushXX # XX replaced by macro
|
||||
1..16 Op.DupXX: GAS_VERY_LOW dupXX
|
||||
1..16 Op.SwapXX: GAS_VERY_LOW swapXX
|
||||
Op.Pop: GasBase stack_ops.pop
|
||||
1..32 Op.PushXX: GasVeryLow pushXX # XX replaced by macro
|
||||
1..16 Op.DupXX: GasVeryLow dupXX
|
||||
1..16 Op.SwapXX: GasVeryLow swapXX
|
||||
|
||||
|
||||
# memory
|
||||
Op.MLoad: GAS_VERY_LOW mload
|
||||
Op.MStore: GAS_VERY_LOW mstore
|
||||
Op.MStore8: GAS_VERY_LOW mstore8
|
||||
Op.MSize: GAS_BASE msize
|
||||
Op.MLoad: GasVeryLow mload
|
||||
Op.MStore: GasVeryLow mstore
|
||||
Op.MStore8: GasVeryLow mstore8
|
||||
Op.MSize: GasBase msize
|
||||
|
||||
# storage
|
||||
Op.SLoad: GAS_SLOAD sload
|
||||
Op.SStore: GAS_ZERO sstore
|
||||
Op.SLoad: GasSload sload
|
||||
Op.SStore: GasInHandler sstore
|
||||
|
||||
|
||||
# flow
|
||||
Op.Jump: GAS_MID jump
|
||||
Op.JumpI: GAS_MID jumpi
|
||||
Op.PC: GAS_HIGH pc
|
||||
Op.Gas: GAS_BASE flow.gas
|
||||
Op.JumpDest: GAS_JUMP_DEST jumpdest
|
||||
Op.Stop: GAS_ZERO stop
|
||||
Op.Jump: GasMid jump
|
||||
Op.JumpI: GasMid jumpi
|
||||
Op.PC: GasHigh pc
|
||||
Op.Gas: GasBase flow.gas
|
||||
Op.JumpDest: GasJumpDest jumpdest
|
||||
Op.Stop: GasZero stop
|
||||
|
||||
|
||||
# logging
|
||||
0..4 Op.LogXX: GAS_IN_HANDLER logXX
|
||||
0..4 Op.LogXX: GasInHandler logXX
|
||||
|
||||
|
||||
# invalid
|
||||
Op.Invalid: GAS_ZERO invalidOp
|
||||
Op.Invalid: GasZero invalidOp
|
||||
|
||||
|
||||
# system
|
||||
Op.Return: 0.u256 returnOp
|
||||
Op.SelfDestruct: GAS_SELF_DESTRUCT_COST selfdestruct
|
||||
Op.Return: GasZero returnOp
|
||||
Op.SelfDestruct: GasSelfDestruct selfdestruct
|
||||
|
||||
|
||||
# call
|
||||
|
|
|
@ -29,6 +29,7 @@ type
|
|||
accountsToDelete*: Table[string, string]
|
||||
opcodes*: Table[Op, Opcode] # TODO array[Op, Opcode]
|
||||
precompiles*: Table[string, Opcode]
|
||||
gasCosts*: GasCosts # TODO separate opcode processing and gas computation
|
||||
|
||||
Error* = ref object
|
||||
info*: string
|
||||
|
@ -45,5 +46,29 @@ type
|
|||
## it uses the peek methods of the stack and calculates the cost
|
||||
## then it actually pops/pushes stuff in exec
|
||||
## I followed the py-evm approach which does that in opcode logic
|
||||
gasCostConstant*: UInt256
|
||||
gasCostKind*: GasCostKind
|
||||
runLogic*: proc(computation: var BaseComputation)
|
||||
|
||||
GasCostKind* = enum
|
||||
GasZero
|
||||
GasBase
|
||||
GasVeryLow
|
||||
GasLow
|
||||
GasMid
|
||||
GasHigh
|
||||
GasSload
|
||||
GasJumpDest
|
||||
GasSset
|
||||
GasSreset
|
||||
GasExtCode
|
||||
GasCoinbase
|
||||
GasSelfDestruct
|
||||
GasInHandler
|
||||
GasRefundSclear
|
||||
|
||||
GasBalance
|
||||
GasCall
|
||||
GasExp
|
||||
GasSHA3
|
||||
|
||||
GasCosts* = array[GasCostKind, UInt256]
|
||||
|
|
|
@ -25,9 +25,11 @@ type
|
|||
state*: BaseVMState
|
||||
`block`*: Block
|
||||
|
||||
proc newVM*(header: BlockHeader, chainDB: BaseChainDB): VM =
|
||||
new(result)
|
||||
result.chainDB = chainDB
|
||||
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
|
||||
|
||||
# proc newVM*(header: BlockHeader, chainDB: BaseChainDB): VM =
|
||||
# new(result)
|
||||
# result.chainDB = chainDB
|
||||
|
||||
method name*(vm: VM): string =
|
||||
"VM"
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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.
|
||||
|
||||
import
|
||||
../../../logging, ../../../constants, ../../../errors, ../../../transaction,
|
||||
../../../block_types,
|
||||
../../../utils/header
|
||||
|
||||
type
|
||||
TangerineBlock* = ref object of Block
|
||||
transactions*: seq[BaseTransaction]
|
||||
stateRoot*: cstring
|
||||
|
||||
proc makeTangerineBlock*(header: BlockHeader; transactions: seq[BaseTransaction]; uncles: void): TangerineBlock =
|
||||
new result
|
||||
if transactions.len == 0:
|
||||
result.transactions = @[]
|
|
@ -0,0 +1,31 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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.
|
||||
|
||||
import
|
||||
strformat, stint,
|
||||
../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header
|
||||
|
||||
proc validateTangerineTransaction*(vmState: BaseVmState, transaction: BaseTransaction) =
|
||||
let gasCost = transaction.gas * transaction.gasPrice
|
||||
var senderBalance: UInt256
|
||||
# inDB(vmState.stateDB(readOnly=true):
|
||||
# senderBalance = db.getBalance(transaction.sender)
|
||||
senderBalance = gasCost # TODO
|
||||
if senderBalance < gasCost:
|
||||
raise newException(ValidationError, &"Sender account balance cannot afford txn gas: {transaction.sender}")
|
||||
|
||||
let totalCost = transaction.value + gasCost
|
||||
|
||||
if senderBalance < totalCost:
|
||||
raise newException(ValidationError, "Sender account balance cannot afford txn")
|
||||
|
||||
if vmState.blockHeader.gasUsed + transaction.gas > vmState.blockHeader.gasLimit:
|
||||
raise newException(ValidationError, "Transaction exceeds gas limit")
|
||||
|
||||
# inDB(vmState.stateDb(readOnly=true):
|
||||
# if db.getNonce(transaction.sender) != transaction.nonce:
|
||||
# raise newException(ValidationError, "Invalid transaction nonce")
|
|
@ -0,0 +1,37 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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.
|
||||
|
||||
import
|
||||
../../../logging, ../../../constants, ../../../errors,
|
||||
stint,
|
||||
../../../block_types,
|
||||
../../../vm/[base, stack], ../../../db/db_chain, ../../../utils/header,
|
||||
./tangerine_block, ./tangerine_vm_state, ./tangerine_validation
|
||||
|
||||
|
||||
type
|
||||
TangerineVM* = ref object of VM
|
||||
|
||||
method name*(vm: TangerineVM): string =
|
||||
"TangerineVM"
|
||||
|
||||
method getBlockReward(vm: TangerineVM): UInt256 =
|
||||
BLOCK_REWARD
|
||||
|
||||
method getUncleReward(vm: TangerineVM, blockNumber: UInt256, uncle: Block): UInt256 =
|
||||
BLOCK_REWARD * (UNCLE_DEPTH_PENALTY_FACTOR + uncle.blockNumber - blockNumber) div UNCLE_DEPTH_PENALTY_FACTOR
|
||||
|
||||
|
||||
method getNephewReward(vm: TangerineVM): UInt256 =
|
||||
vm.getBlockReward() div 32
|
||||
|
||||
proc newTangerineVM*(header: BlockHeader, chainDB: BaseChainDB): TangerineVM =
|
||||
new(result)
|
||||
result.chainDB = chainDB
|
||||
result.isStateless = true
|
||||
result.state = newTangerineVMState()
|
||||
result.`block` = makeTangerineBlock(header, @[])
|
|
@ -0,0 +1,23 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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.
|
||||
|
||||
import
|
||||
../../../logging, ../../../constants, ../../../errors, ../../../vm_state,
|
||||
../../../utils/header, ../../../db/db_chain
|
||||
|
||||
type
|
||||
TangerineVMState* = ref object of BaseVMState
|
||||
# receipts*:
|
||||
# computationClass*: Any
|
||||
# accessLogs*: AccessLogs
|
||||
|
||||
proc newTangerineVMState*: TangerineVMState =
|
||||
new(result)
|
||||
result.prevHeaders = @[]
|
||||
result.name = "TangerineVM"
|
||||
result.accessLogs = newAccessLogs()
|
||||
result.blockHeader = BlockHeader(hash: "TODO", coinbase: "TODO", stateRoot: "TODO")
|
|
@ -0,0 +1,17 @@
|
|||
# List of Ethereum hard forks with VM impact.
|
||||
|
||||
From https://www.etherchain.org/hardForks
|
||||
|
||||
|
||||
| Name | On Roadmap | Date | Block |
|
||||
| --------------------------- | ------------ | ------------------- | ------- |
|
||||
| Frontier | Yes | 30/07/2015 19:26:28 | 1 |
|
||||
| Frontier Thawing | Yes | 08/09/2015 01:33:09 | 200000 |
|
||||
| Homestead | Yes | 14/03/2016 20:49:53 | 1150000 |
|
||||
| DAO Fork | No | 20/07/2016 17:20:40 | 1920000 |
|
||||
| Tangerine Whistle (EIP-150) | No | 18/10/2016 17:19:31 | 2463000 |
|
||||
| Spurious Dragon | No | 22/11/2016 18:15:44 | 2675000 |
|
||||
| Byzantium | Yes | 16/10/2017 09:22:11 | 4370000 |
|
||||
|
||||
From https://ethereum.stackexchange.com/a/28409
|
||||
![](forks_list.png)
|
Binary file not shown.
After Width: | Height: | Size: 104 KiB |
|
@ -1,28 +0,0 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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.
|
||||
|
||||
import
|
||||
logging, constants, errors, validation, utils/header, vm / forks / frontier / vm
|
||||
|
||||
method computeDifficulty*(parentHeader: BlockHeader, timestamp: int): Int256 =
|
||||
validateGt(timestamp, parentHeader.timestamp, title="BlockHeader timestamp")
|
||||
let offset = parentHeader.difficulty div DIFFICULTY_ADJUSTMENT_DENOMINATOR
|
||||
# We set the minimum to the lowest of the protocol minimum and the parent
|
||||
# minimum to allow for the initial frontier *warming* period during which
|
||||
# the difficulty begins lower than the protocol minimum
|
||||
let difficultyMinimum = min(parentHeader.difficulty, DIFFICULTY_MINIMUM)
|
||||
# let test = (timestamp - parentHeader.timestamp).Int256 < FRONTIER_DIFFICULTY_ADJUSTMENT_CUTOFF
|
||||
# let baseDifficulty = max(parent.BlockHeader.difficulty + (if test: offset else: -offset), difficultyMinimum)
|
||||
# # Adjust for difficulty bomb
|
||||
# let numBombPeriods = ((parentHeader.blockNumber + 1) div BOMB_EXPONENTIAL_PERIOD) - BOMB_EXPONENTIAL_FREE_PERIODS
|
||||
# result = if numBombPeriods >= 0: max(baseDifficulty + 2.Int256 ^ numBombPeriods, DIFFICULTY_MINIMUM) else: baseDifficulty
|
||||
result = 0.Int256
|
||||
|
||||
method createHeaderFromParent*(parentHeader: BlockHeader): BlockHeader =
|
||||
# TODO
|
||||
result = BlockHeader()
|
||||
|
|
@ -1 +0,0 @@
|
|||
p:"../../../"
|
|
@ -0,0 +1,45 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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.
|
||||
|
||||
import
|
||||
stint, ../../types
|
||||
|
||||
# TODO: Make that computation at compile-time.
|
||||
# Go-Ethereum uses pure uint64 for gas computation
|
||||
let BaseGasCosts*: GasCosts = [
|
||||
GasZero: 0.u256,
|
||||
GasBase: 2.u256,
|
||||
GasVeryLow: 3.u256,
|
||||
GasLow: 5.u256,
|
||||
GasMid: 8.u256,
|
||||
GasHigh: 10.u256,
|
||||
GasSload: 50.u256, # Changed to 200 in Tangerine (EIP150)
|
||||
GasJumpDest: 1.u256,
|
||||
GasSset: 20_000.u256,
|
||||
GasSreset: 5_000.u256,
|
||||
GasExtCode: 20.u256,
|
||||
GasCoinbase: 20.u256,
|
||||
GasSelfDestruct: 0.u256, # Changed to 5000 in Tangerine (EIP150)
|
||||
GasInHandler: 0.u256, # to be calculated in handler
|
||||
GasRefundSclear: 15000.u256,
|
||||
|
||||
GasBalance: 20.u256, # Changed to 400 in Tangerine (EIP150)
|
||||
GasCall: 40.u256, # Changed to 700 in Tangerine (EIP150)
|
||||
GasExp: 10.u256,
|
||||
GasSHA3: 30.u256
|
||||
]
|
||||
|
||||
proc tangerineGasCosts(baseCosts: GasCosts): GasCosts =
|
||||
|
||||
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md
|
||||
result = baseCosts
|
||||
result[GasSload] = 200.u256
|
||||
result[GasSelfDestruct] = 5000.u256
|
||||
result[GasBalance] = 400.u256
|
||||
result[GasCall] = 40.u256
|
||||
|
||||
let TangerineGasCosts* = BaseGasCosts.tangerineGasCosts
|
|
@ -0,0 +1,26 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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.
|
||||
|
||||
import
|
||||
../../db/db_chain, ../../constants,
|
||||
../../utils/header,
|
||||
../base,
|
||||
./f20150730_frontier/frontier_vm,
|
||||
./f20161018_tangerine_whistle/tangerine_vm,
|
||||
stint
|
||||
|
||||
# Note (mamy): refactoring is in progress (2018-05-23), this is redundant with
|
||||
# - `Chain` in src/chain.nim, to be honest I don't understand the need of this abstraction at the moment
|
||||
# - `toFork` in src/constant. This is temporary until more VMs are implemented
|
||||
|
||||
proc newNimbusVM*(header: BlockHeader, chainDB: BaseChainDB): VM =
|
||||
|
||||
# TODO: deal with empty BlockHeader
|
||||
if header.blockNumber < FORK_TANGERINE_WHISTLE_BLKNUM:
|
||||
result = newFrontierVM(header, chainDB)
|
||||
else:
|
||||
result = newTangerineVM(header, chainDB)
|
|
@ -1,17 +0,0 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * 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.
|
||||
|
||||
import
|
||||
strformat, stint,
|
||||
../types, ../constants, ../opcode, ../computation, stack
|
||||
|
||||
# TODO - currently unused
|
||||
|
||||
proc expGasCost*(computation: var BaseComputation): UInt256 =
|
||||
let arg = computation.stack.getInt(0)
|
||||
result = if arg == 0: 10.u256 else: (10.u256 + 10.u256 * (1.u256 + arg.log256))
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
import
|
||||
unittest, strformat, tables, times,
|
||||
stint,
|
||||
../src/[constants, chain, vm/base, vm/forks/frontier/vm, utils/header, utils/address, db/db_chain, db/backends/memory_backend]
|
||||
../src/[constants, chain, vm/base, vm/forks/f20150730_frontier/frontier_vm, utils/header, utils/address, db/db_chain, db/backends/memory_backend]
|
||||
|
||||
proc chainWithoutBlockValidation*: Chain =
|
||||
result = configureChain("TestChain", GENESIS_BLOCK_NUMBER, vmkFrontier, false, false)
|
||||
|
|
|
@ -10,7 +10,7 @@ import
|
|||
stint,
|
||||
../src/utils/[hexadecimal, address, padding],
|
||||
../src/[chain, vm_state, constants],
|
||||
../src/db/[db_chain, state_db], ../src/vm/forks/frontier/vm,
|
||||
../src/db/[db_chain, state_db], ../src/vm/forks/f20150730_frontier/frontier_vm,
|
||||
../src/vm/base, ../src/transaction
|
||||
|
||||
type
|
||||
|
|
|
@ -11,14 +11,14 @@ import
|
|||
../src/[chain, vm_state, computation, opcode, opcode_table],
|
||||
../src/[utils/header, utils/padding],
|
||||
../src/vm/[gas_meter, message, code_stream, stack],
|
||||
../src/vm/forks/frontier/vm,
|
||||
../src/vm/forks/vm_forks,
|
||||
../src/db/[db_chain, state_db, backends/memory_backend],
|
||||
test_helpers
|
||||
|
||||
|
||||
proc testCode(code: string, gas: UInt256): BaseComputation =
|
||||
var vm = newFrontierVM(BlockHeader(), newBaseChainDB(newMemoryDB()))
|
||||
let header = BlockHeader()
|
||||
proc testCode(code: string, initialGas: UInt256, blockNum: UInt256): BaseComputation =
|
||||
let header = BlockHeader(blockNumber: blockNum)
|
||||
var vm = newNimbusVM(header, newBaseChainDB(newMemoryDB()))
|
||||
# coinbase: "",
|
||||
# difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.u256,
|
||||
# blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.u256,
|
||||
|
@ -31,15 +31,15 @@ proc testCode(code: string, gas: UInt256): BaseComputation =
|
|||
value=0.u256,
|
||||
data = @[],
|
||||
code=code,
|
||||
gas=gas,
|
||||
gasPrice=1.u256)
|
||||
gas=initial_gas,
|
||||
gasPrice=1.u256) # What is this used for?
|
||||
# gasPrice=fixture{"exec"}{"gasPrice"}.getHexadecimalInt.u256,
|
||||
#options=newMessageOptions(origin=fixture{"exec"}{"origin"}.getStr))
|
||||
|
||||
#echo fixture{"exec"}
|
||||
var c = newCodeStreamFromUnescaped(code)
|
||||
#if DEBUG:
|
||||
c.displayDecompiled()
|
||||
if DEBUG:
|
||||
c.displayDecompiled()
|
||||
|
||||
var computation = newBaseComputation(vm.state, message)
|
||||
computation.accountsToDelete = initTable[string, string]()
|
||||
|
@ -51,7 +51,11 @@ proc testCode(code: string, gas: UInt256): BaseComputation =
|
|||
|
||||
suite "opcodes":
|
||||
test "add":
|
||||
var c = testCode("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01", 100_000.u256)
|
||||
var c = testCode(
|
||||
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01",
|
||||
100_000.u256,
|
||||
0.u256
|
||||
)
|
||||
check(c.gasMeter.gasRemaining == 99_991.u256)
|
||||
check(c.stack.peek == "115792089237316195423570985008687907853269984665640564039457584007913129639934".u256)
|
||||
# let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||
|
@ -71,3 +75,38 @@ suite "opcodes":
|
|||
# assert_eq!(gas_left, U256::from(79_988));
|
||||
# assert_store(&ext, 0, "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
|
||||
# }
|
||||
|
||||
test "Frontier VM computation - pre-EIP150 gas cost properly applied":
|
||||
block: # Using Balance (0x31)
|
||||
var c = testCode(
|
||||
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff31",
|
||||
100_000.u256,
|
||||
0.u256
|
||||
)
|
||||
check: c.gasMeter.gasRemaining == 100000.u256 - 3.u256 - 20.u256 # Starting gas - push32 (verylow) - balance
|
||||
|
||||
block: # Using SLOAD (0x54)
|
||||
var c = testCode(
|
||||
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff54",
|
||||
100_000.u256,
|
||||
0.u256
|
||||
)
|
||||
check: c.gasMeter.gasRemaining == 100000.u256 - 3.u256 - 50.u256 # Starting gas - push32 (verylow) - SLOAD
|
||||
|
||||
|
||||
test "Tangerine VM computation - post-EIP150 gas cost properly applied":
|
||||
block: # Using Balance (0x31)
|
||||
var c = testCode(
|
||||
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff31",
|
||||
100_000.u256,
|
||||
2_463_000.u256 # Tangerine block
|
||||
)
|
||||
check: c.gasMeter.gasRemaining == 100000.u256 - 3.u256 - 400.u256 # Starting gas - push32 (verylow) - balance
|
||||
|
||||
block: # Using SLOAD (0x54)
|
||||
var c = testCode(
|
||||
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff54",
|
||||
100_000.u256,
|
||||
2_463_000.u256
|
||||
)
|
||||
check: c.gasMeter.gasRemaining == 100000.u256 - 3.u256 - 200.u256 # Starting gas - push32 (verylow) - SLOAD
|
||||
|
|
|
@ -41,4 +41,3 @@ suite "VM":
|
|||
# state_db.getBalance(recipient) == amount
|
||||
# b.transactions[txIdx] == tx
|
||||
# b.header.gasUsed == constants.GAS_TX
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import
|
|||
../src/[chain, vm_state, computation, opcode, types, opcode_table],
|
||||
../src/utils/[header, padding],
|
||||
../src/vm/[gas_meter, message, code_stream, stack],
|
||||
../src/vm/forks/frontier/vm, ../src/db/[db_chain, state_db, backends/memory_backend]
|
||||
../src/vm/forks/vm_forks, ../src/db/[db_chain, state_db, backends/memory_backend]
|
||||
|
||||
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus)
|
||||
|
||||
|
@ -24,13 +24,14 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
|||
for label, child in fixtures:
|
||||
fixture = child
|
||||
break
|
||||
var vm = newFrontierVM(BlockHeader(), newBaseChainDB(newMemoryDB()))
|
||||
let header = BlockHeader(
|
||||
coinbase: fixture{"env"}{"currentCoinbase"}.getStr,
|
||||
difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.u256,
|
||||
blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.u256,
|
||||
# gasLimit: fixture{"env"}{"currentGasLimit"}.getHexadecimalInt.u256,
|
||||
timestamp: fixture{"env"}{"currentTimestamp"}.getHexadecimalInt.int64.fromUnix)
|
||||
timestamp: fixture{"env"}{"currentTimestamp"}.getHexadecimalInt.int64.fromUnix
|
||||
)
|
||||
var vm = newNimbusVM(header, newBaseChainDB(newMemoryDB()))
|
||||
|
||||
var code = ""
|
||||
vm.state.db(readOnly=false):
|
||||
|
|
Loading…
Reference in New Issue