More flexible gas cost definitions, start registering more opcodes in the runner
This commit is contained in:
parent
38a02250b5
commit
fba1052b4c
|
@ -11,6 +11,8 @@ proc memoryGasCost*(sizeInBytes: Int256): Int256 =
|
|||
totalCost = linearCost + quadraticCost
|
||||
result = totalCost
|
||||
|
||||
const VARIABLE_GAS_COST_OPS* = {Op.Exp}
|
||||
|
||||
type
|
||||
BaseComputation* = ref object of RootObj
|
||||
# The execution computation
|
||||
|
@ -36,8 +38,11 @@ type
|
|||
erasesReturnData*: bool
|
||||
|
||||
Opcode* = ref object of RootObj
|
||||
kind*: Op
|
||||
gasCost*: Int256
|
||||
case kind*: Op
|
||||
of VARIABLE_GAS_COST_OPS:
|
||||
gasCostHandler*: proc(computation: var BaseComputation): Int256
|
||||
else:
|
||||
gasCostConstant*: Int256
|
||||
runLogic*: proc(computation: var BaseComputation)
|
||||
|
||||
proc newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputation =
|
||||
|
|
|
@ -10,6 +10,9 @@ type
|
|||
proc int256*(i: int): Int256 =
|
||||
i.initBigInt
|
||||
|
||||
template i256*(i: int): Int256 =
|
||||
i.int256
|
||||
|
||||
# TODO
|
||||
# We'll have a fast fixed int256, for now this works
|
||||
|
||||
|
@ -47,6 +50,10 @@ proc `mod`*(a: Int256, b: int): Int256 =
|
|||
proc `div`*(a: Int256, b: int): Int256 =
|
||||
a div b.int256
|
||||
|
||||
proc log256*(a: Int256): Int256 =
|
||||
# TODO
|
||||
2.int256
|
||||
|
||||
proc setXLen[T](s: var seq[T]; newlen: Natural) =
|
||||
if s.isNil:
|
||||
s = newSeq[T](newlen)
|
||||
|
|
|
@ -61,6 +61,10 @@ type
|
|||
## Error raised to indicate an attempt was made to read data beyond the
|
||||
## boundaries of the buffer (such as with RETURNDATACOPY)
|
||||
|
||||
TypeError* = object of VMError
|
||||
## Error when invalid values are found
|
||||
|
||||
|
||||
proc makeVMError*(): VMError =
|
||||
result.burnsGas = true
|
||||
result.erasesReturnData = true
|
||||
|
|
|
@ -76,20 +76,20 @@ proc sdiv*(computation: var BaseComputation) =
|
|||
pushRes()
|
||||
|
||||
# no curry
|
||||
proc exp*(computation: var BaseComputation, gasPerByte: Int256) =
|
||||
proc exp*(computation: var BaseComputation) =
|
||||
# Exponentiation
|
||||
var (base, exponent) = computation.stack.popInt(2)
|
||||
|
||||
var bitSize = 0.int256 # TODO exponent.bitLength()
|
||||
var byteSize = ceil8(bitSize) div 8
|
||||
var res = if base == 0: 0.int256 else: (base ^ exponent.getInt) mod constants.UINT_256_CEILING
|
||||
computation.gasMeter.consumeGas(
|
||||
gasPerByte * byteSize,
|
||||
reason="EXP: exponent bytes"
|
||||
)
|
||||
# computation.gasMeter.consumeGas(
|
||||
# gasPerByte * byteSize,
|
||||
# reason="EXP: exponent bytes"
|
||||
# )
|
||||
pushRes()
|
||||
|
||||
proc signextend(computation: var BaseComputation) =
|
||||
proc signextend*(computation: var BaseComputation) =
|
||||
# Signed Extend
|
||||
var (bits, value) = computation.stack.popInt(2)
|
||||
|
||||
|
|
|
@ -4,18 +4,30 @@ import
|
|||
|
||||
template run*(opcode: Opcode, computation: var BaseComputation) =
|
||||
# Hook for performing the actual VM execution
|
||||
computation.gasMeter.consumeGas(opcode.gasCost, reason = $opcode.kind)
|
||||
computation.gasMeter.consumeGas(opcode.gasCost(computation), reason = $opcode.kind)
|
||||
opcode.runLogic(computation)
|
||||
|
||||
method logger*(opcode: Opcode): Logger =
|
||||
logging.getLogger(&"vm.opcode.{opcode.kind}")
|
||||
|
||||
proc newOpcode*(kind: Op, gasCost: Int256, logic: proc(computation: var BaseComputation)): Opcode =
|
||||
Opcode(kind: kind, gasCost: gasCost, runLogic: logic)
|
||||
method gasCost*(opcode: Opcode, computation: var BaseComputation): Int256 =
|
||||
if opcode.kind in VARIABLE_GAS_COST_OPS:
|
||||
opcode.gasCostHandler(computation)
|
||||
else:
|
||||
opcode.gasCostConstant
|
||||
|
||||
template newOpcode*(kind: Op, gasCost: Int256, logic: proc(computation: var BaseComputation)): Opcode =
|
||||
Opcode(kind: kind, gasCostConstant: gasCost, runLogic: logic)
|
||||
|
||||
template newOpcode*(kind: Op, gasHandler: proc(computation: var BaseComputation): Int256, logic: proc(computation: var BaseComputation)): Opcode =
|
||||
Opcode(kind: kind, gasCostHandler: gasHandler, runLogic: logic)
|
||||
|
||||
method `$`*(opcode: Opcode): string =
|
||||
&"{opcode.kind}(0x{opcode.kind.int.toHex(2)}: {opcode.gasCost})"
|
||||
let gasCost = if opcode.kind in VARIABLE_GAS_COST_OPS:
|
||||
"variable"
|
||||
else:
|
||||
$opcode.gasCostConstant
|
||||
&"{opcode.kind}(0x{opcode.kind.int.toHex(2)}: {gasCost})"
|
||||
|
||||
macro initOpcodes*(spec: untyped): untyped =
|
||||
var value = ident("value")
|
||||
|
@ -27,8 +39,12 @@ macro initOpcodes*(spec: untyped): untyped =
|
|||
let op = child[0]
|
||||
let gasCost = child[1][0][0]
|
||||
let handler = child[1][0][1]
|
||||
var opcode = quote:
|
||||
`value`[`op`] = newOpcode(`op`, `gasCost`, `handler`)
|
||||
let opcode = if gasCost.repr[0].isLowerAscii():
|
||||
quote:
|
||||
`value`[`op`] = Opcode(kind: `op`, gasCostHandler: `gasCost`, runLogic: `handler`)
|
||||
else:
|
||||
quote:
|
||||
`value`[`op`] = Opcode(kind: `op`, gasCostConstant: `gasCost`, runLogic: `handler`)
|
||||
result[1].add(opcode)
|
||||
|
||||
result[1].add(value)
|
||||
|
|
|
@ -1,14 +1,30 @@
|
|||
import
|
||||
strformat, strutils, tables, macros,
|
||||
constants, bigints, errors, logging, vm_state,
|
||||
vm / [gas_meter, stack, code_stream, memory, message, value], db / chain, computation, opcode, opcode_values, utils / [header, address],
|
||||
vm / [gas_meter, stack, code_stream, memory, message, value, gas_costs], db / chain, computation, opcode, opcode_values, utils / [header, address],
|
||||
logic / [arithmetic, comparison]
|
||||
|
||||
var opcodes = initOpcodes:
|
||||
Op.Add: GAS_VERY_LOW add
|
||||
Op.Sub: GAS_VERY_LOW sub
|
||||
Op.Mul: GAS_LOW mul
|
||||
Op.Div: GAS_LOW divide
|
||||
# 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: expGasCost arithmetic.exp
|
||||
Op.SignExtend: GAS_LOW 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
|
||||
|
||||
var mem = newMemory(pow(1024.int256, 2))
|
||||
|
||||
|
@ -81,8 +97,12 @@ macro runOpcodes*(computation: untyped, program: untyped): untyped =
|
|||
|
||||
# useful for testing simple cases
|
||||
runOpcodes(c):
|
||||
stack: @[2.vint, 2.vint, 2.vint]
|
||||
stack: @[2.vint, 2.vint, 2.vint, 2.vint, 2.vint, 2.vint, 4.vint]
|
||||
|
||||
Op.Add
|
||||
Op.Mul
|
||||
Op.Div
|
||||
Op.Sub
|
||||
Op.Mul
|
||||
Op.Mul
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import
|
||||
strformat, bigints,
|
||||
../constants, ../opcode, ../computation, stack
|
||||
|
||||
proc expGasCost*(computation: var BaseComputation): Int256 =
|
||||
let arg = computation.stack.getInt(0)
|
||||
result = if arg == 0: 10.i256 else: (10.i256 + 10.i256 * (1.i256 + arg.log256))
|
||||
|
|
@ -168,6 +168,22 @@ proc dup*(stack: var Stack; position: int) =
|
|||
&"Insufficient stack items for DUP{position}")
|
||||
|
||||
|
||||
proc getInt*(stack: Stack, position: int): Int256 =
|
||||
let element = stack.values[position]
|
||||
case element.kind:
|
||||
of VInt:
|
||||
result = element.i
|
||||
else:
|
||||
raise newException(TypeError, "Expected int")
|
||||
|
||||
proc getBinary*(stack: Stack, position: int): cstring =
|
||||
let element = stack.values[position]
|
||||
case element.kind:
|
||||
of VBinary:
|
||||
result = element.b
|
||||
else:
|
||||
raise newException(TypeError, "Expected binary")
|
||||
|
||||
proc `$`*(stack: Stack): string =
|
||||
let values = stack.values.mapIt(&" {$it}").join("\n")
|
||||
&"Stack:\n{values}"
|
||||
|
|
Loading…
Reference in New Issue