Pass more tests, use UInt256 more often

82 / 189 arithmetic
55 / 62 logic
77 / 78 stack

Most errors are the same sstore gascost bug, also next work on flow and memory
This commit is contained in:
Alexander Ivanov 2018-02-20 19:27:43 +02:00
parent aa1970013d
commit 99921f38cb
41 changed files with 631 additions and 590 deletions

View File

@ -10,7 +10,7 @@ skipDirs = @["tests"]
requires "nim >= 0.17.0",
"https://github.com/status-im/nim-keccak-tiny.git >= 0.1.0",
"https://github.com/status-im/nim-rlp.git >= 1.0.0",
"https://github.com/status-im/nim-ttmath >= 0.2.0"
"https://github.com/status-im/nim-ttmath >= 0.5.0"

View File

@ -3,12 +3,12 @@ import
type
Account* = ref object
nonce*: Int256
balance*: Int256
nonce*: UInt256
balance*: UInt256
storageRoot*: string
codeHash*: string
rlpFields Account, nonce, balance
proc newAccount*(nonce: Int256 = 0.i256, balance: Int256 = 0.i256): Account =
proc newAccount*(nonce: UInt256 = 0.u256, balance: UInt256 = 0.u256): Account =
Account(nonce: nonce, balance: balance)

View File

@ -8,4 +8,4 @@ type
Block* = ref object of RootObj
header*: Header
uncles*: CountableList[Header]
blockNumber*: Int256
blockNumber*: UInt256

View File

@ -1,9 +1,9 @@
import
strformat, strutils, sequtils, tables, macros, ttmath,
strformat, strutils, sequtils, tables, macros, ttmath, terminal,
constants, errors, utils/hexadecimal, utils_numeric, validation, vm_state, logging, opcode_values,
vm / [code_stream, gas_meter, memory, message, stack]
proc memoryGasCost*(sizeInBytes: Int256): Int256 =
proc memoryGasCost*(sizeInBytes: UInt256): UInt256 =
var
sizeInWords = ceil32(sizeInBytes) div 32
linearCost = sizeInWords * GAS_MEMORY
@ -26,7 +26,7 @@ type
rawOutput*: string
returnData*: string
error*: Error
logEntries*: seq[(string, seq[Int256], string)]
logEntries*: seq[(string, seq[UInt256], string)]
shouldEraseReturnData*: bool
accountsToDelete*: Table[string, string]
opcodes*: Table[Op, Opcode] # TODO array[Op, Opcode]
@ -40,9 +40,9 @@ type
Opcode* = ref object of RootObj
case kind*: Op
of VARIABLE_GAS_COST_OPS:
gasCostHandler*: proc(computation: var BaseComputation): Int256
gasCostHandler*: proc(computation: var BaseComputation): UInt256
else:
gasCostConstant*: Int256
gasCostConstant*: UInt256
runLogic*: proc(computation: var BaseComputation)
proc newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputation =
@ -87,9 +87,9 @@ method shouldEraseReturnData*(c: BaseComputation): bool =
method prepareChildMessage*(
c: var BaseComputation,
gas: Int256,
gas: UInt256,
to: string,
value: Int256,
value: UInt256,
data: seq[byte],
code: string,
options: MessageOptions = newMessageOptions()): Message =
@ -106,13 +106,13 @@ method prepareChildMessage*(
code,
childOptions)
method extendMemory*(c: var BaseComputation, startPosition: Int256, size: Int256) =
method extendMemory*(c: var BaseComputation, startPosition: UInt256, size: UInt256) =
# Memory Management
#
# 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).u256)
let afterSize = ceil32(startPosition + size)
let beforeCost = memoryGasCost(beforeSize)
@ -182,7 +182,7 @@ method registerAccountForDeletion*(c: var BaseComputation, beneficiary: string)
"registered for deletion multiple times")
c.accountsToDelete[c.msg.storageAddress] = beneficiary
method addLogEntry*(c: var BaseComputation, account: string, topics: seq[Int256], data: string) =
method addLogEntry*(c: var BaseComputation, account: string, topics: seq[UInt256], data: string) =
validateCanonicalAddress(account, title="log entry address")
c.logEntries.add((account, topics, data))
@ -193,28 +193,28 @@ method getAccountsForDeletion*(c: BaseComputation): seq[(string, string)] =
else:
result = @[]
method getLogEntries*(c: BaseComputation): seq[(string, seq[Int256], string)] =
method getLogEntries*(c: BaseComputation): seq[(string, seq[UInt256], string)] =
# TODO
if c.isError:
result = @[]
else:
result = @[]
method getGasRefund*(c: BaseComputation): Int256 =
method getGasRefund*(c: BaseComputation): UInt256 =
if c.isError:
result = 0.int256
result = 0.u256
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.u256)
method getGasUsed*(c: BaseComputation): Int256 =
method getGasUsed*(c: BaseComputation): UInt256 =
if c.shouldBurnGas:
result = c.msg.gas
else:
result = max(0.int256, c.msg.gas - c.gasMeter.gasRemaining)
result = max(0.u256, c.msg.gas - c.gasMeter.gasRemaining)
method getGasRemaining*(c: BaseComputation): Int256 =
method getGasRemaining*(c: BaseComputation): UInt256 =
if c.shouldBurnGas:
result = 0.int256
result = 0.u256
else:
result = c.gasMeter.gasRemaining
@ -295,6 +295,7 @@ macro applyComputation*(t: typed, vmState: untyped, message: untyped): untyped =
opcode.run(c)
except Halt:
break
c.logger.log($c.stack & "\n\n", fgGreen)
return c
inComputation(c):
res = handler()

View File

@ -1,10 +1,14 @@
import
ttmath, math, strutils, tables, utils/padding
ttmath, math, strutils, tables, utils/padding, rlp
# rlpFields UInt256, table
type
TypeHint* {.pure.} = enum UInt256, Bytes, Any
Bytes* = seq[byte]
# Int256* = BigInt #distinct int # TODO
proc int256*(i: int): Int256 =
@ -16,6 +20,12 @@ proc int256*(i: int): Int256 =
template i256*(i: Int256): Int256 =
i
template u256*(i: int): UInt256 =
i.uint.u256
template u256*(i: UInt256): UInt256 =
i
template getInt*(i: int): int =
i
@ -28,10 +38,16 @@ proc `==`*(a: Int256, b: int): bool =
proc `!=`*(a: Int256, b: int): bool =
a != b.i256
proc `^`*(base: int; exp: int): Int256 =
let base = base.i256
proc `==`*(a: UInt256, b: int): bool =
a == b.u256
proc `!=`*(a: UInt256, b: int): bool =
a != b.u256
proc `^`*(base: int; exp: int): UInt256 =
let base = base.u256
var ex = exp
result = 1.i256
result = 1.u256
while ex > 0:
result *= base
dec(ex)
@ -44,22 +60,47 @@ proc `^`*(left: Int256, right: int): Int256 =
result = result * m
value -= 1.i256
proc `^`*(left: UInt256, right: int): UInt256 =
var value = right.u256
result = 1.u256
var m = right.u256
while value > 0.u256:
result = result * m
value -= 1.u256
proc `>`*(a: Int256, b: int): bool =
a > b.i256
proc `<`*(a: Int256, b: int): bool =
a < b.i256
proc `>`*(a: UInt256, b: int): bool =
a > b.u256
proc `<`*(a: UInt256, b: int): bool =
a < b.u256
proc `mod`*(a: Int256, b: int): Int256 =
a mod b.i256
proc `div`*(a: Int256, b: int): Int256 =
a div b.i256
proc `mod`*(a: UInt256, b: int): UInt256 =
a mod b.u256
proc `div`*(a: UInt256, b: int): UInt256 =
a div b.u256
proc log256*(a: Int256): Int256 =
# TODO
2.i256
proc log256*(a: UInt256): UInt256 =
# TODO
2.u256
proc setXLen[T](s: var seq[T]; newlen: Natural) =
if s.isNil:
s = newSeq[T](newlen)
@ -69,13 +110,11 @@ proc setXLen[T](s: var seq[T]; newlen: Natural) =
template mapOp(op: untyped): untyped =
proc `op`*(left: Int256, right: int): Int256 =
result = left.i256
result = `op`(result, right.i256) # for now we dont have so many bits
# var maxRight = right.i256
# 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])
result = `op`(result, right.i256)
proc `op`*(left: UInt256, right: int): UInt256 =
result = left.u256
result = `op`(result, right.u256)
mapOp(`and`)
mapOp(`or`)
@ -84,109 +123,115 @@ mapOp(`xor`)
proc `abs`*(a: Int256): Int256 =
if a >= 0.i256: a else: -a
# constants
let
UINT_256_MAX*: Int256 = 2 ^ 256 - 1.i256
UINT_256_CEILING*: Int256 = 2 ^ 256
UINT_255_MAX*: Int256 = 2 ^ (256 - 1) - 1.i256
UINT_255_CEILING*: Int256 = 2 ^ (256 - 1)
UINT_256_MAX*: UInt256 = 2 ^ 256 - 1.u256
UINT_256_CEILING*: UInt256 = 2 ^ 256
UINT_255_MAX*: UInt256 = 2 ^ (255 - 1) - 1.u256
UINT_255_CEILING*: UInt256 = 2 ^ 255
UINT_256_CEILING_INT*: Int256 = 2.i256 ^ 256
UINT_255_MAX_INT*: Int256 = 2.i256 ^ (255 - 1) - 1.i256
UINT_256_MAX_INT*: Int256 = 2.i256 ^ 256 - 1.i256
UINT_255_CEILING_INT*: Int256 = 2.i256 ^ 255
NULLBYTE* = "\x00"
EMPTYWORD* = repeat(NULLBYTE, 32)
UINT160CEILING*: Int256 = 2 ^ 160
UINT160CEILING*: UInt256 = 2 ^ 160
CREATE_CONTRACT_ADDRESS* = ""
ZERO_ADDRESS* = repeat("\x00", 20)
ZERO_HASH32* = repeat("\x00", 20)
STACK_DEPTH_LIMIT* = 1024
GAS_NULL* = 0.i256
GAS_ZERO* = 0.i256
GAS_BASE* = 2.i256
GAS_VERY_LOW* = 3.i256
GAS_LOW* = 5.i256
GAS_MID* = 8.i256
GAS_HIGH* = 10.i256
GAS_EXT_CODE* = 20.i256
GAS_BALANCE* = 20.i256
GAS_SLOAD* = 50.i256
GAS_JUMP_DEST* = 1.i256
GAS_SSET* = 20_000.i256
GAS_SRESET* = 5000.i256
GAS_EXT_CODE_COST* = 700.i256
GAS_COINBASE* = 20.i256
GAS_SLOAD_COST* = 20.i256
GAS_SELF_DESTRUCT_COST* = 5_000.i256
GAS_IN_HANDLER* = 0.i256 # to be calculated in handler
REFUNDS_CLEAR* = 15_000.i256
GAS_NULL* = 0.u256
GAS_ZERO* = 0.u256
GAS_BASE* = 2.u256
GAS_VERY_LOW* = 3.u256
GAS_LOW* = 5.u256
GAS_MID* = 8.u256
GAS_HIGH* = 10.u256
GAS_EXT_CODE* = 20.u256
GAS_BALANCE* = 20.u256
GAS_SLOAD* = 50.u256
GAS_JUMP_DEST* = 1.u256
GAS_SSET* = 20_000.u256
GAS_SRESET* = 5_000.u256
GAS_EXT_CODE_COST* = 700.u256
GAS_COINBASE* = 20.u256
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
GAS_SELF_DESTRUCT* = 0.i256
GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.i256
GAS_CREATE* = 32_000.i256
GAS_CALL* = 40.i256
GAS_CALL_VALUE* = 9_000.i256
GAS_CALL_STIPEND* = 2_300.i256
GAS_NEW_ACCOUNT* = 25_000.i256
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_COST_BALANCE* = 400.i256
GAS_COST_BALANCE* = 400.u256
GAS_EXP* = 10.i256
GAS_EXP_BYTE* = 10.i256
GAS_MEMORY* = 3.i256
GAS_TX_CREATE* = 32_000.i256
GAS_TX_DATA_ZERO* = 4.i256
GAS_TX_DATA_NON_ZERO* = 68.i256
GAS_TX* = 21_000.i256
GAS_LOG* = 375.i256
GAS_LOG_DATA* = 8.i256
GAS_LOG_TOPIC* = 375.i256
GAS_SHA3* = 30.i256
GAS_SHA3_WORD* = 6.i256
GAS_COPY* = 3.i256
GAS_BLOCK_HASH* = 20.i256
GAS_CODE_DEPOSIT* = 200.i256
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512.i256
GAS_SHA256* = 60.i256
GAS_SHA256WORD* = 12.i256
GAS_RIP_EMD160* = 600.i256
GAS_RIP_EMD160WORD* = 120.i256
GAS_IDENTITY* = 15.i256
GAS_EXP* = 10.u256
GAS_EXP_BYTE* = 10.u256
GAS_MEMORY* = 3.u256
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_SHA3* = 30.u256
GAS_SHA3_WORD* = 6.u256
GAS_COPY* = 3.u256
GAS_BLOCK_HASH* = 20.u256
GAS_CODE_DEPOSIT* = 200.u256
GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512.u256
GAS_SHA256* = 60.u256
GAS_SHA256WORD* = 12.u256
GAS_RIP_EMD160* = 600.u256
GAS_RIP_EMD160WORD* = 120.u256
GAS_IDENTITY* = 15.u256
GAS_IDENTITY_WORD* = 3
GAS_ECRECOVER* = 3_000.i256
GAS_ECADD* = 500.i256
GAS_ECMUL* = 40_000.i256
GAS_ECPAIRING_BASE* = 100_000.i256
GAS_ECPAIRING_PER_POINT* = 80_000.i256
GAS_LIMIT_EMA_DENOMINATOR* = 1_024.i256
GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024.i256
GAS_LIMIT_MAXIMUM*: Int256 = 2 ^ 63 - 1.i256
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3.i256
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.i256
GAS_ECRECOVER* = 3_000.u256
GAS_ECADD* = 500.u256
GAS_ECMUL* = 40_000.u256
GAS_ECPAIRING_BASE* = 100_000.u256
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_USAGE_ADJUSTMENT_NUMERATOR* = 3.u256
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.u256
DIFFICULTY_ADJUSTMENT_DENOMINATOR* = 2_048.i256
DIFFICULTY_MINIMUM* = 131_072.i256
DIFFICULTY_ADJUSTMENT_DENOMINATOR* = 2_048.u256
DIFFICULTY_MINIMUM* = 131_072.u256
BOMB_EXPONENTIAL_PERIOD* = 100_000.i256
BOMB_EXPONENTIAL_FREE_PERIODS* = 2.i256
BOMB_EXPONENTIAL_PERIOD* = 100_000.u256
BOMB_EXPONENTIAL_FREE_PERIODS* = 2.u256
BLOCK_REWARD* = 5.i256 * 2.i256 # denoms.ether
BLOCK_REWARD* = 5.u256 * 2.u256 # denoms.ether
UNCLE_DEPTH_PENALTY_FACTOR* = 8.i256
UNCLE_DEPTH_PENALTY_FACTOR* = 8.u256
MAX_UNCLE_DEPTH* = 6.i256
MAX_UNCLES* = 2.i256
MAX_UNCLE_DEPTH* = 6.u256
MAX_UNCLES* = 2.u256
SECPK1_P*: Int256 = 2 ^ 256 - 2 ^ 32 - 977.i256
SECPK1_N*: Int256 = "115792089237316195423570985008687907852837564279074904382605163141518161494337".i256
SECPK1_A* = 0.i256
SECPK1_B* = 7.i256
SECPK1_Gx* = 0.i256
SECPK1_Gy* = 0.i256
SECPK1_P*: UInt256 = 2 ^ 256 - 2 ^ 32 - 977.u256
SECPK1_N*: UInt256 = "115792089237316195423570985008687907852837564279074904382605163141518161494337".u256
SECPK1_A* = 0.u256
SECPK1_B* = 7.u256
SECPK1_Gx* = 0.u256
SECPK1_Gy* = 0.u256
SECPK1_G* = (SECPK1Gx, SECPK1Gy)
EMPTY_UNCLE_HASH* = "\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.i256
GENESIS_DIFFICULTY* = 131_072.i256
GENESIS_GAS_LIMIT* = 3_141_592.i256
GENESIS_BLOCK_NUMBER* = 0.u256
GENESIS_DIFFICULTY* = 131_072.u256
GENESIS_GAS_LIMIT* = 3_141_592.u256
GENESIS_PARENT_HASH* = ZERO_HASH32
GENESIS_COINBASE* = ZERO_ADDRESS
GENESIS_NONCE* = "\x00\x00\x00\x00\x00\x00\x00B"
@ -196,7 +241,7 @@ let
EMPTYSHA3 = "\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p"
BLANK_ROOT_HASH* = "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.i256
GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20.u256
MAX_PREV_HEADER_DEPTH* = 256.i256
MAX_PREV_HEADER_DEPTH* = 256.u256

View File

@ -1,14 +1,14 @@
import
strformat, tables,
../constants, ../errors, ../validation, ../account, ../logging, ../utils/keccak, ttmath
../constants, ../errors, ../validation, ../account, ../logging, ../utils_numeric, .. / utils / [padding, bytes, keccak], ttmath, rlp
type
AccountStateDB* = ref object
db*: Table[string, string]
db*: Table[string, UInt256]
rootHash*: string # TODO trie
proc newAccountStateDB*(db: Table[string, string], readOnly: bool = false): AccountStateDB =
result = AccountStateDB(db: db)
result = AccountStateDB(db: initTable[string, UInt256]())
proc logger*(db: AccountStateDB): Logger =
logging.getLogger("db.State")
@ -32,68 +32,72 @@ proc getCodeHash*(db: AccountStateDB, address: string): string =
let account = db.getAccount(address)
result = account.codeHash
proc getBalance*(db: AccountStateDB, address: string): Int256 =
proc getBalance*(db: AccountStateDB, address: string): UInt256 =
validateCanonicalAddress(address, title="Storage Address")
let account = db.getAccount(address)
account.balance
proc setBalance*(db: var AccountStateDB, address: string, balance: Int256) =
proc setBalance*(db: var AccountStateDB, address: string, balance: UInt256) =
validateCanonicalAddress(address, title="Storage Address")
let account = db.getAccount(address)
account.balance = balance
db.setAccount(address, account)
proc deltaBalance*(db: var AccountStateDB, address: string, delta: Int256) =
proc deltaBalance*(db: var AccountStateDB, address: string, delta: UInt256) =
db.setBalance(address, db.getBalance(address) + delta)
proc setStorage*(db: var AccountStateDB, address: string, slot: Int256, value: Int256) =
validateGte(value, 0, title="Storage Value")
validateGte(slot, 0, title="Storage Slot")
proc setStorage*(db: var AccountStateDB, address: string, slot: UInt256, value: UInt256) =
#validateGte(value, 0, title="Storage Value")
#validateGte(slot, 0, title="Storage Slot")
validateCanonicalAddress(address, title="Storage Address")
let account = db.getAccount(address)
# TODO
# let account = db.getAccount(address)
# var storage = HashTrie(HexaryTrie(self.db, account.storageRoot))
# let slotAsKey = pad32(intToBigEndian(slot))
# if value:
# let encodedValue = rlp.encode(value)
# storage[slotAsKey] = encodedValue
# else:
# del storage[slotAsKey]
let slotAsKey = slot.intToBigEndian.pad32.toString
var storage = db.db
# TODO fix
#if value > 0:
# let encodedValue = rlp.encode(value)
# storage[slotAsKey] = encodedValue
#else:
# storage.del(slotAsKey)
storage[slotAsKey] = value
# account.storageRoot = storage.rootHash
# db.setAccount(address, account)
proc getStorage*(db: var AccountStateDB, address: string, slot: Int256): Int256 =
proc getStorage*(db: var AccountStateDB, address: string, slot: UInt256): UInt256 =
validateCanonicalAddress(address, title="Storage Address")
validateGte(slot, 0, title="Storage Slot")
0.i256
#validateGte(slot, 0, title="Storage Slot")
# TODO
# make it more correct
# for now, we just use a table
# let account = db.GetAccount(address)
# var storage = HashTrie(HexaryTrie(self.db, account.storageRoot))
# let slotAsKey = pad32(intToBigEndian(slot))
let slotAsKey = slot.intToBigEndian.pad32.toString
var storage = db.db
if storage.hasKey(slotAsKey):
result = storage[slotAsKey]
#let encodedValue = storage[slotAsKey]
#result = rlp.decode(encodedValue)
else:
result = 0.u256
# if slotAsKey in storage:
# let encodedValue = storage[slotAsKey]
# return rlp.decode(encodedValue)
# else:
# return 0.i256
proc setNonce*(db: var AccountStateDB, address: string, nonce: Int256) =
proc setNonce*(db: var AccountStateDB, address: string, nonce: UInt256) =
validateCanonicalAddress(address, title="Storage Address")
validateGte(nonce, 0, title="Nonce")
#validateGte(nonce, 0, title="Nonce")
var account = db.getAccount(address)
account.nonce = nonce
db.setAccount(address, account)
proc getNonce*(db: AccountStateDB, address: string): Int256 =
proc getNonce*(db: AccountStateDB, address: string): UInt256 =
validateCanonicalAddress(address, title="Storage Address")
let account = db.getAccount(address)
@ -105,15 +109,15 @@ proc setCode*(db: var AccountStateDB, address: string, code: string) =
var account = db.getAccount(address)
account.codeHash = keccak(code)
db.db[account.codeHash] = code
#db.db[account.codeHash] = code
db.setAccount(address, account)
proc getCode*(db: var AccountStateDB, address: string): string =
let codeHash = db.getCodeHash(address)
if db.db.hasKey(codeHash):
result = db.db[codeHash]
else:
result = ""
#if db.db.hasKey(codeHash):
# result = db.db[codeHash]
#else:
result = ""
# proc rootHash*(db: AccountStateDB): string =
# TODO return self.Trie.rootHash

View File

@ -1,14 +1,14 @@
import strformat
import strformat, terminal
type
Logger* = object
name*: string
var DEBUG = true
var DEBUG* = false
proc log*(l: Logger, msg: string) =
proc log*(l: Logger, msg: string, color: ForegroundColor = fgBlack) =
if DEBUG:
echo &"#{l.name}: {msg}"
styledWriteLine(stdout, color, &"#{l.name}: {msg}", resetStyle)
proc debug*(l: Logger, msg: string) =
if DEBUG:
@ -16,7 +16,7 @@ proc debug*(l: Logger, msg: string) =
proc trace*(l: Logger, msg: string) =
if DEBUG:
l.log(msg)
l.log(msg, fgBlue)
proc getLogger*(name: string): Logger =
result = Logger(name: name)

View File

@ -7,21 +7,22 @@ proc add*(computation: var BaseComputation) =
# Addition
var (left, right) = computation.stack.popInt(2)
var res = (left + right) and constants.UINT_256_MAX
var res = (left + right) and UINT_256_MAX
pushRes()
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.u256 else: (left + right) mod arg
echo left + right, " ", arg
pushRes()
proc sub*(computation: var BaseComputation) =
# Subtraction
var (left, right) = computation.stack.popInt(2)
var res = (left - right) and constants.UINT_256_MAX
var res = (left - right) and UINT_256_MAX
pushRes()
@ -29,50 +30,50 @@ 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.u256 else: value mod arg
pushRes()
proc smod*(computation: var BaseComputation) =
# Signed Modulo
var (value, arg) = computation.stack.popInt(2)
value = unsignedToSigned(value)
arg = unsignedToSigned(value)
let signedValue = unsignedToSigned(value)
let signedArg = unsignedToSigned(arg)
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)
var posOrNeg = if signedValue < 0: -1.i256 else: 1.i256
var signedRes = if signedArg == 0: 0.i256 else: ((signedValue.abs mod signedArg.abs) * posOrNeg) and UINT_256_MAX_INT
var res = signedToUnsigned(signedRes)
pushRes()
proc mul*(computation: var BaseComputation) =
# Multiplication
var (left, right) = computation.stack.popInt(2)
var res = (left * right) and constants.UINT_256_MAX
var res = (left * right) and UINT_256_MAX
pushRes()
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.u256 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.u256 else: (numerator div denominator) and UINT_256_MAX
pushRes()
proc sdiv*(computation: var BaseComputation) =
# Signed Division
var (numerator, denominator) = computation.stack.popInt(2)
numerator = unsignedToSigned(numerator)
denominator = unsignedToSigned(denominator)
let signedNumerator = unsignedToSigned(numerator)
let signedDenominator = 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)
var posOrNeg = if signedNumerator * signedDenominator < 0: -1.i256 else: 1.i256
var signedRes = if signedDenominator == 0: 0.i256 else: (posOrNeg * (signedNumerator.abs div signedDenominator.abs))
var res = signedToUnsigned(signedRes)
pushRes()
# no curry
@ -80,9 +81,9 @@ proc exp*(computation: var BaseComputation) =
# Exponentiation
var (base, exponent) = computation.stack.popInt(2)
var bitSize = 0.int256 # TODO exponent.bitLength()
var bitSize = 0.u256 # 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
var res = if base == 0: 0.u256 else: (base ^ exponent.getUInt.int) mod UINT_256_CEILING
# computation.gasMeter.consumeGas(
# gasPerByte * byteSize,
# reason="EXP: exponent bytes"
@ -93,11 +94,11 @@ proc signextend*(computation: var BaseComputation) =
# Signed Extend
var (bits, value) = computation.stack.popInt(2)
var res: 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)
var res: UInt256
if bits <= 31.u256:
var testBit = bits.getUInt.int * 8 + 7
var signBit = (1 shl testBit)
res = if value != 0 and signBit != 0: value or (UINT_256_CEILING - signBit.u256) else: value and (signBit.u256 - 1.u256)
else:
res = value
pushRes()

View File

@ -16,7 +16,7 @@ proc coinbase*(computation) =
stack.push(vmState.coinbase)
proc timestamp*(computation) =
stack.push(vmState.timestamp)
stack.push(vmState.timestamp.u256)
proc number*(computation) =
stack.push(vmState.blockNumber)

View File

@ -30,16 +30,16 @@ type
using
computation: var BaseComputation
method msgExtraGas*(call: BaseCall, computation; gas: Int256, to: string, value: Int256): Int256 {.base.} =
method msgExtraGas*(call: BaseCall, computation; gas: UInt256, to: string, value: UInt256): UInt256 {.base.} =
raise newException(NotImplementedError, "Must be implemented by subclasses")
method msgGas*(call: BaseCall, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) {.base.} =
method msgGas*(call: BaseCall, computation; gas: UInt256, to: string, value: UInt256): (UInt256, UInt256) {.base.} =
let extraGas = call.msgExtraGas(computation, gas, to, value)
let totalFee = gas + extraGas
let childMsgGas = gas + (if value != 0: GAS_CALL_STIPEND else: 0.i256)
let childMsgGas = gas + (if value != 0: GAS_CALL_STIPEND else: 0.u256)
(childMsgGas, totalFee)
method callParams*(call: BaseCall, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) {.base.} =
method callParams*(call: BaseCall, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) {.base.} =
raise newException(NotImplementedError, "Must be implemented subclasses")
method runLogic*(call: BaseCall, computation) =
@ -61,7 +61,7 @@ method runLogic*(call: BaseCall, computation) =
# TODO: Pre-call checks
# with computation.vm_state.state_db(read_only=True) as state_db:
# sender_balance = state_db.get_balance(computation.msg.storage_address)
let senderBalance = 0.i256
let senderBalance = 0.u256
let insufficientFunds = shouldTransferValue and senderBalance < value
let stackTooDeep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT
@ -78,7 +78,7 @@ method runLogic*(call: BaseCall, computation) =
call.logger.debug(&"{call.kind} failure: {errMessage}")
computation.gasMeter.returnGas(childMsgGas)
computation.stack.push(0.i256)
computation.stack.push(0.u256)
else:
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
@ -103,31 +103,31 @@ method runLogic*(call: BaseCall, computation) =
# TODO
var childComputation: BaseComputation
if childComputation.isError:
computation.stack.push(0.i256)
computation.stack.push(0.u256)
else:
computation.stack.push(1.i256)
computation.stack.push(1.u256)
if not childComputation.shouldEraseReturnData:
let actualOutputSize = min(memoryOutputSize, childComputation.output.len.i256)
let actualOutputSize = min(memoryOutputSize, childComputation.output.len.u256)
computation.memory.write(
memoryOutputStartPosition,
actualOutputSize,
childComputation.output.toBytes[0 ..< actualOutputSize.getInt])
childComputation.output.toBytes[0 ..< actualOutputSize.getUInt.int])
if not childComputation.shouldBurnGas:
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
method msgExtraGas(call: Call, computation; gas: Int256, to: string, value: Int256): Int256 =
method msgExtraGas(call: Call, computation; gas: UInt256, to: string, value: UInt256): UInt256 =
# 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.i256
let createGasFee = if not accountExists: GAS_NEW_ACCOUNT else: 0.i256
let transferGasFee = if value != 0: GAS_CALL_VALUE else: 0.u256
let createGasFee = if not accountExists: GAS_NEW_ACCOUNT else: 0.u256
transferGasFee + createGasFee
method callParams(call: CallCode, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) =
method callParams(call: CallCode, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let to = forceBytesToAddress(computation.stack.popBinary)
let to = forceBytesToAddress(computation.stack.popString)
let (value,
memoryInputStartPosition, memoryInputSize,
@ -145,12 +145,12 @@ method callParams(call: CallCode, computation): (Int256, Int256, string, string,
true, # should_transfer_value,
computation.msg.isStatic)
method msgExtraGas(call: CallCode, computation; gas: Int256, to: string, value: Int256): Int256 =
if value != 0: GAS_CALL_VALUE else: 0.i256
method msgExtraGas(call: CallCode, computation; gas: UInt256, to: string, value: UInt256): UInt256 =
if value != 0: GAS_CALL_VALUE else: 0.u256
method callParams(call: Call, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) =
method callParams(call: Call, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let codeAddress = forceBytesToAddress(computation.stack.popBinary)
let codeAddress = forceBytesToAddress(computation.stack.popString)
let (value,
memoryInputStartPosition, memoryInputSize,
@ -171,15 +171,15 @@ method callParams(call: Call, computation): (Int256, Int256, string, string, str
true, # should_transfer_value,
computation.msg.isStatic)
method msgGas(call: DelegateCall, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) =
method msgGas(call: DelegateCall, computation; gas: UInt256, to: string, value: UInt256): (UInt256, UInt256) =
(gas, gas)
method msgExtraGas(call: DelegateCall, computation; gas: Int256, to: string, value: Int256): Int256 =
0.i256
method msgExtraGas(call: DelegateCall, computation; gas: UInt256, to: string, value: UInt256): UInt256 =
0.u256
method callParams(call: DelegateCall, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) =
method callParams(call: DelegateCall, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let codeAddress = forceBytesToAddress(computation.stack.popBinary)
let codeAddress = forceBytesToAddress(computation.stack.popString)
let (memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4)
@ -200,30 +200,30 @@ method callParams(call: DelegateCall, computation): (Int256, Int256, string, str
false, # should_transfer_value,
computation.msg.isStatic)
proc maxChildGasEIP150*(gas: Int256): Int256 =
proc maxChildGasEIP150*(gas: UInt256): UInt256 =
gas - gas div 64
proc computeEIP150MsgGas(computation; gas: Int256, extraGas: Int256, value: Int256, name: string, callStipend: Int256): (Int256, Int256) =
proc computeEIP150MsgGas(computation; gas: UInt256, extraGas: UInt256, value: UInt256, name: string, callStipend: UInt256): (UInt256, UInt256) =
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.i256)
let childMsgGas = gas + (if value != 0: callStipend else: 0.u256)
(childMsgGas, totalFee)
method msgGas(call: CallEIP150, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) =
method msgGas(call: CallEIP150, computation; gas: UInt256, to: string, value: UInt256): (UInt256, UInt256) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)
method msgGas(call: CallCodeEIP150, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) =
method msgGas(call: CallCodeEIP150, computation; gas: UInt256, to: string, value: UInt256): (UInt256, UInt256) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)
method msgGas(call: DelegateCallEIP150, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) =
method msgGas(call: DelegateCallEIP150, computation; gas: UInt256, to: string, value: UInt256): (UInt256, UInt256) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0.i256)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0.u256)
proc msgExtraGas*(call: CallEIP161, computation; gas: Int256, to: string, value: Int256): Int256 =
proc msgExtraGas*(call: CallEIP161, computation; gas: UInt256, to: string, value: UInt256): UInt256 =
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
# account_is_dead = (
@ -231,20 +231,20 @@ proc msgExtraGas*(call: CallEIP161, computation; gas: Int256, to: string, value:
# state_db.account_is_empty(to))
let accountIsDead = true
let transferGasFee = if value != 0: GAS_CALL_VALUE else: 0.i256
let createGasFee = if accountIsDead and value != 0: GAS_NEW_ACCOUNT else: 0.i256
let transferGasFee = if value != 0: GAS_CALL_VALUE else: 0.u256
let createGasFee = if accountIsDead and value != 0: GAS_NEW_ACCOUNT else: 0.u256
transferGasFee + createGasFee
method callParams(call: StaticCall, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) =
method callParams(call: StaticCall, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let to = forceBytesToAddress(computation.stack.popBinary)
let to = forceBytesToAddress(computation.stack.popString)
let (memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4)
result = (gas,
0.i256, # value
0.u256, # value
to,
nil, # sender
nil, # codeAddress
@ -256,7 +256,7 @@ method callParams(call: StaticCall, computation): (Int256, Int256, string, strin
true) # is_static
method callParams(call: CallByzantium, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) =
method callParams(call: CallByzantium, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
result = procCall callParams(call, computation)
if computation.msg.isStatic and result[1] != 0:
raise newException(WriteProtection, "Cannot modify state while inside of a STATICCALL context")

View File

@ -21,7 +21,7 @@ quasiBoolean(xorOp, `xor`, nonzero=true) # Bitwise XOr
proc iszero*(computation: var BaseComputation) =
var value = computation.stack.popInt()
var res = if value == 0: 1.i256 else: 0.i256
var res = if value == 0: 1.u256 else: 0.u256
pushRes()
proc notOp*(computation: var BaseComputation) =
@ -34,6 +34,6 @@ proc byteOp*(computation: var BaseComputation) =
# Bitwise And
var (position, value) = computation.stack.popInt(2)
var res = if position >= 32.i256: 0.i256 else: (value div (256.i256.pow(31 - position.getInt))) mod 256
var res = if position >= 32.u256: 0.u256 else: (value div (256.u256.pow(31'u - position.getUInt))) mod 256
pushRes()

View File

@ -4,7 +4,7 @@ import
.. / vm / [stack, message, gas_meter, memory, code_stream], .. / utils / [address, padding, bytes], ttmath
proc balance*(computation: var BaseComputation) =
let address = forceBytesToAddress(computation.stack.popBinary)
let address = forceBytesToAddress(computation.stack.popString)
var balance: Int256
# TODO computation.vmState.stateDB(read_only=True):
# balance = db.getBalance(address)
@ -25,15 +25,15 @@ proc callValue*(computation: var BaseComputation) =
proc callDataLoad*(computation: var BaseComputation) =
# Load call data into memory
let startPosition = computation.stack.popInt.getInt
let value = computation.msg.data[startPosition ..< startPosition + 32].toString
let paddedValue = padRight(value, 32, "\x00")
let normalizedValue = paddedValue.lStrip(0.char)
let startPosition = computation.stack.popInt.getUInt.int
let value = computation.msg.data[startPosition ..< startPosition + 32]
let paddedValue = padRight(value, 32, 0.byte)
let normalizedValue = paddedValue.lStrip(0.byte)
computation.stack.push(normalizedValue)
proc callDataSize*(computation: var BaseComputation) =
let size = computation.msg.data.len
let size = computation.msg.data.len.u256
computation.stack.push(size)
proc callDataCopy*(computation: var BaseComputation) =
@ -45,13 +45,13 @@ proc callDataCopy*(computation: var BaseComputation) =
let wordCount = ceil32(size) div 32
let copyGasCost = wordCount * constants.GAS_COPY
computation.gasMeter.consumeGas(copyGasCost, reason="CALLDATACOPY fee")
let value = computation.msg.data[calldataStartPosition.getInt ..< (calldataStartPosition + size).getInt].toString
let paddedValue = padRight(value, size.getInt, "\x00")
let value = computation.msg.data[calldataStartPosition.getUInt.int ..< (calldataStartPosition + size).getUInt.int]
let paddedValue = padRight(value, size.getUInt.int, 0.byte)
computation.memory.write(memStartPosition, size, paddedValue)
proc codesize*(computation: var BaseComputation) =
let size = computation.code.len.i256
let size = computation.code.len.u256
computation.stack.push(size)
@ -76,7 +76,7 @@ proc gasprice*(computation: var BaseComputation) =
proc extCodeSize*(computation: var BaseComputation) =
let account = forceBytesToAddress(computation.stack.popBinary)
let account = forceBytesToAddress(computation.stack.popString)
# TODO
# with computation.vm_state.state_db(read_only=True) as state_db:
# code_size = len(state_db.get_code(account))
@ -84,7 +84,7 @@ proc extCodeSize*(computation: var BaseComputation) =
# computation.stack.push(code_size)
proc extCodeCopy*(computation: var BaseComputation) =
let account = forceBytesToAddress(computation.stack.popBinary)
let account = forceBytesToAddress(computation.stack.popString)
let (memStartPosition, codeStartPosition, size) = computation.stack.popInt(3)
computation.extendMemory(memStartPosition, size)
let wordCount = ceil32(size) div 32
@ -100,7 +100,7 @@ proc extCodeCopy*(computation: var BaseComputation) =
# computation.memory.write(mem_start_position, size, padded_code_bytes)
proc returnDataSize*(computation: var BaseComputation) =
let size = computation.returnData.len
let size = computation.returnData.len.u256
computation.stack.push(size)
proc returnDataCopy*(computation: var BaseComputation) =
@ -115,5 +115,5 @@ proc returnDataCopy*(computation: var BaseComputation) =
let wordCount = ceil32(size) div 32
let copyGasCost = wordCount * constants.GAS_COPY
computation.gasMeter.consumeGas(copyGasCost, reason="RETURNDATACOPY fee")
let value = ($computation.returnData)[returnDataStartPosition.getInt ..< (returnDataStartPosition + size).getInt]
let value = ($computation.returnData)[returnDataStartPosition.getUInt.int ..< (returnDataStartPosition + size).getUInt.int]
computation.memory.write(memStartPosition, size, value)

View File

@ -14,7 +14,7 @@ proc stop*(computation) =
proc jump*(computation) =
let jumpDest = stack.popInt.getInt
let jumpDest = stack.popInt.getUInt.int
code.pc = jumpDest
@ -30,21 +30,21 @@ proc jumpi*(computation) =
let (jumpDest, checkValue) = stack.popInt(2)
if checkValue > 0:
code.pc = jumpDest.getInt
code.pc = jumpDest.getUInt.int
let nextOpcode = code.peek()
if nextOpcode != JUMPDEST:
raise newException(InvalidJumpDestination, "Invalid Jump Destination")
if not code.isValidOpcode(jumpDest.getInt):
if not code.isValidOpcode(jumpDest.getUInt.int):
raise newException(InvalidInstruction, "Jump resulted in invalid instruction")
proc jumpdest*(computation) =
discard
proc pc*(computation) =
let pc = max(code.pc - 1, 0)
let pc = max(code.pc - 1, 0).u256
stack.push(pc)
proc gas*(computation) =

View File

@ -10,25 +10,26 @@ macro quasiBoolean*(name: untyped, op: untyped, signed: untyped = nil, nonzero:
var signedNode = newEmptyNode()
var finishSignedNode = newEmptyNode()
let resNode = ident("res")
let leftNode = ident("left")
let rightNode = ident("right")
var leftNode = ident("left")
var rightNode = ident("right")
var actualLeftNode = leftNode
var actualRightNode = rightNode
if not signed.isNil:
actualLeftNode = ident("leftSigned")
actualRightNode = ident("rightSigned")
signedNode = quote:
`leftNode` = unsignedToSigned(`leftNode`)
`rightNode` = unsignedToSigned(`rightNode`)
finishSignedNode = quote:
`resNode` = signedToUnsigned(`resNode`)
let `actualLeftNode` = unsignedToSigned(`leftNode`)
let `actualRightNode` = unsignedToSigned(`rightNode`)
var test = if nonzero.isNil:
quote:
`op`(`leftNode`, `rightNode`)
`op`(`actualLeftNode`, `actualRightNode`)
else:
quote:
`op`(`leftNode`, `rightNode`) != 0
`op`(`actualLeftNode`, `actualRightNode`) != 0
result = quote:
proc `name`*(computation: var BaseComputation) =
var (`leftNode`, `rightNode`) = computation.stack.popInt(2)
`signedNode`
var `resNode` = if `test`: 1.int256 else: 0.int256
`finishSignedNode`
var `resNode` = if `test`: 1.u256 else: 0.u256
computation.stack.push(`resNode`)

View File

@ -22,7 +22,7 @@ macro logXX(topicCount: static[int]): untyped =
result = quote:
proc `name`*(`computation`: var BaseComputation) =
let (`memStartPosition`, `size`) = `computation`.stack.popInt(2)
var `topics`: seq[Int256]
var `topics`: seq[UInt256]
var topicCode: NimNode
if topicCount == 0:
@ -44,7 +44,7 @@ macro logXX(topicCount: static[int]): untyped =
let logicCode = quote:
let dataGasCost = constants.GAS_LOG_DATA * `size`
let topicGasCost = constants.GAS_LOG_TOPIC * `topicCount`.i256
let topicGasCost = constants.GAS_LOG_TOPIC * `topicCount`.u256
let totalGasCost = dataGasCost + topicGasCost
`computation`.gasMeter.consumeGas(totalGasCost, reason="Log topic and data gas cost")
`computation`.extendMemory(`memStartPosition`, `size`)

View File

@ -12,11 +12,11 @@ proc mstoreX(computation; x: int) =
let start = stack.popInt()
let value = stack.popBinary()
let paddedValue = padLeft(value, x, "\x00")
let normalizedValue = ($paddedValue)[^x .. ^1]
extendMemory(start, x.int256)
memory.write(start, 32.int256, normalizedValue)
let paddedValue = padLeft(value, x, 0.byte)
let normalizedValue = paddedValue[^x .. ^1]
extendMemory(start, x.u256)
memory.write(start, 32.u256, normalizedValue)
# TODO template handler
@ -29,10 +29,10 @@ proc mstore8*(computation) =
proc mload*(computation) =
let start = stack.popInt()
extendMemory(start, 32.int256)
extendMemory(start, 32.u256)
let value = memory.read(start, 32.int256).toString
let value = memory.read(start, 32.u256)
stack.push(value)
proc msize*(computation) =
stack.push(memory.len)
stack.push(memory.len.u256)

View File

@ -5,7 +5,7 @@ proc sha3op*(computation: var BaseComputation) =
let (startPosition, size) = computation.stack.popInt(2)
computation.extendMemory(startPosition, size)
let sha3Bytes = computation.memory.read(startPosition, size)
let wordCount = sha3Bytes.len.i256.ceil32 div 32
let wordCount = sha3Bytes.len.u256.ceil32 div 32
let gasCost = constants.GAS_SHA3_WORD * wordCount
computation.gasMeter.consumeGas(gasCost, reason="SHA3: word gas cost")
var res = keccak("")

View File

@ -1,5 +1,5 @@
import
strformat, macros,
strformat, macros, sequtils,
../constants, ../errors, ../computation, .. / vm / [stack, code_stream], .. / utils / [padding, bytes], ttmath
{.this: computation.}
@ -9,7 +9,7 @@ using
computation: var BaseComputation
proc pop*(computation) =
discard stack.pop()
discard stack.popInt()
macro pushXX(size: static[int]): untyped =
let computation = ident("computation")
@ -17,12 +17,12 @@ macro pushXX(size: static[int]): untyped =
let name = ident(&"push{size}")
result = quote:
proc `name`*(`computation`: var BaseComputation) =
let `value` = `computation`.code.read(`size`).toString
let stripped = `value`.strip(0.char)
let `value` = `computation`.code.read(`size`)
let stripped = `value`.toString.strip(0.char)
if stripped.len == 0:
`computation`.stack.push(0.i256)
`computation`.stack.push(0.u256)
else:
let paddedValue = `value`.padRight(`size`, "\x00")
let paddedValue = `value`.padRight(`size`, 0.byte)
`computation`.stack.push(paddedValue)

View File

@ -1,5 +1,5 @@
import
../constants, ../errors, ../computation, .. / db / state_db, .. / vm / [stack, gas_meter, message], strformat
../constants, ../errors, ../computation, ../vm_state, .. / db / [db_chain, state_db], .. / vm / [stack, gas_meter, message], strformat, ttmath, utils / header
{.this: computation.}
{.experimental.}
@ -9,52 +9,26 @@ using
proc sstore*(computation) =
let (slot, value) = stack.popInt(2)
#if value != 0: #and slot == 0:
computation.gasMeter.consumeGas(GAS_SSET, &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} (TODO)")
#else:
#computation.gasMeter.consumeGas(GAS_SRESET, &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} (TODO)")
# with computation.vm_state.state_db(read_only=True) as state_db:
# current_value = state_db.get_storage(
# address=computation.msg.storage_address,
# slot=slot,
# )
# let isCurrentlyEmpty = not bool(current_value)
# let isGoingToBeEmpty = not bool(value)
var currentValue = 0.u256
# if is_currently_empty:
# gas_refund = 0
# elif is_going_to_be_empty:
# gas_refund = constants.REFUND_SCLEAR
# else:
# gas_refund = 0
computation.vmState.db(readOnly=false):
currentValue = db.getStorage(computation.msg.storageAddress, slot)
# if is_currently_empty and is_going_to_be_empty:
# gas_cost = constants.GAS_SRESET
# elif is_currently_empty:
# gas_cost = constants.GAS_SSET
# elif is_going_to_be_empty:
# gas_cost = constants.GAS_SRESET
# else:
# gas_cost = constants.GAS_SRESET
let isCurrentlyEmpty = currentValue == 0
let isGoingToBeEmpty = value == 0
# computation.gas_meter.consume_gas(gas_cost, reason="SSTORE: {0}[{1}] -> {2} ({3})".format(
# encode_hex(computation.msg.storage_address),
# slot,
# value,
# current_value,
# ))
let gasRefund = if isCurrentlyEmpty or not isGoingToBeEmpty: 0.u256 else: REFUND_SCLEAR
# if gas_refund:
# computation.gas_meter.refund_gas(gas_refund)
let gasCost = if isCurrentlyEmpty and not isGoingToBeEmpty: GAS_SSET else: GAS_SRESET
# with computation.vm_state.state_db() as state_db:
# state_db.set_storage(
# address=computation.msg.storage_address,
# slot=slot,
# value=value,
# )
computation.gasMeter.consumeGas(gasCost, &"SSTORE: {computation.msg.storageAddress}[slot] -> {value} ({currentValue})")
if gasRefund > 0:
computation.gasMeter.refundGas(gasRefund)
computation.vmState.db(readOnly=false):
db.setStorage(computation.msg.storageAddress, slot, value)
proc sload*(computation) =
let slot = stack.popInt()

View File

@ -17,7 +17,7 @@ type
CreateByzantium* = ref object of CreateEIP150
method maxChildGasModifier(create: Create, gas: Int256): Int256 {.base.} =
method maxChildGasModifier(create: Create, gas: UInt256): UInt256 {.base.} =
gas
method runLogic*(create: Create, computation) =
@ -55,7 +55,7 @@ method runLogic*(create: Create, computation) =
if isCollision:
computation.vmState.logger.debug(&"Address collision while creating contract: {contractAddress.encodeHex}")
computation.stack.push(0.i256)
computation.stack.push(0.u256)
return
let childMsg = computation.prepareChildMessage(
@ -70,12 +70,12 @@ method runLogic*(create: Create, computation) =
var childComputation: BaseComputation
if childComputation.isError:
computation.stack.push(0.i256)
computation.stack.push(0.u256)
else:
computation.stack.push(contractAddress)
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
method maxChildGasModifier(create: CreateEIP150, gas: Int256): Int256 =
method maxChildGasModifier(create: CreateEIP150, gas: UInt256): UInt256 =
maxChildGasEIP150(gas)
method runLogic*(create: CreateByzantium, computation) =
@ -84,7 +84,7 @@ method runLogic*(create: CreateByzantium, computation) =
procCall runLogic(create, computation)
proc selfdestructEIP150(computation) =
let beneficiary = forceBytesToAddress(stack.popBinary)
let beneficiary = forceBytesToAddress(stack.popString)
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
# if not state_db.account_exists(beneficiary):
@ -95,7 +95,7 @@ proc selfdestructEIP150(computation) =
# _selfdestruct(computation, beneficiary)
proc selfdestructEIP161(computation) =
let beneficiary = forceBytesToAddress(stack.popBinary)
let beneficiary = forceBytesToAddress(stack.popString)
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
# is_dead = (
@ -136,8 +136,8 @@ proc selfdestruct(computation; beneficiary: string) =
proc returnOp*(computation) =
let (startPosition, size) = stack.popInt(2)
computation.extendMemory(startPosition, size)
let output = memory.read(startPosition, size).toString
computation.output = output
let output = memory.read(startPosition, size)
computation.output = output.toString
raise newException(Halt, "RETURN")
proc revert*(computation) =
@ -148,7 +148,7 @@ proc revert*(computation) =
raise newException(Revert, $output)
proc selfdestruct*(computation) =
let beneficiary = forceBytesToAddress(stack.popBinary)
let beneficiary = forceBytesToAddress(stack.popString)
selfdestruct(computation, beneficiary)
raise newException(Halt, "SELFDESTRUCT")

View File

@ -10,16 +10,16 @@ template run*(opcode: Opcode, computation: var BaseComputation) =
method logger*(opcode: Opcode): Logger =
logging.getLogger(&"vm.opcode.{opcode.kind}")
method gasCost*(opcode: Opcode, computation: var BaseComputation): Int256 =
method gasCost*(opcode: Opcode, computation: var BaseComputation): UInt256 =
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 =
template newOpcode*(kind: Op, gasCost: UInt256, 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 =
template newOpcode*(kind: Op, gasHandler: proc(computation: var BaseComputation): UInt256, logic: proc(computation: var BaseComputation)): Opcode =
Opcode(kind: kind, gasCostHandler: gasHandler, runLogic: logic)
method `$`*(opcode: Opcode): string =

View File

@ -97,7 +97,7 @@ var OPCODE_TABLE* = initOpcodes:
# system
Op.Return: 0.i256 returnOp
Op.Return: 0.u256 returnOp
Op.SelfDestruct: GAS_SELF_DESTRUCT_COST selfdestruct

View File

@ -4,16 +4,16 @@ import
type
BaseTransaction* = ref object
nonce*: Int256
gasPrice*: Int256
gas*: Int256
gasPrice*: UInt256
gas*: UInt256
to*: string
value*: Int256
value*: UInt256
data*: string
v*: Int256
r*: Int256
s*: Int256
proc intrinsicGas*(t: BaseTransaction): Int256 =
proc intrinsicGas*(t: BaseTransaction): UInt256 =
# Compute the baseline gas cost for this transaction. This is the amount
# of gas needed to send this transaction (but that is not actually used
# for computation)

View File

@ -13,7 +13,9 @@ import strutils, sequtils
# cstring(res) # TODO: faster
proc toString*(value: seq[byte]): string =
value.mapIt(it.char).join("")
"0x" & value.mapIt(it.int.toHex(2)).join("").toLowerAscii
proc toBytes*(value: string): seq[byte] =
result = value.mapIt(it.byte)

View File

@ -3,11 +3,11 @@ import ../constants, ttmath, strformat
type
Header* = ref object
timestamp*: int
difficulty*: Int256
blockNumber*: Int256
difficulty*: UInt256
blockNumber*: UInt256
hash*: string
coinbase*: string
gasLimit*: Int256
gasLimit*: UInt256
stateRoot*: string
# TODO
@ -52,14 +52,14 @@ proc generateHeaderFromParentHeader*(
# return header
proc computeGasLimit*(header: Header, gasLimitFloor: Int256): Int256 =
proc computeGasLimit*(header: Header, gasLimitFloor: UInt256): UInt256 =
# TODO
gasLimitFloor
proc gasUsed*(header: Header): Int256 =
proc gasUsed*(header: Header): UInt256 =
# TODO
0.int256
0.u256
proc gasLimit*(header: Header): Int256 =
proc gasLimit*(header: Header): UInt256 =
# TODO
0.int256
0.u256

View File

@ -26,6 +26,17 @@ proc pad(value: string, size: int, with: string, left: bool): string =
else:
result = value
proc pad[T](value: seq[T], size: int, element: T, left: bool): seq[T] =
let padAmount = size - value.len
if padAmount > 0:
let fill = repeat(element, padAmount)
if left:
result = fill.concat(value)
else:
result = value.concat(fill)
else:
result = value
template padLeft*(value: cstring, size: int, with: cstring): cstring =
pad(value, size, with, true)
@ -94,3 +105,45 @@ proc lStrip*(value: string, c: char): string =
proc rStip*(value: string, c: char): string =
value.strip(chars={c}, leading=false)
template padLeft*[T](value: seq[T], size: int, element: T): seq[T] =
pad(value, size, element, true)
template padRight*[T](value: seq[T], size: int, element: T): seq[T] =
pad(value, size, element, false)
template zpadRight*[T](value: seq[T], size: int): seq[T] =
padRight(value, size, 0.byte)
template zpadLeft*[T](value: seq[T], size: int): seq[T] =
padLeft(value, size, 0.byte)
template pad32*[T](value: seq[T]): seq[T] =
zpadLeft(value, 32)
template pad32r*[T](value: seq[T]): seq[T] =
zpadRight(value, 32)
proc lStrip*[T](value: seq[T], element: T): seq[T] =
var z = 0
while z < value.len and value[z] == element:
z += 1
if z == 0:
result = value
elif z == value.len:
result = @[]
else:
result = value[z..^1]
proc rStrip*[T](value: seq[T], element: T): seq[T] =
var z = value.len - 1
while z >= 0 and value[z] == element:
z -= 1
if z == value.len - 1:
result = value
elif z == -1:
result = @[]
else:
result = value[0..z]

View File

@ -1,32 +1,48 @@
import ttmath, constants, strformat, macros
import ttmath, constants, strformat, sequtils, endians, macros, utils / padding
proc intToBigEndian*(value: Int256): string =
result = ""
# TODO improve
proc bigEndianToInt*(value: string): Int256 =
result = 0.int256
proc intToBigEndian*(value: UInt256): Bytes =
result = repeat(0.byte, 32)
for z in 0 ..< 4:
var temp = value.table[z]
bigEndian64(result[24 - z * 8].addr, temp.addr)
proc bigEndianToInt*(value: Bytes): UInt256 =
var bytes = value.padLeft(32, 0.byte)
result = 0.u256
for z in 0 ..< 4:
var temp = 0.uint
bigEndian64(temp.addr, bytes[24 - z * 8].addr)
result.table[z] = temp
proc unsignedToSigned*(value: Int256): Int256 =
if value <= UINT_255_MAX:
return value
else:
return value - UINT_256_CEILING
#echo intToBigEndian("32482610168005790164680892356840817100452003984372336767666156211029086934369".u256)
proc signedToUnsigned*(value: Int256): Int256 =
if value < 0:
return value + UINT_256_CEILING
else:
return value
proc unsignedToSigned*(value: UInt256): Int256 =
0.i256
# TODO
# if value <= UINT_255_MAX_INT:
# return value
# else:
# return value - UINT_256_CEILING_INT
proc signedToUnsigned*(value: Int256): UInt256 =
0.u256
# TODO
# if value < 0:
# return value + UINT_256_CEILING_INT
# 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
proc `name`*(value: UInt256): UInt256 =
var remainder = value mod `ceiling`.u256
if remainder == 0:
return value
else:
return value + `ceiling`.int256 - remainder
return value + `ceiling`.u256 - remainder
ceilXX(32)

View File

@ -26,13 +26,13 @@ proc validateLength*[T](values: seq[T], size: int) =
raise newException(ValidationError,
&"seq expected {size} len, got {values.len}")
proc validateLte*(value: Int256 | int, maximum: int, title: string = "Value") =
if value.i256 > maximum.i256:
proc validateLte*(value: UInt256 | int, maximum: int, title: string = "Value") =
if value.u256 > maximum.u256:
raise newException(ValidationError,
&"{title} {value} is not less or equal to {maximum}")
proc validateLt*(value: Int256 | int, maximum: int, title: string = "Value") =
if value.i256 >= maximum.i256:
proc validateLt*(value: UInt256 | int, maximum: int, title: string = "Value") =
if value.u256 >= maximum.u256:
raise newException(ValidationError,
&"{title} {value} is not less than {maximum}")
@ -40,3 +40,8 @@ proc validateStackItem*(value: string) =
if value.len > 32:
raise newException(ValidationError,
&"Invalid stack item: expected 32 bytes, got {value.len}: value is {value}")
proc validateStackItem*(value: string | seq[byte]) =
if value.len > 32:
raise newException(ValidationError,
&"Invalid stack item: expected 32 bytes, got {value.len}: value is {value}")

View File

@ -4,7 +4,7 @@ import
type
CodeStream* = ref object
bytes: seq[byte]
bytes*: seq[byte]
depthProcessed: int
invalidPositions: HashSet[int]
pc*: int

View File

@ -4,7 +4,7 @@ import
proc validateFrontierTransaction*(vmState: BaseVmState, transaction: BaseTransaction) =
let gasCost = transaction.gas * transaction.gasPrice
var senderBalance: Int256
var senderBalance: UInt256
# inDB(vmState.stateDB(readOnly=true):
# senderBalance = db.getBalance(transaction.sender)
senderBalance = gasCost # TODO

View File

@ -9,14 +9,14 @@ type
method name*(vm: FrontierVM): string =
"FrontierVM"
method getBlockReward(vm: FrontierVM): Int256 =
method getBlockReward(vm: FrontierVM): UInt256 =
BLOCK_REWARD
method getUncleReward(vm: FrontierVM, blockNumber: Int256, uncle: Block): Int256 =
method getUncleReward(vm: FrontierVM, blockNumber: UInt256, uncle: Block): UInt256 =
BLOCK_REWARD * (UNCLE_DEPTH_PENALTY_FACTOR + uncle.blockNumber - blockNumber) div UNCLE_DEPTH_PENALTY_FACTOR
method getNephewReward(vm: FrontierVM): Int256 =
method getNephewReward(vm: FrontierVM): UInt256 =
vm.getBlockReward() div 32
proc newFrontierVM*(header: Header, chainDB: BaseChainDB): FrontierVM =

View File

@ -2,7 +2,7 @@ import
strformat, ttmath,
../constants, ../opcode, ../computation, stack
proc expGasCost*(computation: var BaseComputation): Int256 =
proc expGasCost*(computation: var BaseComputation): UInt256 =
let arg = computation.stack.getInt(0)
result = if arg == 0: 10.i256 else: (10.i256 + 10.i256 * (1.i256 + arg.log256))
result = if arg == 0: 10.u256 else: (10.u256 + 10.u256 * (1.u256 + arg.log256))

View File

@ -5,20 +5,20 @@ import
type
GasMeter* = ref object
logger*: Logger
gasRefunded*: Int256
startGas*: Int256
gasRemaining*: Int256
gasRefunded*: UInt256
startGas*: UInt256
gasRemaining*: UInt256
proc newGasMeter*(startGas: Int256): GasMeter =
proc newGasMeter*(startGas: UInt256): GasMeter =
new(result)
result.startGas = startGas
result.gasRemaining = result.startGas
result.gasRefunded = 0.int256
result.gasRefunded = 0.u256
result.logger = logging.getLogger("gas")
proc consumeGas*(gasMeter: var GasMeter; amount: Int256; reason: string) =
if amount < 0.int256:
raise newException(ValidationError, "Gas consumption amount must be positive")
proc consumeGas*(gasMeter: var GasMeter; amount: UInt256; reason: string) =
#if amount < 0.u256:
# 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}")
@ -26,16 +26,16 @@ proc consumeGas*(gasMeter: var GasMeter; amount: Int256; reason: string) =
gasMeter.logger.trace(
&"GAS CONSUMPTION: {gasMeter.gasRemaining + amount} - {amount} -> {gasMeter.gasRemaining} ({reason})")
proc returnGas*(gasMeter: var GasMeter; amount: Int256) =
if amount < 0.int256:
raise newException(ValidationError, "Gas return amount must be positive")
proc returnGas*(gasMeter: var GasMeter; amount: UInt256) =
#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}")
proc refundGas*(gasMeter: var GasMeter; amount: Int256) =
if amount < 0.int256:
raise newException(ValidationError, "Gas refund amount must be positive")
proc refundGas*(gasMeter: var GasMeter; amount: UInt256) =
#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}")

View File

@ -15,37 +15,37 @@ proc newMemory*: Memory =
proc len*(memory: Memory): int =
result = memory.bytes.len
proc extend*(memory: var Memory; startPosition: Int256; size: Int256) =
proc extend*(memory: var Memory; startPosition: UInt256; size: UInt256) =
if size == 0:
return
var newSize = ceil32(startPosition + size)
if newSize <= len(memory).int256:
if newSize <= len(memory).u256:
return
var sizeToExtend = newSize - len(memory).int256
memory.bytes = memory.bytes.concat(repeat(0.byte, sizeToExtend.getInt))
var sizeToExtend = newSize - len(memory).u256
memory.bytes = memory.bytes.concat(repeat(0.byte, sizeToExtend.getUInt.int))
proc newMemory*(size: Int256): Memory =
proc newMemory*(size: UInt256): Memory =
result = newMemory()
result.extend(0.int256, size)
result.extend(0.u256, size)
proc read*(memory: var Memory, startPosition: Int256, size: Int256): seq[byte] =
result = memory.bytes[startPosition.getInt ..< (startPosition + size).getInt]
proc read*(memory: var Memory, startPosition: UInt256, size: UInt256): seq[byte] =
result = memory.bytes[startPosition.getUInt.int ..< (startPosition + size).getUInt.int]
proc write*(memory: var Memory, startPosition: Int256, size: Int256, value: seq[byte]) =
proc write*(memory: var Memory, startPosition: UInt256, size: UInt256, value: seq[byte]) =
if size == 0:
return
#echo size
#echo startPosition
validateGte(startPosition, 0)
validateGte(size, 0)
validateLength(value, size.getInt)
#validateGte(startPosition, 0)
#validateGte(size, 0)
validateLength(value, size.getUInt.int)
validateLte(startPosition + size, memory.len)
let index = memory.len
if memory.len.i256 < startPosition + size:
memory.bytes = memory.bytes.concat(repeat(0.byte, memory.len - (startPosition + size).getInt)) # TODO: better logarithmic scaling?
if memory.len.u256 < startPosition + size:
memory.bytes = memory.bytes.concat(repeat(0.byte, memory.len - (startPosition + size).getUInt.int)) # TODO: better logarithmic scaling?
for z, b in value:
memory.bytes[z + startPosition.getInt] = b
memory.bytes[z + startPosition.getUInt.int] = b
template write*(memory: var Memory, startPosition: Int256, size: Int256, value: cstring) =
template write*(memory: var Memory, startPosition: UInt256, size: UInt256, value: cstring) =
memory.write(startPosition, size, value.toBytes)

View File

@ -17,11 +17,11 @@ type
# logger = logging.getLogger("evm.vm.message.Message")
gas*: Int256
gasPrice*: Int256
gas*: UInt256
gasPrice*: UInt256
to*: string
sender*: string
value*: Int256
value*: UInt256
data*: seq[byte]
code*: string
internalOrigin: string
@ -66,11 +66,11 @@ proc newMessageOptions*(
isStatic: isStatic)
proc newMessage*(
gas: Int256,
gasPrice: Int256,
gas: UInt256,
gasPrice: UInt256,
to: string,
sender: string,
value: Int256,
value: UInt256,
data: seq[byte],
code: string,
options: MessageOptions = newMessageOptions()): Message =

View File

@ -1,13 +1,12 @@
import
strformat, strutils, sequtils, macros,
value, ../errors, ../validation, ../utils_numeric, ../constants, ttmath, ../logging
value, ../errors, ../validation, ../utils_numeric, ../constants, ttmath, ../logging, .. / utils / bytes
type
Stack* = ref object of RootObj
## VM Stack
logger*: Logger
values*: seq[Value]
values*: seq[UInt256]
template ensureStackLimit: untyped =
if len(stack.values) > 1023:
@ -16,86 +15,80 @@ template ensureStackLimit: untyped =
proc len*(stack: Stack): int =
len(stack.values)
proc push*(stack: var Stack; value: Value) =
## Push an item onto the stack
template toType(i: UInt256, _: typedesc[UInt256]): UInt256 =
i
template toType(i: UInt256, _: typedesc[string]): string =
i.intToBigEndian.toString
template toType(i: UInt256, _: typedesc[Bytes]): Bytes =
i.intToBigEndian
template toType(b: string, _: typedesc[UInt256]): UInt256 =
b.toBytes.bigEndianToInt
template toType(b: string, _: typedesc[string]): string =
b
template toType(b: string, _: typedesc[Bytes]): Bytes =
b.toBytes
template toType(b: Bytes, _: typedesc[UInt256]): UInt256 =
b.bigEndianToInt
template toType(b: Bytes, _: typedesc[string]): string =
b.toString
template toType(b: Bytes, _: typedesc[Bytes]): Bytes =
b
proc push*(stack: var Stack, value: uint) =
## Push an integer onto the stack
ensureStackLimit()
stack.values.add(value.u256)
proc push*(stack: var Stack, value: UInt256) =
## Push an integer onto the stack
ensureStackLimit()
if value.kind == VInt:
validateGte(value.i, 0)
else:
validateStackItem(value.b)
stack.values.add(value)
proc push*(stack: var Stack; value: int) =
## Push an integer onto the stack
ensureStackLimit()
validateGte(value, 0)
stack.values.add(Value(kind: VInt, i: value.int256))
proc push*(stack: var Stack; value: Int256) =
## Push an integer onto the stack
ensureStackLimit()
validateGte(value, 0)
stack.values.add(Value(kind: VInt, i: value))
proc push*(stack: var Stack; value: string) =
proc push*(stack: var Stack, value: string) =
## Push a binary onto the stack
ensureStackLimit()
validateStackItem(value)
stack.values.add(Value(kind: VBinary, b: value))
stack.values.add(value.toType(UInt256))
proc internalPop(stack: var Stack; numItems: int): seq[Value] =
proc push*(stack: var Stack, value: Bytes) =
ensureStackLimit()
validateStackItem(value)
stack.values.add(value.toType(UInt256))
proc internalPop(stack: var Stack, numItems: int): seq[UInt256] =
if len(stack) < numItems:
result = @[]
else:
result = stack.values[^numItems .. ^1]
stack.values = stack.values[0 ..< ^numItems]
template toType(i: Int256, _: typedesc[Int256]): Int256 =
i
template toType(i: Int256, _: typedesc[string]): string =
intToBigEndian(i)
template toType(b: string, _: typedesc[Int256]): Int256 =
bigEndianToInt(b)
template toType(b: string, _: typedesc[string]): string =
b
proc 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
for z in 0 ..< numItems:
var value = stack.values.pop()
case value.kind:
of VInt:
result.add(toType(value.i, T))
of VBinary:
result.add(toType(value.b, T))
result.add(toType(value, T))
template ensurePop(elements: untyped, a: untyped): untyped =
if len(`elements`) < `a`:
raise newException(InsufficientStack, "No stack items")
proc pop*(stack: var Stack): Value =
## Pop an item off the stack
var elements = stack.internalPop(1)
ensurePop(elements, 1)
result = elements[0]
proc pop*(stack: var Stack; numItems: int): seq[Value] =
## Pop many items off the stack
result = stack.internalPop(numItems)
ensurePop(result, numItems)
proc popInt*(stack: var Stack): Int256 =
var elements = stack.internalPop(1, Int256)
proc popInt*(stack: var Stack): UInt256 =
var elements = stack.internalPop(1, UInt256)
ensurePop(elements, 1)
result = elements[0]
@ -114,11 +107,7 @@ macro internalPopTuple(numItems: static[int]): untyped =
var zNode = newLit(z)
var element = quote:
var value = `stackNode`.values.pop()
case value.kind:
of VInt:
`resultNode`[`zNode`] = toType(value.i, `t`)
of VBinary:
`resultNode`[`zNode`] = toType(value.b, `t`)
`resultNode`[`zNode`] = toType(value, `t`)
result[^1].add(element)
# define pop<T> for tuples
@ -129,26 +118,31 @@ internalPopTuple(5)
internalPopTuple(6)
internalPopTuple(7)
macro popInt*(stack: typed; numItems: static[int]): untyped =
macro popInt*(stack: typed, numItems: static[int]): untyped =
var resultNode = ident("result")
if numItems >= 8:
result = quote:
`stack`.internalPop(`numItems`, Int256)
`stack`.internalPop(`numItems`, UInt256)
else:
var name = ident(&"internalPopTuple{numItems}")
result = quote:
`name`(`stack`, Int256)
`name`(`stack`, UInt256)
# proc popInt*(stack: var Stack, numItems: int): seq[Int256] =
# result = stack.internalPop(numItems, Int256)
# ensurePop(result, numItems)
proc popBinary*(stack: var Stack): Bytes =
var elements = stack.internalPop(1, Bytes)
ensurePop(elements, 1)
result = elements[0]
proc popBinary*(stack: var Stack): string =
proc popBinary*(stack: var Stack, numItems: int): seq[Bytes] =
result = stack.internalPop(numItems, Bytes)
ensurePop(result, numItems)
proc popString*(stack: var Stack): string =
var elements = stack.internalPop(1, string)
ensurePop(elements, 1)
result = elements[0]
proc popBinary*(stack: var Stack; numItems: int): seq[string] =
proc popString*(stack: var Stack, numItems: int): seq[string] =
result = stack.internalPop(numItems, string)
ensurePop(result, numItems)
@ -157,7 +151,7 @@ proc newStack*(): Stack =
result.logger = logging.getLogger("stack.Stack")
result.values = @[]
proc 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:
@ -166,7 +160,7 @@ proc swap*(stack: var Stack; position: int) =
raise newException(InsufficientStack,
&"Insufficient stack items for SWAP{position}")
proc dup*(stack: var Stack; position: int | Int256) =
proc dup*(stack: var Stack, position: int | UInt256) =
## Perform a DUP operation on the stack
if (position != 0 and position.getInt < stack.len + 1) or (position == 0 and position.getInt < stack.len):
stack.push(stack.values[^position.getInt])
@ -175,21 +169,14 @@ proc dup*(stack: var Stack; position: int | Int256) =
&"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 getInt*(stack: Stack, position: int): UInt256 =
stack.values[position]
proc getBinary*(stack: Stack, position: int): string =
let element = stack.values[position]
case element.kind:
of VBinary:
result = element.b
else:
raise newException(TypeError, "Expected binary")
proc getBinary*(stack: Stack, position: int): Bytes =
stack.values[position].toType(Bytes)
proc getString*(stack: Stack, position: int): string =
stack.values[position].toType(string)
proc `$`*(stack: Stack): string =
let values = stack.values.mapIt(&" {$it}").join("\n")

View File

@ -1,5 +1,5 @@
import
strformat, strutils,
strformat, strutils, sequtils,
../constants, ttmath
type
@ -10,7 +10,7 @@ type
of VInt:
i*: Int256
of VBinary:
b*: string
b*: seq[byte]
proc `$`*(value: Value): string =
case value.kind:
@ -26,6 +26,9 @@ proc vint*(i: Int256): Value =
Value(kind: VInt, i: i)
proc vbinary*(b: string): Value =
Value(kind: VBinary, b: b.mapIt(it.byte))
proc vbinary*(b: seq[byte]): Value =
Value(kind: VBinary, b: b)
proc `==`*(a: Value, b: Value): bool =

View File

@ -47,22 +47,22 @@ method coinbase*(vmState: BaseVMState): string =
method timestamp*(vmState: BaseVMState): int =
vmState.blockHeader.timestamp
method blockNumber*(vmState: BaseVMState): Int256 =
method blockNumber*(vmState: BaseVMState): UInt256 =
vmState.blockHeader.blockNumber
method difficulty*(vmState: BaseVMState): Int256 =
method difficulty*(vmState: BaseVMState): UInt256 =
vmState.blockHeader.difficulty
method gasLimit*(vmState: BaseVMState): Int256 =
method gasLimit*(vmState: BaseVMState): UInt256 =
vmState.blockHeader.gasLimit
method getAncestorHash*(vmState: BaseVMState, blockNumber: Int256): string =
var ancestorDepth = vmState.blockHeader.blockNumber - blockNumber - 1.int256
method getAncestorHash*(vmState: BaseVMState, blockNumber: UInt256): string =
var ancestorDepth = vmState.blockHeader.blockNumber - blockNumber - 1.u256
if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH or
ancestorDepth < 0 or
ancestorDepth >= vmState.prevHeaders.len.int256:
ancestorDepth >= vmState.prevHeaders.len.u256:
return ""
var header = vmState.prevHeaders[ancestorDepth.getInt]
var header = vmState.prevHeaders[ancestorDepth.getUInt.int]
result = header.hash
macro db*(vmState: untyped, readOnly: untyped, handler: untyped): untyped =
@ -72,7 +72,6 @@ macro db*(vmState: untyped, readOnly: untyped, handler: untyped): untyped =
let db = ident("db")
result = quote:
block:
echo `vmState`
var `db` = `vmState`.chaindb.getStateDB(`vmState`.blockHeader.stateRoot, `readOnly`)
`handler`
if `readOnly`:

View File

@ -6,7 +6,7 @@ import unittest, macros, strformat, strutils, sequtils, constants, opcode_values
disableLogging()
proc gasMeters: seq[GasMeter] =
@[newGasMeter(10.i256), newGasMeter(100.i256), newGasMeter(999.i256)]
@[newGasMeter(10.u256), newGasMeter(100.u256), newGasMeter(999.u256)]
macro all(element: untyped, handler: untyped): untyped =
let name = ident(&"{element.repr}s")
@ -47,20 +47,21 @@ macro all(element: untyped, handler: untyped): untyped =
suite "gasMeter":
test "consume rejects negative":
all(gasMeter):
expect(ValidationError):
gasMeter.consumeGas(-1.i256, "independent")
# types
# test "consume rejects negative":
# all(gasMeter):
# expect(ValidationError):
# gasMeter.consumeGas(-1.i256, "independent")
test "return rejects negative":
all(gasMeter):
expect(ValidationError):
gasMeter.returnGas(-1.i256)
# test "return rejects negative":
# all(gasMeter):
# expect(ValidationError):
# gasMeter.returnGas(-1.i256)
test "refund rejects negative":
all(gasMeter):
expect(ValidationError):
gasMeter.returnGas(-1.i256)
# test "refund rejects negative":
# all(gasMeter):
# expect(ValidationError):
# gasMeter.returnGas(-1.i256)
# TODO: -0/+0
test "consume spends":
@ -74,15 +75,15 @@ suite "gasMeter":
all(gasMeter):
check(gasMeter.gasRemaining == gasMeter.startGas)
expect(OutOfGas):
gasMeter.consumeGas(gasMeter.startGas + 1.i256, "")
gasMeter.consumeGas(gasMeter.startGas + 1.u256, "")
test "return refund works correctly":
all(gasMeter):
check(gasMeter.gasRemaining == gasMeter.startGas)
check(gasMeter.gasRefunded == 0)
gasMeter.consumeGas(5.i256, "")
check(gasMeter.gasRemaining == gasMeter.startGas - 5.i256)
gasMeter.returnGas(5.i256)
gasMeter.consumeGas(5.u256, "")
check(gasMeter.gasRemaining == gasMeter.startGas - 5.u256)
gasMeter.returnGas(5.u256)
check(gasMeter.gasRemaining == gasMeter.startGas)
gasMeter.refundGas(5.i256)
check(gasMeter.gasRefunded == 5.i256)
gasMeter.refundGas(5.u256)
check(gasMeter.gasRefunded == 5.u256)

View File

@ -1,59 +1,6 @@
import
os, macros, json, strformat, strutils, ttmath, utils / [hexadecimal, address], chain, vm_state, constants, db / [db_chain, state_db], vm / forks / frontier / vm, parseutils
os, macros, json, strformat, strutils, ttmath, utils / [hexadecimal, address], chain, vm_state, constants, db / [db_chain, state_db], vm / forks / frontier / vm, parseutils, ospaths
# TODO
# This block is a child of the genesis defined in the chain fixture above and contains a single tx
# that transfers 10 wei from 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b to
# 0x095e7baea6a6c7c4c2dfeb977efac326af552d87.
# var validBlockRlp = decodeHex(
# "0xf90260f901f9a07285abd5b24742f184ad676e31f6054663b3529bc35ea2fcad8a3e0f642a46f7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0964e6c9995e7e3757e934391b4f16b50c20409ee4eb9abd4c4617cb805449b9aa053d5b71a8fbb9590de82d69dfa4ac31923b0c8afce0d30d0d8d1e931f25030dca0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845754132380a0194605bacef646779359318c7b5899559a5bf4074bbe2cfb7e1b83b1504182dd88e0205813b22e5a9cf861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba0f3266921c93d600c43f6fa4724b7abae079b35b9e95df592f95f9f3445e94c88a012f977552ebdb7a492cf35f3106df16ccb4576ebad4113056ee1f52cbe4978c1c0") # noqa: E501
#
# type
# PrivateKey = object # TODO
# publicKey*: cstring
#
# proc testChain*: Chain =
# # Return a Chain object containing just the genesis block.
# #
# # This Chain does not perform any validation when importing new blocks.
# #
# # The Chain's state includes one funded account and a private key for it, which can be found in
# # the funded_address and private_keys variables in the chain itself.
# # Disable block validation so that we don't need to construct finalized blocks.
#
# var klass = configureChain(
# "TestChainWithoutBlockValidation",
# constants.GENESIS_BLOCK_NUMBER,
# FrontierVM,
# importBlock=false,
# validateBlock=false)
#
# var privateKey = PrivateKey(publicKey: cstring"0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8") # TODO
# var fundedAddress = privateKey.publicKey.toCanonicalAddress()
# var initialBalance = 100_000_000
# var genesisParams = GenesisParams(
# blockNumber: constants.GENESIS_BLOCK_NUMBER,
# difficulty: constants.GENESIS_DIFFICULTY,
# gasLimit: constants.GENESIS_GAS_LIMIT,
# parentHash: constants.GENESIS_PARENT_HASH,
# coinbase: constants.GENESIS_COINBASE,
# nonce: constants.GENESIS_NONCE,
# mixHash: constants.GENESIS_MIX_HASH,
# extraData: constants.GENESIS_EXTRA_DATA,
# timestamp: 1501851927,
# stateRoot: decodeHex("0x9d354f9b5ba851a35eced279ef377111387197581429cfcc7f744ef89a30b5d4"))
#
# var genesisState = GenesisState(
# fundedAddress: FundedAddress(
# balance: initialBalance,
# nonce: 0,
# code: cstring""))
#
# result = klass.fromGenesis(newBaseChainDB(getDbBackend()), genesisParams, genesisState)
# result.fundedAddress = fundedAddress
# result.fundedAddressInitialBalance = initialBalance
# result.fundedAddressPrivateKey = privateKey
#
proc generateTest(filename: string, handler: NimNode): NimNode =
echo filename
let testStatusIMPL = ident("testStatusIMPL")
@ -63,23 +10,24 @@ proc generateTest(filename: string, handler: NimNode): NimNode =
macro jsonTest*(s: static[string], handler: untyped): untyped =
result = nnkStmtList.newTree()
echo &"tests/fixtures/{s}"
#echo &"tests/fixtures/{s}"
var z = 0
for filename in walkDirRec(&"tests/fixtures/{s}"):
if "mul" in filename:
if z >= 4:
break
for filename in walkDirRec("tests" / "fixtures" / s):
var (folder, name) = filename.splitPath()
#if "Arithmetic" in folder: #
if name.startswith("swap"):
echo name
result.add(generateTest(filename, handler))
z += 1
proc setupStateDB*(desiredState: JsonNode, stateDB: var AccountStateDB) =
for account, accountData in desiredState:
for slot, value in accountData{"storage"}:
stateDB.setStorage(account, slot.parseInt.i256, value.getInt.i256)
stateDB.setStorage(account, slot.parseInt.u256, value.getInt.u256)
let nonce = accountData{"nonce"}.getInt.i256
let nonce = accountData{"nonce"}.getInt.u256
let code = accountData{"code"}.getStr
let balance = accountData{"balance"}.getInt.i256
let balance = accountData{"balance"}.getInt.u256
stateDB.setNonce(account, nonce)
stateDB.setCode(account, code)

View File

@ -1,7 +1,7 @@
import
unittest, strformat, strutils, sequtils, tables, ttmath, json,
test_helpers, constants, errors, logging,
chain, vm_state, computation, opcode, opcode_table, utils / header, vm / [gas_meter, message, code_stream], vm / forks / frontier / vm, db / [db_chain, state_db], db / backends / memory_backend
chain, vm_state, computation, opcode, opcode_table, utils / header, vm / [gas_meter, message, code_stream, stack], vm / forks / frontier / vm, db / [db_chain, state_db], db / backends / memory_backend
proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus)
@ -17,9 +17,9 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
var vm = newFrontierVM(Header(), newBaseChainDB(newMemoryDB()))
let header = Header(
coinbase: fixture{"env"}{"currentCoinbase"}.getStr,
difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.i256,
blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.i256,
gasLimit: fixture{"env"}{"currentGasLimit"}.getHexadecimalInt.i256,
difficulty: fixture{"env"}{"currentDifficulty"}.getHexadecimalInt.u256,
blockNumber: fixture{"env"}{"currentNumber"}.getHexadecimalInt.u256,
gasLimit: fixture{"env"}{"currentGasLimit"}.getHexadecimalInt.u256,
timestamp: fixture{"env"}{"currentTimestamp"}.getHexadecimalInt)
var code = ""
@ -31,18 +31,19 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
let message = newMessage(
to=fixture{"exec"}{"address"}.getStr,
sender=fixture{"exec"}{"caller"}.getStr,
value=fixture{"exec"}{"value"}.getHexadecimalInt.i256,
value=fixture{"exec"}{"value"}.getHexadecimalInt.u256,
data=fixture{"exec"}{"data"}.getStr.mapIt(it.byte),
code=code,
gas=fixture{"exec"}{"gas"}.getHexadecimalInt.i256,
gasPrice=fixture{"exec"}{"gasPrice"}.getHexadecimalInt.i256,
gas=fixture{"exec"}{"gas"}.getHexadecimalInt.u256,
gasPrice=fixture{"exec"}{"gasPrice"}.getHexadecimalInt.u256,
options=newMessageOptions(origin=fixture{"exec"}{"origin"}.getStr))
echo fixture{"exec"}
#echo fixture{"exec"}
var c = newCodeStreamFromUnescaped(code)
var opcodes = c.decompile
for opcode in opcodes:
echo opcode[0], " ", opcode[1], " ", opcode[2]
if DEBUG:
for opcode in opcodes:
echo opcode[0], " ", opcode[1], " ", opcode[2]
var computation = newBaseComputation(vm.state, message)
computation.accountsToDelete = initTable[string, string]()
@ -69,10 +70,10 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
check(computation.output == expectedOutput)
let gasMeter = computation.gasMeter
let expectedGasRemaining = fixture{"gas"}.getHexadecimalInt.i256
let expectedGasRemaining = fixture{"gas"}.getHexadecimalInt.u256
let actualGasRemaining = gasMeter.gasRemaining
let gasDelta = actualGasRemaining - expectedGasRemaining
check(gasDelta == 0)
#let gasDelta = actualGasRemaining - expectedGasRemaining
check(actualGasRemaining == expectedGasRemaining)
let callCreatesJson = fixture{"callcreates"}
var callCreates: seq[JsonNode] = @[]
@ -85,8 +86,8 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
var (childComputation, createdCall) = child
let toAddress = createdCall{"destination"}.getStr
let data = createdCall{"data"}.getStr.mapIt(it.byte)
let gasLimit = createdCall{"gasLimit"}.getHexadecimalInt.i256
let value = createdCall{"value"}.getHexadecimalInt.i256
let gasLimit = createdCall{"gasLimit"}.getHexadecimalInt.u256
let value = createdCall{"value"}.getHexadecimalInt.u256
check(childComputation.msg.to == toAddress)
check(data == childComputation.msg.data or childComputation.msg.code.len > 0)