Skip to content

Commit

Permalink
Gas & Memory ops refactoring - uses native integer instead of uint256 f…
Browse files Browse the repository at this point in the history
…ixes #35 and #39 (#40)

* Gas refactoring - uses int64

* Use primitive int in test_vm_json
  • Loading branch information
mratsim committed May 25, 2018
1 parent c77b7cd commit cb49352
Show file tree
Hide file tree
Showing 21 changed files with 263 additions and 244 deletions.
27 changes: 12 additions & 15 deletions src/computation.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, strutils, sequtils, tables, macros, stint, terminal,
strformat, strutils, sequtils, tables, macros, stint, terminal, math,
constants, errors, utils/hexadecimal, utils_numeric, validation, vm_state, logging, opcode_values, types,
vm / [code_stream, gas_meter, memory, message, stack],

Expand All @@ -15,7 +15,7 @@ import
vm/forks/f20150730_frontier/frontier_vm_state,
vm/forks/f20161018_tangerine_whistle/tangerine_vm_state

proc memoryGasCost*(sizeInBytes: UInt256): UInt256 =
proc memoryGasCost*(sizeInBytes: Natural): GasInt =
let
sizeInWords = ceil32(sizeInBytes) div 32
linearCost = sizeInWords * GAS_MEMORY
Expand Down Expand Up @@ -86,7 +86,7 @@ method shouldEraseReturnData*(c: BaseComputation): bool =

method prepareChildMessage*(
c: var BaseComputation,
gas: UInt256,
gas: GasInt,
to: string,
value: UInt256,
data: seq[byte],
Expand All @@ -105,13 +105,10 @@ method prepareChildMessage*(
code,
childOptions)

method extendMemory*(c: var BaseComputation, startPosition: UInt256, size: UInt256) =
method extendMemory*(c: var BaseComputation, startPosition: Natural, size: Natural) =
# Memory Management
#
# validate_uint256(start_position, title="Memory start position")
# validate_uint256(size, title="Memory size")

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

let beforeCost = memoryGasCost(beforeSize)
Expand Down Expand Up @@ -201,21 +198,21 @@ method getLogEntries*(c: BaseComputation): seq[(string, seq[UInt256], string)] =
else:
result = @[]

method getGasRefund*(c: BaseComputation): UInt256 =
method getGasRefund*(c: BaseComputation): GasInt =
if c.isError:
result = 0.u256
result = 0
else:
result = c.gasMeter.gasRefunded + c.children.mapIt(it.getGasRefund()).foldl(a + b, 0.u256)
result = c.gasMeter.gasRefunded + c.children.mapIt(it.getGasRefund()).foldl(a + b, 0'i64)

method getGasUsed*(c: BaseComputation): UInt256 =
method getGasUsed*(c: BaseComputation): GasInt =
if c.shouldBurnGas:
result = c.msg.gas
else:
result = max(0.u256, c.msg.gas - c.gasMeter.gasRemaining)
result = max(0, c.msg.gas - c.gasMeter.gasRemaining)

method getGasRemaining*(c: BaseComputation): UInt256 =
method getGasRemaining*(c: BaseComputation): GasInt =
if c.shouldBurnGas:
result = 0.u256
result = 0
else:
result = c.gasMeter.gasRemaining

Expand Down
40 changes: 20 additions & 20 deletions src/constants.nim
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ proc `==`*(a: UInt256, b: int): bool =
proc `!=`*(a: UInt256, b: int): bool =
a != b.u256

proc `^`*(base: int; exp: int): UInt256 =
let base = base.u256
var ex = exp
result = 1.u256
while ex > 0:
result = result * base
dec(ex)
# proc `^`*(base: int; exp: int): UInt256 =
# let base = base.u256
# var ex = exp
# result = 1.u256
# while ex > 0:
# result = result * base
# dec(ex)

proc `^`*(left: Int256, right: int): Int256 =
var value = right.i256
Expand Down Expand Up @@ -123,7 +123,7 @@ let
INT_256_MAX_AS_UINT256* = cast[Uint256](high(Int256))
NULLBYTE* = "\x00"
EMPTYWORD* = repeat(NULLBYTE, 32)
UINT160CEILING*: UInt256 = 2 ^ 160
UINT160CEILING*: UInt256 = 2.u256 ^ 160
CREATE_CONTRACT_ADDRESS* = ""
ZERO_ADDRESS* = repeat("\x00", 20)
ZERO_HASH32* = repeat("\x00", 20)
Expand All @@ -147,34 +147,34 @@ let
# 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
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.u256
GAS_CALL_STIPEND* = 2_300.u256
GAS_NEW_ACCOUNT* = 25_000.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.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.u256
GAS_LOG* = 375.u256
GAS_LOG_DATA* = 8.u256
GAS_LOG_TOPIC* = 375.u256
GAS_LOG_DATA* = 8
GAS_LOG_TOPIC* = 375
# GAS_SHA3* = 30.u256
GAS_SHA3_WORD* = 6.u256
GAS_COPY* = 3.u256
GAS_SHA3_WORD* = 6
GAS_COPY* = 3
GAS_BLOCK_HASH* = 20.u256
GAS_CODE_DEPOSIT* = 200.u256
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512.u256
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512
GAS_SHA256* = 60.u256
GAS_SHA256WORD* = 12.u256
GAS_RIP_EMD160* = 600.u256
Expand All @@ -188,7 +188,7 @@ let
GAS_ECPAIRING_PER_POINT* = 80_000.u256
GAS_LIMIT_EMA_DENOMINATOR* = 1_024.u256
GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024.u256
GAS_LIMIT_MAXIMUM*: UInt256 = 2 ^ 63 - 1.u256
GAS_LIMIT_MAXIMUM* = high(int64)
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3.u256
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.u256

Expand All @@ -206,7 +206,7 @@ let
MAX_UNCLE_DEPTH* = 6.u256
MAX_UNCLES* = 2.u256

SECPK1_P*: UInt256 = 2 ^ 256 - 2 ^ 32 - 977.u256
SECPK1_P*: UInt256 = 2.u256 ^ 256 - 2.u256 ^ 32 - 977.u256
SECPK1_N*: UInt256 = "115792089237316195423570985008687907852837564279074904382605163141518161494337".u256
SECPK1_A* = 0.u256
SECPK1_B* = 7.u256
Expand Down
2 changes: 1 addition & 1 deletion src/logic/arithmetic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ proc exp*(computation: var BaseComputation) =

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

let res = if base.isZero: 0.u256 # 0^0 is 0 in py-evm
Expand Down
58 changes: 30 additions & 28 deletions src/logic/call.nim
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ type
using
computation: var BaseComputation

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

method msgGas*(call: BaseCall, computation; gas: UInt256, to: string, value: UInt256): (UInt256, UInt256) {.base.} =
method msgGas*(call: BaseCall, computation; gas: GasInt, to: string, 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.u256)
let childMsgGas = gas + (if value != 0: GAS_CALL_STIPEND else: 0)
(childMsgGas, totalFee)

method callParams*(call: BaseCall, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) {.base.} =
Expand All @@ -60,11 +60,13 @@ method runLogic*(call: BaseCall, computation) =
shouldTransferValue,
isStatic) = call.callParams(computation)

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

let callData = computation.memory.read(memoryInputStartPosition, memoryInputSize)
let (childMsgGas, childMsgGasFee) = call.msgGas(computation, gas, to, value)
computation.extendMemory(memInPos, memInLen)
computation.extendMemory(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
Expand Down Expand Up @@ -116,22 +118,22 @@ method runLogic*(call: BaseCall, computation) =
else:
computation.stack.push(1.u256)
if not childComputation.shouldEraseReturnData:
let actualOutputSize = min(memoryOutputSize, childComputation.output.len.u256)
let actualOutputSize = min(memOutLen, childComputation.output.len)
computation.memory.write(
memoryOutputStartPosition,
memOutPos,
actualOutputSize,
childComputation.output.toBytes[0 ..< actualOutputSize.toInt])
childComputation.output.toBytes[0 ..< actualOutputSize])
if not childComputation.shouldBurnGas:
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)

method msgExtraGas(call: Call, computation; gas: UInt256, to: string, value: UInt256): UInt256 =
method msgExtraGas(call: Call, computation; gas: GasInt, to: string, 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.u256
let createGasFee = if not accountExists: GAS_NEW_ACCOUNT else: 0.u256
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, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
Expand All @@ -154,8 +156,8 @@ method callParams(call: CallCode, computation): (UInt256, UInt256, string, strin
true, # should_transfer_value,
computation.msg.isStatic)

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

method callParams(call: Call, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
Expand All @@ -180,11 +182,11 @@ method callParams(call: Call, computation): (UInt256, UInt256, string, string, s
true, # should_transfer_value,
computation.msg.isStatic)

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

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

method callParams(call: DelegateCall, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
Expand All @@ -209,39 +211,39 @@ method callParams(call: DelegateCall, computation): (UInt256, UInt256, string, s
false, # should_transfer_value,
computation.msg.isStatic)

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

proc computeEIP150MsgGas(computation; gas: UInt256, extraGas: UInt256, value: UInt256, name: string, callStipend: UInt256): (UInt256, UInt256) =
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.u256)
let childMsgGas = gas + (if value != 0: callStipend else: 0)
(childMsgGas, totalFee)

method msgGas(call: CallEIP150, computation; gas: UInt256, to: string, value: UInt256): (UInt256, UInt256) =
method msgGas(call: CallEIP150, computation; gas: GasInt, to: string, 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: UInt256, to: string, value: UInt256): (UInt256, UInt256) =
method msgGas(call: CallCodeEIP150, computation; gas: GasInt, to: string, 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: UInt256, to: string, value: UInt256): (UInt256, UInt256) =
method msgGas(call: DelegateCallEIP150, computation; gas: GasInt, to: string, value: UInt256): (GasInt, GasInt) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0.u256)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0)

proc msgExtraGas*(call: CallEIP161, computation; gas: UInt256, to: string, value: UInt256): UInt256 =
proc msgExtraGas*(call: CallEIP161, computation; gas: GasInt, to: string, 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.u256
let createGasFee = if accountIsDead and value != 0: GAS_NEW_ACCOUNT else: 0.u256
let transferGasFee = if value != 0: GAS_CALL_VALUE else: 0
let createGasFee = if accountIsDead and value != 0: GAS_NEW_ACCOUNT else: 0
transferGasFee + createGasFee


Expand Down
35 changes: 20 additions & 15 deletions src/logic/context.nim
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@ proc callDataCopy*(computation: var BaseComputation) =
let (memStartPosition,
calldataStartPosition,
size) = computation.stack.popInt(3)
computation.extendMemory(memStartPosition, size)
let (memPos, callPos, len) = (memStartPosition.toInt, calldataStartPosition.toInt, size.toInt)
computation.extendMemory(memPos, len)

let wordCount = ceil32(size) div 32
let wordCount = ceil32(len) div 32
let copyGasCost = wordCount * constants.GAS_COPY
computation.gasMeter.consumeGas(copyGasCost, reason="CALLDATACOPY fee")
let value = computation.msg.data[calldataStartPosition.toInt ..< (calldataStartPosition + size).toInt]
let paddedValue = padRight(value, size.toInt, 0.byte)
computation.memory.write(memStartPosition, size, paddedValue)
let value = computation.msg.data[callPos ..< callPos + len]
let paddedValue = padRight(value, len, 0.byte)
computation.memory.write(memPos, len, paddedValue)


proc codesize*(computation: var BaseComputation) =
Expand All @@ -66,8 +67,10 @@ proc codecopy*(computation: var BaseComputation) =
let (memStartPosition,
codeStartPosition,
size) = computation.stack.popInt(3)
computation.extendMemory(memStartPosition, size)
let wordCount = ceil32(size) div 32
let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)
computation.extendMemory(memPos, len)

let wordCount = ceil32(len) div 32
let copyGasCost = constants.GAS_COPY * wordCount

computation.gasMeter.consumeGas(copyGasCost, reason="CODECOPY: word gas cost")
Expand All @@ -79,7 +82,7 @@ proc codecopy*(computation: var BaseComputation) =


proc gasprice*(computation: var BaseComputation) =
computation.stack.push(computation.msg.gasPrice)
computation.stack.push(computation.msg.gasPrice.u256)


proc extCodeSize*(computation: var BaseComputation) =
Expand All @@ -93,8 +96,9 @@ proc extCodeSize*(computation: var BaseComputation) =
proc extCodeCopy*(computation: var BaseComputation) =
let account = forceBytesToAddress(computation.stack.popString)
let (memStartPosition, codeStartPosition, size) = computation.stack.popInt(3)
computation.extendMemory(memStartPosition, size)
let wordCount = ceil32(size) div 32
let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)
computation.extendMemory(memPos, len)
let wordCount = ceil32(len) div 32
let copyGasCost = constants.GAS_COPY * wordCount

computation.gasMeter.consumeGas(copyGasCost, reason="EXTCODECOPY: word gas cost")
Expand All @@ -112,15 +116,16 @@ proc returnDataSize*(computation: var BaseComputation) =

proc returnDataCopy*(computation: var BaseComputation) =
let (memStartPosition, returnDataStartPosition, size) = computation.stack.popInt(3)
if returnDataStartPosition + size > computation.returnData.len:
let (memPos, returnPos, len) = (memStartPosition.toInt, returnDataStartPosition.toInt, size.toInt)
if returnPos + len > computation.returnData.len:
raise newException(OutOfBoundsRead,
"Return data length is not sufficient to satisfy request. Asked \n" &
&"for data from index {returnDataStartPosition} to {returnDataStartPosition + size}. Return data is {computation.returnData.len} in \n" &
"length")

computation.extendMemory(memStartPosition, size)
let wordCount = ceil32(size) div 32
computation.extendMemory(memPos, len)
let wordCount = ceil32(len) div 32
let copyGasCost = wordCount * constants.GAS_COPY
computation.gasMeter.consumeGas(copyGasCost, reason="RETURNDATACOPY fee")
let value = ($computation.returnData)[returnDataStartPosition.toInt ..< (returnDataStartPosition + size).toInt]
computation.memory.write(memStartPosition, size, value)
let value = ($computation.returnData)[returnPos ..< returnPos + len]
computation.memory.write(memPos, len, value)
2 changes: 1 addition & 1 deletion src/logic/flow.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ proc pc*(computation) =

proc gas*(computation) =
let gasRemaining = gasMeter.gasRemaining
stack.push(gasRemaining)
stack.push(gasRemaining.u256)
Loading

0 comments on commit cb49352

Please sign in to comment.