diff --git a/nimbus/constants.nim b/nimbus/constants.nim index 6a40fc293..668ce76cb 100644 --- a/nimbus/constants.nim +++ b/nimbus/constants.nim @@ -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 diff --git a/nimbus/utils/address.nim b/nimbus/utils/address.nim deleted file mode 100644 index ebc452016..000000000 --- a/nimbus/utils/address.nim +++ /dev/null @@ -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 diff --git a/nimbus/utils/bytes.nim b/nimbus/utils/bytes.nim deleted file mode 100644 index cdbfabcce..000000000 --- a/nimbus/utils/bytes.nim +++ /dev/null @@ -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) diff --git a/nimbus/utils/padding.nim b/nimbus/utils/padding.nim deleted file mode 100644 index 5f6c4f025..000000000 --- a/nimbus/utils/padding.nim +++ /dev/null @@ -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] - diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index 58a0e4ff6..580e71cfa 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -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)) diff --git a/nimbus/vm/interpreter.nim b/nimbus/vm/interpreter.nim index 1a31787dc..88db95d00 100644 --- a/nimbus/vm/interpreter.nim +++ b/nimbus/vm/interpreter.nim @@ -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 diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index 3b8b95049..e40f9ca16 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -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. diff --git a/nimbus/vm/interpreter/utils/macros_procs_opcodes.nim b/nimbus/vm/interpreter/utils/macros_procs_opcodes.nim index 02cadcec4..ec4e2e562 100644 --- a/nimbus/vm/interpreter/utils/macros_procs_opcodes.nim +++ b/nimbus/vm/interpreter/utils/macros_procs_opcodes.nim @@ -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, diff --git a/nimbus/vm/interpreter/utils/utils_numeric.nim b/nimbus/vm/interpreter/utils/utils_numeric.nim index a2e615eee..83ef0c2f2 100644 --- a/nimbus/vm/interpreter/utils/utils_numeric.nim +++ b/nimbus/vm/interpreter/utils/utils_numeric.nim @@ -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 diff --git a/nimbus/vm/memory.nim b/nimbus/vm/memory.nim index 8f25912f2..22ec7edbe 100644 --- a/nimbus/vm/memory.nim +++ b/nimbus/vm/memory.nim @@ -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 diff --git a/nimbus/vm/message.nim b/nimbus/vm/message.nim index 64c833100..74df93646 100644 --- a/nimbus/vm/message.nim +++ b/nimbus/vm/message.nim @@ -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 diff --git a/nimbus/vm/stack.nim b/nimbus/vm/stack.nim index fd7e6be44..7f45e69a1 100644 --- a/nimbus/vm/stack.nim +++ b/nimbus/vm/stack.nim @@ -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() diff --git a/nimbus/vm_types.nim b/nimbus/vm_types.nim index c322f4805..e21b45a40 100644 --- a/nimbus/vm_types.nim +++ b/nimbus/vm_types.nim @@ -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 diff --git a/tests/test_helpers.nim b/tests/test_helpers.nim index 694ea16c1..f5ae3851f 100644 --- a/tests/test_helpers.nim +++ b/tests/test_helpers.nim @@ -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 diff --git a/tests/test_stack.nim b/tests/test_stack.nim index 78cc80d8c..008e7d0e7 100644 --- a/tests/test_stack.nim +++ b/tests/test_stack.nim @@ -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) diff --git a/tests/test_vm_json.nim b/tests/test_vm_json.nim index f30483bd1..bf1c58ed1 100644 --- a/tests/test_vm_json.nim +++ b/tests/test_vm_json.nim @@ -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