Delete utilities: address, padding, bytes (#68)
* Delete utilities: address, padding, bytes * Remove bigEndianToInt * Fix C compiler error: member reference base type 'char' is not a structure or union * fix todo comment
This commit is contained in:
parent
4b5eada322
commit
4dd75aee84
|
@ -1,6 +1,6 @@
|
|||
|
||||
import
|
||||
math, strutils, utils/padding, eth_common
|
||||
math, strutils, eth_common
|
||||
|
||||
proc default(t: typedesc): t = discard
|
||||
|
||||
|
@ -8,8 +8,6 @@ proc default(t: typedesc): t = discard
|
|||
const
|
||||
UINT_256_MAX*: UInt256 = high(UInt256)
|
||||
INT_256_MAX_AS_UINT256* = high(Uint256) shr 1
|
||||
NULLBYTE* = "\x00"
|
||||
EMPTYWORD* = repeat(NULLBYTE, 32)
|
||||
UINT160CEILING*: UInt256 = 2.u256.pow(160)
|
||||
ZERO_ADDRESS* = default(EthAddress)
|
||||
CREATE_CONTRACT_ADDRESS* = ZERO_ADDRESS
|
||||
|
|
|
@ -1,153 +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, encodings, keccak, padding
|
||||
|
||||
proc toText*(c: cstring): string =
|
||||
($c).convert(destEncoding="iso-8859-1")
|
||||
|
||||
proc toCanonicalAddress*(address: string): string =
|
||||
# TODO
|
||||
address
|
||||
# address.toNormalizedAddress.decodeHex
|
||||
|
||||
template toCanonicalAddress*(address: cstring): string =
|
||||
address.toText.toCanonicalAddress
|
||||
|
||||
proc forceBytesToAddress*(address: string): string =
|
||||
padLeft(address[^20..^1], 20, "\x00")
|
||||
|
||||
template forceBytesToAddress*(address: cstring): string =
|
||||
address.toText.forceBytesToAddress
|
||||
|
||||
proc generateContractAddress*(address: string, nonce: string): string =
|
||||
""
|
||||
# keccak(rlp.encode(@[address, nonce]))[^20..^1]
|
||||
|
||||
# proc isNormalizedAddress*(value: string): bool =
|
||||
# # Returns whether the provided value is an address in it's normalized form
|
||||
# if not value.isAddress:
|
||||
# false
|
||||
# else:
|
||||
# value == value.toNormalizedAddress
|
||||
|
||||
# proc toNormalizedAddress*(address: string): string =
|
||||
# # Converts an address to it's normalized hexidecimal representation
|
||||
# if address.isHexAddress:
|
||||
# address.normalizeHexAddress
|
||||
# elif address.isBinaryAddress:
|
||||
# address.normalizeBinaryAddress
|
||||
# elif address.is32byteaddress:
|
||||
# address.normalize32byteAddress
|
||||
# else:
|
||||
# raise newException(ValueError, &"Unknown address format {address}")
|
||||
|
||||
# proc toNormalizedAddress*(address: cstring): string =
|
||||
# toNormalizedAddress(address.toText)
|
||||
|
||||
# proc isAddress*(value: string): bool
|
||||
# # Checks if the given string is an address in any of the known formats
|
||||
# if value.isChecksumFormattedAddress:
|
||||
# value.isChecksumAddress
|
||||
# elif value.isHexAddress:
|
||||
# true
|
||||
# elif value.isBinaryAddress:
|
||||
# true
|
||||
# elif value.is32byteAddress:
|
||||
# true
|
||||
# else:
|
||||
# false
|
||||
|
||||
# proc toCanonicalAddress*(address: cstring): string =
|
||||
# address.toText.toNormalizedAddress.decodeHex
|
||||
|
||||
# proc toCanonicalAddress*(address: string): string =
|
||||
# address.toNormalizedAddress.decodeHex
|
||||
|
||||
# proc isHexAddress(value: string): cstring =
|
||||
# # Checks if the given string is an address in hexidecimal encoded form
|
||||
# if value.len notin {42, 40}:
|
||||
# false
|
||||
# else:
|
||||
# value.isHex:
|
||||
|
||||
# proc isBinaryAddress(value: string): bool =
|
||||
# # Checks if the given string is an address in raw bytes form
|
||||
# value.len == 20
|
||||
|
||||
# proc is32byteAddress(value: string): bool =
|
||||
# # Checks if the given string is an address in hexidecimal encoded form padded to 32 bytes
|
||||
# let valueAsHex = ""
|
||||
# if value.len == 32:
|
||||
# valueAsHex = value.encodeHex
|
||||
# elif value.len in {66, 64}:
|
||||
# valueAsHex = value.add0xprefix
|
||||
# else:
|
||||
# return false
|
||||
|
||||
# if valueAsHex.isPrefixed("0x000000000000000000000000"):
|
||||
# try:
|
||||
# return valueAsHex.parseHexInt > 0
|
||||
# except ValueError:
|
||||
# false
|
||||
# else:
|
||||
# return false
|
||||
|
||||
# proc normalizeHexAddress(address: string): string =
|
||||
# # Returns a hexidecimal address in its normalized hexidecimal representation
|
||||
# address.toLowerAscii.add0xPrefix
|
||||
|
||||
# proc normalizeBinaryAddress(address: string): string =
|
||||
# # Returns a raw binary address in its normalized hexidecimal representation
|
||||
# address.encodeHex.normalizeHexAddress
|
||||
|
||||
# proc normalize32byteAddress(address: string): string =
|
||||
# if address.len == 32:
|
||||
# address[^20..^1].normalizeBinaryAddress
|
||||
# elif address.len in {66, 64}:
|
||||
# address[^40..^1].normalizeHexAddress
|
||||
# else:
|
||||
# raise newException(ValueError, "Invalid address(must be 32 byte value)")
|
||||
|
||||
# proc isCanonicalAddress(value: string): bool =
|
||||
# if not value.isAddress
|
||||
# false
|
||||
# else:
|
||||
# value == value.toCanonicalAddress
|
||||
|
||||
# proc isSameAddress(left: string, right: string): bool =
|
||||
# # Checks if both addresses are same or not
|
||||
# if not left.isAddress or not right.isAddress:
|
||||
# raise newException(ValueError, "Both values must be valid addresses")
|
||||
# else:
|
||||
# left.toNormalizedAddress == right.toNormalizedAddress
|
||||
|
||||
# proc toChecksumAddress*(address: string): string =
|
||||
# # Makes a checksum address
|
||||
# let normAddress = address.toNormalizedAddress
|
||||
# let addressHash = normAddress.remove0xPrefix.keccak.encodeHex
|
||||
# var s = ""
|
||||
# for z in 2 ..< 42:
|
||||
# s.add(if addressHash[z].parseHexInt > 7: normAddress[z].toUpperAscii() else: normAddress[z])
|
||||
|
||||
# result = s.join("").add0xPrefix
|
||||
|
||||
# proc toChecksumAddress*(address: cstring): string =
|
||||
# address.toText.toChecksumAddress
|
||||
|
||||
# proc isChecksumAddress(value: string): bool =
|
||||
# if not value.isHexAddress:
|
||||
# false
|
||||
# else:
|
||||
# value == value.toChecksumAddress
|
||||
|
||||
# proc isChecksumFormattedAddress*(value: string): bool =
|
||||
# if not value.isHexAddress:
|
||||
# false
|
||||
# else:
|
||||
# let r = value.remove0xPrefix
|
||||
# r != r.toLowerAscii and r != r.toUpperAscii
|
|
@ -1,26 +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 strutils, sequtils
|
||||
|
||||
# proc toBytes*(value: cstring): seq[byte] =
|
||||
# result = newSeq[byte](value.len)
|
||||
# for z, c in value:
|
||||
# result[z] = c.byte
|
||||
# # result = toSeq(value)
|
||||
|
||||
# proc toCString*(value: seq[byte]): cstring =
|
||||
# var res = ""
|
||||
# for c in value:
|
||||
# res.add(c.char)
|
||||
# cstring(res) # TODO: faster
|
||||
|
||||
proc toString*(value: seq[byte]): string =
|
||||
"0x" & value.mapIt(it.int.toHex(2)).join("").toLowerAscii
|
||||
|
||||
proc toBytes*(value: string): seq[byte] =
|
||||
result = value.mapIt(it.byte)
|
|
@ -1,156 +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
|
||||
|
||||
proc repeat*(b: cstring, count: int): cstring =
|
||||
# TODO
|
||||
result = cstring(repeat($b, count))
|
||||
|
||||
proc pad(value: cstring, size: int, with: cstring, left: bool): cstring =
|
||||
let padAmount = size - value.len
|
||||
if padAmount > 0:
|
||||
let fill = repeat(($with), padAmount)
|
||||
if left:
|
||||
result = cstring(&"{fill}{value}")
|
||||
else:
|
||||
result = cstring(&"{value}{fill}")
|
||||
else:
|
||||
result = value
|
||||
|
||||
proc pad(value: string, size: int, with: string, left: bool): string =
|
||||
let padAmount = size - value.len
|
||||
if padAmount > 0:
|
||||
let fill = repeat(with, padAmount)
|
||||
if left:
|
||||
result = &"{fill}{value}"
|
||||
else:
|
||||
result = &"{value}{fill}"
|
||||
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)
|
||||
|
||||
template padRight*(value: cstring, size: int, with: cstring): cstring =
|
||||
pad(value, size, with, false)
|
||||
|
||||
template zpadRight*(value: cstring, size: int): cstring =
|
||||
padRight(value, size, with=cstring"\x00")
|
||||
|
||||
template zpadLeft*(value: cstring, size: int): cstring =
|
||||
padLeft(value, size, with=cstring"\x00")
|
||||
|
||||
template pad32*(value: cstring): cstring =
|
||||
zpadLeft(value, size=32)
|
||||
|
||||
template pad32r*(value: cstring): cstring =
|
||||
zpadRight(value, size=32)
|
||||
|
||||
|
||||
template padLeft*(value: string, size: int, with: string): string =
|
||||
pad(value, size, with, true)
|
||||
|
||||
template padRight*(value: string, size: int, with: string): string =
|
||||
pad(value, size, with, false)
|
||||
|
||||
template zpadRight*(value: string, size: int): string =
|
||||
padRight(value, size, with="\x00")
|
||||
|
||||
template zpadLeft*(value: string, size: int): string =
|
||||
padLeft(value, size, with="\x00")
|
||||
|
||||
template pad32*(value: string): string =
|
||||
zpadLeft(value, size=32)
|
||||
|
||||
template pad32r*(value: string): string =
|
||||
zpadRight(value, size=32)
|
||||
|
||||
|
||||
proc lStrip*(value: cstring, c: char): cstring =
|
||||
var z = 0
|
||||
while z < value.len and value[z] == c:
|
||||
z += 1
|
||||
if z == 0:
|
||||
result = value
|
||||
elif z == value.len:
|
||||
result = cstring""
|
||||
else:
|
||||
result = cstring(($value)[z..^1])
|
||||
|
||||
proc rStrip*(value: cstring, c: char): cstring =
|
||||
var z = value.len - 1
|
||||
while z >= 0 and value[z] == c:
|
||||
z -= 1
|
||||
if z == value.len - 1:
|
||||
result = value
|
||||
elif z == -1:
|
||||
result = cstring""
|
||||
else:
|
||||
result = cstring(($value)[0..z])
|
||||
|
||||
proc strip*(value: cstring, c: char): cstring =
|
||||
result = value.lStrip(c).rStrip(c)
|
||||
|
||||
proc lStrip*(value: string, c: char): string =
|
||||
value.strip(chars={c}, trailing=false)
|
||||
|
||||
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]
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import
|
||||
strformat, strutils, sequtils, macros, terminal, math, tables,
|
||||
eth_common, byteutils,
|
||||
eth_common,
|
||||
../constants, ../errors, ../validation, ../vm_state, ../logging, ../vm_types,
|
||||
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
|
||||
./code_stream, ./memory, ./message, ./stack
|
||||
|
@ -23,7 +23,7 @@ proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Me
|
|||
result.accountsToDelete = initTable[EthAddress, EthAddress]()
|
||||
result.logEntries = @[]
|
||||
result.code = newCodeStreamFromUnescaped(message.code) # TODO: what is the best repr
|
||||
result.rawOutput = "0x"
|
||||
# result.rawOutput = "0x"
|
||||
result.gasCosts = blockNumber.toFork.forkToSchedule
|
||||
|
||||
proc logger*(computation: BaseComputation): Logger =
|
||||
|
@ -45,13 +45,17 @@ proc shouldBurnGas*(c: BaseComputation): bool =
|
|||
proc shouldEraseReturnData*(c: BaseComputation): bool =
|
||||
c.isError and c.error.erasesReturnData
|
||||
|
||||
func bytesToHex(x: openarray[byte]): string {.inline.} =
|
||||
## TODO: use seq[byte] for raw data and delete this proc
|
||||
foldl(x, a & b.int.toHex(2).toLowerAscii, "0x")
|
||||
|
||||
proc prepareChildMessage*(
|
||||
c: var BaseComputation,
|
||||
gas: GasInt,
|
||||
to: EthAddress,
|
||||
value: UInt256,
|
||||
data: seq[byte],
|
||||
code: string,
|
||||
code: seq[byte],
|
||||
options: MessageOptions = newMessageOptions()): Message =
|
||||
|
||||
var childOptions = options
|
||||
|
@ -63,17 +67,22 @@ proc prepareChildMessage*(
|
|||
to,
|
||||
value,
|
||||
data,
|
||||
code,
|
||||
code.bytesToHex, # TODO: use seq[byte] for Message as well
|
||||
childOptions)
|
||||
|
||||
proc output*(c: BaseComputation): string =
|
||||
func output*(c: BaseComputation): seq[byte] =
|
||||
if c.shouldEraseReturnData:
|
||||
""
|
||||
@[]
|
||||
else:
|
||||
c.rawOutput
|
||||
|
||||
proc `output=`*(c: var BaseComputation, value: string) =
|
||||
c.rawOutput = value
|
||||
func `output=`*(c: var BaseComputation, value: openarray[byte]) =
|
||||
c.rawOutput = @value
|
||||
|
||||
proc outputHex*(c: BaseComputation): string =
|
||||
if c.shouldEraseReturnData:
|
||||
return "0x"
|
||||
c.rawOutput.bytesToHex
|
||||
|
||||
proc registerAccountForDeletion*(c: var BaseComputation, beneficiary: EthAddress) =
|
||||
validateCanonicalAddress(beneficiary, title="self destruct beneficiary address")
|
||||
|
@ -84,7 +93,7 @@ proc registerAccountForDeletion*(c: var BaseComputation, beneficiary: EthAddress
|
|||
"registered for deletion multiple times")
|
||||
c.accountsToDelete[c.msg.storageAddress] = beneficiary
|
||||
|
||||
proc addLogEntry*(c: var BaseComputation, account: EthAddress, topics: seq[UInt256], data: string) =
|
||||
proc addLogEntry*(c: var BaseComputation, account: EthAddress, topics: seq[UInt256], data: seq[byte]) =
|
||||
validateCanonicalAddress(account, title="log entry address")
|
||||
c.logEntries.add((account, topics, data))
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@ import
|
|||
./interpreter/[opcode_values, gas_meter],
|
||||
./interpreter/vm_forks
|
||||
|
||||
from ./interpreter/utils/utils_numeric import bigEndianToInt
|
||||
|
||||
import # Used in vm_types. Beware of recursive dependencies
|
||||
./code_stream, ./computation, ./stack, ./message, interpreter_dispatch
|
||||
|
||||
|
@ -18,7 +16,5 @@ export
|
|||
opcode_values, gas_meter,
|
||||
vm_forks
|
||||
|
||||
export utils_numeric.bigEndianToInt
|
||||
|
||||
export
|
||||
code_stream, computation, stack, message, interpreter_dispatch
|
||||
|
|
|
@ -12,8 +12,7 @@ import
|
|||
./gas_meter, ./gas_costs, ./opcode_values, ./vm_forks,
|
||||
../memory, ../message, ../stack, ../code_stream, ../computation,
|
||||
../../vm_state, ../../errors, ../../constants, ../../vm_types, ../../logging,
|
||||
../../db/[db_chain, state_db],
|
||||
../../utils/[bytes, padding, address] # TODO remove those dependencies
|
||||
../../db/[db_chain, state_db]
|
||||
|
||||
# ##################################
|
||||
# Syntactic sugar
|
||||
|
@ -266,15 +265,7 @@ op callDataCopy, inline = false, memStartPos, copyStartPos, size:
|
|||
computation.gasCosts[CallDataCopy].m_handler(memPos, copyPos, len),
|
||||
reason="CallDataCopy fee")
|
||||
|
||||
computation.memory.extend(memPos, len)
|
||||
|
||||
# If the data does not take 32 bytes, pad with zeros
|
||||
let lim = min(computation.msg.data.len, copyPos + len)
|
||||
let padding = copyPos + len - lim
|
||||
# Note: when extending, extended memory is zero-ed, we only need to offset with padding value
|
||||
# Also memory.write handles the case where copyPos+padding is out of bounds
|
||||
computation.memory.write(memPos):
|
||||
computation.msg.data.toOpenArray(copyPos+padding, copyPos+lim)
|
||||
computation.memory.writePaddedResult(computation.code.bytes, memPos, copyPos, len)
|
||||
|
||||
op codesize, inline = true:
|
||||
## 0x38, Get size of code running in current environment.
|
||||
|
@ -548,7 +539,7 @@ op create, inline = false, value, startPosition, size:
|
|||
to = CREATE_CONTRACT_ADDRESS,
|
||||
value = value,
|
||||
data = @[],
|
||||
code = callData.toString,
|
||||
code = callData,
|
||||
options = MessageOptions(createAddress: contractAddress)
|
||||
)
|
||||
|
||||
|
@ -709,7 +700,7 @@ template genCall(callName: untyped): untyped =
|
|||
# computation.vmState.db(readOnly = true):
|
||||
# let code = if codeAddress != ZERO_ADDRESS: db.getCode(codeAddress)
|
||||
# else: db.getCode(to)
|
||||
let code = "0x" # This is a stub hack, newCodeStreamFromUnescaped expects length 2 at least
|
||||
let code: seq[byte] = @[]
|
||||
|
||||
var childMsg = prepareChildMessage(
|
||||
computation,
|
||||
|
@ -740,7 +731,7 @@ template genCall(callName: untyped): untyped =
|
|||
let actualOutputSize = min(memOutLen, childComputation.output.len)
|
||||
computation.memory.write(
|
||||
memOutPos,
|
||||
childComputation.output.toBytes[0 ..< actualOutputSize])
|
||||
childComputation.output.toOpenArray(0, actualOutputSize))
|
||||
if not childComputation.shouldBurnGas:
|
||||
computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining)
|
||||
|
||||
|
@ -759,8 +750,7 @@ op returnOp, inline = false, startPos, size:
|
|||
)
|
||||
|
||||
computation.memory.extend(pos, len)
|
||||
let output = computation.memory.read(pos, len)
|
||||
computation.output = output.toString
|
||||
computation.output = computation.memory.read(pos, len)
|
||||
|
||||
op revert, inline = false, startPos, size:
|
||||
## 0xf0, Halt execution reverting state changes but returning data and remaining gas.
|
||||
|
@ -772,8 +762,7 @@ op revert, inline = false, startPos, size:
|
|||
)
|
||||
|
||||
computation.memory.extend(pos, len)
|
||||
let output = computation.memory.read(pos, len).toString
|
||||
computation.output = output
|
||||
computation.output = computation.memory.read(pos, len)
|
||||
|
||||
op selfDestruct, inline = false:
|
||||
## 0xff Halt execution and register account for later deletion.
|
||||
|
|
|
@ -142,7 +142,7 @@ proc logImpl(topicCount: int): NimNode =
|
|||
`computation`.gasCosts[`OpName`].m_handler(`computation`.memory.len, `memPos`, `len`),
|
||||
reason="Memory expansion, Log topic and data gas cost")
|
||||
`computation`.memory.extend(`memPos`, `len`)
|
||||
let logData = `computation`.memory.read(`memPos`, `len`).toString
|
||||
let logData = `computation`.memory.read(`memPos`, `len`)
|
||||
addLogEntry(
|
||||
`computation`,
|
||||
account = `computation`.msg.storageAddress,
|
||||
|
|
|
@ -8,25 +8,19 @@
|
|||
import
|
||||
strformat, strutils, sequtils, endians, macros,
|
||||
eth_common/eth_types, rlp,
|
||||
../../../constants, ../../../utils/padding
|
||||
../../../constants
|
||||
|
||||
# some methods based on py-evm utils/numeric
|
||||
|
||||
proc bigEndianToInt*(value: openarray[byte]): UInt256 =
|
||||
if value.len == 32:
|
||||
readUintBE[256](value)
|
||||
else:
|
||||
readUintBE[256](padLeft(@value, 32, 0.byte))
|
||||
|
||||
proc log256*(value: UInt256): Natural {.inline.}=
|
||||
func log256*(value: UInt256): Natural {.inline.}=
|
||||
(255 - value.countLeadingZeroBits) shr 3 # div 8
|
||||
|
||||
proc unsignedToPseudoSigned*(value: UInt256): UInt256 {.inline.}=
|
||||
func unsignedToPseudoSigned*(value: UInt256): UInt256 {.inline.}=
|
||||
result = value
|
||||
if value > INT_256_MAX_AS_UINT256:
|
||||
result -= INT_256_MAX_AS_UINT256
|
||||
|
||||
proc pseudoSignedToUnsigned*(value: UInt256): UInt256 {.inline.}=
|
||||
func pseudoSignedToUnsigned*(value: UInt256): UInt256 {.inline.}=
|
||||
result = value
|
||||
if value > INT_256_MAX_AS_UINT256:
|
||||
result += INT_256_MAX_AS_UINT256
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import
|
||||
sequtils,
|
||||
eth_common/eth_types,
|
||||
../constants, ../errors, ../logging, ../validation, ../utils/bytes,
|
||||
../constants, ../errors, ../logging, ../validation,
|
||||
./interpreter/utils/utils_numeric
|
||||
|
||||
type
|
||||
|
@ -57,10 +57,3 @@ proc write*(memory: var Memory, startPos: Natural, value: openarray[byte]) =
|
|||
|
||||
for z, b in value:
|
||||
memory.bytes[z + startPos] = b
|
||||
|
||||
template write*(memory: var Memory, startPos: Natural, size: Natural, value: cstring) =
|
||||
memory.write(startPos, value.toBytes)
|
||||
# TODO ~ O(n^3):
|
||||
# - there is a string allocation with $ (?)
|
||||
# - then a conversion to seq (= new allocation) with toBytes
|
||||
# - then writing to memory
|
||||
|
|
|
@ -44,39 +44,30 @@ proc newMessage*(
|
|||
code: string,
|
||||
options: MessageOptions = newMessageOptions()): Message =
|
||||
|
||||
if to != CREATE_CONTRACT_ADDRESS:
|
||||
validateCanonicalAddress(to, title="Message.to")
|
||||
validateCanonicalAddress(sender, title="Message.sender")
|
||||
validateGte(options.depth, minimum=0, title="Message.depth")
|
||||
|
||||
new(result)
|
||||
result.gas = gas
|
||||
result.gasPrice = gasPrice
|
||||
|
||||
if to != CREATE_CONTRACT_ADDRESS:
|
||||
validateCanonicalAddress(to, title="Message.to")
|
||||
result.to = to
|
||||
|
||||
validateCanonicalAddress(sender, title="Message.sender")
|
||||
result.sender = sender
|
||||
|
||||
result.value = value
|
||||
|
||||
result.data = data
|
||||
result.depth = options.depth
|
||||
result.storageAddress = options.createAddress
|
||||
result.codeAddress = options.codeAddress
|
||||
result.shouldTransferValue = options.shouldTransferValue
|
||||
result.isStatic = options.isStatic
|
||||
result.code = code
|
||||
|
||||
if options.origin != ZERO_ADDRESS:
|
||||
result.internalOrigin = options.origin
|
||||
else:
|
||||
result.internalOrigin = sender
|
||||
|
||||
validateGte(options.depth, minimum=0, title="Message.depth")
|
||||
result.depth = options.depth
|
||||
|
||||
result.code = code
|
||||
|
||||
result.storageAddress = options.createAddress
|
||||
|
||||
result.codeAddress = options.codeAddress
|
||||
|
||||
result.shouldTransferValue = options.shouldTransferValue
|
||||
|
||||
result.isStatic = options.isStatic
|
||||
|
||||
proc origin*(message: Message): EthAddress =
|
||||
if message.internalOrigin != ZERO_ADDRESS:
|
||||
message.internalOrigin
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import
|
||||
strformat, strutils, sequtils, macros, rlp, eth_common, nimcrypto,
|
||||
../errors, ../validation, ./interpreter/utils/utils_numeric, ../constants, ../logging, .. / utils / bytes
|
||||
../errors, ../validation, ./interpreter/utils/utils_numeric, ../constants, ../logging
|
||||
|
||||
type
|
||||
Stack* = ref object of RootObj
|
||||
|
@ -25,8 +25,8 @@ proc len*(stack: Stack): int {.inline.} =
|
|||
|
||||
proc toStackElement(v: UInt256, elem: var StackElement) {.inline.} = elem = v
|
||||
proc toStackElement(v: uint | int | GasInt, 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)
|
||||
proc toStackElement(v: EthAddress, elem: var StackElement) {.inline.} = elem.initFromBytesBE(v)
|
||||
proc toStackElement(v: MDigest, elem: var StackElement) {.inline.} = elem.initFromBytesBE(v.data, allowPadding = false)
|
||||
|
||||
proc fromStackElement(elem: StackElement, v: var UInt256) {.inline.} = v = elem
|
||||
proc fromStackElement(elem: StackElement, v: var EthAddress) {.inline.} = v[0 .. ^1] = elem.toByteArrayBE().toOpenArray(12, 31)
|
||||
|
@ -35,7 +35,7 @@ proc fromStackElement(elem: StackElement, v: var Hash256) {.inline.} = v.data =
|
|||
proc toStackElement(v: openarray[byte], elem: var StackElement) {.inline.} =
|
||||
# TODO: This needs to go
|
||||
validateStackItem(v) # This is necessary to pass stack tests
|
||||
elem = bigEndianToInt(v)
|
||||
elem.initFromBytesBE(v)
|
||||
|
||||
proc pushAux[T](stack: var Stack, value: T) =
|
||||
ensureStackLimit()
|
||||
|
|
|
@ -23,10 +23,10 @@ type
|
|||
gasMeter*: GasMeter
|
||||
code*: CodeStream
|
||||
children*: seq[BaseComputation]
|
||||
rawOutput*: string
|
||||
rawOutput*: seq[byte]
|
||||
returnData*: seq[byte]
|
||||
error*: Error
|
||||
logEntries*: seq[(EthAddress, seq[UInt256], string)]
|
||||
logEntries*: seq[(EthAddress, seq[UInt256], seq[byte])]
|
||||
shouldEraseReturnData*: bool
|
||||
accountsToDelete*: Table[EthAddress, EthAddress]
|
||||
opcodes*: Table[Op, proc(computation: var BaseComputation){.nimcall.}]
|
||||
|
@ -72,7 +72,7 @@ type
|
|||
sender*: EthAddress
|
||||
value*: UInt256
|
||||
data*: seq[byte]
|
||||
code*: string
|
||||
code*: string # TODO: seq[byte] is probably a better representation
|
||||
internalOrigin*: EthAddress
|
||||
internalCodeAddress*: EthAddress
|
||||
depth*: int
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import
|
||||
os, macros, json, strformat, strutils, parseutils, ospaths, tables,
|
||||
byteutils, eth_common, eth_keys, ranges/typedranges,
|
||||
../nimbus/utils/[address, padding],
|
||||
../nimbus/[vm_state, constants],
|
||||
../nimbus/db/[db_chain, state_db],
|
||||
../nimbus/transaction
|
||||
|
@ -31,7 +30,13 @@ proc validTest*(folder: string, name: string): bool =
|
|||
#result = name == "exp2.json"
|
||||
|
||||
macro jsonTest*(s: static[string], handler: untyped): untyped =
|
||||
let testStatusIMPL = ident("testStatusIMPL")
|
||||
let
|
||||
testStatusIMPL = ident("testStatusIMPL")
|
||||
# workaround for strformat in quote do: https://github.com/nim-lang/Nim/issues/8220
|
||||
symbol = newIdentNode"symbol"
|
||||
final = newIdentNode"final"
|
||||
name = newIdentNode"name"
|
||||
formatted = newStrLitNode"{symbol[final]} {name:<64}{$final}{'\n'}"
|
||||
result = quote:
|
||||
var z = 0
|
||||
var filenames: seq[(string, string, string)] = @[]
|
||||
|
@ -57,7 +62,7 @@ macro jsonTest*(s: static[string], handler: untyped): untyped =
|
|||
status.sort do (a: (string, OrderedTable[string, Status]),
|
||||
b: (string, OrderedTable[string, Status])) -> int: cmp(a[0], b[0])
|
||||
|
||||
let symbol: array[Status, string] = ["+", "-", " "]
|
||||
let `symbol`: array[Status, string] = ["+", "-", " "]
|
||||
var raw = ""
|
||||
raw.add(`s` & "\n")
|
||||
raw.add("===\n")
|
||||
|
@ -70,9 +75,9 @@ macro jsonTest*(s: static[string], handler: untyped): untyped =
|
|||
var okCount = 0
|
||||
var failCount = 0
|
||||
var skipCount = 0
|
||||
for name, final in sortedStatuses:
|
||||
raw.add(symbol[final] & " " & name.padRight(64, " ") & $final & "\n")
|
||||
case final:
|
||||
for `name`, `final` in sortedStatuses:
|
||||
raw.add(&`formatted`)
|
||||
case `final`:
|
||||
of Status.OK: okCount += 1
|
||||
of Status.Fail: failCount += 1
|
||||
of Status.Skip: skipCount += 1
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import
|
||||
unittest,
|
||||
eth_common/eth_types,
|
||||
../nimbus/[constants, errors, vm/interpreter, utils/bytes]
|
||||
../nimbus/[constants, errors, vm/interpreter]
|
||||
|
||||
|
||||
template testPush(value: untyped, expected: untyped): untyped =
|
||||
|
@ -21,6 +21,12 @@ template testFailPush(value: untyped): untyped =
|
|||
expect(ValidationError):
|
||||
stack.push(value)
|
||||
|
||||
func toBytes(s: string): seq[byte] =
|
||||
cast[seq[byte]](s)
|
||||
|
||||
func bigEndianToInt*(value: openarray[byte]): UInt256 =
|
||||
result.initFromBytesBE(value)
|
||||
|
||||
suite "stack":
|
||||
test "push only valid":
|
||||
testPush(0'u, 0.u256)
|
||||
|
|
|
@ -12,7 +12,7 @@ import
|
|||
./test_helpers,
|
||||
../nimbus/[constants, errors, logging],
|
||||
../nimbus/[vm_state, vm_types],
|
||||
../nimbus/utils/[header, padding],
|
||||
../nimbus/utils/header,
|
||||
../nimbus/vm/interpreter,
|
||||
../nimbus/db/[db_chain, state_db, backends/memory_backend]
|
||||
|
||||
|
@ -94,7 +94,7 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) =
|
|||
fail()
|
||||
|
||||
let expectedOutput = fixture{"out"}.getStr
|
||||
check(computation.output == expectedOutput)
|
||||
check(computation.outputHex == expectedOutput)
|
||||
let gasMeter = computation.gasMeter
|
||||
|
||||
let expectedGasRemaining = fixture{"gas"}.getHexadecimalInt
|
||||
|
|
Loading…
Reference in New Issue