Implement duplication and block, add more opcodes, improve math support

This commit is contained in:
Alexander Ivanov 2018-01-23 00:23:07 +02:00
parent 637b56fa4c
commit 72c8ad4877
22 changed files with 509 additions and 283 deletions

10
nimbus.nimble Normal file
View File

@ -0,0 +1,10 @@
mode = ScriptMode.Verbose
packageName = "nimbus"
version = "0.1.0"
author = "Status Research & Development GmbH"
description = "An Ethereum 2.0 Sharding Client for Resource-Restricted Devices"
license = "Apache License 2.0"
skipDirs = @["tests"]
requires "nim >= 0.17.0"

View File

@ -1,5 +1,5 @@
import
strformat, strutils, sequtils, tables, macros,
strformat, strutils, sequtils, tables, macros, bigints,
constants, errors, utils/hexadecimal, utils_numeric, validation, vm_state, logging,
vm / [code_stream, gas_meter, memory, message, stack]
@ -86,7 +86,7 @@ method prepareChildMessage*(
# ? kwargs.setdefault('sender', self.msg.storage_address)
var childOptions = options
childOptions.depth = c.msg.depth + 1.Int256
childOptions.depth = c.msg.depth + 1.int256
result = newMessage(
gas,
c.msg.gasPrice,
@ -104,20 +104,20 @@ method extendMemory*(c: var BaseComputation, startPosition: Int256, size: Int256
# validate_uint256(start_position, title="Memory start position")
# validate_uint256(size, title="Memory size")
let beforeSize = ceil32(len(c.memory).Int256)
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})")
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}")
reason = &"Expanding memory {beforeSize} -> {afterSize}")
c.memory.extend(startPosition, size)
@ -132,7 +132,7 @@ method `output=`*(c: var BaseComputation, value: cstring) =
macro generateChildBaseComputation*(t: typed, vmState: typed, childMsg: typed): untyped =
var typ = repr(getType(t)[1]).split(":", 1)[0]
var name = ident(%"new{typ}")
var name = ident(&"new{typ}")
var typName = ident(typ)
result = quote:
block:
@ -194,19 +194,19 @@ method getLogEntries*(c: BaseComputation): seq[(cstring, seq[Int256], cstring)]
method getGasRefund*(c: BaseComputation): Int256 =
if c.isError:
result = 0.Int256
result = 0.int256
else:
result = c.gasMeter.gasRefunded + c.children.mapIt(it.getGasRefund()).foldl(a + b, 0.Int256)
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)
result = max(0.int256, c.msg.gas - c.gasMeter.gasRemaining)
method getGasRemaining*(c: BaseComputation): Int256 =
if c.shouldBurnGas:
result = 0.Int256
result = 0.int256
else:
result = c.gasMeter.gasRemaining
@ -258,7 +258,7 @@ template inComputation*(c: untyped, handler: untyped): untyped =
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 name = ident(&"new{typ}")
var typName = ident(typ)
result = quote:
block:

View File

@ -1,205 +1,185 @@
import
math, strutils, tables #, eth_utils
bigints, math, strutils, tables, utils/padding
type
TypeHint* {.pure.} = enum UInt256, Bytes, Any
Int256* = distinct int # TODO
Int256* = BigInt #distinct int # TODO
proc int256*(i: int): Int256 =
i.initBigInt
# TODO
# We'll have a fast fixed Int256, for now this
proc `==`*(a: Int256, b: Int256): bool =
a.int == b.int
# We'll have a fast fixed int256, for now this works
proc `==`*(a: Int256, b: int): bool =
a.int == b
proc `!=`*(a: Int256, b: Int256): bool =
a.int != b.int
a == b.int256
proc `!=`*(a: Int256, b: int): bool =
a.int != b
a != b.int256
proc `^`*(a: Int256, b: Int256): Int256 =
(a.int ^ b.int).Int256
proc `^`*(base: int; exp: int): Int256 =
let base = base.initBigInt
var ex = exp
result = 1.initBigInt
while ex > 0:
result *= base
dec(ex)
proc `^`*(a: Int256, b: int): Int256 =
(a.int ^ b).Int256
proc `>`*(a: Int256, b: Int256): bool =
a.int > b.int
proc `^`*(left: Int256, right: int): Int256 =
var value = right.initBigInt
result = 1.initBigInt
var m = right.int256
while value > 0.int256:
result = result * m
value -= 1.int256
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
a > b.int256
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 =
(a.int - b.int).Int256
proc `+`*(a: Int256, b: Int256): Int256 =
(a.int + b.int).Int256
proc `-=`*(a: var Int256, b: Int256) =
a = (a - 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
a < b.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
a mod b.int256
proc `div`*(a: Int256, b: int): Int256 =
(a.int div b).Int256
a div b.int256
proc setXLen[T](s: var seq[T]; newlen: Natural) =
if s.isNil:
s = newSeq[T](newlen)
else:
s.setLen(newlen)
template mapOp(op: untyped): untyped =
proc `op`*(left: Int256, right: Int256): Int256 =
result = left.initBigInt
var maxRight = right.initBigInt
var l = max(left.limbs.len, right.limbs.len)
result.limbs.setXLen(l)
maxRight.limbs.setXLen(l)
for z in 0 ..< l:
result.limbs[z] = `op`(result.limbs[z], maxRight.limbs[z])
mapOp(`and`)
mapOp(`or`)
mapOp(`xor`)
proc `abs`*(a: Int256): Int256 =
a.int.abs.Int256
if a >= 0.int256: a else: -a
proc `and`*(a: Int256, b: Int256): Int256 =
(a.int and b.int).Int256
proc `getInt`*(a: Int256): int =
a.limbs[0].int
proc `or`*(a: Int256, b: Int256): Int256 =
(a.int or b.int).Int256
let
UINT_256_MAX*: Int256 = 2 ^ 256 - 1
UINT_256_CEILING*: Int256 = 2 ^ 256
UINT_255_MAX*: Int256 = 2 ^ (256 - 1) - 1
UINT_255_CEILING*: Int256 = 2 ^ (256 - 1)
proc `xor`*(a: Int256, b: Int256): Int256 =
(a.int xor b.int).Int256
NULLBYTE* = cstring"\x00"
EMPTYWORD* = repeat(NULLBYTE, 32)
UINT160CEILING*: Int256 = 2 ^ 160
CREATE_CONTRACT_ADDRESS* = cstring""
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* = 20_000.int256
GAS_SRESET* = 5000.int256
REFUNDS_CLEAR* = 15_000.int256
GAS_SELF_DESTRUCT* = 0.int256
GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.int256
GAS_CREATE* = 32_000.int256
GAS_CALL* = 40.int256
GASCALLVALUE = 9_000.int256
GAS_CALL_STIPEND* = 2_300.int256
GAS_NEW_ACCOUNT* = 25_000.int256
GAS_EXP* = 10.int256
GAS_EXP_BYTE* = 10.int256
GAS_MEMORY* = 3.int256
GAS_TX_CREATE* = 32_000.int256
GAS_TX_DATA_ZERO* = 4.int256
GAS_TX_DATA_NON_ZERO* = 68.int256
GAS_TX* = 21_000.int256
GAS_LOG* = 375.int256
GAS_LOG_DATA* = 8.int256
GAS_LOG_TOPIC* = 375.int256
GAS_SHA3* = 30.int256
GAS_SHA3_WORD* = 6.int256
GAS_COPY* = 3.int256
GAS_BLOCK_HASH* = 20.int256
GAS_CODE_DEPOSIT* = 200.int256
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512.int256
GAS_SHA256* = 60.int256
GAS_SHA256WORD* = 12.int256
GAS_RIP_EMD160* = 600.int256
GAS_RIP_EMD160WORD* = 120.int256
GAS_IDENTITY* = 15.int256
GAS_IDENTITY_WORD* = 3
GAS_ECRECOVER* = 3_000.int256
GAS_ECADD* = 500.int256
GAS_ECMUL* = 40_000.int256
GAS_ECPAIRING_BASE* = 100_000.int256
GAS_ECPAIRING_PER_POINT* = 80_000.int256
GAS_LIMIT_EMA_DENOMINATOR* = 1_024.int256
GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024.int256
GAS_LIMIT_MAXIMUM*: Int256 = 2 ^ 63 - 1
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3.int256
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.int256
DIFFICULTY_ADJUSTMENT_DENOMINATOR* = 2_048.int256
DIFFICULTY_MINIMUM* = 131_072.int256
BOMB_EXPONENTIAL_PERIOD* = 100_000.int256
BOMB_EXPONENTIAL_FREE_PERIODS* = 2.int256
BLOCK_REWARD* = 5.int256 * 2.int256 # denoms.ether
UNCLE_DEPTH_PENALTY_FACTOR* = 8.int256
MAX_UNCLE_DEPTH* = 6.int256
MAX_UNCLES* = 2.int256
SECPK1_P*: Int256 = 2 ^ 256 - 2 ^ 32 - 977
SECPK1_N*: Int256 = "115792089237316195423570985008687907852837564279074904382605163141518161494337".initBigInt
SECPK1_A* = 0.int256
SECPK1_B* = 7.int256
SECPK1_Gx* = 0.int256
SECPK1_Gy* = 0.int256
SECPK1_G* = (SECPK1Gx, SECPK1Gy)
EMPTY_UNCLE_HASH* = cstring"\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@\xd4\x93G"
GENESIS_BLOCK_NUMBER* = 0.int256
GENESIS_DIFFICULTY* = 131_072.int256
GENESIS_GAS_LIMIT* = 3_141_592.int256
GENESIS_PARENT_HASH* = ZERO_HASH32
GENESIS_COINBASE* = ZERO_ADDRESS
GENESIS_NONCE* = cstring"\x00\x00\x00\x00\x00\x00\x00B"
GENESIS_MIX_HASH* = ZERO_HASH32
GENESIS_EXTRA_DATA = cstring""
EMPTYSHA3 = cstring"\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p"
BLANK_ROOT_HASH* = cstring"V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!"
GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20.int256
proc max*(a: Int256, b: Int256): Int256 =
max(a.int, b.int).Int256
MAX_PREV_HEADER_DEPTH* = 256.int256
proc min*(a: Int256, b: Int256): Int256 =
min(a.int, b.int).Int256
proc `-`*(a: Int256): Int256 =
(-(a.int)).Int256
proc `shl`*(a: Int256, b: Int256): Int256 =
(a.int shl b.int).Int256
proc `shr`*(a: Int256, b: Int256): Int256 =
(a.int shr b.int).Int256
proc repeat(b: cstring, count: int): cstring =
# TODO: faster
var s = $b
result = cstring(repeat(s, count))
const
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""
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
GASCREATE = 32000
GASCALL = 40
GASCALLVALUE = 9000
GASCALLSTIPEND = 2300
GASNEWACCOUNT = 25000
GASEXP = 10
GASEXPBYTE = 10
GAS_MEMORY* = 3.Int256
GAS_TX_CREATE* = 32000.Int256
GAS_TX_DATA_ZERO* = 4.Int256
GAS_TX_DATA_NON_ZERO* = 68.Int256
GAS_TX* = 21000.Int256
GAS_LOG* = 375.Int256
GASLOGDATA = 8
GASLOGTOPIC = 375
GASSHA3 = 30
GASSHA3WORD = 6
GASCOPY = 3
GASBLOCKHASH = 20
GASCODEDEPOSIT = 200
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512.Int256
GAS_SHA256 = 60.Int256
GASSHA256WORD = 12
GASRIPEMD160 = 600
GASRIPEMD160WORD = 120
GASIDENTITY = 15
GASIDENTITYWORD = 3
GASECRECOVER = 3000
GASECADD = 500
GASECMUL = 40000
GASECPAIRINGBASE = 100000
GASECPAIRINGPERPOINT = 80000
GASLIMITEMADENOMINATOR = 1024
GASLIMITADJUSTMENTFACTOR = 1024
GASLIMITMINIMUM = 5000
# GASLIMITMAXIMUM = 2 ^ 63 - 1
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3.Int256
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.Int256
DIFFICULTY_ADJUSTMENT_DENOMINATOR* = 2_048.Int256
DIFFICULTY_MINIMUM* = 131_072.Int256
BOMB_EXPONENTIAL_PERIOD* = 100_000.Int256
BOMB_EXPONENTIAL_FREE_PERIODS* = 2.Int256
BLOCK_REWARD* = 5.Int256 * 2.Int256 # denoms.ether
UNCLE_DEPTH_PENALTY_FACTOR* = 8.Int256
MAXUNCLEDEPTH = 6
MAXUNCLES = 2
# SECPK1P = 2 ^ 256 - 2 ^ 32 - 977
SECPK1N = 0
SECPK1A = 0
SECPK1B = 7
SECPK1Gx = 0
SECPK1Gy = 0
SECPK1G = (SECPK1Gx, SECPK1Gy)
EMPTYUNCLEHASH = cstring"\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@\xd4\x93G"
GENESIS_BLOCK_NUMBER* = 0.Int256
GENESIS_DIFFICULTY* = 131_072.Int256
GENESIS_GAS_LIMIT* = 3_141_592.Int256
GENESIS_PARENT_HASH* = ZERO_HASH32
GENESIS_COINBASE* = ZERO_ADDRESS
GENESIS_NONCE* = cstring"\x00\x00\x00\x00\x00\x00\x00B"
GENESIS_MIX_HASH* = ZERO_HASH32
GENESIS_EXTRA_DATA = cstring""
EMPTYSHA3 = cstring"\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p"
BLANK_ROOT_HASH* = cstring"V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!"
GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20
MAX_PREV_HEADER_DEPTH* = 256

View File

@ -1,7 +1,7 @@
import
../constants, ../utils_numeric, ../computation,
.. / vm / [gas_meter, stack],
helpers
helpers, bigints
proc add*(computation: var BaseComputation) =
# Addition
@ -14,7 +14,7 @@ proc addmod*(computation: var BaseComputation) =
# Modulo Addition
var (left, right, arg) = computation.stack.popInt(3)
var res = if arg == 0: 0.Int256 else: (left + right) mod arg
var res = if arg == 0: 0.int256 else: (left + right) mod arg
pushRes()
proc sub*(computation: var BaseComputation) =
@ -29,7 +29,7 @@ proc modulo*(computation: var BaseComputation) =
# Modulo
var (value, arg) = computation.stack.popInt(2)
var res = if arg == 0: 0.Int256 else: value mod arg
var res = if arg == 0: 0.int256 else: value mod arg
pushRes()
proc smod*(computation: var BaseComputation) =
@ -38,8 +38,8 @@ proc smod*(computation: var BaseComputation) =
value = unsignedToSigned(value)
arg = unsignedToSigned(value)
var posOrNeg = if value < 0: -1.Int256 else: 1.Int256
var res = if arg == 0: 0.Int256 else: ((value.abs mod arg.abs) * posOrNeg) and constants.UINT_256_MAX
var posOrNeg = if value < 0: -1.int256 else: 1.int256
var res = if arg == 0: 0.int256 else: ((value.abs mod arg.abs) * posOrNeg) and constants.UINT_256_MAX
res = signedToUnsigned(res)
pushRes()
@ -54,14 +54,14 @@ proc mulmod*(computation: var BaseComputation) =
# Modulo Multiplication
var (left, right, arg) = computation.stack.popInt(3)
var res = if arg == 0: 0.Int256 else: (left * right) mod arg
var res = if arg == 0: 0.int256 else: (left * right) mod arg
pushRes()
proc divide*(computation: var BaseComputation) =
# Division
var (numerator, denominator) = computation.stack.popInt(2)
var res = if denominator == 0: 0.Int256 else: (numerator div denominator) and constants.UINT_256_MAX
var res = if denominator == 0: 0.int256 else: (numerator div denominator) and constants.UINT_256_MAX
pushRes()
proc sdiv*(computation: var BaseComputation) =
@ -70,8 +70,8 @@ proc sdiv*(computation: var BaseComputation) =
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))
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()
@ -80,9 +80,9 @@ proc exp*(computation: var BaseComputation, gasPerByte: Int256) =
# Exponentiation
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 res = if base == 0: 0.Int256 else: (base ^ exponent) mod constants.UINT_256_CEILING
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"
@ -94,10 +94,10 @@ proc signextend(computation: var BaseComputation) =
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 != 0 and signBit != 0: value or (constants.UINT_256_CEILING - signBit) else: value and (signBit - 1.Int256)
if bits <= 31.int256:
var testBit = bits.getInt * 8 + 7
var signBit = (1.int256 shl testBit)
res = if value != 0 and signBit != 0: value or (constants.UINT_256_CEILING - signBit) else: value and (signBit - 1.int256)
else:
res = value
pushRes()

19
src/logic/block.nim Normal file
View File

@ -0,0 +1,19 @@
import
../constants, ../computation, ../vm/stack, ../vm_state
proc blockhash*(computation: var BaseComputation) =
var blockNumber = computation.stack.popInt()
var blockHash = computation.vmState.getAncestorHash(blockNumber)
computation.stack.push(blockHash)
proc coinbase*(computation: var BaseComputation) =
computation.stack.push(computation.vmState.coinbase)
proc timestamp*(computation: var BaseComputation) =
computation.stack.push(computation.vmState.timestamp.int256)
proc difficulty*(computation: var BaseComputation) =
computation.stack.push(computation.vmState.difficulty)
proc gaslimit*(computation: var BaseComputation) =
computation.stack.push(computation.vmState.gasLimit)

View File

@ -1,6 +1,6 @@
import
../constants, ../utils_numeric, ../computation, ../vm/stack,
helpers
helpers, bigints
quasiBoolean(lt, `<`) # Lesser Comparison

27
src/logic/duplication.nim Normal file
View File

@ -0,0 +1,27 @@
import
macros, strformat,
../computation, ../vm/stack
macro dupXX(position: static[int]): untyped =
let name = ident(&"dup{position}")
result = quote:
proc `name`*(computation: var BaseComputation) =
computation.stack.dup(`position`)
dupXX(1)
dupXX(2)
dupXX(3)
dupXX(4)
dupXX(5)
dupXX(6)
dupXX(7)
dupXX(8)
dupXX(9)
dupXX(10)
dupXX(11)
dupXX(12)
dupXX(13)
dupXX(14)
dupXX(15)
dupXX(16)

45
src/logic/flow.nim Normal file
View File

@ -0,0 +1,45 @@
import
strformat, bigints,
../constants, ../opcode_values, ../logging, ../errors, ../computation, .. /vm / [code_stream, stack]
proc stop*(computation: var BaseComputation) =
raise newException(Halt, "STOP")
proc jump*(computation: var BaseComputation) =
var jumpDest = computation.stack.popInt.getInt
computation.code.pc = jumpDest
let nextOpcode = computation.code.peek()
if nextOpcode != JUMPDEST:
raise newException(InvalidJumpDestination, "Invalid Jump Destination")
if not computation.code.isValidOpcode(jumpDest):
raise newException(InvalidInstruction, "Jump resulted in invalid instruction")
proc jumpi*(computation: var BaseComputation) =
var (jumpDest, checkValue) = computation.stack.popInt(2)
if checkValue > 0:
computation.code.pc = jumpDest.getInt
let nextOpcode = computation.code.peek()
if nextOpcode != JUMPDEST:
raise newException(InvalidJumpDestination, "Invalid Jump Destination")
if not computation.code.isValidOpcode(jumpDest.getInt):
raise newException(InvalidInstruction, "Jump resulted in invalid instruction")
proc jumpdest*(computation: var BaseComputation) =
discard
proc pc*(computation: var BaseComputation) =
var pc = max(computation.code.pc - 1, 0)
computation.stack.push(pc)
proc gas*(computation: var BaseComputation) =
var gasRemaining = computation.gasMeter.gasRemaining
computation.stack.push(gasRemaining)

View File

@ -1,9 +1,10 @@
import macros
template pushRes* =
macro pushRes*: untyped =
let resNode = ident("res")
computation.stack.push(`resNode`)
result = quote:
computation.stack.push(`resNode`)
macro quasiBoolean*(name: untyped, op: untyped, signed: untyped = nil, nonzero: untyped = nil): untyped =
var signedNode = newEmptyNode()
@ -28,6 +29,6 @@ macro quasiBoolean*(name: untyped, op: untyped, signed: untyped = nil, nonzero:
var (`leftNode`, `rightNode`) = computation.stack.popInt(2)
`signedNode`
var `resNode` = if `test`: 1.Int256 else: 0.Int256
var `resNode` = if `test`: 1.int256 else: 0.int256
`finishSignedNode`
computation.stack.push(`resNode`)

View File

@ -0,0 +1,30 @@
import
../constants, ../computation, .. / vm / [stack, memory], ../utils/padding
proc mstoreX(computation: var BaseComputation, x: int) =
let start = computation.stack.popInt()
let value = computation.stack.popBinary()
let paddedValue = pad_left(value, x, cstring"\x00")
let normalizedValue = cstring(($paddedValue)[^x .. ^1])
computation.extendMemory(start, x.int256)
computation.memory.write(start, 32.int256, normalizedValue)
template mstore*(computation: var BaseComputation) =
mstoreX(32)
template mstore8*(computation: var BaseComputation) =
mstoreX(1)
proc mload*(computation: var BaseComputation) =
let start = computation.stack.popInt()
computation.extendMemory(start, 32.int256)
let value = computation.memory.read(start, 32.int256)
computation.stack.push(value)
proc msize*(computation: var BaseComputation) =
computation.stack.push(computation.memory.len)

28
src/logic/swap.nim Normal file
View File

@ -0,0 +1,28 @@
import
macros, strformat,
../computation, ../vm/stack
macro swapXX(position: static[int]): untyped =
let name = ident(&"swap{position}")
result = quote:
proc `name`*(computation: var BaseComputation) =
computation.stack.swap(`position`)
swapXX(0)
swapXX(1)
swapXX(2)
swapXX(3)
swapXX(4)
swapXX(5)
swapXX(6)
swapXX(7)
swapXX(8)
swapXX(9)
swapXX(10)
swapXX(11)
swapXX(12)
swapXX(13)
swapXX(14)
swapXX(15)
swapXX(16)

View File

@ -30,36 +30,54 @@ const
ADDRESS* = 48.byte
BALANCE* = 49.byte
ORIGIN* = 50.byte
CALLER* = 51.byte
CALLVALUE* = 52.byte
CALLDATALOAD* = 53.byte
CALLDATASIZE* = 54.byte
CALLDATACOPY* = 55.byte
CODESIZE* = 56.byte
CODECOPY* = 57.byte
GASPRICE* = 58.byte
EXTCODESIZE* = 59.byte
EXTCODECOPY* = 60.byte
RETURNDATASIZE* = 61.byte
RETURNDATACOPY* = 62.byte
BLOCKHASH* = 64.byte
COINBASE* = 65.byte
TIMESTAMP* = 66.byte
NUMBER* = 67.byte
DIFFICULTY* = 68.byte
GASLIMIT* = 69.byte
POP* = 80.byte
MLOAD* = 81.byte
MSTORE* = 82.byte
MSTORE8 = 83.byte
SLOAD* = 84.byte
SSTORE* = 85.byte
JUMP* = 86.byte
JUMPI* = 87.byte
PC* = 88.byte
MSIZE* = 89.byte
GAS* = 90.byte
JUMPDEST* = 91.byte
PUSH1* = 96.byte
@ -94,48 +112,48 @@ const
PUSH30* = 125.byte
PUSH31* = 126.byte
PUSH32* = 127.byte
DUP1* = 128.byte
DUP2* = 129.byte
DUP3* = 130.byte
DUP4* = 131.byte
DUP5* = 132.byte
DUP6* = 133.byte
DUP7* = 134.byte
DUP8* = 135.byte
DUP9* = 136.byte
DUP10* = 137.byte
DUP11* = 138.byte
DUP12* = 139.byte
DUP13* = 140.byte
DUP14* = 141.byte
DUP15* = 142.byte
DUP16* = 143.byte
SWAP1* = 144.byte
SWAP2* = 145.byte
SWAP3* = 146.byte
SWAP4* = 147.byte
SWAP5* = 148.byte
SWAP6* = 149.byte
SWAP7* = 150.byte
SWAP8* = 151.byte
SWAP9* = 152.byte
SWAP10* = 153.byte
SWAP11* = 154.byte
SWAP12* = 155.byte
SWAP13* = 156.byte
SWAP14* = 157.byte
SWAP15* = 158.byte
SWAP16* = 159.byte
LOG0* = 160.byte
LOG1* = 161.byte
LOG2* = 162.byte
LOG3* = 163.byte
LOG4* = 164.byte
CREATE* = 240.byte
CALL* = 241.byte
CALLCODE* = 242.byte
RETURN* = 243.byte
DELEGATECALL* = 244.byte
DUP1* = 128.byte
DUP2* = 129.byte
DUP3* = 130.byte
DUP4* = 131.byte
DUP5* = 132.byte
DUP6* = 133.byte
DUP7* = 134.byte
DUP8* = 135.byte
DUP9* = 136.byte
DUP10* = 137.byte
DUP11* = 138.byte
DUP12* = 139.byte
DUP13* = 140.byte
DUP14* = 141.byte
DUP15* = 142.byte
DUP16* = 143.byte
SWAP1* = 144.byte
SWAP2* = 145.byte
SWAP3* = 146.byte
SWAP4* = 147.byte
SWAP5* = 148.byte
SWAP6* = 149.byte
SWAP7* = 150.byte
SWAP8* = 151.byte
SWAP9* = 152.byte
SWAP10* = 153.byte
SWAP11* = 154.byte
SWAP12* = 155.byte
SWAP13* = 156.byte
SWAP14* = 157.byte
SWAP15* = 158.byte
SWAP16* = 159.byte
LOG0* = 160.byte
LOG1* = 161.byte
LOG2* = 162.byte
LOG3* = 163.byte
LOG4* = 164.byte
CREATE* = 240.byte
CALL* = 241.byte
CALLCODE* = 242.byte
RETURN* = 243.byte
DELEGATECALL* = 244.byte
STATICCALL* = 250.byte
REVERT* = 253.byte
SELFDESTRUCT* = 255.byte
REVERT* = 253.byte
SELFDESTRUCT* = 255.byte

View File

@ -1,5 +1,5 @@
import
constants, errors
constants, bigints, errors
type
BaseTransaction* = ref object
@ -27,7 +27,7 @@ proc intrinsicGas*(t: BaseTransaction): Int256 =
proc validate*(t: BaseTransaction) =
# Hook called during instantiation to ensure that all transaction
# parameters pass validation rules
if t.intrinsic_gas() > t.gas:
if t.intrinsicGas() > t.gas:
raise newException(ValidationError, "Insufficient gas")
# self.check_signature_validity()

View File

@ -5,6 +5,8 @@ type
timestamp*: int
difficulty*: Int256
blockNumber*: Int256
hash*: cstring
coinbase*: cstring
# TODO
proc generateHeaderFromParentHeader*(
@ -47,8 +49,8 @@ proc computeGasLimit*(header: Header, gasLimitFloor: Int256): Int256 =
proc gasUsed*(header: Header): Int256 =
# TODO
0.Int256
0.int256
proc gasLimit*(header: Header): Int256 =
# TODO
0.Int256
0.int256

36
src/utils/padding.nim Normal file
View File

@ -0,0 +1,36 @@
import strformat, strutils
proc repeat*(b: cstring, count: int): cstring =
# TODO
result = cstring(repeat($b, count))
proc pad(value: cstring, size: int, with: cstring, left: bool): cstring =
let padAmount = size - value.len
if padAmount > 0:
let fill = repeat(($with), padAmount)
if left:
result = cstring(&"{fill}{value}")
else:
result = cstring(&"{value}{fill}")
else:
result = value
template padLeft*(value: cstring, size: int, with: cstring): cstring =
pad(value, size, with, true)
template padRight*(value: cstring, size: int, with: cstring): cstring =
pad(value, size, with, false)
template zpadRight*(value: cstring, size: int): cstring =
padRight(value, size, with=cstring"\x00")
template zpadLeft*(value: cstring, size: int): cstring =
padLeft(value, size, with=cstring"\x00")
template pad32*(value: cstring): cstring =
zpadLeft(value, size=32)
template pad32r*(value: cstring): cstring =
zpadRight(value, size=32)

View File

@ -1,10 +1,10 @@
import constants, strformat, macros
import bigints, constants, strformat, macros
proc intToBigEndian*(value: Int256): cstring =
result = cstring""
proc bigEndianToInt*(value: cstring): Int256 =
result = 0.Int256
result = 0.int256
proc unsignedToSigned*(value: Int256): Int256 =
if value <= UINT_255_MAX:
@ -19,14 +19,14 @@ proc signedToUnsigned*(value: Int256): Int256 =
return value
macro ceilXX(ceiling: static[int]): untyped =
var name = ident(%"ceil{ceiling}")
var name = ident(&"ceil{ceiling}")
result = quote:
proc `name`*(value: Int256): Int256 =
var remainder = value mod `ceiling`.Int256
var remainder = value mod `ceiling`.int256
if remainder == 0:
return value
else:
return value + `ceiling`.Int256 - remainder
return value + `ceiling`.int256 - remainder
ceilXX(32)

View File

@ -2,7 +2,7 @@
import
strformat,
errors, constants
errors, constants, bigints
proc validateCanonicalAddress*(value: cstring, title: string = "Value") =
if len(value) != 20:
@ -13,12 +13,12 @@ proc validateCanonicalAddress*(value: cstring, title: string = "Value") =
proc validateGte*(value: Int256, minimum: int, title: string = "Value") =
if value <= minimum.Int256:
if value <= minimum.int256:
raise newException(ValidationError,
fmt"{title} {value} is not greater than or equal to {minimum}")
proc validateGt*(value: Int256, minimum: int, title: string = "Value") =
if value < minimum.Int256:
if value < minimum.int256:
raise newException(ValidationError,
fmt"{title} {value} is not greater than or equal to {minimum}")

View File

@ -1,6 +1,6 @@
import
strformat,
../logging, ../errors, ../constants
../logging, ../errors, ../constants, bigints
type
GasMeter* = ref object
@ -13,28 +13,28 @@ proc newGasMeter*(startGas: Int256): GasMeter =
new(result)
result.startGas = startGas
result.gasRemaining = result.startGas
result.gasRefunded = 0.Int256
result.gasRefunded = 0.int256
proc consumeGas*(gasMeter: var GasMeter; amount: Int256; reason: string) =
if amount < 0.Int256:
if amount < 0.int256:
raise newException(ValidationError, "Gas consumption amount must be positive")
if amount > gasMeter.gasRemaining:
raise newException(OutOfGas,
%"Out of gas: Needed {amount} - Remaining {gasMeter.gasRemaining} - Reason: {reason}")
&"Out of gas: Needed {amount} - Remaining {gasMeter.gasRemaining} - Reason: {reason}")
gasMeter.gasRemaining -= amount
gasMeter.logger.trace(
%"GAS CONSUMPTION: {gasMeter.gasRemaining + amount} - {amount} -> {gasMeter.gasRemaining} ({reason})")
&"GAS CONSUMPTION: {gasMeter.gasRemaining + amount} - {amount} -> {gasMeter.gasRemaining} ({reason})")
proc returnGas*(gasMeter: var GasMeter; amount: Int256) =
if amount < 0.Int256:
if amount < 0.int256:
raise newException(ValidationError, "Gas return amount must be positive")
gasMeter.gasRemaining += amount
gasMeter.logger.trace(
%"GAS RETURNED: {gasMeter.gasRemaining - amount} + {amount} -> {gasMeter.gasRemaining}")
&"GAS RETURNED: {gasMeter.gasRemaining - amount} + {amount} -> {gasMeter.gasRemaining}")
proc refundGas*(gasMeter: var GasMeter; amount: Int256) =
if amount < 0.Int256:
if amount < 0.int256:
raise newException(ValidationError, "Gas refund amount must be positive")
gasMeter.gasRefunded += amount
gasMeter.logger.trace(
%"GAS REFUND: {gasMeter.gasRemaining - amount} + {amount} -> {gasMeter.gasRefunded}")
&"GAS REFUND: {gasMeter.gasRemaining - amount} + {amount} -> {gasMeter.gasRefunded}")

View File

@ -1,5 +1,5 @@
import
sequtils,
sequtils, bigints,
../constants, ../errors, ../logging, ../validation, ../utils_numeric
type
@ -19,10 +19,13 @@ proc extend*(memory: var Memory; startPosition: Int256; size: Int256) =
if size == 0:
return
var newSize = ceil32(startPosition + size)
if newSize <= len(memory).Int256:
if newSize <= len(memory).int256:
return
var sizeToExtend = newSize.int - len(memory)
memory.bytes = memory.bytes.concat(repeat(0.byte, sizeToExtend))
var sizeToExtend = newSize - len(memory).int256
memory.bytes = memory.bytes.concat(repeat(0.byte, sizeToExtend.getInt))
proc read*(self: var Memory; startPosition: Int256; size: Int256): cstring =
return cstring""
proc write*(self: var Memory, startPosition: Int256, size: Int256, value: cstring) =
echo value

View File

@ -51,7 +51,7 @@ proc `storageAddress=`*(message: var Message, value: cstring) =
proc newMessageOptions*(
origin: cstring = nil,
depth: Int256 = 0.Int256,
depth: Int256 = 0.int256,
createAddress: cstring = nil,
codeAddress: cstring = nil,
shouldTransferValue: bool = true,

View File

@ -26,7 +26,7 @@ proc push*(stack: var Stack; value: int) =
## Push an integer onto the stack
ensureStackLimit()
stack.values.add(Value(kind: VInt, i: value.Int256))
stack.values.add(Value(kind: VInt, i: value.int256))
proc push*(stack: var Stack; value: Int256) =
## Push an integer onto the stack
@ -93,7 +93,7 @@ proc popInt*(stack: var Stack): Int256 =
result = elements[0]
macro internalPopTuple(numItems: static[int]): untyped =
var name = ident(%"internalPopTuple{numItems}")
var name = ident(&"internalPopTuple{numItems}")
var typ = nnkPar.newTree()
var t = ident("T")
var resultNode = ident("result")
@ -128,7 +128,7 @@ macro popInt*(stack: typed; numItems: static[int]): untyped =
result = quote:
`stack`.internalPop(`numItems`, Int256)
else:
var name = ident(%"internalPopTuple{numItems}")
var name = ident(&"internalPopTuple{numItems}")
result = quote:
`name`(`stack`, Int256)
@ -157,7 +157,7 @@ proc swap*(stack: var Stack; position: int) =
(stack.values[^1], stack.values[^idx]) = (stack.values[^idx], stack.values[^1])
else:
raise newException(InsufficientStack,
%"Insufficient stack items for SWAP{position}")
&"Insufficient stack items for SWAP{position}")
proc dup*(stack: var Stack; position: int) =
## Perform a DUP operation on the stack
@ -165,5 +165,5 @@ proc dup*(stack: var Stack; position: int) =
stack.push(stack.values[^position])
else:
raise newException(InsufficientStack,
%"Insufficient stack items for DUP{position}")
&"Insufficient stack items for DUP{position}")

View File

@ -1,6 +1,6 @@
import
strformat, tables,
logging, constants, errors, transaction, db/chain, utils/state, utils/header
logging, constants, bigints, errors, transaction, db/chain, utils/state, utils/header
type
BaseVMState* = ref object of RootObj
@ -22,4 +22,31 @@ proc newBaseVMState*: BaseVMState =
# result.receipts = nil
method logger*(vmState: BaseVMState): Logger =
logging.getLogger(%"evm.vmState.{vmState.name}")
logging.getLogger(&"evm.vmState.{vmState.name}")
method blockhash*(vmState: BaseVMState): cstring =
vmState.blockHeader.hash
method coinbase*(vmState: BaseVMState): cstring =
vmState.blockHeader.coinbase
method timestamp*(vmState: BaseVMState): int =
vmState.blockHeader.timestamp
method blockBumber*(vmState: BaseVMState): Int256 =
vmState.blockHeader.blockNumber
method difficulty*(vmState: BaseVMState): Int256 =
vmState.blockHeader.difficulty
method gasLimit*(vmState: BaseVMState): Int256 =
vmState.blockHeader.gasLimit
method getAncestorHash*(vmState: BaseVMState, blockNumber: Int256): cstring =
var ancestorDepth = vmState.blockHeader.blockNumber - blockNumber - 1.int256
if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH or
ancestorDepth < 0 or
ancestorDepth >= vmState.prevHeaders.len.int256:
return cstring""
var header = vmState.prevHeaders[ancestorDepth.getInt]
result = header.hash