Translate several more modules, start shaping computation module
This commit is contained in:
parent
8ce5fa1773
commit
706f3982a7
|
@ -0,0 +1,314 @@
|
|||
import
|
||||
strformat, strutils, sequtils, tables, macros,
|
||||
constants, errors, utils/hexadecimal, utils_numeric, validation, vm_state, logging,
|
||||
vm / [code_stream, gas_meter, memory, message, stack]
|
||||
|
||||
proc memoryGasCost*(sizeInBytes: Int256): Int256 =
|
||||
var
|
||||
sizeInWords = ceil32(sizeInBytes) div 32
|
||||
linearCost = sizeInWords * GAS_MEMORY
|
||||
quadraticCost = sizeInWords ^ 2 div GAS_MEMORY_QUADRATIC_DENOMINATOR
|
||||
totalCost = linearCost + quadraticCost
|
||||
result = totalCost
|
||||
|
||||
type
|
||||
BaseComputation* = ref object of RootObj
|
||||
# The execution computation
|
||||
vmState*: BaseVMState
|
||||
msg*: Message
|
||||
memory*: Memory
|
||||
stack*: Stack
|
||||
gasMeter*: GasMeter
|
||||
code*: CodeStream
|
||||
children*: seq[BaseComputation]
|
||||
rawOutput*: cstring
|
||||
returnData*: cstring
|
||||
error*: Error
|
||||
logEntries*: seq[(cstring, seq[Int256], cstring)]
|
||||
shouldEraseReturnData*: bool
|
||||
accountsToDelete*: Table[cstring, cstring]
|
||||
opcodes*: cstring
|
||||
precompiles: cstring
|
||||
logs*: bool
|
||||
logger*: Logger
|
||||
|
||||
Error* = ref object
|
||||
info*: string
|
||||
burnsGas*: bool
|
||||
erasesReturnData*: bool
|
||||
|
||||
proc newBaseComputation*(vmState: BaseVMState, 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[cstring, cstring]()
|
||||
result.logEntries = @[]
|
||||
result.code = newCodeStream(message.code)
|
||||
result.logger = logging.getLogger("evm.vm.computation.BaseComputation")
|
||||
|
||||
method applyMessage*(c: var BaseComputation): BaseComputation =
|
||||
# Execution of an VM message
|
||||
raise newException(ValueError, "Must be implemented by subclasses")
|
||||
|
||||
method applyCreateMessage(c: var BaseComputation): BaseComputation =
|
||||
# Execution of an VM message to create a new contract
|
||||
raise newException(ValueError, "Must be implemented by subclasses")
|
||||
|
||||
method isOriginComputation*(c: BaseComputation): bool =
|
||||
# Is this computation the computation initiated by a transaction
|
||||
c.msg.isOrigin
|
||||
|
||||
template isSuccess*(c: BaseComputation): bool =
|
||||
c.error.isNil
|
||||
|
||||
template isError*(c: BaseComputation): bool =
|
||||
not c.isSuccess
|
||||
|
||||
method shouldBurnGas*(c: BaseComputation): bool =
|
||||
c.isError and c.error.burnsGas
|
||||
|
||||
method shouldEraseReturnData*(c: BaseComputation): bool =
|
||||
c.isError and c.error.erasesReturnData
|
||||
|
||||
method prepareChildMessage*(
|
||||
c: var BaseComputation,
|
||||
gas: Int256,
|
||||
to: cstring,
|
||||
value: Int256,
|
||||
data: cstring,
|
||||
code: cstring,
|
||||
options: MessageOptions = newMessageOptions()): Message =
|
||||
|
||||
# ? kwargs.setdefault('sender', self.msg.storage_address)
|
||||
|
||||
var childOptions = options
|
||||
childOptions.depth = c.msg.depth + 1.Int256
|
||||
result = newMessage(
|
||||
gas,
|
||||
c.msg.gasPrice,
|
||||
c.msg.origin,
|
||||
to,
|
||||
value,
|
||||
data,
|
||||
code,
|
||||
childOptions)
|
||||
|
||||
#
|
||||
method extendMemory*(c: var BaseComputation, startPosition: Int256, size: Int256) =
|
||||
# Memory Management
|
||||
#
|
||||
# validate_uint256(start_position, title="Memory start position")
|
||||
# validate_uint256(size, title="Memory size")
|
||||
|
||||
let beforeSize = ceil32(len(c.memory).Int256)
|
||||
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): cstring =
|
||||
if c.shouldEraseReturnData:
|
||||
cstring""
|
||||
else:
|
||||
c.rawOutput
|
||||
|
||||
method `output=`*(c: var BaseComputation, value: cstring) =
|
||||
c.rawOutput = value
|
||||
|
||||
macro generateChildBaseComputation*(t: typed, vmState: typed, childMsg: typed): untyped =
|
||||
var typ = repr(getType(t)[1]).split(":", 1)[0]
|
||||
var name = ident(%"new{typ}")
|
||||
var typName = ident(typ)
|
||||
result = quote:
|
||||
block:
|
||||
var c: `typName`
|
||||
if childMsg.isCreate:
|
||||
var child = `name`(`vmState`, `childMsg`)
|
||||
c = child.applyCreateMessage()
|
||||
else:
|
||||
var child = `name`(`vmState`, `childMsg`)
|
||||
c = child.applyMessage()
|
||||
c
|
||||
|
||||
method addChildBaseComputation*(c: var BaseComputation, childBaseComputation: BaseComputation) =
|
||||
if childBaseComputation.isError:
|
||||
if childBaseComputation.msg.isCreate:
|
||||
c.returnData = childBaseComputation.output
|
||||
elif childBaseComputation.shouldBurnGas:
|
||||
c.returnData = cstring""
|
||||
else:
|
||||
c.returnData = childBaseComputation.output
|
||||
else:
|
||||
if childBaseComputation.msg.isCreate:
|
||||
c.returnData = cstring""
|
||||
else:
|
||||
c.returnData = childBaseComputation.output
|
||||
c.children.add(childBaseComputation)
|
||||
|
||||
method applyChildBaseComputation*(c: var BaseComputation, childMsg: Message): BaseComputation =
|
||||
var childBaseComputation = generateChildBaseComputation(c, c.vmState, childMsg)
|
||||
c.addChildBaseComputation(childBaseComputation)
|
||||
result = childBaseComputation
|
||||
|
||||
method registerAccountForDeletion*(c: var BaseComputation, beneficiary: cstring) =
|
||||
validateCanonicalAddress(beneficiary, title="self destruct beneficiary address")
|
||||
|
||||
if c.msg.storageAddress in c.accountsToDelete:
|
||||
raise newException(ValueError,
|
||||
"invariant: should be impossible for an account to be " &
|
||||
"registered for deletion multiple times")
|
||||
c.accountsToDelete[c.msg.storageAddress] = beneficiary
|
||||
|
||||
method addLogEntry*(c: var BaseComputation, account: cstring, topics: seq[Int256], data: cstring) =
|
||||
validateCanonicalAddress(account, title="log entry address")
|
||||
c.logEntries.add((account, topics, data))
|
||||
|
||||
method getAccountsForDeletion*(c: BaseComputation): seq[(cstring, cstring)] =
|
||||
# TODO
|
||||
if c.isError:
|
||||
result = @[]
|
||||
else:
|
||||
result = @[]
|
||||
|
||||
method getLogEntries*(c: BaseComputation): seq[(cstring, seq[Int256], cstring)] =
|
||||
# TODO
|
||||
if c.isError:
|
||||
result = @[]
|
||||
else:
|
||||
result = @[]
|
||||
|
||||
method getGasRefund*(c: BaseComputation): Int256 =
|
||||
if c.isError:
|
||||
result = 0.Int256
|
||||
else:
|
||||
result = c.gasMeter.gasRefunded + c.children.mapIt(it.getGasRefund()).foldl(a + b, 0.Int256)
|
||||
|
||||
method getGasUsed*(c: BaseComputation): Int256 =
|
||||
if c.shouldBurnGas:
|
||||
result = c.msg.gas
|
||||
else:
|
||||
result = max(0.Int256, c.msg.gas - c.gasMeter.gasRemaining)
|
||||
|
||||
method getGasRemaining*(c: BaseComputation): Int256 =
|
||||
if c.shouldBurnGas:
|
||||
result = 0.Int256
|
||||
else:
|
||||
result = c.gasMeter.gasRemaining
|
||||
|
||||
#
|
||||
# Context Manager API
|
||||
#
|
||||
|
||||
template inBaseComputation*(c: untyped, handler: untyped): untyped =
|
||||
`c`.logger.debug(
|
||||
"COMPUTATION STARTING: gas: $1 | from: $2 | to: $3 | value: $4 | depth: $5 | static: $6" % [
|
||||
$`c`.msg.gas,
|
||||
$encodeHex(`c`.msg.sender),
|
||||
$encodeHex(`c`.msg.to),
|
||||
$`c`.msg.value,
|
||||
$`c`.msg.depth,
|
||||
if c.msg.isStatic: "y" else: "n"])
|
||||
try:
|
||||
`handler`
|
||||
c.logger.debug(
|
||||
"COMPUTATION SUCCESS: from: $1 | to: $2 | value: $3 | depth: $4 | static: $5 | gas-used: $6 | gas-remaining: $7" % [
|
||||
$encodeHex(c.msg.sender),
|
||||
$encodeHex(c.msg.to),
|
||||
$c.msg.value,
|
||||
$c.msg.depth,
|
||||
if c.msg.isStatic: "y" else: "n",
|
||||
$(c.msg.gas - c.gasMeter.gasRemaining),
|
||||
$c.msg.gasRemaining])
|
||||
except VMError:
|
||||
`c`.logger.debug(
|
||||
"COMPUTATION ERROR: gas: $1 | from: $2 | to: $3 | value: $4 | depth: $5 | static: $6 | error: $7" % [
|
||||
$`c`.msg.gas,
|
||||
$encodeHex(`c`.msg.sender),
|
||||
$encodeHex(`c`.msg.to),
|
||||
$c.msg.value,
|
||||
$c.msg.depth,
|
||||
if c.msg.isStatic: "y" else: "n",
|
||||
getCurrentExceptionMsg()])
|
||||
`c`.error = Error(info: getCurrentExceptionMsg())
|
||||
if c.shouldBurnGas:
|
||||
c.gasMeter.consumeGas(
|
||||
c.gasMeter.gasRemaining,
|
||||
reason="Zeroing gas due to VM Exception: $1" % getCurrentExceptionMsg())
|
||||
|
||||
macro applyComputation(t: typed, vmState: untyped, message: untyped): untyped =
|
||||
# Perform the computation that would be triggered by the VM message
|
||||
var typ = repr(getType(t)[1]).split(":", 1)[0]
|
||||
var name = ident(%"new{typ}")
|
||||
var typName = ident(typ)
|
||||
result = quote:
|
||||
block:
|
||||
var res: `typName`
|
||||
var c = `name`(`vmState`, `message`)
|
||||
var handler = proc: `typName` =
|
||||
if `message`.codeAddress in c.precompiles:
|
||||
c.precompiles[`message`.codeAddress](c)
|
||||
return c
|
||||
|
||||
for opcode in c.code:
|
||||
var opcodeFn = c.getOpcodeFn(c.opcodes, opcode)
|
||||
c.logger.trace(
|
||||
"OPCODE: 0x$1 ($2) | pc: $3" % [$opcode, $opcodeFn, $max(0, c.code.pc - 1)])
|
||||
|
||||
#try:
|
||||
# somehow call opcodeFn(c)
|
||||
#except halt:
|
||||
# break
|
||||
return c
|
||||
inComputation(c):
|
||||
res = handler()
|
||||
res
|
||||
|
||||
# #
|
||||
# # Opcode API
|
||||
# #
|
||||
# @property
|
||||
# def precompiles(self):
|
||||
# if self._precompiles is None:
|
||||
# return dict()
|
||||
# else:
|
||||
# return self._precompiles
|
||||
|
||||
# def get_opcode_fn(self, opcodes, opcode):
|
||||
# try:
|
||||
# return opcodes[opcode]
|
||||
# except KeyError:
|
||||
# return InvalidOpcode(opcode)
|
||||
|
||||
# #
|
||||
# # classmethod
|
||||
# #
|
||||
# @classmethod
|
||||
# def configure(cls,
|
||||
# name,
|
||||
# **overrides):
|
||||
# """
|
||||
# Class factory method for simple inline subclassing.
|
||||
# """
|
||||
# for key in overrides:
|
||||
# if not hasattr(cls, key):
|
||||
# raise TypeError(
|
||||
# "The BaseComputation.configure cannot set attributes that are not "
|
||||
# "already present on the base class. The attribute `{0}` was "
|
||||
# "not found on the base class `{1}`".format(key, cls)
|
||||
# )
|
||||
# return type(name, (cls,), overrides)
|
|
@ -7,23 +7,45 @@ type
|
|||
|
||||
Int256* = distinct int # TODO
|
||||
|
||||
proc `==`*(a: Int256, b: Int256): bool =
|
||||
a.int == b.int
|
||||
|
||||
proc `==`*(a: Int256, b: int): bool =
|
||||
a.int == b
|
||||
|
||||
proc `!=`*(a: Int256, b: Int256): bool =
|
||||
a.int != b.int
|
||||
|
||||
proc `!=`*(a: Int256, b: int): bool =
|
||||
a.int != b
|
||||
|
||||
proc `^`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int ^ b.int).Int256
|
||||
|
||||
proc `^`*(a: Int256, b: int): Int256 =
|
||||
(a.int ^ b).Int256
|
||||
|
||||
proc `>`*(a: Int256, b: Int256): bool =
|
||||
a.int > b.int
|
||||
|
||||
proc `>`*(a: Int256, b: int): bool =
|
||||
a.int > b
|
||||
|
||||
proc `>=`*(a: Int256, b: Int256): bool =
|
||||
a.int >= b.int
|
||||
|
||||
proc `<`*(a: Int256, b: Int256): bool =
|
||||
a.int < b.int
|
||||
|
||||
proc `<`*(a: Int256, b: int): bool =
|
||||
a.int < b
|
||||
|
||||
proc `<=`*(a: Int256, b: Int256): bool =
|
||||
a.int <= b.int
|
||||
|
||||
proc `$`*(a: Int256): string =
|
||||
$(a.int)
|
||||
|
||||
# proc `-`*(a: Int256, b: Int256): Int256 {.borrow.}
|
||||
|
||||
# proc `+`*(a: Int256, b: Int256): Int256 {.borrow.}
|
||||
|
||||
# proc `-=`*(a: var Int256, b: Int256) {.borrow.}
|
||||
|
||||
# proc `+=`*(a: var Int256, b: Int256) {.borrow.}
|
||||
|
||||
proc `-`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int - b.int).Int256
|
||||
|
||||
|
@ -36,36 +58,67 @@ proc `-=`*(a: var Int256, b: Int256) =
|
|||
proc `+=`*(a: var Int256, b: Int256) =
|
||||
a = (a + b).Int256
|
||||
|
||||
proc `*`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int * b.int).Int256
|
||||
|
||||
proc `mod`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int mod b.int).Int256
|
||||
|
||||
proc `mod`*(a: Int256, b: int): Int256 =
|
||||
(a.int mod b).Int256
|
||||
|
||||
proc `div`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int div b.int).Int256
|
||||
|
||||
proc `div`*(a: Int256, b: int): Int256 =
|
||||
(a.int div b).Int256
|
||||
|
||||
proc `abs`*(a: Int256): Int256 =
|
||||
a.int.abs.Int256
|
||||
|
||||
proc `and`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int and b.int).Int256
|
||||
|
||||
proc `or`*(a: Int256, b: Int256): Int256 =
|
||||
(a.int or b.int).Int256
|
||||
|
||||
proc max*(a: Int256, b: Int256): Int256 =
|
||||
max(a.int, b.int).Int256
|
||||
|
||||
proc min*(a: Int256, b: Int256): Int256 =
|
||||
min(a.int, b.int).Int256
|
||||
|
||||
proc repeat(b: cstring, count: int): cstring =
|
||||
# TODO: faster
|
||||
var s = $b
|
||||
result = cstring(repeat(s, count))
|
||||
|
||||
const
|
||||
# UINT256MAX = 2 ^ 256 - 1
|
||||
# UINT256CEILING = 2 ^ 256
|
||||
# UINT255MAX = 2 ^ 255 - 1
|
||||
# UINT255CEILING = 2 ^ 255
|
||||
X = 62 # 256
|
||||
UINT_256_MAX* = (2 ^ X - 1).Int256
|
||||
UINT_256_CEILING* = (2 ^ X).Int256
|
||||
UINT_255_MAX* = (2 ^ (X - 1) - 1).Int256
|
||||
UINT_255_CEILING* = (2 ^ (X - 1)).Int256
|
||||
NULLBYTE = cstring"\\x00"
|
||||
EMPTYWORD = repeat(NULLBYTE, 32)
|
||||
# UINT160CEILING = 2 ^ 160
|
||||
CREATE_CONTRACT_ADDRESS* = cstring""
|
||||
ZEROADDRESS = repeat(cstring"\\x00", 20)
|
||||
ZEROHASH32 = repeat(cstring"\\x00", 20)
|
||||
STACKDEPTHLIMIT = 1024
|
||||
GASNULL = 0
|
||||
GASZERO = 0
|
||||
GASBASE = 2
|
||||
GASVERYLOW = 3
|
||||
GASLOW = 5
|
||||
GASMID = 8
|
||||
GASHIGH = 10
|
||||
GASEXTCODE = 20
|
||||
GASBALANCE = 20
|
||||
GASSLOAD = 50
|
||||
GASJUMPDEST = 1
|
||||
GASSSET = 20000
|
||||
GASSRESET = 5000
|
||||
ZERO_ADDRESS* = repeat(cstring"\x00", 20)
|
||||
ZERO_HASH32* = repeat(cstring"\x00", 20)
|
||||
STACKDEPTHLIMIT* = 1024
|
||||
GAS_NULL* = 0.Int256
|
||||
GAS_ZERO* = 0.Int256
|
||||
GAS_BASE* = 2.Int256
|
||||
GAS_VERY_LOW* = 3.Int256
|
||||
GAS_LOW* = 5.Int256
|
||||
GAS_MID* = 8.Int256
|
||||
GAS_HIGH* = 10.Int256
|
||||
GAS_EXT_CODE* = 20.Int256
|
||||
GAS_BALANCE* = 20.Int256
|
||||
GAS_SLOAD* = 50.Int256
|
||||
GAS_JUMP_DEST* = 1.Int256
|
||||
GAS_SSET* = 20000.Int256
|
||||
GAS_SRESET* = 5000.Int256
|
||||
REFUNDSCLEAR = 15000
|
||||
GASSELFDESTRUCT = 0
|
||||
GASSELFDESTRUCTNEWACCOUNT = 25000
|
||||
|
@ -76,8 +129,8 @@ const
|
|||
GASNEWACCOUNT = 25000
|
||||
GASEXP = 10
|
||||
GASEXPBYTE = 10
|
||||
GASMEMORY = 3
|
||||
GASTXCREATE = 32000
|
||||
GAS_MEMORY* = 3.Int256
|
||||
GAS_TX_CREATE* = 32000.Int256
|
||||
GASTXDATAZERO = 4
|
||||
GASTXDATANONZERO = 68
|
||||
GASTX = 21000
|
||||
|
@ -89,8 +142,8 @@ const
|
|||
GASCOPY = 3
|
||||
GASBLOCKHASH = 20
|
||||
GASCODEDEPOSIT = 200
|
||||
GASMEMORYQUADRATICDENOMINATOR = 512
|
||||
GASSHA256 = 60
|
||||
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512.Int256
|
||||
GAS_SHA256 = 60.Int256
|
||||
GASSHA256WORD = 12
|
||||
GASRIPEMD160 = 600
|
||||
GASRIPEMD160WORD = 120
|
||||
|
|
|
@ -4,11 +4,17 @@ type
|
|||
Logger* = object
|
||||
name*: string
|
||||
|
||||
const DEBUG = true
|
||||
|
||||
proc log*(l: Logger, msg: string) =
|
||||
echo fmt"#{l.name}: {msg}"
|
||||
|
||||
proc debug*(l: Logger, msg: string) =
|
||||
if DEBUG:
|
||||
l.log(msg)
|
||||
|
||||
proc trace*(l: Logger, msg: string) =
|
||||
echo fmt"#{l.name}: {msg}"
|
||||
l.log(msg)
|
||||
|
||||
proc getLogger*(name: string): Logger =
|
||||
result = Logger(name: name)
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
import
|
||||
../constants, ../utils_numeric, ../stack
|
||||
|
||||
template pushRes =
|
||||
computation.stack.push(res)
|
||||
|
||||
proc add*(computation: var Computation) =
|
||||
# Addition
|
||||
var (left, right = computation.stack.popInt(2)
|
||||
|
||||
var res = (left + right) and constants.UINT_256_MAX
|
||||
pushRes()
|
||||
|
||||
proc addmod*(computation: var Computation) =
|
||||
# Modulo Addition
|
||||
var (left, right, arg) = computation.stack.popInt(3)
|
||||
|
||||
var res = if arg == 0: 0.Int256 else: (left + right) mod arg
|
||||
pushRes()
|
||||
|
||||
proc sub*(computation: var Computation) =
|
||||
# Subtraction
|
||||
var (left, right) = computation.stack.popInt(2)
|
||||
|
||||
var res = (left - right) and constants.UINT_256_MAX
|
||||
pushRes()
|
||||
|
||||
|
||||
proc modulo*(computation: var Computation) =
|
||||
# Modulo
|
||||
var (value, arg) = computation.stack.popInt(2)
|
||||
|
||||
var res = if arg == 0: 0.Int256 else: value mod arg
|
||||
pushRes()
|
||||
|
||||
proc smod*(computation: var Computation) =
|
||||
# Signed Modulo
|
||||
var (value, arg) = computation.stack.popInt(2)
|
||||
value = unsignedToSigned(value)
|
||||
arg = unsignedToSigned(value)
|
||||
|
||||
var posOrNeg = if value < 0: -1.Int256 else 1.Int256
|
||||
var res = if mod == 0: 0.Int256 else: ((value.abs mod arg.abs) * posOrNeg) and constants.UINT_256_MAX
|
||||
res = signedToUnsigned(res)
|
||||
pushRes()
|
||||
|
||||
proc mul*(computation: var Computation) =
|
||||
# Multiplication
|
||||
var (left, right) = computation.stack.popInt(2)
|
||||
|
||||
var res = (left * right) and constants.UINT_256_MAX
|
||||
pushRes()
|
||||
|
||||
proc mulmod*(computation: var Computation) =
|
||||
# Modulo Multiplication
|
||||
var (left, right, arg) = computation.stack.popInt(3)
|
||||
|
||||
var res = if mod == 0: 0.Int256 else: (left * right) mod arg
|
||||
pushRes()
|
||||
|
||||
proc divide*(computation: var Computation) =
|
||||
# Division
|
||||
var (numerator, denominator) = computation.stack.popInt(2)
|
||||
|
||||
var res = if denominator == 0: 0.Int256 else: (numerator div denominator) and constants.UINT_256_MAX
|
||||
pushRes()
|
||||
|
||||
proc sdiv*(computation: var Computation) =
|
||||
# Signed Division
|
||||
var (numerator, denominator) = computation.stack.popInt(2)
|
||||
numerator = unsignedToSigned(numerator)
|
||||
denominator = unsignedToSigned(denominator)
|
||||
|
||||
var posOrNeg = if numerator * denominator < 0: -1.Int256 else 1.Int256
|
||||
var res = if denominator == 0: 0.Int256 else: (posOrNeg * (numerator.abs div denominator.abs))
|
||||
res = unsignedToSigned(res)
|
||||
pushRes()
|
||||
|
||||
# no curry
|
||||
proc exp*(computation: var Computation, gasPerByte: Int256) =
|
||||
# Exponentiation
|
||||
var (base, exponent) = computation.stack.popInt(2)
|
||||
|
||||
var bitSize = exponent.bitLength()
|
||||
var byteSize = ceil8(bitSize) div 8
|
||||
var res = if base == 0: 0.Int256 else: (base ^ exponent) mod constants.UINT_256_CEILING
|
||||
computation.gasMeter.consumeGas(
|
||||
gasPerByte * byteSize,
|
||||
reason="EXP: exponent bytes"
|
||||
)
|
||||
pushRes()
|
||||
|
||||
proc signextend(computation: var Computation) =
|
||||
# Signed Extend
|
||||
var (bits, value) = computation.stack.popInt(2)
|
||||
|
||||
var res: Int256
|
||||
if bits <= 31.Int256:
|
||||
var testBit = bits * 8.Int256 + 7.Int256
|
||||
var signBit = (1.Int256 shl testBit)
|
||||
res = if value and signBit != 0: value or (constants.UINT_256_CEILING - signBit) else: value and (signBit - 1)
|
||||
else:
|
||||
res = value
|
||||
pushRes()
|
|
@ -0,0 +1,11 @@
|
|||
import strutils
|
||||
|
||||
proc encodeHex*(value: cstring): string =
|
||||
# return "0x" & codecs.decode(codecs.encode(value, "hex"), "utf8")
|
||||
return $value
|
||||
|
||||
proc decodeHex*(value: string): cstring =
|
||||
# var hexPart = value.rsplit("x", 1)[1]
|
||||
return cstring(value)
|
||||
# return codecs.decode(hexPart, "hex")
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# import
|
||||
# eth_utils, rlp, trie, evm.db.backends.memory, evm.db.chain
|
||||
|
||||
# proc makeTrieRootAndNodes*(transactions: auto; trieClass: auto): auto =
|
||||
# var
|
||||
# chaindb = BaseChainDB(MemoryDB())
|
||||
# db = chaindb.db
|
||||
# transactionDb = trieClass(db)
|
||||
# for index, transaction in transactions:
|
||||
# var indexKey = rlp.encode(index)
|
||||
# transactionDb[indexKey] = rlp.encode(transaction)
|
||||
# return (transactionDb.rootHash, transactionDb.db.wrappedDb.kvStore)
|
||||
|
|
@ -1,5 +1,33 @@
|
|||
proc intToBigEndian*(value: int): cstring =
|
||||
import constants, strformat, macros
|
||||
|
||||
proc intToBigEndian*(value: Int256): cstring =
|
||||
result = cstring""
|
||||
|
||||
proc bigEndianToInt*(value: cstring): int =
|
||||
result = 0
|
||||
proc bigEndianToInt*(value: cstring): Int256 =
|
||||
result = 0.Int256
|
||||
|
||||
proc unsignedToSigned(value: Int256): Int256 =
|
||||
if value <= UINT_255_MAX:
|
||||
return value
|
||||
else:
|
||||
return value - UINT_256_CEILING
|
||||
|
||||
proc signedToUnsigned(value: Int256): Int256 =
|
||||
if value < 0:
|
||||
return value + UINT_256_CEILING
|
||||
else:
|
||||
return value
|
||||
|
||||
macro ceilXX(ceiling: static[int]): untyped =
|
||||
var name = ident(%"ceil{ceiling}")
|
||||
result = quote:
|
||||
proc `name`*(value: Int256): Int256 =
|
||||
var remainder = value mod `ceiling`.Int256
|
||||
if remainder == 0:
|
||||
return value
|
||||
else:
|
||||
return value + `ceiling`.Int256 - remainder
|
||||
echo result.repr
|
||||
|
||||
ceilXX(32)
|
||||
ceilXX(8)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import
|
||||
sequtils,
|
||||
../constants, ../errors, ../logging, ../validation, ../utils_numeric
|
||||
|
||||
type
|
||||
Memory* = ref object
|
||||
logger*: Logger
|
||||
bytes*: seq[byte]
|
||||
|
||||
proc newMemory*: Memory =
|
||||
new(result)
|
||||
result.bytes = @[]
|
||||
result.logger = logging.getLogger("evm.vm.memory.Memory")
|
||||
|
||||
proc len*(memory: Memory): int =
|
||||
result = len(memory.bytes)
|
||||
|
||||
proc extend*(memory: var Memory; startPosition: Int256; size: Int256) =
|
||||
if size == 0:
|
||||
return
|
||||
var newSize = ceil32(startPosition + size)
|
||||
if newSize <= len(memory).Int256:
|
||||
return
|
||||
var sizeToExtend = newSize.int - len(memory)
|
||||
memory.bytes = memory.bytes.concat(repeat(0.byte, sizeToExtend))
|
||||
|
||||
proc read*(self: var Memory; startPosition: Int256; size: Int256): cstring =
|
||||
return cstring""
|
|
@ -30,6 +30,15 @@ type
|
|||
internalStorageAddress: cstring
|
||||
shouldTransferValue*: bool
|
||||
isStatic*: bool
|
||||
isCreate*: bool
|
||||
|
||||
MessageOptions* = ref object
|
||||
origin*: cstring
|
||||
depth*: Int256
|
||||
createAddress*: cstring
|
||||
codeAddress*: cstring
|
||||
shouldTransferValue*: bool
|
||||
isStatic*: bool
|
||||
|
||||
proc `origin=`*(message: var Message, value: cstring) =
|
||||
message.internalOrigin = value
|
||||
|
@ -40,6 +49,22 @@ proc `codeAddress=`*(message: var Message, value: cstring) =
|
|||
proc `storageAddress=`*(message: var Message, value: cstring) =
|
||||
message.internalStorageAddress = value
|
||||
|
||||
proc newMessageOptions*(
|
||||
origin: cstring = nil,
|
||||
depth: Int256 = 0.Int256,
|
||||
createAddress: cstring = nil,
|
||||
codeAddress: cstring = nil,
|
||||
shouldTransferValue: bool = true,
|
||||
isStatic: bool = false): MessageOptions =
|
||||
|
||||
result = MessageOptions(
|
||||
origin: origin,
|
||||
depth: depth,
|
||||
createAddress: createAddress,
|
||||
codeAddress: codeAddress,
|
||||
shouldTransferValue: shouldTransferValue,
|
||||
isStatic: isStatic)
|
||||
|
||||
proc newMessage*(
|
||||
gas: Int256,
|
||||
gasPrice: Int256,
|
||||
|
@ -48,12 +73,7 @@ proc newMessage*(
|
|||
value: Int256,
|
||||
data: cstring,
|
||||
code: cstring,
|
||||
origin: cstring = nil,
|
||||
depth: Int256 = 0.Int256,
|
||||
createAddress: cstring = nil,
|
||||
codeAddress: cstring = nil,
|
||||
shouldTransferValue: bool = true,
|
||||
isStatic: bool = false): Message =
|
||||
options: MessageOptions = newMessageOptions()): Message =
|
||||
|
||||
new(result)
|
||||
result.gas = gas
|
||||
|
@ -70,26 +90,26 @@ proc newMessage*(
|
|||
|
||||
result.data = data
|
||||
|
||||
if not origin.isNil:
|
||||
validateCanonicalAddress(origin, title="Message.origin")
|
||||
result.internalOrigin = origin
|
||||
if not options.origin.isNil:
|
||||
validateCanonicalAddress(options.origin, title="Message.origin")
|
||||
result.internalOrigin = options.origin
|
||||
|
||||
validateGte(depth, minimum=0, title="Message.depth")
|
||||
result.depth = depth
|
||||
validateGte(options.depth, minimum=0, title="Message.depth")
|
||||
result.depth = options.depth
|
||||
|
||||
result.code = code
|
||||
|
||||
if not createAddress.isNil:
|
||||
validateCanonicalAddress(createAddress, title="Message.storage_address")
|
||||
result.storageAddress = createAddress
|
||||
if not options.createAddress.isNil:
|
||||
validateCanonicalAddress(options.createAddress, title="Message.storage_address")
|
||||
result.storageAddress = options.createAddress
|
||||
|
||||
if not codeAddress.isNil:
|
||||
validateCanonicalAddress(codeAddress, title="Message.code_address")
|
||||
result.codeAddress = codeAddress
|
||||
if not options.codeAddress.isNil:
|
||||
validateCanonicalAddress(options.codeAddress, title="Message.code_address")
|
||||
result.codeAddress = options.codeAddress
|
||||
|
||||
result.shouldTransferValue = shouldTransferValue
|
||||
result.shouldTransferValue = options.shouldTransferValue
|
||||
|
||||
result.isStatic = isStatic
|
||||
result.isStatic = options.isStatic
|
||||
|
||||
proc origin*(message: Message): cstring =
|
||||
if not message.internalOrigin.isNil:
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
import
|
||||
strformat,
|
||||
strformat, macros,
|
||||
value, ../errors, ../validation, ../utils_numeric, ../constants, ../logging
|
||||
|
||||
type
|
||||
|
@ -14,47 +13,53 @@ template ensureStackLimit: untyped =
|
|||
if len(stack.values) > 1023:
|
||||
raise newException(FullStack, "Stack limit reached")
|
||||
|
||||
method len*(stack: Stack): int =
|
||||
proc len*(stack: Stack): int =
|
||||
len(stack.values)
|
||||
|
||||
method push*(stack: var Stack; value: Value) =
|
||||
proc push*(stack: var Stack; value: Value) =
|
||||
## Push an item onto the stack
|
||||
ensureStackLimit()
|
||||
|
||||
stack.values.add(value)
|
||||
|
||||
method push*(stack: var Stack; value: int) =
|
||||
proc push*(stack: var Stack; value: int) =
|
||||
## Push an integer onto the stack
|
||||
ensureStackLimit()
|
||||
|
||||
stack.values.add(Value(kind: VInt, i: value.Int256))
|
||||
|
||||
proc push*(stack: var Stack; value: Int256) =
|
||||
## Push an integer onto the stack
|
||||
ensureStackLimit()
|
||||
|
||||
stack.values.add(Value(kind: VInt, i: value))
|
||||
|
||||
method push*(stack: var Stack; value: cstring) =
|
||||
proc push*(stack: var Stack; value: cstring) =
|
||||
## Push a binary onto the stack
|
||||
ensureStackLimit()
|
||||
|
||||
stack.values.add(Value(kind: VBinary, b: value))
|
||||
|
||||
method internalPop(stack: var Stack; numItems: int): seq[Value] =
|
||||
proc internalPop(stack: var Stack; numItems: int): seq[Value] =
|
||||
if len(stack) < numItems:
|
||||
result = @[]
|
||||
else:
|
||||
result = stack.values[^numItems .. ^1]
|
||||
stack.values = stack.values[0 ..< ^numItems]
|
||||
|
||||
template toType(i: int, _: typedesc[int]): int =
|
||||
template toType(i: Int256, _: typedesc[Int256]): Int256 =
|
||||
i
|
||||
|
||||
template toType(i: int, _: typedesc[cstring]): cstring =
|
||||
template toType(i: Int256, _: typedesc[cstring]): cstring =
|
||||
intToBigEndian(i)
|
||||
|
||||
template toType(b: cstring, _: typedesc[int]): int =
|
||||
template toType(b: cstring, _: typedesc[Int256]): Int256 =
|
||||
bigEndianToInt(b)
|
||||
|
||||
template toType(b: cstring, _: typedesc[cstring]): cstring =
|
||||
b
|
||||
|
||||
method internalPop(stack: var Stack; numItems: int, T: typedesc): seq[T] =
|
||||
proc internalPop(stack: var Stack; numItems: int, T: typedesc): seq[T] =
|
||||
result = @[]
|
||||
if len(stack) < numItems:
|
||||
return
|
||||
|
@ -71,40 +76,75 @@ template ensurePop(elements: untyped, a: untyped): untyped =
|
|||
if len(`elements`) < `a`:
|
||||
raise newException(InsufficientStack, "No stack items")
|
||||
|
||||
method pop*(stack: var Stack): Value =
|
||||
proc pop*(stack: var Stack): Value =
|
||||
## Pop an item off the stack
|
||||
var elements = stack.internalPop(1)
|
||||
ensurePop(elements, 1)
|
||||
result = elements[0]
|
||||
|
||||
method pop*(stack: var Stack; numItems: int): seq[Value] =
|
||||
proc pop*(stack: var Stack; numItems: int): seq[Value] =
|
||||
## Pop many items off the stack
|
||||
result = stack.internalPop(numItems)
|
||||
ensurePop(result, numItems)
|
||||
|
||||
method popInt*(stack: var Stack): int =
|
||||
var elements = stack.internalPop(1, int)
|
||||
ensurePop(elements, 1)
|
||||
result = elements[0]
|
||||
# proc popInt*(stack: var Stack): Int256 =
|
||||
# var elements = stack.internalPop(1, Int256)
|
||||
# ensurePop(elements, 1)
|
||||
# result = elements[0]
|
||||
|
||||
method popInt*(stack: var Stack; numItems: int): seq[int] =
|
||||
result = stack.internalPop(numItems, int)
|
||||
ensurePop(result, numItems)
|
||||
macro internalPopTuple(numItems: static[int]): untyped =
|
||||
var name = ident(%"internalPopTuple{numItems}")
|
||||
var typ = nnkPar.newTree()
|
||||
var t = ident("T")
|
||||
for z in 0 ..< numItems:
|
||||
typ.add(t)
|
||||
result = quote:
|
||||
proc `name`(stack: var Stack, `t`: typedesc): `typ` =
|
||||
for z in 0 ..< `numItems`:
|
||||
var value = stack.values.pop()
|
||||
case value.kind:
|
||||
of VInt:
|
||||
result[z] = toType(value.i, `t`)
|
||||
of VBinary:
|
||||
result[z] = toType(value.b, `t`)
|
||||
|
||||
method popBinary*(stack: var Stack): cstring =
|
||||
# define pop<T> for tuples
|
||||
internalPopTuple(2)
|
||||
internalPopTuple(3)
|
||||
internalPopTuple(4)
|
||||
internalPopTuple(5)
|
||||
internalPopTuple(6)
|
||||
internalPopTuple(7)
|
||||
|
||||
macro popInt*(stack: typed; numItems: static[int]): untyped =
|
||||
var resultNode = ident("result")
|
||||
if numItems >= 8:
|
||||
result = quote:
|
||||
`resultNode` = `stack`.internalPop(`numItems`, Int256)
|
||||
else:
|
||||
var name = ident(%"internalPopTuple{numItems}")
|
||||
result = quote:
|
||||
`resultNode` = `name`(`stack`, Int256)
|
||||
|
||||
# proc popInt*(stack: var Stack, numItems: int): seq[Int256] =
|
||||
# result = stack.internalPop(numItems, Int256)
|
||||
# ensurePop(result, numItems)
|
||||
|
||||
proc popBinary*(stack: var Stack): cstring =
|
||||
var elements = stack.internalPop(1, cstring)
|
||||
ensurePop(elements, 1)
|
||||
result = elements[0]
|
||||
|
||||
method popBinary*(stack: var Stack; numItems: int): seq[cstring] =
|
||||
proc popBinary*(stack: var Stack; numItems: int): seq[cstring] =
|
||||
result = stack.internalPop(numItems, cstring)
|
||||
ensurePop(result, numItems)
|
||||
|
||||
proc makeStack*(): Stack =
|
||||
# result.logger = logging.getLogger("evm.vm.stack.Stack")
|
||||
proc newStack*(): Stack =
|
||||
new(result)
|
||||
result.logger = logging.getLogger("evm.vm.stack.Stack")
|
||||
result.values = @[]
|
||||
|
||||
method swap*(stack: var Stack; position: int) =
|
||||
proc swap*(stack: var Stack; position: int) =
|
||||
## Perform a SWAP operation on the stack
|
||||
var idx = position + 1
|
||||
if idx < len(stack) + 1:
|
||||
|
@ -113,7 +153,7 @@ method swap*(stack: var Stack; position: int) =
|
|||
raise newException(InsufficientStack,
|
||||
%"Insufficient stack items for SWAP{position}")
|
||||
|
||||
method dup*(stack: var Stack; position: int) =
|
||||
proc dup*(stack: var Stack; position: int) =
|
||||
## Perform a DUP operation on the stack
|
||||
if position < len(stack) + 1:
|
||||
stack.push(stack.values[^position])
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import ../constants
|
||||
|
||||
type
|
||||
ValueKind* = enum VInt, VBinary
|
||||
|
||||
Value* = ref object
|
||||
case kind*: ValueKind:
|
||||
of VInt:
|
||||
i*: int
|
||||
i*: Int256
|
||||
of VBinary:
|
||||
b*: cstring
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import
|
||||
strformat,
|
||||
logging, constants, errors, utils/state
|
||||
|
||||
type
|
||||
BaseVMState* = ref object of RootObj
|
||||
prevHeaders*: bool
|
||||
receipts*: bool
|
||||
computationClass*: bool
|
||||
chaindb*: bool
|
||||
accessLogs*: seq[bool]
|
||||
blockHeader*: bool
|
||||
name*: string
|
||||
|
||||
proc newBaseVMState*: BaseVMState =
|
||||
new(result)
|
||||
# result.chaindb = nil
|
||||
# result.blockHeader = nil
|
||||
# result.prevHeaders = nil
|
||||
# result.computationClass = nil
|
||||
# result.accessLogs = nil
|
||||
# result.receipts = nil
|
||||
|
||||
method logger*(vmState: BaseVMState): Logger =
|
||||
logging.getLogger(%"evm.vmState.{vmState.name}")
|
Loading…
Reference in New Issue