From a233cef719c5ae63baa43e70b6863667deaf247b Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Wed, 31 Jan 2018 14:57:05 +0200 Subject: [PATCH] CodeStream impl and tests, fix byte and string fields --- src/chain.nim | 16 ++--- src/computation.nim | 34 +++++------ src/constants.nim | 18 +++--- src/db/state_db.nim | 14 ++--- src/logic/call.nim | 44 +++++++------- src/logic/context.nim | 10 ++-- src/logic/logging_ops.nim | 2 +- src/logic/memory_ops.nim | 6 +- src/logic/sha3.nim | 2 +- src/logic/stack_ops.nim | 4 +- src/logic/system_ops.nim | 10 ++-- src/opcode_values.nim | 22 +++---- src/runner.nim | 14 ++--- src/transaction.nim | 8 +-- src/utils/bytes.nim | 28 +++++---- src/utils/header.nim | 8 +-- src/utils/hexadecimal.nim | 8 +-- src/utils/keccak.nim | 6 +- src/utils/padding.nim | 6 ++ src/utils_numeric.nim | 6 +- src/validation.nim | 2 +- src/vm/code_stream.nim | 44 ++++++++------ src/vm/gas_costs.nim | 2 +- src/vm/message.nim | 48 +++++++-------- src/vm/stack.nim | 18 +++--- src/vm/value.nim | 4 +- src/vm_state.nim | 8 +-- src/vm_state_transactions.nim | 10 ++-- tests/README.md | 8 +++ tests/code_stream_test.nim | 108 ++++++++++++++++++++++++++++++++++ tests/nim.cfg | 1 + 31 files changed, 329 insertions(+), 190 deletions(-) create mode 100644 tests/README.md create mode 100644 tests/code_stream_test.nim create mode 100644 tests/nim.cfg diff --git a/src/chain.nim b/src/chain.nim index 7a15c292d..0c4bb736a 100644 --- a/src/chain.nim +++ b/src/chain.nim @@ -13,7 +13,7 @@ type ## current block number. header*: BlockHeader logger*: Logger - networkId*: cstring + networkId*: string vmsByRange*: seq[tuple[blockNumber: Int256, vm: VM]] # TODO importBlock*: bool validateBlock*: bool @@ -23,18 +23,18 @@ type blockNumber*: Int256 difficulty*: Int256 gasLimit*: Int256 - parentHash*: cstring - coinbase*: cstring - nonce: cstring - mixHash: cstring - extraData: cstring + parentHash*: string + coinbase*: string + nonce: string + mixHash: string + extraData: string timestamp: int, - stateRoot: cstring + stateRoot: string FundedAddress* = ref object balance*: Int256 nonce*: int - code*: cstring + code*: string proc configureChain*(name: string, blockNumber: Int256, vm: VM, importBlock: bool = true, validateBlock: bool = true): Chain = diff --git a/src/computation.nim b/src/computation.nim index 05bdb8030..88a649891 100644 --- a/src/computation.nim +++ b/src/computation.nim @@ -23,14 +23,14 @@ type gasMeter*: GasMeter code*: CodeStream children*: seq[BaseComputation] - rawOutput*: cstring - returnData*: cstring + rawOutput*: string + returnData*: string error*: Error - logEntries*: seq[(cstring, seq[Int256], cstring)] + logEntries*: seq[(string, seq[Int256], string)] shouldEraseReturnData*: bool - accountsToDelete*: Table[cstring, cstring] + accountsToDelete*: Table[string, string] opcodes*: Table[Op, Opcode] # TODO array[Op, Opcode] - precompiles*: Table[cstring, Opcode] + precompiles*: Table[string, Opcode] Error* = ref object info*: string @@ -53,7 +53,7 @@ proc newBaseComputation*(vmState: BaseVMState, message: Message): BaseComputatio result.stack = newStack() result.gasMeter = newGasMeter(message.gas) result.children = @[] - result.accountsToDelete = initTable[cstring, cstring]() + result.accountsToDelete = initTable[string, string]() result.logEntries = @[] result.code = newCodeStream(message.code) @@ -87,10 +87,10 @@ method shouldEraseReturnData*(c: BaseComputation): bool = method prepareChildMessage*( c: var BaseComputation, gas: Int256, - to: cstring, + to: string, value: Int256, data: seq[byte], - code: cstring, + code: string, options: MessageOptions = newMessageOptions()): Message = var childOptions = options @@ -128,13 +128,13 @@ method extendMemory*(c: var BaseComputation, startPosition: Int256, size: Int256 c.memory.extend(startPosition, size) -method output*(c: BaseComputation): cstring = +method output*(c: BaseComputation): string = if c.shouldEraseReturnData: - cstring"" + "" else: c.rawOutput -method `output=`*(c: var BaseComputation, value: cstring) = +method `output=`*(c: var BaseComputation, value: string) = c.rawOutput = value macro generateChildBaseComputation*(t: typed, vmState: typed, childMsg: typed): untyped = @@ -157,12 +157,12 @@ method addChildBaseComputation*(c: var BaseComputation, childBaseComputation: Ba if childBaseComputation.msg.isCreate: c.returnData = childBaseComputation.output elif childBaseComputation.shouldBurnGas: - c.returnData = cstring"" + c.returnData = "" else: c.returnData = childBaseComputation.output else: if childBaseComputation.msg.isCreate: - c.returnData = cstring"" + c.returnData = "" else: c.returnData = childBaseComputation.output c.children.add(childBaseComputation) @@ -172,7 +172,7 @@ method applyChildBaseComputation*(c: var BaseComputation, childMsg: Message): Ba c.addChildBaseComputation(childBaseComputation) result = childBaseComputation -method registerAccountForDeletion*(c: var BaseComputation, beneficiary: cstring) = +method registerAccountForDeletion*(c: var BaseComputation, beneficiary: string) = validateCanonicalAddress(beneficiary, title="self destruct beneficiary address") if c.msg.storageAddress in c.accountsToDelete: @@ -181,18 +181,18 @@ method registerAccountForDeletion*(c: var BaseComputation, beneficiary: cstring) "registered for deletion multiple times") c.accountsToDelete[c.msg.storageAddress] = beneficiary -method addLogEntry*(c: var BaseComputation, account: cstring, topics: seq[Int256], data: cstring) = +method addLogEntry*(c: var BaseComputation, account: string, topics: seq[Int256], data: string) = validateCanonicalAddress(account, title="log entry address") c.logEntries.add((account, topics, data)) -method getAccountsForDeletion*(c: BaseComputation): seq[(cstring, cstring)] = +method getAccountsForDeletion*(c: BaseComputation): seq[(string, string)] = # TODO if c.isError: result = @[] else: result = @[] -method getLogEntries*(c: BaseComputation): seq[(cstring, seq[Int256], cstring)] = +method getLogEntries*(c: BaseComputation): seq[(string, seq[Int256], string)] = # TODO if c.isError: result = @[] diff --git a/src/constants.nim b/src/constants.nim index 9ade467e9..ab68f5359 100644 --- a/src/constants.nim +++ b/src/constants.nim @@ -86,12 +86,12 @@ let UINT_255_MAX*: Int256 = 2 ^ (256 - 1) - 1 UINT_255_CEILING*: Int256 = 2 ^ (256 - 1) - NULLBYTE* = cstring"\x00" + NULLBYTE* = "\x00" EMPTYWORD* = repeat(NULLBYTE, 32) UINT160CEILING*: Int256 = 2 ^ 160 - CREATE_CONTRACT_ADDRESS* = cstring"" - ZERO_ADDRESS* = repeat(cstring"\x00", 20) - ZERO_HASH32* = repeat(cstring"\x00", 20) + CREATE_CONTRACT_ADDRESS* = "" + ZERO_ADDRESS* = repeat("\x00", 20) + ZERO_HASH32* = repeat("\x00", 20) STACK_DEPTH_LIMIT* = 1024 GAS_NULL* = 0.i256 @@ -178,19 +178,19 @@ let SECPK1_Gy* = 0.i256 SECPK1_G* = (SECPK1Gx, SECPK1Gy) - EMPTY_UNCLE_HASH* = cstring"\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@\xd4\x93G" + EMPTY_UNCLE_HASH* = "\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@\xd4\x93G" GENESIS_BLOCK_NUMBER* = 0.i256 GENESIS_DIFFICULTY* = 131_072.i256 GENESIS_GAS_LIMIT* = 3_141_592.i256 GENESIS_PARENT_HASH* = ZERO_HASH32 GENESIS_COINBASE* = ZERO_ADDRESS - GENESIS_NONCE* = cstring"\x00\x00\x00\x00\x00\x00\x00B" + GENESIS_NONCE* = "\x00\x00\x00\x00\x00\x00\x00B" GENESIS_MIX_HASH* = ZERO_HASH32 - GENESIS_EXTRA_DATA = cstring"" + GENESIS_EXTRA_DATA = "" - EMPTYSHA3 = cstring"\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p" - BLANK_ROOT_HASH* = cstring"V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!" + EMPTYSHA3 = "\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p" + BLANK_ROOT_HASH* = "V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!" GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20.i256 diff --git a/src/db/state_db.nim b/src/db/state_db.nim index 743938a02..48446b55d 100644 --- a/src/db/state_db.nim +++ b/src/db/state_db.nim @@ -4,15 +4,15 @@ import type AccountStateDB* = ref object - db*: Table[cstring, Int256] + db*: Table[string, Int256] -proc newAccountStateDB*(db: Table[cstring, Int256], readOnly: bool = false): AccountStateDB = +proc newAccountStateDB*(db: Table[string, Int256], readOnly: bool = false): AccountStateDB = result = AccountStateDB(db: db) proc logger*(db: AccountStateDB): Logger = logging.getLogger("db.State") -proc getAccount(db: AccountStateDB, address: cstring): Account = +proc getAccount(db: AccountStateDB, address: string): Account = # let rlpAccount = db.trie[address] # if not rlpAccount.isNil: # account = rlp.decode[Account](rlpAccount) @@ -21,23 +21,23 @@ proc getAccount(db: AccountStateDB, address: cstring): Account = # account = newAccount() result = newAccount() # TODO -proc setAccount(db: AccountStateDB, address: cstring, account: Account) = +proc setAccount(db: AccountStateDB, address: string, account: Account) = # db.trie[address] = rlp.encode[Account](account) discard # TODO # public -proc getBalance*(db: AccountStateDB, address: cstring): Int256 = +proc getBalance*(db: AccountStateDB, address: string): Int256 = validateCanonicalAddress(address, title="Storage Address") let account = db.getAccount(address) account.balance -proc setBalance*(db: var AccountStateDB, address: cstring, balance: Int256) = +proc setBalance*(db: var AccountStateDB, address: string, balance: Int256) = validateCanonicalAddress(address, title="Storage Address") let account = db.getAccount(address) account.balance = balance db.setAccount(address, account) -proc deltaBalance*(db: var AccountStateDB, address: cstring, delta: Int256) = +proc deltaBalance*(db: var AccountStateDB, address: string, delta: Int256) = db.setBalance(address, db.getBalance(address) + delta) diff --git a/src/logic/call.nim b/src/logic/call.nim index 07151329d..fc7927a49 100644 --- a/src/logic/call.nim +++ b/src/logic/call.nim @@ -30,16 +30,16 @@ type using computation: var BaseComputation -method msgExtraGas*(call: BaseCall, computation; gas: Int256, to: cstring, value: Int256): Int256 {.base.} = +method msgExtraGas*(call: BaseCall, computation; gas: Int256, to: string, value: Int256): Int256 {.base.} = raise newException(NotImplementedError, "Must be implemented by subclasses") -method msgGas*(call: BaseCall, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) {.base.} = +method msgGas*(call: BaseCall, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) {.base.} = let extraGas = call.msgExtraGas(computation, gas, to, value) let totalFee = gas + extraGas let childMsgGas = gas + (if value != 0: GAS_CALL_STIPEND else: 0.i256) (childMsgGas, totalFee) -method callParams*(call: BaseCall, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) {.base.} = +method callParams*(call: BaseCall, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) {.base.} = raise newException(NotImplementedError, "Must be implemented subclasses") method runLogic*(call: BaseCall, computation) = @@ -67,7 +67,7 @@ method runLogic*(call: BaseCall, computation) = let stackTooDeep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT if insufficientFunds or stackTooDeep: - computation.returnData = cstring"" + computation.returnData = "" var errMessage: string if insufficientFunds: errMessage = &"Insufficient Funds: have: {senderBalance} | need: {value}" @@ -86,7 +86,7 @@ method runLogic*(call: BaseCall, computation) = # code = state_db.get_code(code_address) # else: # code = state_db.get_code(to) - let code = cstring"" + let code = "" let childMsg = computation.prepareChildMessage( childMsgGas, @@ -115,7 +115,7 @@ method runLogic*(call: BaseCall, computation) = if not childComputation.shouldBurnGas: computation.gasMeter.returnGas(childComputation.gasMeter.gasRemaining) -method msgExtraGas(call: Call, computation; gas: Int256, to: cstring, value: Int256): Int256 = +method msgExtraGas(call: Call, computation; gas: Int256, to: string, value: Int256): Int256 = # TODO: db # with computation.vm_state.state_db(read_only=True) as state_db: # let accountExists = db.accountExists(to) @@ -125,9 +125,9 @@ method msgExtraGas(call: Call, computation; gas: Int256, to: cstring, value: Int let createGasFee = if not accountExists: GAS_NEW_ACCOUNT else: 0.i256 transferGasFee + createGasFee -method callParams(call: CallCode, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) = +method callParams(call: CallCode, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) = let gas = computation.stack.popInt() - let to = cstring(forceBytesToAddress(computation.stack.popBinary)) + let to = forceBytesToAddress(computation.stack.popBinary) let (value, memoryInputStartPosition, memoryInputSize, @@ -145,12 +145,12 @@ method callParams(call: CallCode, computation): (Int256, Int256, cstring, cstrin true, # should_transfer_value, computation.msg.isStatic) -method msgExtraGas(call: CallCode, computation; gas: Int256, to: cstring, value: Int256): Int256 = +method msgExtraGas(call: CallCode, computation; gas: Int256, to: string, value: Int256): Int256 = if value != 0: GAS_CALL_VALUE else: 0.i256 -method callParams(call: Call, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) = +method callParams(call: Call, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) = let gas = computation.stack.popInt() - let codeAddress = cstring(forceBytesToAddress(computation.stack.popBinary)) + let codeAddress = forceBytesToAddress(computation.stack.popBinary) let (value, memoryInputStartPosition, memoryInputSize, @@ -171,15 +171,15 @@ method callParams(call: Call, computation): (Int256, Int256, cstring, cstring, c true, # should_transfer_value, computation.msg.isStatic) -method msgGas(call: DelegateCall, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) = +method msgGas(call: DelegateCall, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) = (gas, gas) -method msgExtraGas(call: DelegateCall, computation; gas: Int256, to: cstring, value: Int256): Int256 = +method msgExtraGas(call: DelegateCall, computation; gas: Int256, to: string, value: Int256): Int256 = 0.i256 -method callParams(call: DelegateCall, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) = +method callParams(call: DelegateCall, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) = let gas = computation.stack.popInt() - let codeAddress = cstring(forceBytesToAddress(computation.stack.popBinary)) + let codeAddress = forceBytesToAddress(computation.stack.popBinary) let (memoryInputStartPosition, memoryInputSize, memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4) @@ -211,19 +211,19 @@ proc computeEIP150MsgGas(computation; gas: Int256, extraGas: Int256, value: Int2 let childMsgGas = gas + (if value != 0: callStipend else: 0.i256) (childMsgGas, totalFee) -method msgGas(call: CallEIP150, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) = +method msgGas(call: CallEIP150, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) = let extraGas = call.msgExtraGas(computation, gas, to, value) computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND) -method msgGas(call: CallCodeEIP150, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) = +method msgGas(call: CallCodeEIP150, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) = let extraGas = call.msgExtraGas(computation, gas, to, value) computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, GAS_CALL_STIPEND) -method msgGas(call: DelegateCallEIP150, computation; gas: Int256, to: cstring, value: Int256): (Int256, Int256) = +method msgGas(call: DelegateCallEIP150, computation; gas: Int256, to: string, value: Int256): (Int256, Int256) = let extraGas = call.msgExtraGas(computation, gas, to, value) computeEIP150MsgGas(computation, gas, extraGas, value, $call.kind, 0.i256) -proc msgExtraGas*(call: CallEIP161, computation; gas: Int256, to: cstring, value: Int256): Int256 = +proc msgExtraGas*(call: CallEIP161, computation; gas: Int256, to: string, value: Int256): Int256 = # TODO: with # with computation.vm_state.state_db(read_only=True) as state_db: # account_is_dead = ( @@ -236,9 +236,9 @@ proc msgExtraGas*(call: CallEIP161, computation; gas: Int256, to: cstring, value transferGasFee + createGasFee -method callParams(call: StaticCall, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) = +method callParams(call: StaticCall, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, bool, bool) = let gas = computation.stack.popInt() - let to = cstring(forceBytesToAddress(computation.stack.popBinary)) + let to = forceBytesToAddress(computation.stack.popBinary) let (memoryInputStartPosition, memoryInputSize, memoryOutputStartPosition, memoryOutputSize) = computation.stack.popInt(4) @@ -256,7 +256,7 @@ method callParams(call: StaticCall, computation): (Int256, Int256, cstring, cstr true) # is_static -method callParams(call: CallByzantium, computation): (Int256, Int256, cstring, cstring, cstring, Int256, Int256, Int256, Int256, bool, bool) = +method callParams(call: CallByzantium, computation): (Int256, Int256, string, string, string, Int256, Int256, Int256, Int256, 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") diff --git a/src/logic/context.nim b/src/logic/context.nim index cbbb30b5d..01a666dd6 100644 --- a/src/logic/context.nim +++ b/src/logic/context.nim @@ -26,8 +26,8 @@ proc callValue*(computation: var BaseComputation) = proc callDataLoad*(computation: var BaseComputation) = # Load call data into memory let startPosition = computation.stack.popInt.getInt - let value = computation.msg.data[startPosition ..< startPosition + 32].toCString - let paddedValue = padRight(value, 32, cstring"\x00") + let value = computation.msg.data[startPosition ..< startPosition + 32].toString + let paddedValue = padRight(value, 32, "\x00") let normalizedValue = paddedValue.lStrip(0.char) computation.stack.push(normalizedValue) @@ -45,8 +45,8 @@ proc callDataCopy*(computation: var BaseComputation) = let wordCount = ceil32(size) div 32 let copyGasCost = wordCount * constants.GAS_COPY computation.gasMeter.consumeGas(copyGasCost, reason="CALLDATACOPY fee") - let value = computation.msg.data[calldataStartPosition.getInt ..< (calldataStartPosition + size).getInt].toCString - let paddedValue = padRight(value, size.getInt, cstring"\x00") + let value = computation.msg.data[calldataStartPosition.getInt ..< (calldataStartPosition + size).getInt].toString + let paddedValue = padRight(value, size.getInt, "\x00") computation.memory.write(memStartPosition, size, paddedValue) @@ -115,5 +115,5 @@ proc returnDataCopy*(computation: var BaseComputation) = let wordCount = ceil32(size) div 32 let copyGasCost = wordCount * constants.GAS_COPY computation.gasMeter.consumeGas(copyGasCost, reason="RETURNDATACOPY fee") - let value = cstring(($computation.returnData)[returnDataStartPosition.getInt ..< (returnDataStartPosition + size).getInt]) + let value = ($computation.returnData)[returnDataStartPosition.getInt ..< (returnDataStartPosition + size).getInt] computation.memory.write(memStartPosition, size, value) diff --git a/src/logic/logging_ops.nim b/src/logic/logging_ops.nim index b36b24528..5cd8572eb 100644 --- a/src/logic/logging_ops.nim +++ b/src/logic/logging_ops.nim @@ -48,7 +48,7 @@ macro logXX(topicCount: static[int]): untyped = let totalGasCost = dataGasCost + topicGasCost `computation`.gasMeter.consumeGas(totalGasCost, reason="Log topic and data gas cost") `computation`.extendMemory(`memStartPosition`, `size`) - let logData = `computation`.memory.read(`memStartPosition`, `size`).toCString + let logData = `computation`.memory.read(`memStartPosition`, `size`).toString `computation`.addLogEntry( account=`computation`.msg.storageAddress, topics=`topics`, diff --git a/src/logic/memory_ops.nim b/src/logic/memory_ops.nim index 8078cbc03..519e6de0c 100644 --- a/src/logic/memory_ops.nim +++ b/src/logic/memory_ops.nim @@ -12,8 +12,8 @@ proc mstoreX(computation; x: int) = let start = stack.popInt() let value = stack.popBinary() - let paddedValue = padLeft(value, x, cstring"\x00") - let normalizedValue = cstring(($paddedValue)[^x .. ^1]) + let paddedValue = padLeft(value, x, "\x00") + let normalizedValue = ($paddedValue)[^x .. ^1] extendMemory(start, x.int256) memory.write(start, 32.int256, normalizedValue) @@ -31,7 +31,7 @@ proc mload*(computation) = extendMemory(start, 32.int256) - let value = memory.read(start, 32.int256).toCstring + let value = memory.read(start, 32.int256).toString stack.push(value) proc msize*(computation) = diff --git a/src/logic/sha3.nim b/src/logic/sha3.nim index 3b8b8798a..ee52e352e 100644 --- a/src/logic/sha3.nim +++ b/src/logic/sha3.nim @@ -8,5 +8,5 @@ proc sha3op*(computation: var BaseComputation) = let wordCount = sha3Bytes.len.i256.ceil32 div 32 let gasCost = constants.GAS_SHA3_WORD * wordCount computation.gasMeter.consumeGas(gasCost, reason="SHA3: word gas cost") - var res = keccak(cstring"") + var res = keccak("") pushRes() diff --git a/src/logic/stack_ops.nim b/src/logic/stack_ops.nim index 58705d6ee..af7511ac1 100644 --- a/src/logic/stack_ops.nim +++ b/src/logic/stack_ops.nim @@ -17,12 +17,12 @@ macro pushXX(size: static[int]): untyped = let name = ident(&"push{size}") result = quote: proc `name`*(`computation`: var BaseComputation) = - let `value` = `computation`.code.read(`size`).toCString + let `value` = `computation`.code.read(`size`).toString let stripped = `value`.strip(0.char) if stripped.len == 0: `computation`.stack.push(0.i256) else: - let paddedValue = `value`.padRight(`size`, cstring"\x00") + let paddedValue = `value`.padRight(`size`, "\x00") `computation`.stack.push(paddedValue) diff --git a/src/logic/system_ops.nim b/src/logic/system_ops.nim index e27015eed..2b4466272 100644 --- a/src/logic/system_ops.nim +++ b/src/logic/system_ops.nim @@ -50,7 +50,7 @@ method runLogic*(create: Create, computation) = # ) # is_collision = state_db.account_has_code_or_nonce(contract_address) - let contractAddress = cstring"" + let contractAddress = "" let isCollision = false if isCollision: @@ -63,7 +63,7 @@ method runLogic*(create: Create, computation) = to=constants.CREATE_CONTRACT_ADDRESS, value=value, data=cast[seq[byte]](@[]), - code=callData.toCString, + code=callData.toString, options=MessageOptions(createAddress: contractAddress)) # let childComputation = computation.applyChildComputation(childMsg) @@ -109,7 +109,7 @@ proc selfdestructEIP161(computation) = # ) # _selfdestruct(computation, beneficiary) -proc selfdestruct(computation; beneficiary: cstring) = +proc selfdestruct(computation; beneficiary: string) = discard # TODO: with # with computation.vm_state.state_db() as state_db: # local_balance = state_db.get_balance(computation.msg.storage_address) @@ -136,14 +136,14 @@ proc selfdestruct(computation; beneficiary: cstring) = proc returnOp*(computation) = let (startPosition, size) = stack.popInt(2) computation.extendMemory(startPosition, size) - let output = memory.read(startPosition, size).toCString + let output = memory.read(startPosition, size).toString computation.output = output raise newException(Halt, "RETURN") proc revert*(computation) = let (startPosition, size) = stack.popInt(2) computation.extendMemory(startPosition, size) - let output = memory.read(startPosition, size).toCString + let output = memory.read(startPosition, size).toString computation.output = output raise newException(Revert, $output) diff --git a/src/opcode_values.nim b/src/opcode_values.nim index ce69c2f05..64753eeb4 100644 --- a/src/opcode_values.nim +++ b/src/opcode_values.nim @@ -1,7 +1,7 @@ # TODO : hex type Op* {.pure.} = enum - STOP, # 0 + STOP = 0x0, # 0 ADD, # 1 MUL, # 2 SUB, # 3 @@ -14,7 +14,7 @@ type EXP, # 10 SIGNEXTEND, # 11 - LT, # 16 + LT = 0x10, # 16 GT, # 17 SLT, # 18 SGT, # 19 @@ -26,9 +26,9 @@ type NOT, # 25 BYTE, # 26 - SHA3, # 32 + SHA3 = 0x20, # 32 - ADDRESS, # 48 + ADDRESS = 0x30,# 48 BALANCE, # 49 ORIGIN, # 50 @@ -49,7 +49,7 @@ type RETURNDATASIZE, # 61 RETURNDATACOPY, # 62 - BLOCKHASH, # 64 + BLOCKHASH = 0x40,# 64 COINBASE, # 65 @@ -61,7 +61,7 @@ type GASLIMIT, # 69 - POP, # 80 + POP = 0x50, # 80 MLOAD, # 81 MSTORE, # 82 @@ -81,7 +81,7 @@ type JUMPDEST, # 91 - PUSH1, # 96 + PUSH1 = 0x60, # 96 PUSH2, # 97 PUSH3, # 98 PUSH4, # 99 @@ -150,13 +150,13 @@ type LOG2, # 162 LOG3, # 163 LOG4, # 164 - CREATE, # 240 + CREATE = 0xf0, # 240 CALL, # 241 CALLCODE, # 242 RETURN, # 243 DELEGATECALL, # 244 - STATICCALL, # 250 - REVERT, # 253 - SELFDESTRUCT # 255 + STATICCALL = 0xfa,# 250 + REVERT = 0xfd, # 253 + SELFDESTRUCT = 0xff,# 255 INVALID # invalid diff --git a/src/runner.nim b/src/runner.nim index cc62548f8..fc9009600 100644 --- a/src/runner.nim +++ b/src/runner.nim @@ -112,10 +112,10 @@ opcodes[Op.Create] = Create(kind: Op.Create) var mem = newMemory(pow(1024.int256, 2)) -var to = toCanonicalAddress(cstring"0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6") -var sender = toCanonicalAddress(cstring"0xcd1722f3947def4cf144679da39c4c32bdc35681") +var to = toCanonicalAddress("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6") +var sender = toCanonicalAddress("0xcd1722f3947def4cf144679da39c4c32bdc35681") -var code = cstring"" +var code = "" var data: seq[byte] = @[] var msg = newMessage( @@ -140,14 +140,14 @@ var c = BaseComputation( gasMeter: newGasMeter(msg.gas), code: newCodeStream(code), children: @[], - rawOutput: cstring"", - returnData: cstring"", + rawOutput: "", + returnData: "", error: nil, logEntries: @[], shouldEraseReturnData: false, - accountsToDelete: initTable[cstring, cstring](), + accountsToDelete: initTable[string, string](), opcodes: opcodes, - precompiles: initTable[cstring, Opcode]()) + precompiles: initTable[string, Opcode]()) # var c2 = c.applyComputation(c.vmState, c.msg) diff --git a/src/transaction.nim b/src/transaction.nim index 493fa9dab..6c2fe522a 100644 --- a/src/transaction.nim +++ b/src/transaction.nim @@ -6,9 +6,9 @@ type nonce*: Int256 gasPrice*: Int256 gas*: Int256 - to*: cstring + to*: string value*: Int256 - data*: cstring + data*: string v*: Int256 r*: Int256 s*: Int256 @@ -31,6 +31,6 @@ proc validate*(t: BaseTransaction) = raise newException(ValidationError, "Insufficient gas") # self.check_signature_validity() -proc sender*(t: BaseTransaction): cstring = +proc sender*(t: BaseTransaction): string = # TODO - cstring"" \ No newline at end of file + "" \ No newline at end of file diff --git a/src/utils/bytes.nim b/src/utils/bytes.nim index 9a2954911..e826c0ab7 100644 --- a/src/utils/bytes.nim +++ b/src/utils/bytes.nim @@ -1,11 +1,19 @@ -proc toBytes*(value: cstring): seq[byte] = - result = newSeq[byte](value.len) - for z, c in value: - result[z] = c.byte - # result = toSeq(value) +import strutils, sequtils -proc toCString*(value: seq[byte]): cstring = - var res = "" - for c in value: - res.add(c.char) - cstring(res) # TODO: faster +# 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 = + value.mapIt(it.char).join("") + +proc toBytes*(value: string): seq[byte] = + result = value.mapIt(it.byte) diff --git a/src/utils/header.nim b/src/utils/header.nim index 58776c193..106742dc7 100644 --- a/src/utils/header.nim +++ b/src/utils/header.nim @@ -5,16 +5,16 @@ type timestamp*: int difficulty*: Int256 blockNumber*: Int256 - hash*: cstring - coinbase*: cstring + hash*: string + coinbase*: string # TODO proc generateHeaderFromParentHeader*( computeDifficultyFn: proc(parentHeader: Header, timestamp: int): int, parentHeader: Header, - coinbase: cstring, + coinbase: string, timestamp: int = -1, - extraData: cstring = cstring""): Header = + extraData: string = string""): Header = Header() # Generate BlockHeader from state_root and parent_header # if timestamp is None: diff --git a/src/utils/hexadecimal.nim b/src/utils/hexadecimal.nim index 0f603da1e..47789a647 100644 --- a/src/utils/hexadecimal.nim +++ b/src/utils/hexadecimal.nim @@ -1,11 +1,11 @@ import strutils -proc encodeHex*(value: cstring): string = +proc encodeHex*(value: string): string = # return "0x" & codecs.decode(codecs.encode(value, "hex"), "utf8") - return $value + return value -proc decodeHex*(value: string): cstring = +proc decodeHex*(value: string): string = # var hexPart = value.rsplit("x", 1)[1] - return cstring(value) + return value # return codecs.decode(hexPart, "hex") diff --git a/src/utils/keccak.nim b/src/utils/keccak.nim index 06fc6cf15..ec35b24c1 100644 --- a/src/utils/keccak.nim +++ b/src/utils/keccak.nim @@ -1,10 +1,10 @@ import keccak_tiny, strutils -template keccak*(value: string): cstring = - cstring($keccak_256(value)) +template keccak*(value: string): string = + $keccak_256(value) -template keccak*(value: cstring): cstring = +template keccak*(value: cstring): string = ($value).keccak \ No newline at end of file diff --git a/src/utils/padding.nim b/src/utils/padding.nim index c9813d33c..e9f0a7b1e 100644 --- a/src/utils/padding.nim +++ b/src/utils/padding.nim @@ -88,3 +88,9 @@ proc rStrip*(value: cstring, c: char): cstring = 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) diff --git a/src/utils_numeric.nim b/src/utils_numeric.nim index 8b892f432..39b84f979 100644 --- a/src/utils_numeric.nim +++ b/src/utils_numeric.nim @@ -1,9 +1,9 @@ import bigints, constants, strformat, macros -proc intToBigEndian*(value: Int256): cstring = - result = cstring"" +proc intToBigEndian*(value: Int256): string = + result = "" -proc bigEndianToInt*(value: cstring): Int256 = +proc bigEndianToInt*(value: string): Int256 = result = 0.int256 proc unsignedToSigned*(value: Int256): Int256 = diff --git a/src/validation.nim b/src/validation.nim index 4d005cfd3..305966110 100644 --- a/src/validation.nim +++ b/src/validation.nim @@ -4,7 +4,7 @@ import strformat, errors, constants, bigints -proc validateCanonicalAddress*(value: cstring, title: string = "Value") = +proc validateCanonicalAddress*(value: string, title: string = "Value") = # TODO if false: #len(value) != 20: raise newException(ValidationError, diff --git a/src/vm/code_stream.nim b/src/vm/code_stream.nim index 2686c13f0..ec04c4c1f 100644 --- a/src/vm/code_stream.nim +++ b/src/vm/code_stream.nim @@ -1,5 +1,5 @@ import - strformat, strutils, sequtils, sets, + strformat, strutils, sequtils, sets, macros, ../logging, ../constants, ../opcode_values type @@ -13,37 +13,43 @@ type proc `$`*(b: byte): string = $(b.int) -proc newCodeStream*(codeBytes: cstring): CodeStream = +proc newCodeStream*(codeBytes: seq[byte]): CodeStream = new(result) - result.bytes = codeBytes.mapIt(it.byte) + result.bytes = codeBytes result.pc = 0 result.invalidPositions = initSet[int]() result.depthProcessed = 0 - result.logger = logging.getLogger("evm.vm.CodeStream") + result.logger = logging.getLogger("vm.code_stream") + +proc newCodeStream*(codeBytes: string): CodeStream = + newCodeStream(codeBytes.mapIt(it.byte)) proc read*(c: var CodeStream, size: int): seq[byte] = - result = c.bytes[c.pc .. c.pc + size - 1] - c.pc += size + if c.pc + size - 1 < c.bytes.len: + result = c.bytes[c.pc .. c.pc + size - 1] + c.pc += size + else: + result = @[] + c.pc = c.bytes.len proc len*(c: CodeStream): int = len(c.bytes) proc next*(c: var CodeStream): Op = var nextOpcode = c.read(1) - if nextOpcode[0] != 0x0.byte: + if nextOpcode.len != 0: return Op(nextOpcode[0]) else: return Op.STOP - iterator items*(c: var CodeStream): Op = var nextOpcode = c.next() while nextOpcode != Op.STOP: yield nextOpcode nextOpcode = c.next() -proc `[]`*(c: CodeStream, offset: int): byte = - c.bytes[offset] +proc `[]`*(c: CodeStream, offset: int): Op = + Op(c.bytes[offset]) proc peek*(c: var CodeStream): Op = var currentPc = c.pc @@ -53,14 +59,16 @@ proc peek*(c: var CodeStream): Op = proc updatePc*(c: var CodeStream, value: int) = c.pc = min(value, len(c)) -template seek*(c: var CodeStream, pc: int, handler: untyped): untyped = - var anchorPc = pc - `c`.pc = pc - try: - var c = `c` {.inject.} - `handler` - finally: - `c`.pc = anchorPc +macro seek*(c: var CodeStream, pc: int, handler: untyped): untyped = + let c2 = ident("c") + result = quote: + var anchorPc = `c`.pc + `c`.pc = `pc` + try: + var `c2` = `c` + `handler` + finally: + `c`.pc = anchorPc proc isValidOpcode*(c: var CodeStream, position: int): bool = if position >= len(c): diff --git a/src/vm/gas_costs.nim b/src/vm/gas_costs.nim index 59f57ba21..f9d4eadef 100644 --- a/src/vm/gas_costs.nim +++ b/src/vm/gas_costs.nim @@ -5,4 +5,4 @@ import proc expGasCost*(computation: var BaseComputation): Int256 = let arg = computation.stack.getInt(0) result = if arg == 0: 10.i256 else: (10.i256 + 10.i256 * (1.i256 + arg.log256)) - \ No newline at end of file + diff --git a/src/vm/message.nim b/src/vm/message.nim index 5c58b5cef..841a52220 100644 --- a/src/vm/message.nim +++ b/src/vm/message.nim @@ -19,41 +19,41 @@ type gas*: Int256 gasPrice*: Int256 - to*: cstring - sender*: cstring + to*: string + sender*: string value*: Int256 data*: seq[byte] - code*: cstring - internalOrigin: cstring - internalCodeAddress: cstring + code*: string + internalOrigin: string + internalCodeAddress: string depth*: int - internalStorageAddress: cstring + internalStorageAddress: string shouldTransferValue*: bool isStatic*: bool isCreate*: bool MessageOptions* = ref object - origin*: cstring + origin*: string depth*: int - createAddress*: cstring - codeAddress*: cstring + createAddress*: string + codeAddress*: string shouldTransferValue*: bool isStatic*: bool -proc `origin=`*(message: var Message, value: cstring) = +proc `origin=`*(message: var Message, value: string) = message.internalOrigin = value -proc `codeAddress=`*(message: var Message, value: cstring) = +proc `codeAddress=`*(message: var Message, value: string) = message.internalCodeAddress = value -proc `storageAddress=`*(message: var Message, value: cstring) = +proc `storageAddress=`*(message: var Message, value: string) = message.internalStorageAddress = value proc newMessageOptions*( - origin: cstring = nil, + origin: string = "", depth: int = 0, - createAddress: cstring = nil, - codeAddress: cstring = nil, + createAddress: string = "", + codeAddress: string = "", shouldTransferValue: bool = true, isStatic: bool = false): MessageOptions = @@ -68,11 +68,11 @@ proc newMessageOptions*( proc newMessage*( gas: Int256, gasPrice: Int256, - to: cstring, - sender: cstring, + to: string, + sender: string, value: Int256, data: seq[byte], - code: cstring, + code: string, options: MessageOptions = newMessageOptions()): Message = new(result) @@ -111,8 +111,8 @@ proc newMessage*( result.isStatic = options.isStatic -proc origin*(message: Message): cstring = - if not message.internalOrigin.isNil: +proc origin*(message: Message): string = + if not message.internalOrigin.len == 0: message.internalOrigin else: message.sender @@ -120,14 +120,14 @@ proc origin*(message: Message): cstring = proc isOrigin*(message: Message): bool = message.sender == message.origin -proc codeAddress*(message: Message): cstring = - if not message.internalCodeAddress.isNil: +proc codeAddress*(message: Message): string = + if not message.internalCodeAddress.len == 0: message.internalCodeAddress else: message.to -proc `storageAddress`*(message: Message): cstring = - if not message.internalStorageAddress.isNil: +proc `storageAddress`*(message: Message): string = + if not message.internalStorageAddress.len == 0: message.internalStorageAddress else: message.to diff --git a/src/vm/stack.nim b/src/vm/stack.nim index 753fc0ca0..c8abfb388 100644 --- a/src/vm/stack.nim +++ b/src/vm/stack.nim @@ -34,7 +34,7 @@ proc push*(stack: var Stack; value: Int256) = stack.values.add(Value(kind: VInt, i: value)) -proc push*(stack: var Stack; value: cstring) = +proc push*(stack: var Stack; value: string) = ## Push a binary onto the stack ensureStackLimit() @@ -50,13 +50,13 @@ proc internalPop(stack: var Stack; numItems: int): seq[Value] = template toType(i: Int256, _: typedesc[Int256]): Int256 = i -template toType(i: Int256, _: typedesc[cstring]): cstring = +template toType(i: Int256, _: typedesc[string]): string = intToBigEndian(i) -template toType(b: cstring, _: typedesc[Int256]): Int256 = +template toType(b: string, _: typedesc[Int256]): Int256 = bigEndianToInt(b) -template toType(b: cstring, _: typedesc[cstring]): cstring = +template toType(b: string, _: typedesc[string]): string = b proc internalPop(stack: var Stack; numItems: int, T: typedesc): seq[T] = @@ -136,13 +136,13 @@ macro popInt*(stack: typed; numItems: static[int]): untyped = # result = stack.internalPop(numItems, Int256) # ensurePop(result, numItems) -proc popBinary*(stack: var Stack): cstring = - var elements = stack.internalPop(1, cstring) +proc popBinary*(stack: var Stack): string = + var elements = stack.internalPop(1, string) ensurePop(elements, 1) result = elements[0] -proc popBinary*(stack: var Stack; numItems: int): seq[cstring] = - result = stack.internalPop(numItems, cstring) +proc popBinary*(stack: var Stack; numItems: int): seq[string] = + result = stack.internalPop(numItems, string) ensurePop(result, numItems) proc newStack*(): Stack = @@ -176,7 +176,7 @@ proc getInt*(stack: Stack, position: int): Int256 = else: raise newException(TypeError, "Expected int") -proc getBinary*(stack: Stack, position: int): cstring = +proc getBinary*(stack: Stack, position: int): string = let element = stack.values[position] case element.kind: of VBinary: diff --git a/src/vm/value.nim b/src/vm/value.nim index 4dbd0d770..c6f1e8536 100644 --- a/src/vm/value.nim +++ b/src/vm/value.nim @@ -10,7 +10,7 @@ type of VInt: i*: Int256 of VBinary: - b*: cstring + b*: string proc `$`*(value: Value): string = case value.kind: @@ -25,6 +25,6 @@ proc vint*(i: int): Value = proc vint*(i: Int256): Value = Value(kind: VInt, i: i) -proc vbinary*(b: cstring): Value = +proc vbinary*(b: string): Value = Value(kind: VBinary, b: b) diff --git a/src/vm_state.nim b/src/vm_state.nim index 1e2679714..08901ba56 100644 --- a/src/vm_state.nim +++ b/src/vm_state.nim @@ -19,10 +19,10 @@ proc newBaseVMState*: BaseVMState = method logger*(vmState: BaseVMState): Logger = logging.getLogger(&"evm.vmState.{vmState.name}") -method blockhash*(vmState: BaseVMState): cstring = +method blockhash*(vmState: BaseVMState): string = vmState.blockHeader.hash -method coinbase*(vmState: BaseVMState): cstring = +method coinbase*(vmState: BaseVMState): string = vmState.blockHeader.coinbase method timestamp*(vmState: BaseVMState): int = @@ -37,11 +37,11 @@ method difficulty*(vmState: BaseVMState): Int256 = method gasLimit*(vmState: BaseVMState): Int256 = vmState.blockHeader.gasLimit -method getAncestorHash*(vmState: BaseVMState, blockNumber: Int256): cstring = +method getAncestorHash*(vmState: BaseVMState, blockNumber: Int256): string = var ancestorDepth = vmState.blockHeader.blockNumber - blockNumber - 1.int256 if ancestorDepth >= constants.MAX_PREV_HEADER_DEPTH or ancestorDepth < 0 or ancestorDepth >= vmState.prevHeaders.len.int256: - return cstring"" + return "" var header = vmState.prevHeaders[ancestorDepth.getInt] result = header.hash diff --git a/src/vm_state_transactions.nim b/src/vm_state_transactions.nim index 2295cea22..8c78b7652 100644 --- a/src/vm_state_transactions.nim +++ b/src/vm_state_transactions.nim @@ -7,7 +7,7 @@ method executeTransaction(vmState: var BaseVMState, transaction: BaseTransaction raise newException(ValueError, "Must be implemented by subclasses") -method addTransaction*(vmState: var BaseVMState, transaction: BaseTransaction, computation: BaseComputation, b: Block): (Block, Table[cstring, cstring]) = +method addTransaction*(vmState: var BaseVMState, transaction: BaseTransaction, computation: BaseComputation, b: Block): (Block, Table[string, string]) = # Add a transaction to the given block and # return `trieData` to store the transaction data in chaindb in VM layer # Update the bloomFilter, transaction trie and receipt trie roots, bloom_filter, @@ -34,13 +34,13 @@ method addTransaction*(vmState: var BaseVMState, transaction: BaseTransaction, c # block.header.gas_used = receipt.gas_used # return block, trie_data - result = (b, initTable[cstring, cstring]()) + result = (b, initTable[string, string]()) method applyTransaction*( vmState: var BaseVMState, transaction: BaseTransaction, b: Block, - isStateless: bool): (BaseComputation, Block, Table[cstring, cstring]) = + isStateless: bool): (BaseComputation, Block, Table[string, string]) = # Apply transaction to the given block # transaction: the transaction need to be applied # b: the block which the transaction applies on @@ -52,10 +52,10 @@ method applyTransaction*( var (computation, blockHeader) = vmState.executeTransaction(transaction) ourBlock.header = blockHeader - var trieData: Table[cstring, cstring] + var trieData: Table[string, string] (ourBlock, trieData) = vmState.addTransaction(transaction, computation, ourBlock) result = (computation, ourBlock, trieData) else: var (computation, blockHeader) = vmState.executeTransaction(transaction) - return (computation, nil, initTable[cstring, cstring]()) + return (computation, nil, initTable[string, string]()) diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..5df4b96b6 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,8 @@ +# tests + +TODO: more vm tests and fixtures! + +```bash +nim c tests/code_stream_test.nim +./tests/code_stream_test +``` diff --git a/tests/code_stream_test.nim b/tests/code_stream_test.nim new file mode 100644 index 000000000..613cfc9a3 --- /dev/null +++ b/tests/code_stream_test.nim @@ -0,0 +1,108 @@ +import unittest, strutils, sequtils, opcode_values, vm / code_stream + +suite "parse bytecode": + test "accepts bytes": + let codeStream = newCodeStream("\x01") + check(codeStream.len == 1) + + +# quicktest +# @pytest.mark.parametrize("code_bytes", (1010, '1010', True, bytearray(32))) +# def test_codeStream_rejects_invalid_code_byte_values(code_bytes): +# with pytest.raises(ValidationError): +# CodeStream(code_bytes) + + test "next returns the correct opcode": + var codeStream = newCodeStream("\x01\x02\x30") + check(codeStream.next == Op.ADD) + check(codeStream.next == Op.MUL) + check(codeStream.next == Op.ADDRESS) + + + test "peek returns next opcode without changing location": + var codeStream = newCodeStream("\x01\x02\x30") + check(codeStream.pc == 0) + check(codeStream.peek == Op.ADD) + check(codeStream.pc == 0) + check(codeStream.next == Op.ADD) + check(codeStream.pc == 1) + check(codeStream.peek == Op.MUL) + check(codeStream.pc == 1) + + + test "stop opcode is returned when end reached": + var codeStream = newCodeStream("\x01\x02") + discard codeStream.next + discard codeStream.next + check(codeStream.next == Op.STOP) + + test "seek reverts to original position on exit": + var codeStream = newCodeStream("\x01\x02\x30") + check(codeStream.pc == 0) + codeStream.seek(1): + check(codeStream.pc == 1) + check(codeStream.next == Op.MUL) + check(codeStream.pc == 0) + check(codeStream.peek == Op.ADD) + + test "[] returns opcode": + let codeStream = newCodeStream("\x01\x02\x30") + check(codeStream[0] == Op.ADD) + check(codeStream[1] == Op.MUL) + check(codeStream[2] == Op.ADDRESS) + + test "isValidOpcode invalidates after PUSHXX": + var codeStream = newCodeStream("\x02\x60\x02\x04") + check(codeStream.isValidOpcode(0)) + check(codeStream.isValidOpcode(1)) + check(not codeStream.isValidOpcode(2)) + check(codeStream.isValidOpcode(3)) + check(not codeStream.isValidOpcode(4)) + + + test "isValidOpcode 0": + var codeStream = newCodeStream(@[2.byte, 3.byte, 0x72.byte].concat(repeat(4.byte, 32)).concat(@[5.byte])) + # valid: 0 - 2 :: 22 - 35 + # invalid: 3-21 (PUSH19) :: 36+ (too long) + check(codeStream.isValidOpcode(0)) + check(codeStream.isValidOpcode(1)) + check(codeStream.isValidOpcode(2)) + check(not codeStream.isValidOpcode(3)) + check(not codeStream.isValidOpcode(21)) + check(codeStream.isValidOpcode(22)) + check(codeStream.isValidOpcode(35)) + check(not codeStream.isValidOpcode(36)) + + + test "isValidOpcode 1": + let test = @[2.byte, 3.byte, 0x7d.byte].concat(repeat(4.byte, 32)).concat(@[5.byte, 0x7e.byte]).concat(repeat(4.byte, 35)).concat(@[1.byte, 0x61.byte, 1.byte, 1.byte, 1.byte]) + var codeStream = newCodeStream(test) + # valid: 0 - 2 :: 33 - 36 :: 68 - 73 :: 76 + # invalid: 3 - 32 (PUSH30) :: 37 - 67 (PUSH31) :: 74, 75 (PUSH2) :: 77+ (too long) + check(codeStream.isValidOpcode(0)) + check(codeStream.isValidOpcode(1)) + check(codeStream.isValidOpcode(2)) + check(not codeStream.isValidOpcode(3)) + check(not codeStream.isValidOpcode(32)) + check(codeStream.isValidOpcode(33)) + check(codeStream.isValidOpcode(36)) + check(not codeStream.isValidOpcode(37)) + check(not codeStream.isValidOpcode(67)) + check(codeStream.isValidOpcode(68)) + check(codeStream.isValidOpcode(71)) + check(codeStream.isValidOpcode(72)) + check(codeStream.isValidOpcode(73)) + check(not codeStream.isValidOpcode(74)) + check(not codeStream.isValidOpcode(75)) + check(codeStream.isValidOpcode(76)) + check(not codeStream.isValidOpcode(77)) + + + test "right number of bytes invalidates": + var codeStream = newCodeStream("\x02\x03\x60\x02\x02") + check(codeStream.isValidOpcode(0)) + check(codeStream.isValidOpcode(1)) + check(codeStream.isValidOpcode(2)) + check(not codeStream.isValidOpcode(3)) + check(codeStream.isValidOpcode(4)) + check(not codeStream.isValidOpcode(5)) diff --git a/tests/nim.cfg b/tests/nim.cfg new file mode 100644 index 000000000..149123bad --- /dev/null +++ b/tests/nim.cfg @@ -0,0 +1 @@ +-p:"../src"