Hash256/EthAddrres refactoring

This commit is contained in:
Yuriy Glukhov 2018-05-30 19:11:15 +03:00
parent 6473610574
commit 8bdf09683b
38 changed files with 384 additions and 547 deletions

15
.gitignore vendored
View File

@ -1,2 +1,17 @@
# Ignore all files without extensions (unix executable files)
*
!*.*
!*/
!LICENSE*
nimcache/
# Executables shall be put in an ignored build/ directory
# Ignore dynamic, static libs and libtool archive files
build/
*.so
*.dylib
*.a
*.la
*.exe
*.dll

View File

@ -11,6 +11,7 @@ requires "nim >= 0.18.1",
"nimcrypto",
"rlp",
"stint",
"https://github.com/status-im/nim-eth-common",
"https://github.com/status-im/nim-eth-p2p",
"https://github.com/status-im/nim-eth-keyfile"

View File

@ -6,14 +6,14 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
constants, errors, stint, rlp
constants, errors, stint, rlp, eth_common
type
Account* = ref object
nonce*: UInt256
balance*: UInt256
storageRoot*: string
codeHash*: string
storageRoot*: Hash256
codeHash*: Hash256
rlpFields Account, nonce, balance

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
tables, stint,
tables, stint, eth_common, eth_keys,
./logging, ./constants, ./errors, ./validation, ./utils/hexadecimal, ./vm/base, ./db/db_chain,
./utils/header, ./vm/forks/f20150730_frontier/frontier_vm
@ -23,21 +23,21 @@ type
importBlock*: bool
validateBlock*: bool
db*: BaseChainDB
fundedAddress*: string
fundedAddress*: EthAddress
fundedAddressInitialBalance*: int
fundedAddressPrivateKey*: string
fundedAddressPrivateKey*: PrivateKey
GenesisParams* = ref object
blockNumber*: UInt256
blockNumber*: BlockNumber
difficulty*: UInt256
gasLimit*: UInt256
parentHash*: string
coinbase*: string
gasLimit*: GasInt
parentHash*: Hash256
coinbase*: EthAddress
nonce*: string
mixHash*: string
mixHash*: Hash256
extraData*: string
timestamp*: EthTime
stateRoot*: string
stateRoot*: Hash256
FundedAddress* = ref object
balance*: Int256
@ -75,7 +75,7 @@ proc fromGenesis*(
# chainDB.persistBlockToDB(result.getBlock)
proc getVMClassForBlockNumber*(chain: Chain, blockNumber: UInt256): VMKind =
proc getVMClassForBlockNumber*(chain: Chain, blockNumber: BlockNumber): VMKind =
## Returns the VM class for the given block number
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
# TODO - Refactoring: redundant with constants.nim `toFork`
@ -91,17 +91,16 @@ proc getVMClassForBlockNumber*(chain: Chain, blockNumber: UInt256): VMKind =
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
proc getVM*(chain: Chain, header: BlockHeader = nil): VM =
proc getVM*(chain: Chain, header: BlockHeader): VM =
## Returns the VM instance for the given block number
# TODO - Refactoring: superseded by newNimbusVM for the time being #https://github.com/status-im/nimbus/pull/37
# TODO - Refactoring: redundant with constants.nim `toFork`
# shadowing input param
let header = if header.isNil: chain.header
else: header
let vm_class = chain.getVMClassForBlockNumber(header.blockNumber)
case vm_class:
of vmkFrontier: result = newFrontierVM(header, chain.db)
else:
raise newException(ValueError, "Chain: only FrontierVM is implemented")
proc getVM*(chain: Chain): VM {.inline.} = getVM(chain, chain.header)

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, strutils, sequtils, tables, macros, stint, terminal, math,
strformat, strutils, sequtils, tables, macros, stint, terminal, math, eth_common, byteutils,
constants, errors, utils/hexadecimal, utils_numeric, validation, vm_state, logging, opcode_values, vm_types,
vm / [code_stream, gas_meter, memory, message, stack],
@ -37,7 +37,7 @@ method newBaseComputation*(vmState: FrontierVMState, message: Message): BaseComp
result.stack = newStack()
result.gasMeter = newGasMeter(message.gas)
result.children = @[]
result.accountsToDelete = initTable[string, string]()
result.accountsToDelete = initTable[EthAddress, EthAddress]()
result.logEntries = @[]
result.code = newCodeStreamFromUnescaped(message.code) # TODO: what is the best repr
result.rawOutput = "0x"
@ -51,7 +51,7 @@ method newBaseComputation*(vmState: TangerineVMState, message: Message): BaseCom
result.stack = newStack()
result.gasMeter = newGasMeter(message.gas)
result.children = @[]
result.accountsToDelete = initTable[string, string]()
result.accountsToDelete = initTable[EthAddress, EthAddress]()
result.logEntries = @[]
result.code = newCodeStreamFromUnescaped(message.code) # TODO: what is the best repr
result.rawOutput = "0x"
@ -87,7 +87,7 @@ method shouldEraseReturnData*(c: BaseComputation): bool =
method prepareChildMessage*(
c: var BaseComputation,
gas: GasInt,
to: string,
to: EthAddress,
value: UInt256,
data: seq[byte],
code: string,
@ -169,7 +169,7 @@ method applyChildBaseComputation*(c: var BaseComputation, childMsg: Message): Ba
c.addChildBaseComputation(childBaseComputation)
result = childBaseComputation
method registerAccountForDeletion*(c: var BaseComputation, beneficiary: string) =
method registerAccountForDeletion*(c: var BaseComputation, beneficiary: EthAddress) =
validateCanonicalAddress(beneficiary, title="self destruct beneficiary address")
if c.msg.storageAddress in c.accountsToDelete:
@ -178,7 +178,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[UInt256], data: string) =
method addLogEntry*(c: var BaseComputation, account: EthAddress, topics: seq[UInt256], data: string) =
validateCanonicalAddress(account, title="log entry address")
c.logEntries.add((account, topics, data))
@ -229,8 +229,8 @@ template inComputation*(c: untyped, handler: untyped): untyped =
`c`.logger.debug(
"COMPUTATION STARTING: gas: $1 | from: $2 | to: $3 | value: $4 | depth: $5 | static: $6" % [
$`c`.msg.gas,
$encodeHex(`c`.msg.sender),
$encodeHex(`c`.msg.to),
toHex(`c`.msg.sender),
toHex(`c`.msg.to),
$`c`.msg.value,
$`c`.msg.depth,
if c.msg.isStatic: "y" else: "n"])
@ -238,8 +238,8 @@ template inComputation*(c: untyped, handler: untyped): untyped =
`handler`
c.logger.debug(
"COMPUTATION SUCCESS: from: $1 | to: $2 | value: $3 | depth: $4 | static: $5 | gas-used: $6 | gas-remaining: $7" % [
$encodeHex(c.msg.sender),
$encodeHex(c.msg.to),
toHex(c.msg.sender),
toHex(c.msg.to),
$c.msg.value,
$c.msg.depth,
if c.msg.isStatic: "y" else: "n",
@ -249,8 +249,8 @@ template inComputation*(c: untyped, handler: untyped): untyped =
`c`.logger.debug(
"COMPUTATION ERROR: gas: $1 | from: $2 | to: $3 | value: $4 | depth: $5 | static: $6 | error: $7" % [
$`c`.msg.gas,
$encodeHex(`c`.msg.sender),
$encodeHex(`c`.msg.to),
toHex(`c`.msg.sender),
toHex(`c`.msg.to),
$c.msg.value,
$c.msg.depth,
if c.msg.isStatic: "y" else: "n",

View File

@ -1,17 +1,6 @@
import
stint, math, strutils, tables, utils/padding, rlp, times
# rlpFields UInt256, table
type
TypeHint* {.pure.} = enum UInt256, Bytes, Any # TODO Bytes is in conflict with nim-rlp Bytes = seq[byte]
EthTime* = Time
#Bytes* = seq[byte]
# Int256* = BigInt #distinct int # TODO
stint, math, strutils, utils/padding, eth_common
proc int256*(i: int): Int256 =
i.i256
@ -97,12 +86,6 @@ proc `mod`*(a: UInt256, b: int): UInt256 =
proc `div`*(a: UInt256, b: int): UInt256 =
a div b.u256
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: int): Int256 =
result = left.i256
@ -116,6 +99,8 @@ mapOp(`and`)
mapOp(`or`)
mapOp(`xor`)
proc default(t: typedesc): t = discard
# constants
let
@ -124,9 +109,9 @@ let
NULLBYTE* = "\x00"
EMPTYWORD* = repeat(NULLBYTE, 32)
UINT160CEILING*: UInt256 = 2.u256 ^ 160
CREATE_CONTRACT_ADDRESS* = ""
ZERO_ADDRESS* = repeat("\x00", 20)
ZERO_HASH32* = repeat("\x00", 20)
ZERO_ADDRESS* = default(EthAddress)
CREATE_CONTRACT_ADDRESS* = ZERO_ADDRESS
ZERO_HASH32* = Hash256()
STACK_DEPTH_LIMIT* = 1024
# GAS_NULL* = 0.u256
@ -165,7 +150,7 @@ let
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_TX* = 21_000
GAS_LOG* = 375.u256
GAS_LOG_DATA* = 8
GAS_LOG_TOPIC* = 375
@ -186,11 +171,11 @@ let
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* = high(int64)
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3.u256
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.u256
GAS_LIMIT_EMA_DENOMINATOR* = 1_024
GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024
GAS_LIMIT_MAXIMUM* = high(GasInt)
GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3
GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2
DIFFICULTY_ADJUSTMENT_DENOMINATOR* = 2_048.u256
DIFFICULTY_MINIMUM* = 131_072.u256
@ -214,20 +199,20 @@ let
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"
EMPTY_UNCLE_HASH* = "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".toDigest
GENESIS_BLOCK_NUMBER* = 0.u256
GENESIS_DIFFICULTY* = 131_072.u256
GENESIS_GAS_LIMIT* = 3_141_592.u256
GENESIS_GAS_LIMIT* = 3_141_592
GENESIS_PARENT_HASH* = ZERO_HASH32
GENESIS_COINBASE* = ZERO_ADDRESS
GENESIS_NONCE* = "\x00\x00\x00\x00\x00\x00\x00B"
GENESIS_MIX_HASH* = ZERO_HASH32
GENESIS_EXTRA_DATA* = ""
GAS_LIMIT_MINIMUM* = 5000.u256
GAS_LIMIT_MINIMUM* = 5000
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!"
BLANK_ROOT_HASH* = "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest()
GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20.u256

View File

@ -5,26 +5,54 @@
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import tables, stint
import tables, hashes, eth_common
type
MemoryDB* = ref object
kvStore*: Table[string, Int256]
DBKeyKind = enum
genericHash
blockNumberToHash
blockHashToScore
proc newMemoryDB*(kvStore: Table[string, Int256]): MemoryDB =
DbKey* = object
case kind: DBKeyKind
of genericHash, blockHashToScore:
h: Hash256
of blockNumberToHash:
u: BlockNumber
MemoryDB* = ref object
kvStore*: Table[DbKey, seq[byte]]
proc genericHashKey*(h: Hash256): DbKey {.inline.} = DbKey(kind: genericHash, h: h)
proc blockHashToScoreKey*(h: Hash256): DbKey {.inline.} = DbKey(kind: blockHashToScore, h: h)
proc blockNumberToHashKey*(u: BlockNumber): DbKey {.inline.} = DbKey(kind: blockNumberToHash, u: u)
proc hash(k: DbKey): Hash =
result = result !& hash(k.kind)
case k.kind
of genericHash, blockHashToScore:
result = result !& hash(k.h)
of blockNumberToHash:
result = result !& hashData(unsafeAddr k.u, sizeof(k.u))
result = result
proc `==`(a, b: DbKey): bool {.inline.} =
equalMem(unsafeAddr a, unsafeAddr b, sizeof(a))
proc newMemoryDB*(kvStore: Table[DbKey, seq[byte]]): MemoryDB =
MemoryDB(kvStore: kvStore)
proc newMemoryDB*: MemoryDB =
MemoryDB(kvStore: initTable[string, Int256]())
MemoryDB(kvStore: initTable[DbKey, seq[byte]]())
proc get*(db: MemoryDB, key: string): Int256 =
proc get*(db: MemoryDB, key: DbKey): seq[byte] =
db.kvStore[key]
proc set*(db: var MemoryDB, key: string, value: Int256) =
proc set*(db: var MemoryDB, key: DbKey, value: seq[byte]) =
db.kvStore[key] = value
proc exists*(db: MemoryDB, key: string): bool =
proc contains*(db: MemoryDB, key: DbKey): bool =
db.kvStore.hasKey(key)
proc delete*(db: var MemoryDB, key: string) =
proc delete*(db: var MemoryDB, key: DbKey) =
db.kvStore.del(key)

View File

@ -5,39 +5,60 @@
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import strformat, tables, stint, state_db, backends / memory_backend
import strformat, tables, stint, rlp, ranges, state_db, backends / memory_backend,
../errors, ../utils/header, ../constants, eth_common, byteutils
type
BaseChainDB* = ref object
db*: MemoryDB
# TODO db*: JournalDB
KeyType = enum
blockNumberToHash
blockHashToScore
proc newBaseChainDB*(db: MemoryDB): BaseChainDB =
new(result)
result.db = db
proc exists*(self: BaseChainDB; key: string): bool =
return self.db.exists(key)
proc contains*(self: BaseChainDB; key: Hash256): bool =
return self.db.contains(genericHashKey(key))
proc `$`*(db: BaseChainDB): string =
result = "BaseChainDB"
proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader =
## Returns the requested block header as specified by block hash.
##
## Raises BlockNotFound if it is not present in the db.
var blk: seq[byte]
try:
blk = self.db.get(genericHashKey(blockHash))
except KeyError:
raise newException(BlockNotFound, "No block with hash " & blockHash.data.toHex)
let rng = blk.toRange
return decode(rng, BlockHeader)
# proc getCanonicalHead*(self: BaseChainDB): BlockHeader =
# if notself.exists(CANONICALHEADHASHDBKEY):
# raise newException(CanonicalHeadNotFound,
# "No canonical head set for this chain")
# return self.getBlockHeaderByHash(self.db.get(CANONICALHEADHASHDBKEY))
# proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; blockNumber: int): BlockHeader =
# ## Returns the block header with the given number in the canonical chain.
# ##
# ## Raises BlockNotFound if there's no block header with the given number in the
# ## canonical chain.
# validateUint256(blockNumber)
# return self.getBlockHeaderByHash(self.lookupBlockHash(blockNumber))
proc lookupBlockHash*(self: BaseChainDB; n: BlockNumber): Hash256 {.inline.} =
## Return the block hash for the given block number.
let numberToHashKey = blockNumberToHashKey(n)
result = rlp.decode(self.db.get(numberToHashKey).toRange, Hash256)
# proc getScore*(self: BaseChainDB; blockHash: cstring): int =
# return rlp.decode(self.db.get(makeBlockHashToScoreLookupKey(blockHash)))
proc getCanonicalBlockHeaderByNumber*(self: BaseChainDB; n: BlockNumber): BlockHeader =
## Returns the block header with the given number in the canonical chain.
##
## Raises BlockNotFound if there's no block header with the given number in the
## canonical chain.
self.getBlockHeaderByHash(self.lookupBlockHash(n))
proc getScore*(self: BaseChainDB; blockHash: Hash256): int =
rlp.decode(self.db.get(blockHashToScoreKey(blockHash)).toRange, int)
# proc setAsCanonicalChainHead*(self: BaseChainDB; header: BlockHeader): void =
# ## Sets the header as the canonical chain HEAD.
@ -50,43 +71,23 @@ proc `$`*(db: BaseChainDB): string =
# header.hash))
# self.db.set(CANONICALHEADHASHDBKEY, header.hash)
# iterator findCommonAncestor*(self: BaseChainDB; header: BlockHeader): BlockHeader =
# ## Returns the chain leading up from the given header until the first ancestor it has in
# ## common with our canonical chain.
# var h = header
# while true:
# yield h
# if h.parentHash == GENESISPARENTHASH:
# break
# try:
# var orig = self.getCanonicalBlockHeaderByNumber(h.blockNumber)
# except KeyError:
# nil
# h = self.getBlockHeaderByHash(h.parentHash)
iterator findCommonAncestor*(self: BaseChainDB; header: BlockHeader): BlockHeader =
## Returns the chain leading up from the given header until the first ancestor it has in
## common with our canonical chain.
var h = header
while true:
yield h
if h.parentHash == GENESIS_PARENT_HASH:
break
try:
var orig = self.getCanonicalBlockHeaderByNumber(h.blockNumber)
except KeyError:
discard # TODO: break??
h = self.getBlockHeaderByHash(h.parentHash)
# proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: cstring): BlockHeader =
# ## Returns the requested block header as specified by block hash.
# ##
# ## Raises BlockNotFound if it is not present in the db.
# validateWord(blockHash)
# try:
# var block = self.db.get(blockHash)
# except KeyError:
# raise newException(BlockNotFound, "No block with hash {0} found".format(
# encodeHex(blockHash)))
# return rlp.decode(block)
# proc headerExists*(self: BaseChainDB; blockHash: cstring): bool =
# ## Returns True if the header with the given block hash is in our DB.
# return self.db.exists(blockHash)
# proc lookupBlockHash*(self: BaseChainDB; blockNumber: int): cstring =
# ## Return the block hash for the given block number.
# validateUint256(blockNumber)
# var
# numberToHashKey = makeBlockNumberToHashLookupKey(blockNumber)
# blockHash = rlp.decode(self.db.get(numberToHashKey))
# return blockHash
proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool =
## Returns True if the header with the given block hash is in our DB.
self.contains(blockHash)
# iterator getReceipts*(self: BaseChainDB; header: BlockHeader; receiptClass: typedesc): Receipt =
# var receiptDb = HexaryTrie()
@ -159,7 +160,7 @@ proc `$`*(db: BaseChainDB): string =
# proc clear*(self: BaseChainDB): void =
# self.db.clear()
method getStateDb*(self: BaseChainDB; stateRoot: string; readOnly: bool = false): AccountStateDB =
method getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB =
# TODO
result = newAccountStateDB(initTable[string, string]())

View File

@ -6,14 +6,14 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, tables,
strformat, tables, eth_common,
../constants, ../errors, ../validation, ../account, ../logging, ../utils_numeric, .. / utils / [padding, bytes, keccak],
stint, rlp
type
AccountStateDB* = ref object
db*: Table[string, BytesRange]
rootHash*: string # TODO trie
rootHash*: Hash256 # TODO trie
proc newAccountStateDB*(db: Table[string, string], readOnly: bool = false): AccountStateDB =
result = AccountStateDB(db: initTable[string, BytesRange]())
@ -21,7 +21,7 @@ proc newAccountStateDB*(db: Table[string, string], readOnly: bool = false): Acco
proc logger*(db: AccountStateDB): Logger =
logging.getLogger("db.State")
proc getAccount(db: AccountStateDB, address: string): Account =
proc getAccount(db: AccountStateDB, address: EthAddress): Account =
# let rlpAccount = db.trie[address]
# if not rlpAccount.isNil:
# account = rlp.decode[Account](rlpAccount)
@ -30,32 +30,32 @@ proc getAccount(db: AccountStateDB, address: string): Account =
# account = newAccount()
result = newAccount() # TODO
proc setAccount(db: AccountStateDB, address: string, account: Account) =
proc setAccount(db: AccountStateDB, address: EthAddress, account: Account) =
# db.trie[address] = rlp.encode[Account](account)
discard # TODO
proc getCodeHash*(db: AccountStateDB, address: string): string =
proc getCodeHash*(db: AccountStateDB, address: EthAddress): Hash256 =
validateCanonicalAddress(address, title="Storage Address")
let account = db.getAccount(address)
result = account.codeHash
proc getBalance*(db: AccountStateDB, address: string): UInt256 =
proc getBalance*(db: AccountStateDB, address: EthAddress): UInt256 =
validateCanonicalAddress(address, title="Storage Address")
let account = db.getAccount(address)
account.balance
proc setBalance*(db: var AccountStateDB, address: string, balance: UInt256) =
proc setBalance*(db: var AccountStateDB, address: EthAddress, 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: UInt256) =
proc deltaBalance*(db: var AccountStateDB, address: EthAddress, delta: UInt256) =
db.setBalance(address, db.getBalance(address) + delta)
proc setStorage*(db: var AccountStateDB, address: string, slot: UInt256, value: UInt256) =
proc setStorage*(db: var AccountStateDB, address: EthAddress, slot: UInt256, value: UInt256) =
#validateGte(value, 0, title="Storage Value")
#validateGte(slot, 0, title="Storage Slot")
validateCanonicalAddress(address, title="Storage Address")
@ -76,7 +76,7 @@ proc setStorage*(db: var AccountStateDB, address: string, slot: UInt256, value:
# account.storageRoot = storage.rootHash
# db.setAccount(address, account)
proc getStorage*(db: var AccountStateDB, address: string, slot: UInt256): (UInt256, bool) =
proc getStorage*(db: var AccountStateDB, address: EthAddress, slot: UInt256): (UInt256, bool) =
validateCanonicalAddress(address, title="Storage Address")
#validateGte(slot, 0, title="Storage Slot")
@ -97,7 +97,7 @@ proc getStorage*(db: var AccountStateDB, address: string, slot: UInt256): (UInt2
else:
result = (0.u256, false)
proc setNonce*(db: var AccountStateDB, address: string, nonce: UInt256) =
proc setNonce*(db: var AccountStateDB, address: EthAddress, nonce: UInt256) =
validateCanonicalAddress(address, title="Storage Address")
#validateGte(nonce, 0, title="Nonce")
@ -106,13 +106,13 @@ proc setNonce*(db: var AccountStateDB, address: string, nonce: UInt256) =
db.setAccount(address, account)
proc getNonce*(db: AccountStateDB, address: string): UInt256 =
proc getNonce*(db: AccountStateDB, address: EthAddress): UInt256 =
validateCanonicalAddress(address, title="Storage Address")
let account = db.getAccount(address)
return account.nonce
proc setCode*(db: var AccountStateDB, address: string, code: string) =
proc setCode*(db: var AccountStateDB, address: EthAddress, code: string) =
validateCanonicalAddress(address, title="Storage Address")
var account = db.getAccount(address)
@ -121,7 +121,7 @@ proc setCode*(db: var AccountStateDB, address: string, code: string) =
#db.db[account.codeHash] = code
db.setAccount(address, account)
proc getCode*(db: var AccountStateDB, address: string): string =
proc getCode*(db: var AccountStateDB, address: EthAddress): string =
let codeHash = db.getCodeHash(address)
#if db.db.hasKey(codeHash):
# result = db.db[codeHash]

View File

@ -35,5 +35,4 @@ proc difficulty*(computation) =
stack.push(vmState.difficulty)
proc gaslimit*(computation) =
stack.push(vmState.gasLimit)
stack.push(vmState.gasLimit.u256)

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat,
strformat, eth_common,
../constants, ../vm_types, ../errors, ../computation, ../opcode, ../opcode_values, ../logging,
.. / vm / [stack, memory, gas_meter, message],
.. / utils / [address, bytes],
@ -39,16 +39,16 @@ type
using
computation: var BaseComputation
method msgExtraGas*(call: BaseCall, computation; gas: GasInt, to: string, value: UInt256): GasInt {.base.} =
method msgExtraGas*(call: BaseCall, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt {.base.} =
raise newException(NotImplementedError, "Must be implemented by subclasses")
method msgGas*(call: BaseCall, computation; gas: GasInt, to: string, value: UInt256): (GasInt, GasInt) {.base.} =
method msgGas*(call: BaseCall, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) {.base.} =
let extraGas = call.msgExtraGas(computation, gas, to, value)
let totalFee = gas + extraGas
let childMsgGas = gas + (if value != 0: GAS_CALL_STIPEND else: 0)
(childMsgGas, totalFee)
method callParams*(call: BaseCall, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) {.base.} =
method callParams*(call: BaseCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) {.base.} =
raise newException(NotImplementedError, "Must be implemented subclasses")
method runLogic*(call: BaseCall, computation) =
@ -108,7 +108,7 @@ method runLogic*(call: BaseCall, computation) =
MessageOptions(
shouldTransferValue: shouldTransferValue,
isStatic: isStatic))
if not sender.isNil:
if sender != ZERO_ADDRESS:
childMsg.sender = sender
# let childComputation = computation.applyChildComputation(childMsg)
# TODO
@ -121,12 +121,11 @@ method runLogic*(call: BaseCall, computation) =
let actualOutputSize = min(memOutLen, childComputation.output.len)
computation.memory.write(
memOutPos,
actualOutputSize,
childComputation.output.toBytes[0 ..< actualOutputSize])
if not childComputation.shouldBurnGas:
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
method msgExtraGas(call: Call, computation; gas: GasInt, to: string, value: UInt256): GasInt =
method msgExtraGas(call: Call, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
# TODO: db
# with computation.vm_state.state_db(read_only=True) as state_db:
# let accountExists = db.accountExists(to)
@ -136,9 +135,9 @@ method msgExtraGas(call: Call, computation; gas: GasInt, to: string, value: UInt
let createGasFee = if not accountExists: GAS_NEW_ACCOUNT else: 0
transferGasFee + createGasFee
method callParams(call: CallCode, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
method callParams(call: CallCode, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let to = forceBytesToAddress(computation.stack.popString)
let to = computation.stack.popAddress()
let (value,
memoryInputStartPosition, memoryInputSize,
@ -147,8 +146,8 @@ method callParams(call: CallCode, computation): (UInt256, UInt256, string, strin
result = (gas,
value,
to,
nil, # sender
nil, # code_address
ZERO_ADDRESS, # sender
ZERO_ADDRESS, # code_address
memoryInputStartPosition,
memoryInputSize,
memoryOutputStartPosition,
@ -156,12 +155,12 @@ method callParams(call: CallCode, computation): (UInt256, UInt256, string, strin
true, # should_transfer_value,
computation.msg.isStatic)
method msgExtraGas(call: CallCode, computation; gas: GasInt, to: string, value: UInt256): GasInt =
method msgExtraGas(call: CallCode, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
if value != 0: GAS_CALL_VALUE else: 0
method callParams(call: Call, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
method callParams(call: Call, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let codeAddress = forceBytesToAddress(computation.stack.popString)
let codeAddress = computation.stack.popAddress()
let (value,
memoryInputStartPosition, memoryInputSize,
@ -182,15 +181,15 @@ method callParams(call: Call, computation): (UInt256, UInt256, string, string, s
true, # should_transfer_value,
computation.msg.isStatic)
method msgGas(call: DelegateCall, computation; gas: GasInt, to: string, value: UInt256): (GasInt, GasInt) =
method msgGas(call: DelegateCall, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
(gas, gas)
method msgExtraGas(call: DelegateCall, computation; gas: GasInt, to: string, value: UInt256): GasInt =
method msgExtraGas(call: DelegateCall, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
0
method callParams(call: DelegateCall, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
method callParams(call: DelegateCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let codeAddress = forceBytesToAddress(computation.stack.popString)
let codeAddress = computation.stack.popAddress()
let (memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4)
@ -222,19 +221,19 @@ proc computeEIP150MsgGas(computation; gas, extraGas: GasInt, value: UInt256, nam
let childMsgGas = gas + (if value != 0: callStipend else: 0)
(childMsgGas, totalFee)
method msgGas(call: CallEIP150, computation; gas: GasInt, to: string, value: UInt256): (GasInt, GasInt) =
method msgGas(call: CallEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)
method msgGas(call: CallCodeEIP150, computation; gas: GasInt, to: string, value: UInt256): (GasInt, GasInt) =
method msgGas(call: CallCodeEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND)
method msgGas(call: DelegateCallEIP150, computation; gas: GasInt, to: string, value: UInt256): (GasInt, GasInt) =
method msgGas(call: DelegateCallEIP150, computation; gas: GasInt, to: EthAddress, value: UInt256): (GasInt, GasInt) =
let extraGas = call.msgExtraGas(computation, gas, to, value)
computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0)
proc msgExtraGas*(call: CallEIP161, computation; gas: GasInt, to: string, value: UInt256): GasInt =
proc msgExtraGas*(call: CallEIP161, computation; gas: GasInt, to: EthAddress, value: UInt256): GasInt =
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
# account_is_dead = (
@ -247,9 +246,9 @@ proc msgExtraGas*(call: CallEIP161, computation; gas: GasInt, to: string, value:
transferGasFee + createGasFee
method callParams(call: StaticCall, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
method callParams(call: StaticCall, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, bool, bool) =
let gas = computation.stack.popInt()
let to = forceBytesToAddress(computation.stack.popString)
let to = computation.stack.popAddress()
let (memoryInputStartPosition, memoryInputSize,
memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4)
@ -257,8 +256,8 @@ method callParams(call: StaticCall, computation): (UInt256, UInt256, string, str
result = (gas,
0.u256, # value
to,
nil, # sender
nil, # codeAddress
ZERO_ADDRESS, # sender
ZERO_ADDRESS, # codeAddress
memoryInputStartPosition,
memoryInputSize,
memoryOutputStartPosition,
@ -267,7 +266,7 @@ method callParams(call: StaticCall, computation): (UInt256, UInt256, string, str
true) # is_static
method callParams(call: CallByzantium, computation): (UInt256, UInt256, string, string, string, UInt256, UInt256, UInt256, UInt256, bool, bool) =
method callParams(call: CallByzantium, computation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, 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

@ -11,7 +11,7 @@ import
.. / vm / [stack, message, gas_meter, memory, code_stream], .. / utils / [address, padding, bytes], stint
proc balance*(computation: var BaseComputation) =
let address = forceBytesToAddress(computation.stack.popString)
let address = computation.stack.popAddress()
var balance: Int256
# TODO computation.vmState.stateDB(read_only=True):
# balance = db.getBalance(address)
@ -55,7 +55,7 @@ proc callDataCopy*(computation: var BaseComputation) =
computation.gasMeter.consumeGas(copyGasCost, reason="CALLDATACOPY fee")
let value = computation.msg.data[callPos ..< callPos + len]
let paddedValue = padRight(value, len, 0.byte)
computation.memory.write(memPos, len, paddedValue)
computation.memory.write(memPos, paddedValue)
proc codesize*(computation: var BaseComputation) =
@ -86,7 +86,7 @@ proc gasprice*(computation: var BaseComputation) =
proc extCodeSize*(computation: var BaseComputation) =
let account = forceBytesToAddress(computation.stack.popString)
let account = computation.stack.popAddress()
# TODO
# with computation.vm_state.state_db(read_only=True) as state_db:
# code_size = len(state_db.get_code(account))
@ -94,7 +94,7 @@ proc extCodeSize*(computation: var BaseComputation) =
# computation.stack.push(code_size)
proc extCodeCopy*(computation: var BaseComputation) =
let account = forceBytesToAddress(computation.stack.popString)
let account = computation.stack.popAddress()
let (memStartPosition, codeStartPosition, size) = computation.stack.popInt(3)
let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)
computation.extendMemory(memPos, len)

View File

@ -7,10 +7,8 @@
import macros
macro pushRes*: untyped =
let resNode = ident("res")
result = quote:
computation.stack.push(`resNode`)
template pushRes*: untyped =
computation.stack.push(res)
macro quasiBoolean*(name: untyped, op: untyped, signed: untyped = nil, nonzero: untyped = nil): untyped =
var signedNode = newEmptyNode()

View File

@ -16,23 +16,22 @@ import
using
computation: var BaseComputation
proc mstoreX(computation; x: int) =
let start = stack.popInt().toInt
let value = stack.popBinary()
let paddedValue = padLeft(value, x, 0.byte)
let normalizedValue = paddedValue[^x .. ^1]
extendMemory(start, x)
memory.write(start, 32, normalizedValue)
# TODO template handler
proc mstore*(computation) =
mstoreX(32)
let start = stack.popInt().toInt
let normalizedValue = stack.popInt().toByteArrayBE
extendMemory(start, 32)
memory.write(start, normalizedValue)
proc mstore8*(computation) =
mstoreX(1)
let start = stack.popInt().toInt
let value = stack.popInt()
let normalizedValue = (value and 0xff).toByteArrayBE
extendMemory(start, 1)
memory.write(start, [normalizedValue[0]])
proc mload*(computation) =
let start = stack.popInt().toInt

View File

@ -9,7 +9,7 @@ import
strformat,
../constants, ../vm_types, ../errors, ../computation, ../opcode, ../opcode_values, ../logging, ../vm_state, call,
.. / vm / [stack, gas_meter, memory, message], .. / utils / [address, hexadecimal, bytes],
stint
stint, byteutils, eth_common
{.this: computation.}
{.experimental.}
@ -58,11 +58,11 @@ method runLogic*(create: Create, computation) =
# )
# is_collision = state_db.account_has_code_or_nonce(contract_address)
let contractAddress = ""
let contractAddress = ZERO_ADDRESS
let isCollision = false
if isCollision:
computation.vmState.logger.debug(&"Address collision while creating contract: {contractAddress.encodeHex}")
computation.vmState.logger.debug(&"Address collision while creating contract: {contractAddress.toHex}")
computation.stack.push(0.u256)
return
@ -92,7 +92,7 @@ method runLogic*(create: CreateByzantium, computation) =
procCall runLogic(create, computation)
proc selfdestructEIP150(computation) =
let beneficiary = forceBytesToAddress(stack.popString)
let beneficiary = stack.popAddress()
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
# if not state_db.account_exists(beneficiary):
@ -103,7 +103,7 @@ proc selfdestructEIP150(computation) =
# _selfdestruct(computation, beneficiary)
proc selfdestructEIP161(computation) =
let beneficiary = forceBytesToAddress(stack.popString)
let beneficiary = stack.popAddress()
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
# is_dead = (
@ -117,7 +117,7 @@ proc selfdestructEIP161(computation) =
# )
# _selfdestruct(computation, beneficiary)
proc selfdestruct(computation; beneficiary: string) =
proc selfdestruct(computation; beneficiary: EthAddress) =
discard # TODO: with
# with computation.vm_state.state_db() as state_db:
# local_balance = state_db.get_balance(computation.msg.storage_address)
@ -158,7 +158,7 @@ proc revert*(computation) =
raise newException(Revert, $output)
proc selfdestruct*(computation) =
let beneficiary = forceBytesToAddress(stack.popString)
let beneficiary = stack.popAddress()
selfdestruct(computation, beneficiary)
raise newException(Halt, "SELFDESTRUCT")

View File

@ -8,7 +8,7 @@
import
strformat, strutils, tables, macros,
constants, stint, errors, logging, vm_state,
vm / [gas_meter, stack, code_stream, memory, message, value], db / db_chain, computation, opcode, opcode_values, utils / [header, address],
vm / [gas_meter, stack, code_stream, memory, message], db / db_chain, computation, opcode, opcode_values, utils / [header, address],
logic / [arithmetic, comparison, sha3, context, block_ops, stack_ops, duplication, swap, memory_ops, storage, flow, logging_ops, invalid, call, system_ops],
./vm_types

View File

@ -6,13 +6,13 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
constants, stint, errors
constants, stint, errors, eth_common
type
BaseTransaction* = ref object
nonce*: Int256
gasPrice*: UInt256
gas*: UInt256
gasPrice*: GasInt
gas*: GasInt
to*: string
value*: UInt256
data*: string
@ -20,7 +20,7 @@ type
r*: Int256
s*: Int256
proc intrinsicGas*(t: BaseTransaction): UInt256 =
proc intrinsicGas*(t: BaseTransaction): GasInt =
# 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

@ -6,44 +6,22 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../constants, stint, strformat, times, ../validation
import eth_common, ../constants, stint, strformat, times, ../validation, rlp, nimcrypto
type
BlockHeader* = ref object
# Note: this is defined in evm/rlp/headers in the original repo
timestamp*: EthTime
difficulty*: UInt256
blockNumber*: UInt256
hash*: string
unclesHash*: string
coinbase*: string
stateRoot*: string
# TODO: incomplete
export BlockHeader
proc hasUncles*(header: BlockHeader): bool = header.uncles_hash != EMPTY_UNCLE_HASH
proc gasUsed*(header: BlockHeader): UInt256 =
# TODO
# Should this be calculated/a proc? Parity and Py-Evm just have it as a field.
0.u256
proc gasLimit*(header: BlockHeader): UInt256 =
# TODO
0.u256
proc hasUncles*(header: BlockHeader): bool = header.ommersHash != EMPTY_UNCLE_HASH
proc `$`*(header: BlockHeader): string =
if header.isNil:
result = "nil"
else:
result = &"BlockHeader(timestamp: {header.timestamp} difficulty: {header.difficulty} blockNumber: {header.blockNumber} gasLimit: {header.gasLimit})"
result = &"BlockHeader(timestamp: {header.timestamp} difficulty: {header.difficulty} blockNumber: {header.blockNumber} gasLimit: {header.gasLimit})"
proc gasLimitBounds*(parent: BlockHeader): (UInt256, UInt256) =
proc gasLimitBounds*(parent: BlockHeader): (GasInt, GasInt) =
## Compute the boundaries for the block gas limit based on the parent block.
let
boundary_range = parent.gasLimit div GAS_LIMIT_ADJUSTMENT_FACTOR
upper_bound = parent.gas_limit + boundary_range
lower_bound = max(GAS_LIMIT_MINIMUM, parent.gas_limit - boundary_range)
return (lower_bound, upper_bound)
boundaryRange = parent.gasLimit div GAS_LIMIT_ADJUSTMENT_FACTOR
upperBound = parent.gasLimit + boundaryRange
lowerBound = max(GAS_LIMIT_MINIMUM, parent.gasLimit - boundaryRange)
return (lowerBound, upperBound)
#[
proc validate_gaslimit(header: BlockHeader):
@ -59,7 +37,7 @@ proc validate_gaslimit(header: BlockHeader):
encode_hex(header.hash), header.gas_limit, high_bound))
]#
proc computeGasLimit*(parent: BlockHeader, gasLimitFloor: UInt256): UInt256 =
proc computeGasLimit*(parent: BlockHeader, gasLimitFloor: GasInt): GasInt =
#[
For each block:
- decrease by 1/1024th of the gas limit from the previous block
@ -69,16 +47,16 @@ proc computeGasLimit*(parent: BlockHeader, gasLimitFloor: UInt256): UInt256 =
If the value is less than the GAS_LIMIT_MINIMUM:
- use the GAS_LIMIT_MINIMUM as the new gas limit.
]#
if gas_limit_floor < GAS_LIMIT_MINIMUM:
if gasLimitFloor < GAS_LIMIT_MINIMUM:
raise newException(ValueError,
&"""
The `gas_limit_floor` value must be greater than the GAS_LIMIT_MINIMUM.
The `gasLimitFloor` value must be greater than the GAS_LIMIT_MINIMUM.
Got {gasLimitFloor}. Must be greater than {GAS_LIMIT_MINIMUM}
"""
)
let decay = parent.gasLimit div GAS_LIMIT_EMA_DENOMINATOR
var usageIncrease = u256(0)
var usageIncrease: GasInt
if parent.gasUsed > 0:
usageIncrease = (
@ -92,7 +70,7 @@ proc computeGasLimit*(parent: BlockHeader, gasLimitFloor: UInt256): UInt256 =
if gas_limit < GAS_LIMIT_MINIMUM:
return GAS_LIMIT_MINIMUM
elif gas_limit < gas_limit_floor:
elif gas_limit < gasLimitFloor:
return parent.gas_limit + decay
else:
return gas_limit
@ -100,21 +78,18 @@ proc computeGasLimit*(parent: BlockHeader, gasLimitFloor: UInt256): UInt256 =
proc generateHeaderFromParentHeader*(
computeDifficultyFn: proc(parentHeader: BlockHeader, timestamp: int): int,
parent: BlockHeader,
coinbase: string,
coinbase: EthAddress,
timestamp: int = -1,
extraData: string = ""): BlockHeader =
# TODO: validateGt(timestamp, parent.timestamp)
result = BlockHeader(
timestamp: max(getTime(), parent.timestamp + 1.milliseconds), # Note: Py-evm uses +1 second, not ms
block_number: (parent.block_number + u256(1)),
blockNumber: (parent.blockNumber + 1.u256),
# TODO: difficulty: parent.computeDifficulty(parent.timestamp),
#[TODO: Make field? Or do we need to keep as a proc?
gas_limit: computeGasLimit(
parent,
gas_limit_floor=GENESIS_GAS_LIMIT,
),]#
hash: parent.hash,
state_root: parent.state_root,
gasLimit: computeGasLimit(parent, gasLimitFloor = GENESIS_GAS_LIMIT),
stateRoot: parent.stateRoot,
coinbase: coinbase,
# TODO: data: extraData,
)
proc hash*(b: BlockHeader): Hash256 {.inline.} = rlpHash(b)

View File

@ -7,12 +7,12 @@
import strutils
proc encodeHex*(value: string): string =
# return "0x" & codecs.decode(codecs.encode(value, "hex"), "utf8")
return value
# proc encodeHex*(value: string): string =
# # return "0x" & codecs.decode(codecs.encode(value, "hex"), "utf8")
# return value
proc decodeHex*(value: string): string =
# var hexPart = value.rsplit("x", 1)[1]
return value
# return codecs.decode(hexPart, "hex")
# proc decodeHex*(value: string): string =
# # var hexPart = value.rsplit("x", 1)[1]
# return value
# # return codecs.decode(hexPart, "hex")

View File

@ -6,11 +6,14 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
nimcrypto, strutils
nimcrypto, strutils, eth_common
proc keccak*(value: string): string {.inline.}=
$keccak256.digest value
proc keccak*(value: openarray[byte]): Hash256 {.inline.} =
keccak256.digest value
proc keccak*(value: cstring): string {.inline.}=
proc keccak*(value: string): Hash256 {.inline.} =
keccak256.digest value
proc keccak*(value: cstring): Hash256 {.inline.} =
# TODO: this is inefficient it allocates for the cstring -> string and then for string -> result
keccak $value

View File

@ -11,25 +11,15 @@ import stint, constants, strformat, strutils, sequtils, endians, macros, utils /
# TODO improve
proc intToBigEndian*(value: UInt256): Bytes =
proc intToBigEndian*(value: UInt256): Bytes {.deprecated.} =
result = newSeq[byte](32)
let v_ptr = cast[ptr array[32, byte]](value.unsafeAddr)
result[0 .. ^1] = value.toByteArrayBE()
for idx, val in result.mpairs:
when system.cpuEndian == littleEndian:
val = v_ptr[32 - 1 - idx]
else:
val = v_ptr[idx]
proc bigEndianToInt*(value: Bytes): UInt256 =
var bytes = value.padLeft(32, 0.byte)
let v_ptr = cast[ptr array[32, byte]](result.addr)
for idx, val in bytes:
when system.cpuEndian == littleEndian:
v_ptr[32 - 1 - idx] = val
else:
v_ptr[idx] = val
proc bigEndianToInt*(value: openarray[byte]): UInt256 =
if value.len == 32:
readUintBE[256](value)
else:
readUintBE[256](padLeft(@value, 32, 0.byte))
#echo intToBigEndian("32482610168005790164680892356840817100452003984372336767666156211029086934369".u256)

View File

@ -7,16 +7,11 @@
import
strformat,
errors, constants, stint
proc validateCanonicalAddress*(value: string, title: string = "Value") =
# TODO
if false: #len(value) != 20:
raise newException(ValidationError,
&"{title} {value} is not a valid canonical address")
errors, constants, stint, eth_common
template validateCanonicalAddress*(value: EthAddress, title: string = "Value") =
# TODO: This needs to be removed
discard
proc validateGte*(value: Int256 | int, minimum: int, title: string = "Value") =
if value.i256 < minimum.i256:

View File

@ -10,7 +10,7 @@ import
../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header
proc validateFrontierTransaction*(vmState: BaseVmState, transaction: BaseTransaction) =
let gasCost = transaction.gas * transaction.gasPrice
let gasCost = u256(transaction.gas * transaction.gasPrice)
var senderBalance: UInt256
# inDB(vmState.stateDB(readOnly=true):
# senderBalance = db.getBalance(transaction.sender)

View File

@ -20,7 +20,7 @@ proc newFrontierVMState*: FrontierVMState =
result.prevHeaders = @[]
result.name = "FrontierVM"
result.accessLogs = newAccessLogs()
result.blockHeader = BlockHeader(hash: "TODO", coinbase: "TODO", stateRoot: "TODO")
# result.blockHeader = # TODO: ...
# import
# py2nim_helpers, __future__, rlp, evm, evm.constants, evm.exceptions, evm.rlp.logs,

View File

@ -10,7 +10,7 @@ import
../../../constants, ../../../errors, ../../../vm_state, ../../../transaction, ../../../utils/header
proc validateTangerineTransaction*(vmState: BaseVmState, transaction: BaseTransaction) =
let gasCost = transaction.gas * transaction.gasPrice
let gasCost = u256(transaction.gas * transaction.gasPrice)
var senderBalance: UInt256
# inDB(vmState.stateDB(readOnly=true):
# senderBalance = db.getBalance(transaction.sender)

View File

@ -20,4 +20,4 @@ proc newTangerineVMState*: TangerineVMState =
result.prevHeaders = @[]
result.name = "TangerineVM"
result.accessLogs = newAccessLogs()
result.blockHeader = BlockHeader(hash: "TODO", coinbase: "TODO", stateRoot: "TODO")
# result.blockHeader = # TODO: ...

View File

@ -23,7 +23,6 @@ proc len*(memory: Memory): int =
result = memory.bytes.len
# TODO: why is the size passed as a UInt256?
proc extend*(memory: var Memory; startPosition: Natural; size: Natural) =
if size == 0:
return
@ -37,19 +36,17 @@ proc newMemory*(size: Natural): Memory =
result = newMemory()
result.extend(0, size)
# TODO: why is the size passed as a UInt256?
proc read*(memory: var Memory, startPosition: Natural, size: Natural): seq[byte] =
result = memory.bytes[startPosition ..< (startPosition + size)]
# TODO: why is the size passed as a UInt256?
proc write*(memory: var Memory, startPosition: Natural, size: Natural, value: seq[byte]) =
proc write*(memory: var Memory, startPosition: Natural, value: openarray[byte]) =
let size = value.len
if size == 0:
return
#echo size
#echo startPosition
#validateGte(startPosition, 0)
#validateGte(size, 0)
validateLength(value, size)
validateLte(startPosition + size, memory.len)
let index = memory.len
if memory.len < startPosition + size:
@ -59,4 +56,4 @@ proc write*(memory: var Memory, startPosition: Natural, size: Natural, value: se
memory.bytes[z + startPosition] = b
template write*(memory: var Memory, startPosition: Natural, size: Natural, value: cstring) =
memory.write(startPosition, size, value.toBytes)
memory.write(startPosition, value.toBytes)

View File

@ -6,22 +6,23 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../logging, ../constants, ../validation, stint, ../vm_types
stint, eth_common,
../logging, ../constants, ../validation, ../vm_types
proc `origin=`*(message: var Message, value: string) =
proc `origin=`*(message: var Message, value: EthAddress) =
message.internalOrigin = value
proc `codeAddress=`*(message: var Message, value: string) =
proc `codeAddress=`*(message: var Message, value: EthAddress) =
message.internalCodeAddress = value
proc `storageAddress=`*(message: var Message, value: string) =
proc `storageAddress=`*(message: var Message, value: EthAddress) =
message.internalStorageAddress = value
proc newMessageOptions*(
origin: string = "",
origin = ZERO_ADDRESS,
depth: int = 0,
createAddress: string = "",
codeAddress: string = "",
createAddress = ZERO_ADDRESS,
codeAddress = ZERO_ADDRESS,
shouldTransferValue: bool = true,
isStatic: bool = false): MessageOptions =
@ -36,8 +37,8 @@ proc newMessageOptions*(
proc newMessage*(
gas: GasInt,
gasPrice: GasInt,
to: string,
sender: string,
to: EthAddress,
sender: EthAddress,
value: UInt256,
data: seq[byte],
code: string,
@ -58,8 +59,6 @@ proc newMessage*(
result.data = data
if not options.origin.isNil:
validateCanonicalAddress(options.origin, title="Message.origin")
result.internalOrigin = options.origin
validateGte(options.depth, minimum=0, title="Message.depth")
@ -67,20 +66,16 @@ proc newMessage*(
result.code = code
if not options.createAddress.isNil:
validateCanonicalAddress(options.createAddress, title="Message.storage_address")
result.storageAddress = options.createAddress
if not options.codeAddress.isNil:
validateCanonicalAddress(options.codeAddress, title="Message.code_address")
result.codeAddress = options.codeAddress
result.shouldTransferValue = options.shouldTransferValue
result.isStatic = options.isStatic
proc origin*(message: Message): string =
if not message.internalOrigin.len == 0:
proc origin*(message: Message): EthAddress =
if message.internalOrigin != ZERO_ADDRESS:
message.internalOrigin
else:
message.sender
@ -88,14 +83,14 @@ proc origin*(message: Message): string =
proc isOrigin*(message: Message): bool =
message.sender == message.origin
proc codeAddress*(message: Message): string =
if not message.internalCodeAddress.len == 0:
proc codeAddress*(message: Message): EthAddress =
if message.internalCodeAddress != ZERO_ADDRESS:
message.internalCodeAddress
else:
message.to
proc `storageAddress`*(message: Message): string =
if not message.internalStorageAddress.len == 0:
proc `storageAddress`*(message: Message): EthAddress =
if message.internalStorageAddress != ZERO_ADDRESS:
message.internalStorageAddress
else:
message.to

View File

@ -6,157 +6,84 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, strutils, sequtils, macros, rlp,
value, ../errors, ../validation, ../utils_numeric, ../constants, stint, ../logging, .. / utils / bytes
strformat, strutils, sequtils, macros, rlp, eth_common, nimcrypto,
../errors, ../validation, ../utils_numeric, ../constants, stint, ../logging, .. / utils / bytes
type
Stack* = ref object of RootObj
logger*: Logger
values*: seq[UInt256]
values*: seq[StackElement]
StackElement = UInt256
template ensureStackLimit: untyped =
if len(stack.values) > 1023:
raise newException(FullStack, "Stack limit reached")
proc len*(stack: Stack): int =
proc len*(stack: Stack): int {.inline.} =
len(stack.values)
template toType(i: UInt256, _: typedesc[UInt256]): UInt256 =
i
proc toStackElement(v: UInt256, elem: var StackElement) {.inline.} = elem = v
proc toStackElement(v: uint | int, elem: var StackElement) {.inline.} = elem = v.u256
proc toStackElement(v: EthAddress, elem: var StackElement) {.inline.} = elem = bigEndianToInt(v)
proc toStackElement(v: MDigest, elem: var StackElement) {.inline.} = elem = readUintBE[256](v.data)
template toType(i: UInt256, _: typedesc[string]): string =
i.intToBigEndian.toString
proc fromStackElement(elem: StackElement, v: var UInt256) {.inline.} = v = elem
proc fromStackElement(elem: StackElement, v: var EthAddress) {.inline.} = v[0 .. ^1] = elem.toByteArrayBE().toOpenArray(0, 19)
proc fromStackElement(elem: StackElement, v: var Hash256) {.inline.} = v.data = elem.toByteArrayBE()
template toType(i: UInt256, _: typedesc[Bytes]): Bytes =
i.intToBigEndian
proc toStackElement(v: seq[byte], elem: var StackElement) {.inline.} =
# TODO: This needs to go
validateStackItem(v)
elem = bigEndianToInt(v)
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
proc pushAux[T](stack: var Stack, value: T) =
ensureStackLimit()
stack.values.setLen(stack.values.len + 1)
toStackElement(value, stack.values[^1])
stack.values.add(value.u256)
proc push*(stack: var Stack, value: uint | UInt256 | EthAddress | Hash256) {.inline.} =
pushAux(stack, value)
proc push*(stack: var Stack, value: UInt256) =
## Push an integer onto the stack
ensureStackLimit()
proc push*(stack: var Stack, value: seq[byte]) {.inline.} =
# TODO: This needs to go...
pushAux(stack, value)
stack.values.add(value)
proc push*(stack: var Stack, value: string) =
## Push a binary onto the stack
ensureStackLimit()
validateStackItem(value)
stack.values.add(value.toType(UInt256))
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] =
# TODO: it is very inefficient to allocate a seq
assert numItems <= stack.len
result = stack.values[^numItems .. ^1]
stack.values = stack.values[0 ..< ^numItems]
proc internalPop(stack: var Stack, numItems: int, T: typedesc): seq[T] =
# TODO: it is very inefficient to allocate a seq
assert numItems <= stack.len
result = @[]
for z in 0 ..< numItems:
var value = stack.values.pop()
result.add(toType(value, T))
proc ensurePop(elements: seq|Stack, a: int) =
proc ensurePop(elements: Stack, a: int) =
let num = elements.len
let expected = a
if num < expected:
raise newException(InsufficientStack,
&"Stack underflow: expected {expected} elements, got {num} instead.")
proc popInt*(stack: var Stack): UInt256 =
proc popAux[T](stack: var Stack, value: var T) =
ensurePop(stack, 1)
var elements = stack.internalPop(1, UInt256)
result = elements[0]
fromStackElement(stack.values[^1], value)
stack.values.setLen(stack.values.len - 1)
macro internalPopTuple(numItems: static[int]): untyped =
var name = ident(&"internalPopTuple{numItems}")
var typ = nnkPar.newTree()
var t = ident("T")
var resultNode = ident("result")
var stackNode = ident("stack")
for z in 0 ..< numItems:
typ.add(t)
result = quote:
proc `name`*(`stackNode`: var Stack, `t`: typedesc): `typ`
result[^1] = nnkStmtList.newTree()
result[^1].add quote do:
ensurePop(`stackNode`, `numItems`)
for z in 0 ..< numItems:
var zNode = newLit(z)
var element = quote:
var value = `stackNode`.values.pop()
`resultNode`[`zNode`] = toType(value, `t`)
result[^1].add(element)
proc internalPopTuple(stack: var Stack, v: var tuple, tupleLen: static[int]) =
ensurePop(stack, tupleLen)
var i = 0
let sz = stack.values.high
for f in fields(v):
fromStackElement(stack.values[sz - i], f)
inc i
stack.values.setLen(sz - tupleLen + 1)
# define pop<T> for tuples
internalPopTuple(2)
internalPopTuple(3)
internalPopTuple(4)
internalPopTuple(5)
internalPopTuple(6)
internalPopTuple(7)
proc popInt*(stack: var Stack): UInt256 {.inline.} =
popAux(stack, result)
macro popInt*(stack: typed, numItems: static[int]): untyped =
var resultNode = ident("result")
if numItems >= 8:
result = quote:
`stack`.internalPop(`numItems`, UInt256)
else:
var name = ident(&"internalPopTuple{numItems}")
result = quote:
`name`(`stack`, UInt256)
macro genTupleType(len: static[int], elemType: untyped): untyped =
result = nnkTupleConstr.newNimNode()
for i in 0 ..< len: result.add(elemType)
proc popBinary*(stack: var Stack): Bytes =
var elements = stack.internalPop(1, Bytes)
ensurePop(elements, 1)
result = elements[0]
proc popInt*(stack: var Stack, numItems: static[int]): auto {.inline.} =
var r: genTupleType(numItems, UInt256)
stack.internalPopTuple(r, numItems)
return r
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 popString*(stack: var Stack, numItems: int): seq[string] =
result = stack.internalPop(numItems, string)
ensurePop(result, numItems)
proc popAddress*(stack: var Stack): EthAddress {.inline.} =
popAux(stack, result)
proc newStack*(): Stack =
new(result)
@ -174,27 +101,16 @@ proc swap*(stack: var Stack, position: int) =
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])
let position = position.getInt
if position in 1 .. stack.len:
stack.push(stack.values[^position])
else:
raise newException(InsufficientStack,
&"Insufficient stack items for DUP{position}")
proc getInt*(stack: Stack, position: int): UInt256 =
if stack.values.len <= position:
raise newException(InsufficientStack, &"No {position} item")
else:
stack.values[position]
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 peek*(stack: Stack): UInt256 =
stack.getInt(stack.values.len - 1)
# This should be used only for testing purposes!
fromStackElement(stack.values[^1], result)
proc `$`*(stack: Stack): string =
let values = stack.values.mapIt(&" {$it}").join("\n")

View File

@ -1,49 +0,0 @@
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
strformat, strutils, sequtils,
../constants, stint
type
ValueKind* = enum VInt, VBinary
Value* = ref object
case kind*: ValueKind:
of VInt:
Fi: array[32, byte] #Int256
of VBinary:
b*: seq[byte]
# TODO: The Int256 value is stored as array[32, byte], and we bitcast it
# back and forth. This is a hacky workaround for the problem that clang
# doesn't let you store stint types inside nim variant types (unions). Things
# should get better when we switch to mpint.
proc i*(v: Value): Int256 {.inline.} =
cast[ptr Int256](unsafeAddr v.Fi)[]
proc `$`*(value: Value): string =
case value.kind:
of VInt:
&"Int({value.i})"
of VBinary:
&"Binary({value.b})"
proc toArr(i: Int256): array[32, byte] {.inline.} =
cast[ptr array[32, byte]](unsafeAddr i)[]
proc vint*(i: Int256): Value =
Value(kind: VInt, Fi: i.toArr)
proc vint*(i: int): Value {.inline.} = vint(i.int256)
proc vbinary*(b: string): Value =
Value(kind: VBinary, b: b.mapIt(it.byte))
proc vbinary*(b: seq[byte]): Value =
Value(kind: VBinary, b: b)

View File

@ -7,8 +7,9 @@
import
macros, strformat, tables,
stint,
./logging, ./constants, ./errors, ./transaction, ./db/[db_chain, state_db], ./utils/state, ./utils/header
stint, eth_common,
./logging, ./constants, ./errors, ./transaction, ./db/[db_chain, state_db],
./utils/[state, header]
type
BaseVMState* = ref object of RootObj
@ -41,35 +42,35 @@ proc newBaseVMState*: BaseVMState =
result.prevHeaders = @[]
result.name = "BaseVM"
result.accessLogs = newAccessLogs()
result.blockHeader = BlockHeader(hash: "TODO", coinbase: "TODO", stateRoot: "TODO")
# result.blockHeader = # TODO...
method logger*(vmState: BaseVMState): Logger =
logging.getLogger(&"evm.vmState.{vmState.name}")
method blockhash*(vmState: BaseVMState): string =
method blockhash*(vmState: BaseVMState): Hash256 =
vmState.blockHeader.hash
method coinbase*(vmState: BaseVMState): string =
method coinbase*(vmState: BaseVMState): EthAddress =
vmState.blockHeader.coinbase
method timestamp*(vmState: BaseVMState): EthTime =
vmState.blockHeader.timestamp
method blockNumber*(vmState: BaseVMState): UInt256 =
method blockNumber*(vmState: BaseVMState): BlockNumber =
vmState.blockHeader.blockNumber
method difficulty*(vmState: BaseVMState): UInt256 =
vmState.blockHeader.difficulty
method gasLimit*(vmState: BaseVMState): UInt256 =
method gasLimit*(vmState: BaseVMState): GasInt =
vmState.blockHeader.gasLimit
method getAncestorHash*(vmState: BaseVMState, blockNumber: UInt256): string =
method getAncestorHash*(vmState: BaseVMState, blockNumber: BlockNumber): Hash256 =
var ancestorDepth = vmState.blockHeader.blockNumber - blockNumber - 1.u256
if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH or
ancestorDepth < 0 or
ancestorDepth >= vmState.prevHeaders.len.u256:
return ""
return
var header = vmState.prevHeaders[ancestorDepth.toInt]
result = header.hash
@ -100,4 +101,4 @@ macro db*(vmState: untyped, readOnly: untyped, handler: untyped): untyped =
# state._trie = None
proc readOnlyStateDB*(vmState: BaseVMState): AccountStateDB {.inline.}=
vmState.chaindb.getStateDb("", readOnly = true)
vmState.chaindb.getStateDb(Hash256(), readOnly = true)

View File

@ -8,10 +8,12 @@
import
tables,
constants, vm_state,
opcode_values, stint,
opcode_values, stint, eth_common,
vm / [code_stream, memory, stack],
./logging
export GasInt
type
BaseComputation* = ref object of RootObj
# The execution computation
@ -25,9 +27,9 @@ type
rawOutput*: string
returnData*: string
error*: Error
logEntries*: seq[(string, seq[UInt256], string)]
logEntries*: seq[(EthAddress, seq[UInt256], string)]
shouldEraseReturnData*: bool
accountsToDelete*: Table[string, string]
accountsToDelete*: Table[EthAddress, EthAddress]
opcodes*: Table[Op, Opcode] # TODO array[Op, Opcode]
precompiles*: Table[string, Opcode]
gasCosts*: GasCosts # TODO separate opcode processing and gas computation
@ -50,10 +52,6 @@ type
gasCostKind*: GasCostKind
runLogic*: proc(computation: var BaseComputation)
GasInt* = int64
## Type alias used for gas computation
# For reference - https://github.com/status-im/nimbus/issues/35#issuecomment-391726518
GasMeter* = ref object
logger*: Logger
gasRefunded*: GasInt
@ -101,23 +99,23 @@ type
gas*: GasInt
gasPrice*: GasInt
to*: string
sender*: string
to*: EthAddress
sender*: EthAddress
value*: UInt256
data*: seq[byte]
code*: string
internalOrigin*: string
internalCodeAddress*: string
internalOrigin*: EthAddress
internalCodeAddress*: EthAddress
depth*: int
internalStorageAddress*: string
internalStorageAddress*: EthAddress
shouldTransferValue*: bool
isStatic*: bool
isCreate*: bool
MessageOptions* = ref object
origin*: string
origin*: EthAddress
depth*: int
createAddress*: string
codeAddress*: string
createAddress*: EthAddress
codeAddress*: EthAddress
shouldTransferValue*: bool
isStatic*: bool

View File

@ -7,13 +7,13 @@
import
unittest, strformat, tables, times,
stint,
stint, eth_keys, eth_common,
../nimbus/[constants, chain, vm/base, vm/forks/f20150730_frontier/frontier_vm, utils/header, utils/address, db/db_chain, db/backends/memory_backend]
proc chainWithoutBlockValidation*: Chain =
result = configureChain("TestChain", GENESIS_BLOCK_NUMBER, vmkFrontier, false, false)
let privateKey = "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" # TODO privateKey(decodeHex("0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"))
let fundedAddr = privateKey # privateKey.publicKey.toCanonicalAddress
let privateKey = initPrivateKey("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
let fundedAddr = privateKey.getPublicKey.toCanonicalAddress
let initialBalance = 100_000_000
let genesisParams = GenesisParams(
blockNumber: GENESIS_BLOCK_NUMBER,
@ -25,7 +25,7 @@ proc chainWithoutBlockValidation*: Chain =
mixHash: GENESIS_MIX_HASH,
extraData: GENESIS_EXTRA_DATA,
timestamp: fromUnix 1501851927,
stateRoot: "0x9d354f9b5ba851a35eced279ef377111387197581429cfcc7f744ef89a30b5d4") #.decodeHex)
stateRoot: "9d354f9b5ba851a35eced279ef377111387197581429cfcc7f744ef89a30b5d4".toDigest)
let genesisState = {"fundedAddr": FundedAddress(balance: initialBalance.int256, nonce: 0, code: "")}.toTable()
result = fromGenesis(
result,

View File

@ -7,7 +7,7 @@
import
os, macros, json, strformat, strutils, parseutils, ospaths, tables,
stint,
stint, byteutils, eth_common, eth_keys,
../nimbus/utils/[hexadecimal, address, padding],
../nimbus/[chain, vm_state, constants],
../nimbus/db/[db_chain, state_db], ../nimbus/vm/forks/f20150730_frontier/frontier_vm,
@ -77,8 +77,11 @@ macro jsonTest*(s: static[string], handler: untyped): untyped =
raw.add("OK: " & $okCount & "/" & $sum & " Fail: " & $failCount & "/" & $sum & " Skip: " & $skipCount & "/" & $sum & "\n")
writeFile(`s` & ".md", raw)
proc accountFromHex(s: string): EthAddress = hexToByteArray(s, result)
proc setupStateDB*(desiredState: JsonNode, stateDB: var AccountStateDB) =
for account, accountData in desiredState:
for ac, accountData in desiredState:
let account = accountFromHex(ac)
for slot, value in accountData{"storage"}:
stateDB.setStorage(account, slot.parseInt.u256, value.getInt.u256)
@ -94,9 +97,9 @@ proc getHexadecimalInt*(j: JsonNode): int =
discard parseHex(j.getStr, result)
method newTransaction*(
vm: VM, addr_from, addr_to: string,
vm: VM, addr_from, addr_to: EthAddress,
amount: UInt256,
private_key: string,
private_key: PrivateKey,
gas_price = 10.u256,
gas = 100000.u256,
data: seq[byte] = @[]

View File

@ -21,7 +21,7 @@ suite "memory":
test "write":
var mem = memory32()
# Test that write creates 32byte string == value padded with zeros
mem.write(startPosition = 0, size = 4, value = @[1.byte, 0.byte, 1.byte, 0.byte])
mem.write(startPosition = 0, value = @[1.byte, 0.byte, 1.byte, 0.byte])
check(mem.bytes == @[1.byte, 0.byte, 1.byte, 0.byte].concat(repeat(0.byte, 28)))
# test "write rejects invalid position":
@ -44,15 +44,10 @@ suite "memory":
# var mem = memory32()
# mem.write(startPosition = 0.u256, size = pow(2.u256, 256), value = @[1.byte, 0.byte])
test "write rejects invalid value":
expect(ValidationError):
var mem = memory32()
mem.write(startPosition = 0, size = 4, value = @[1.byte, 0.byte])
test "write rejects valyes beyond memory size":
expect(ValidationError):
var mem = memory128()
mem.write(startPosition = 128, size = 4, value = @[1.byte, 0.byte, 1.byte, 0.byte])
mem.write(startPosition = 128, value = @[1.byte, 0.byte, 1.byte, 0.byte])
test "extends appropriately extends memory":
var mem = newMemory()
@ -68,7 +63,7 @@ suite "memory":
test "read returns correct bytes":
var mem = memory32()
mem.write(startPosition = 5, size = 4, value = @[1.byte, 0.byte, 1.byte, 0.byte])
mem.write(startPosition = 5, value = @[1.byte, 0.byte, 1.byte, 0.byte])
check(mem.read(startPosition = 5, size = 4) == @[1.byte, 0.byte, 1.byte, 0.byte])
check(mem.read(startPosition = 6, size = 4) == @[0.byte, 1.byte, 0.byte, 0.byte])
check(mem.read(startPosition = 1, size = 3) == @[0.byte, 0.byte, 0.byte])

View File

@ -26,8 +26,8 @@ proc testCode(code: string, initialGas: GasInt, blockNum: UInt256): BaseComputat
# timestamp: fixture{"env"}{"currentTimestamp"}.getHexadecimalInt)
let message = newMessage(
to="", #fixture{"exec"}{"address"}.getStr,
sender="", #fixture{"exec"}{"caller"}.getStr,
to=ZERO_ADDRESS, #fixture{"exec"}{"address"}.getStr,
sender=ZERO_ADDRESS, #fixture{"exec"}{"caller"}.getStr,
value=0.u256,
data = @[],
code=code,
@ -42,7 +42,6 @@ proc testCode(code: string, initialGas: GasInt, blockNum: UInt256): BaseComputat
c.displayDecompiled()
var computation = newBaseComputation(vm.state, message)
computation.accountsToDelete = initTable[string, string]()
computation.opcodes = OPCODE_TABLE
computation.precompiles = initTable[string, Opcode]()

View File

@ -7,18 +7,18 @@
import unittest, macros, strformat, strutils, sequtils,
stint,
../nimbus/[constants, opcode_values, errors, utils_numeric, vm/stack, vm/value, utils/bytes, utils/padding]
../nimbus/[constants, opcode_values, errors, utils_numeric, vm/stack, utils/bytes, utils/padding]
template testPush(value: untyped, expected: untyped): untyped =
var stack = newStack()
stack.push(`value`)
check(stack.values == @[`expected`])
stack.push(value)
check(stack.values == @[expected])
template testFailPush(value: untyped): untyped =
var stack = newStack()
expect(ValidationError):
stack.push(`value`)
stack.push(value)
suite "stack":
test "push only valid":
@ -54,11 +54,6 @@ suite "stack":
stack.push(element)
check(stack.popInt == 3.u256)
stack = newStack()
stack.push("1".toBytes)
check(stack.popBinary == "1".toBytes.pad32)
test "swap correct":
var stack = newStack()
for z in 0 ..< 5:

View File

@ -6,7 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
unittest, stint,
unittest, stint, eth_common,
./test_helpers, ./fixtures,
../nimbus/[db/backends/memory_backend, db/state_db, chain, constants, utils/hexadecimal, vm_state],
../nimbus/[vm/base, computation]
@ -20,7 +20,7 @@ suite "VM":
vm = chain.getVM()
# txIdx = len(vm.`block`.transactions) # Can't take len of a runtime field
let
recipient = decodeHex("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0c")
recipient = parseAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0c")
amount = 100.u256
ethaddr_from = chain.fundedAddress
tx = newTransaction(vm, ethaddr_from, recipient, amount, chain.fundedAddressPrivateKey)