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