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

Gas refactoring - decouple opcode logic and gas #49

Merged
merged 26 commits into from
Jun 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2d1edd1
Decoupling op logic and gas - introduce gasometer, rework opcode decl…
mratsim Jun 9, 2018
d8bbf5a
Remove gas constants for gas opcode computation
mratsim Jun 9, 2018
ad7a814
Remove gas constants for precompiled contracts
mratsim Jun 9, 2018
d854f5f
make vm_types compile
mratsim Jun 9, 2018
c5b593e
Make opcode, call and computation compile
mratsim Jun 9, 2018
c14de5e
Distinguish between dynamic and complex gas costs, fix arithmetic
mratsim Jun 10, 2018
729ecd5
Fix context and sha3
mratsim Jun 10, 2018
d4f0e99
update memory and storage ops
mratsim Jun 10, 2018
78d37db
Log opcode uses memory expansion code
mratsim Jun 10, 2018
21aece0
update/stub system_ops with gas costs
mratsim Jun 10, 2018
db1e4f6
Make test compile. Deactivate stub test_vm
mratsim Jun 10, 2018
1b64ea7
all tests compiles, opcode fails due to https://github.com/nim-lang/N…
mratsim Jun 10, 2018
91ea5f0
Create an enum without holes - workaround: https://github.com/nim-lan…
mratsim Jun 11, 2018
21c25b6
Use arrays instead of tables for GasCosts, remove some unused imports…
mratsim Jun 11, 2018
ac7bd09
Make test_vm_json compile
mratsim Jun 11, 2018
b27996e
Fix test_vm_json - workaround https://github.com/nim-lang/Nim/issues/…
mratsim Jun 11, 2018
d4da927
fix memory expansion cost bug
mratsim Jun 11, 2018
9cde09d
Remove leftover special handling from before GckMemExpansion
mratsim Jun 11, 2018
178312a
cleanup outdated comment, better align =
mratsim Jun 11, 2018
ec59ddc
Fix sha3 gas cost not taking memory expansion into account
mratsim Jun 11, 2018
316f597
Improve gas error reporting of test_vm_json
mratsim Jun 11, 2018
c76abb0
Fix gas computation regression due to mem expansion
mratsim Jun 11, 2018
561da26
mass replace for memExpansion->RequestedMemSize was too eager
mratsim Jun 11, 2018
6848859
fix log gas cost (no tests :/)
mratsim Jun 11, 2018
de282c2
missed a static FeeSchedule
mratsim Jun 11, 2018
7fd3254
static as expression is fickle
mratsim Jun 11, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions VMTests.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ VMTests
+ add4.json OK
+ addmod0.json OK
+ addmod1.json OK
+ addmod1_overflow2.json OK
- addmod1_overflow2.json Fail
+ addmod1_overflow3.json OK
+ addmod1_overflow4.json OK
+ addmod1_overflowDiff.json OK
Expand Down Expand Up @@ -135,7 +135,7 @@ VMTests
+ mulUnderFlow.json OK
+ mulmod0.json OK
+ mulmod1.json OK
+ mulmod1_overflow.json OK
- mulmod1_overflow.json Fail
+ mulmod1_overflow2.json OK
+ mulmod1_overflow3.json OK
+ mulmod1_overflow4.json OK
Expand Down Expand Up @@ -177,7 +177,7 @@ VMTests
+ signextend_BitIsNotSet.json OK
+ signextend_BitIsNotSetInHigherByte.json OK
+ signextend_BitIsSetInHigherByte.json OK
- signextend_Overflow_dj42.json Fail
+ signextend_Overflow_dj42.json OK
+ signextend_bigBytePlus1.json OK
+ signextend_bitIsSet.json OK
+ smod0.json OK
Expand All @@ -198,7 +198,7 @@ VMTests
+ sub3.json OK
+ sub4.json OK
```
OK: 190/195 Fail: 4/195 Skip: 1/195
OK: 189/195 Fail: 5/195 Skip: 1/195
## vmBitwiseLogicOperation
```diff
+ and0.json OK
Expand Down Expand Up @@ -265,20 +265,20 @@ OK: 190/195 Fail: 4/195 Skip: 1/195
OK: 60/60 Fail: 0/60 Skip: 0/60
## vmBlockInfoTest
```diff
- blockhash257Block.json Fail
- blockhash258Block.json Fail
- blockhashInRange.json Fail
- blockhashMyBlock.json Fail
- blockhashNotExistingBlock.json Fail
- blockhashOutOfRange.json Fail
+ blockhash257Block.json OK
+ blockhash258Block.json OK
+ blockhashInRange.json OK
+ blockhashMyBlock.json OK
+ blockhashNotExistingBlock.json OK
+ blockhashOutOfRange.json OK
+ blockhashUnderFlow.json OK
- coinbase.json Fail
+ coinbase.json OK
+ difficulty.json OK
+ gaslimit.json OK
+ number.json OK
+ timestamp.json OK
```
OK: 5/12 Fail: 7/12 Skip: 0/12
OK: 12/12 Fail: 0/12 Skip: 0/12
## vmEnvironmentalInfo
```diff
ExtCodeSizeAddressInputTooBigLeftMyAddress.json Skip
Expand Down
40 changes: 5 additions & 35 deletions nimbus/computation.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,15 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import
strformat, strutils, sequtils, tables, macros, stint, terminal, math, eth_common, byteutils,
constants, errors, utils/hexadecimal, utils_numeric, validation, vm_state, logging, opcode_values, vm_types,
vm / [code_stream, gas_meter, memory, message, stack],
strformat, strutils, sequtils, macros, stint, terminal, math, eth_common, byteutils, tables,
./constants, ./errors, ./utils/hexadecimal, ./utils_numeric, ./validation, ./vm_state, ./logging, ./opcode_values, ./vm_types,
./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: Natural): GasInt =
let
sizeInWords = ceil32(sizeInBytes) div 32
linearCost = sizeInWords * GAS_MEMORY
quadraticCost = sizeInWords ^ 2 div GAS_MEMORY_QUADRATIC_DENOMINATOR
totalCost = linearCost + quadraticCost
result = totalCost

#const VARIABLE_GAS_COST_OPS* = {Op.Exp}

method newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputation {.base.}=
raise newException(ValueError, "Must be implemented by subclasses")

Expand Down Expand Up @@ -105,26 +95,6 @@ method prepareChildMessage*(
code,
childOptions)

method extendMemory*(c: var BaseComputation, startPosition: Natural, size: Natural) =
# Memory Management

let beforeSize = ceil32(len(c.memory))
let afterSize = ceil32(startPosition + size)

let beforeCost = memoryGasCost(beforeSize)
let afterCost = memoryGasCost(afterSize)

c.logger.debug(&"MEMORY: size ({beforeSize} -> {afterSize}) | cost ({beforeCost} -> {afterCost})")

if size > 0:
if beforeCost < afterCost:
var gasFee = afterCost - beforeCost
c.gasMeter.consumeGas(
gasFee,
reason = &"Expanding memory {beforeSize} -> {afterSize}")

c.memory.extend(startPosition, size)

method output*(c: BaseComputation): string =
if c.shouldEraseReturnData:
""
Expand Down Expand Up @@ -261,10 +231,10 @@ template inComputation*(c: untyped, handler: untyped): untyped =
c.gasMeter.gasRemaining,
reason="Zeroing gas due to VM Exception: $1" % getCurrentExceptionMsg())


method getOpcodeFn*(computation: var BaseComputation, op: Op): Opcode =
# TODO use isValidOpcode and remove the Op --> Opcode indirection
if computation.opcodes.len > 0 and computation.opcodes.hasKey(op):
computation.opcodes[op]
OpCode(kind: op, runLogic: computation.opcodes[op])
else:
raise newException(InvalidInstruction,
&"Invalid opcode {op}")
Expand Down
58 changes: 0 additions & 58 deletions nimbus/constants.nim
Original file line number Diff line number Diff line change
Expand Up @@ -114,66 +114,8 @@ let
ZERO_HASH32* = Hash256()
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 # 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

# GAS_SELF_DESTRUCT* = 0.u256
# GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.u256
GAS_CREATE* = 32_000.u256
# GAS_CALL* = 40.u256
GAS_CALL_VALUE* = 9_000
GAS_CALL_STIPEND* = 2_300
GAS_NEW_ACCOUNT* = 25_000

# GAS_COST_BALANCE* = 400.u256 # TODO: this is post-eip150, see also GAS_BALANCE

# GAS_EXP* = 10.u256
# GAS_EXP_BYTE* = 10.u256
GAS_MEMORY* = 3
GAS_TX_CREATE* = 32_000.u256
GAS_TX_DATA_ZERO* = 4.u256
GAS_TX_DATA_NON_ZERO* = 68.u256
GAS_TX* = 21_000
GAS_LOG* = 375.u256
GAS_LOG_DATA* = 8
GAS_LOG_TOPIC* = 375
# GAS_SHA3* = 30.u256
GAS_SHA3_WORD* = 6
GAS_COPY* = 3
GAS_BLOCK_HASH* = 20.u256
GAS_CODE_DEPOSIT* = 200.u256
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512
GAS_SHA256* = 60.u256
GAS_SHA256WORD* = 12.u256
GAS_RIP_EMD160* = 600.u256
GAS_RIP_EMD160WORD* = 120.u256
GAS_IDENTITY* = 15.u256
GAS_IDENTITY_WORD* = 3
GAS_ECRECOVER* = 3_000.u256
GAS_ECADD* = 500.u256
GAS_ECMUL* = 40_000.u256
GAS_ECPAIRING_BASE* = 100_000.u256
GAS_ECPAIRING_PER_POINT* = 80_000.u256
GAS_LIMIT_EMA_DENOMINATOR* = 1_024
GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024
GAS_LIMIT_MAXIMUM* = high(GasInt)
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2

Expand Down
2 changes: 1 addition & 1 deletion nimbus/db/db_chain.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# * 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, tables, stint, rlp, ranges, state_db, backends / memory_backend,
import stint, tables, rlp, ranges, state_db, backends / memory_backend,
../errors, ../utils/header, ../constants, eth_common, byteutils

type
Expand Down
2 changes: 1 addition & 1 deletion nimbus/db/state_db.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import
strformat, tables, eth_common,
eth_common, tables,
../constants, ../errors, ../validation, ../account, ../logging, ../utils_numeric, .. / utils / [padding, bytes, keccak],
stint, rlp

Expand Down
8 changes: 4 additions & 4 deletions nimbus/logic/arithmetic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ proc exp*(computation: var BaseComputation) =
# Exponentiation
let (base, exponent) = computation.stack.popInt(2)

var gasCost = computation.gasCosts[GasExp]
if not exponent.isZero:
gasCost += gasCost * (1 + log256(exponent))
computation.gasMeter.consumeGas(gasCost, reason="EXP: exponent bytes")
computation.gasMeter.consumeGas(
computation.gasCosts[Exp].d_handler(exponent),
reason="EXP: exponent bytes"
)

let res = if base.isZero: 0.u256 # 0^0 is 0 in py-evm
else: base.pow(exponent)
Expand Down
77 changes: 7 additions & 70 deletions nimbus/logic/call.nim
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,10 @@ type
using
computation: var BaseComputation

method msgExtraGas*(call: BaseCall, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt {.base.} =
raise newException(NotImplementedError, "Must be implemented by subclasses")

method msgGas*(call: BaseCall, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) {.base.} =
let extraGas = call.msgExtraGas(computation, gas, to, value)
let totalFee = gas + extraGas
let childMsgGas = gas + (if value != 0: GAS_CALL_STIPEND else: 0)
(childMsgGas, totalFee)

method callParams*(call: BaseCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) {.base.} =
raise newException(NotImplementedError, "Must be implemented subclasses")

method runLogic*(call: BaseCall, computation) =
computation.gasMeter.consumeGas(computation.gasCosts[call.gasCostKind], reason = $call.kind) # TODO: Refactoring call gas costs
let (gas, value, to, sender,
codeAddress,
memoryInputStartPosition, memoryInputSize,
Expand All @@ -62,12 +52,15 @@ method runLogic*(call: BaseCall, computation) =

let (memInPos, memInLen, memOutPos, memOutLen) = (memoryInputStartPosition.toInt, memoryInputSize.toInt, memoryOutputStartPosition.toInt, memoryOutputSize.toInt)

computation.extendMemory(memInPos, memInLen)
computation.extendMemory(memOutPos, memOutLen)
let (gasCost, childMsgGas) = computation.gasCosts[Op.Call].c_handler(
value,
GasParams() # TODO - stub
)

computation.memory.extend(memInPos, memInLen)
computation.memory.extend(memOutPos, memOutLen)

let callData = computation.memory.read(memInPos, memInLen)
let (childMsgGas, childMsgGasFee) = call.msgGas(computation, gas.toInt, to, value)
computation.gasMeter.consumeGas(childMsgGasFee, reason = $call.kind)

# TODO: Pre-call checks
# with computation.vm_state.state_db(read_only=True) as state_db:
Expand Down Expand Up @@ -125,16 +118,6 @@ method runLogic*(call: BaseCall, computation) =
if not childComputation.shouldBurnGas:
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)

method msgExtraGas(call: Call, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
# TODO: db
# with computation.vm_state.state_db(read_only=True) as state_db:
# let accountExists = db.accountExists(to)
let accountExists = false

let transferGasFee = if value != 0: GAS_CALL_VALUE else: 0
let createGasFee = if not accountExists: GAS_NEW_ACCOUNT else: 0
transferGasFee + createGasFee

method callParams(call: CallCode, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let to = computation.stack.popAddress()
Expand All @@ -155,9 +138,6 @@ method callParams(call: CallCode, computation): (UInt256, UInt256, EthAddress, E
true, # should_transfer_value,
computation.msg.isStatic)

method msgExtraGas(call: CallCode, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
if value != 0: GAS_CALL_VALUE else: 0

method callParams(call: Call, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let codeAddress = computation.stack.popAddress()
Expand All @@ -181,12 +161,6 @@ method callParams(call: Call, computation): (UInt256, UInt256, EthAddress, EthAd
true, # should_transfer_value,
computation.msg.isStatic)

method msgGas(call: DelegateCall, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
(gas, gas)

method msgExtraGas(call: DelegateCall, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
0

method callParams(call: DelegateCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let codeAddress = computation.stack.popAddress()
Expand All @@ -210,42 +184,6 @@ method callParams(call: DelegateCall, computation): (UInt256, UInt256, EthAddres
false, # should_transfer_value,
computation.msg.isStatic)

proc maxChildGasEIP150*(gas: GasInt): GasInt =
gas - gas div 64

proc computeEIP150MsgGas(computation; gas, extraGas: GasInt, value: UInt256, name: string, callStipend: GasInt): (GasInt, GasInt) =
if computation.gasMeter.gasRemaining < extraGas:
raise newException(OutOfGas, &"Out of gas: Needed {extraGas} - Remaining {computation.gasMeter.gasRemaining} - Reason: {name}")
let gas = min(gas, maxChildGasEIP150(computation.gasMeter.gasRemaining - extraGas))
let totalFee = gas + extraGas
let childMsgGas = gas + (if value != 0: callStipend else: 0)
(childMsgGas, totalFee)

method msgGas(call: CallEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)

method msgGas(call: CallCodeEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)

method msgGas(call: DelegateCallEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0)

proc msgExtraGas*(call: CallEIP161, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
# account_is_dead = (
# not state_db.account_exists(to) or
# state_db.account_is_empty(to))
let accountIsDead = true

let transferGasFee = if value != 0: GAS_CALL_VALUE else: 0
let createGasFee = if accountIsDead and value != 0: GAS_NEW_ACCOUNT else: 0
transferGasFee + createGasFee


method callParams(call: StaticCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let to = computation.stack.popAddress()
Expand All @@ -265,7 +203,6 @@ method callParams(call: StaticCall, computation): (UInt256, UInt256, EthAddress,
false, # should_transfer_value,
true) # is_static


method callParams(call: CallByzantium, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
result = procCall callParams(call, computation)
if computation.msg.isStatic and result[1] != 0:
Expand Down
Loading