From 5ad14f59b23d763cad712aab84aaa44cfac991b3 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Mon, 29 Jan 2018 19:40:22 +0200 Subject: [PATCH] Implement address/sha3 opcodes, start using nim-keccak-tiny --- nimbus.nimble | 3 +- src/account.nim | 12 +++ src/computation.nim | 11 +-- src/constants.nim | 162 ++++++++++++++++++++------------------- src/db/state_db.nim | 43 +++++++++++ src/logic/comparison.nim | 26 +++---- src/logic/context.nim | 158 ++++++++++++++++++++++++++++++++++++++ src/logic/sha3.nim | 12 +++ src/runner.nim | 57 +++++++++----- src/utils/address.nim | 16 +++- src/utils/bytes.nim | 11 +++ src/utils/keccak.nim | 10 +++ src/utils/padding.nim | 54 +++++++++++++ src/vm/memory.nim | 13 ++-- src/vm/message.nim | 4 +- 15 files changed, 463 insertions(+), 129 deletions(-) create mode 100644 src/account.nim create mode 100644 src/db/state_db.nim create mode 100644 src/logic/context.nim create mode 100644 src/logic/sha3.nim create mode 100644 src/utils/bytes.nim create mode 100644 src/utils/keccak.nim diff --git a/nimbus.nimble b/nimbus.nimble index 0e9c11975..012c55d93 100644 --- a/nimbus.nimble +++ b/nimbus.nimble @@ -7,4 +7,5 @@ description = "An Ethereum 2.0 Sharding Client for Resource-Restricted Devices license = "Apache License 2.0" skipDirs = @["tests"] -requires "nim >= 0.17.0" +requires "nim >= 0.17.0", "https://github.com/status-im/nim-keccak-tiny.git >= 0.1.0" + diff --git a/src/account.nim b/src/account.nim new file mode 100644 index 000000000..c0c5c250b --- /dev/null +++ b/src/account.nim @@ -0,0 +1,12 @@ +import + constants, errors, bigints + +type + Account* = ref object + nonce*: Int256 + balance*: Int256 + #storageRoot*: + #codeHash*: + +proc newAccount*(nonce: Int256 = 0.i256, balance: Int256 = 0.i256): Account = + Account(nonce: nonce, balance: balance) diff --git a/src/computation.nim b/src/computation.nim index f0e9286e5..83dc6cf53 100644 --- a/src/computation.nim +++ b/src/computation.nim @@ -89,14 +89,12 @@ method prepareChildMessage*( gas: Int256, to: cstring, value: Int256, - data: cstring, + data: seq[byte], code: cstring, options: MessageOptions = newMessageOptions()): Message = - - # ? kwargs.setdefault('sender', self.msg.storage_address) var childOptions = options - childOptions.depth = c.msg.depth + 1.int256 + childOptions.depth = c.msg.depth + 1.i256 result = newMessage( gas, c.msg.gasPrice, @@ -107,7 +105,6 @@ method prepareChildMessage*( code, childOptions) - # method extendMemory*(c: var BaseComputation, startPosition: Int256, size: Int256) = # Memory Management # @@ -150,7 +147,7 @@ macro generateChildBaseComputation*(t: typed, vmState: typed, childMsg: typed): if childMsg.isCreate: var child = `name`(`vmState`, `childMsg`) c = child.applyCreateMessage() - else: + else: var child = `name`(`vmState`, `childMsg`) c = child.applyMessage() c @@ -201,7 +198,7 @@ method getLogEntries*(c: BaseComputation): seq[(cstring, seq[Int256], cstring)] result = @[] else: result = @[] - + method getGasRefund*(c: BaseComputation): Int256 = if c.isError: result = 0.int256 diff --git a/src/constants.nim b/src/constants.nim index ff83e5823..87f9fb267 100644 --- a/src/constants.nim +++ b/src/constants.nim @@ -14,13 +14,13 @@ template i256*(i: int): Int256 = i.int256 # TODO -# We'll have a fast fixed int256, for now this works +# We'll have a fast fixed i256, for now this works proc `==`*(a: Int256, b: int): bool = - a == b.int256 + a == b.i256 proc `!=`*(a: Int256, b: int): bool = - a != b.int256 + a != b.i256 proc `^`*(base: int; exp: int): Int256 = let base = base.initBigInt @@ -33,26 +33,26 @@ proc `^`*(base: int; exp: int): Int256 = proc `^`*(left: Int256, right: int): Int256 = var value = right.initBigInt result = 1.initBigInt - var m = right.int256 - while value > 0.int256: + var m = right.i256 + while value > 0.i256: result = result * m - value -= 1.int256 + value -= 1.i256 proc `>`*(a: Int256, b: int): bool = - a > b.int256 + a > b.i256 proc `<`*(a: Int256, b: int): bool = - a < b.int256 + a < b.i256 proc `mod`*(a: Int256, b: int): Int256 = - a mod b.int256 + a mod b.i256 proc `div`*(a: Int256, b: int): Int256 = - a div b.int256 + a div b.i256 proc log256*(a: Int256): Int256 = # TODO - 2.int256 + 2.i256 proc setXLen[T](s: var seq[T]; newlen: Natural) = if s.isNil: @@ -75,7 +75,7 @@ mapOp(`or`) mapOp(`xor`) proc `abs`*(a: Int256): Int256 = - if a >= 0.int256: a else: -a + if a >= 0.i256: a else: -a proc `getInt`*(a: Int256): int = a.limbs[0].int @@ -94,89 +94,91 @@ let ZERO_HASH32* = repeat(cstring"\x00", 20) STACKDEPTHLIMIT* = 1024 - GAS_NULL* = 0.int256 - GAS_ZERO* = 0.int256 - GAS_BASE* = 2.int256 - GAS_VERY_LOW* = 3.int256 - GAS_LOW* = 5.int256 - GAS_MID* = 8.int256 - GAS_HIGH* = 10.int256 - GAS_EXT_CODE* = 20.int256 - GAS_BALANCE* = 20.int256 - GAS_SLOAD* = 50.int256 - GAS_JUMP_DEST* = 1.int256 - GAS_SSET* = 20_000.int256 - GAS_SRESET* = 5000.int256 + GAS_NULL* = 0.i256 + GAS_ZERO* = 0.i256 + GAS_BASE* = 2.i256 + GAS_VERY_LOW* = 3.i256 + GAS_LOW* = 5.i256 + GAS_MID* = 8.i256 + GAS_HIGH* = 10.i256 + GAS_EXT_CODE* = 20.i256 + GAS_BALANCE* = 20.i256 + GAS_SLOAD* = 50.i256 + GAS_JUMP_DEST* = 1.i256 + GAS_SSET* = 20_000.i256 + GAS_SRESET* = 5000.i256 - REFUNDS_CLEAR* = 15_000.int256 + REFUNDS_CLEAR* = 15_000.i256 - GAS_SELF_DESTRUCT* = 0.int256 - GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.int256 - GAS_CREATE* = 32_000.int256 - GAS_CALL* = 40.int256 - GASCALLVALUE = 9_000.int256 - GAS_CALL_STIPEND* = 2_300.int256 - GAS_NEW_ACCOUNT* = 25_000.int256 + GAS_SELF_DESTRUCT* = 0.i256 + GAS_SELF_DESTRUCT_NEW_ACCOUNT* = 25_000.i256 + GAS_CREATE* = 32_000.i256 + GAS_CALL* = 40.i256 + GASCALLVALUE = 9_000.i256 + GAS_CALL_STIPEND* = 2_300.i256 + GAS_NEW_ACCOUNT* = 25_000.i256 - GAS_EXP* = 10.int256 - GAS_EXP_BYTE* = 10.int256 - GAS_MEMORY* = 3.int256 - GAS_TX_CREATE* = 32_000.int256 - GAS_TX_DATA_ZERO* = 4.int256 - GAS_TX_DATA_NON_ZERO* = 68.int256 - GAS_TX* = 21_000.int256 - GAS_LOG* = 375.int256 - GAS_LOG_DATA* = 8.int256 - GAS_LOG_TOPIC* = 375.int256 - GAS_SHA3* = 30.int256 - GAS_SHA3_WORD* = 6.int256 - GAS_COPY* = 3.int256 - GAS_BLOCK_HASH* = 20.int256 - GAS_CODE_DEPOSIT* = 200.int256 - GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512.int256 - GAS_SHA256* = 60.int256 - GAS_SHA256WORD* = 12.int256 - GAS_RIP_EMD160* = 600.int256 - GAS_RIP_EMD160WORD* = 120.int256 - GAS_IDENTITY* = 15.int256 + GAS_COST_BALANCE* = 400.i256 + + GAS_EXP* = 10.i256 + GAS_EXP_BYTE* = 10.i256 + GAS_MEMORY* = 3.i256 + GAS_TX_CREATE* = 32_000.i256 + GAS_TX_DATA_ZERO* = 4.i256 + GAS_TX_DATA_NON_ZERO* = 68.i256 + GAS_TX* = 21_000.i256 + GAS_LOG* = 375.i256 + GAS_LOG_DATA* = 8.i256 + GAS_LOG_TOPIC* = 375.i256 + GAS_SHA3* = 30.i256 + GAS_SHA3_WORD* = 6.i256 + GAS_COPY* = 3.i256 + GAS_BLOCK_HASH* = 20.i256 + GAS_CODE_DEPOSIT* = 200.i256 + GAS_MEMORY_QUADRATIC_DENOMINATOR* = 512.i256 + GAS_SHA256* = 60.i256 + GAS_SHA256WORD* = 12.i256 + GAS_RIP_EMD160* = 600.i256 + GAS_RIP_EMD160WORD* = 120.i256 + GAS_IDENTITY* = 15.i256 GAS_IDENTITY_WORD* = 3 - GAS_ECRECOVER* = 3_000.int256 - GAS_ECADD* = 500.int256 - GAS_ECMUL* = 40_000.int256 - GAS_ECPAIRING_BASE* = 100_000.int256 - GAS_ECPAIRING_PER_POINT* = 80_000.int256 - GAS_LIMIT_EMA_DENOMINATOR* = 1_024.int256 - GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024.int256 + GAS_ECRECOVER* = 3_000.i256 + GAS_ECADD* = 500.i256 + GAS_ECMUL* = 40_000.i256 + GAS_ECPAIRING_BASE* = 100_000.i256 + GAS_ECPAIRING_PER_POINT* = 80_000.i256 + GAS_LIMIT_EMA_DENOMINATOR* = 1_024.i256 + GAS_LIMIT_ADJUSTMENT_FACTOR* = 1_024.i256 GAS_LIMIT_MAXIMUM*: Int256 = 2 ^ 63 - 1 - GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3.int256 - GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.int256 + GAS_LIMIT_USAGE_ADJUSTMENT_NUMERATOR* = 3.i256 + GAS_LIMIT_USAGE_ADJUSTMENT_DENOMINATOR* = 2.i256 - DIFFICULTY_ADJUSTMENT_DENOMINATOR* = 2_048.int256 - DIFFICULTY_MINIMUM* = 131_072.int256 + DIFFICULTY_ADJUSTMENT_DENOMINATOR* = 2_048.i256 + DIFFICULTY_MINIMUM* = 131_072.i256 - BOMB_EXPONENTIAL_PERIOD* = 100_000.int256 - BOMB_EXPONENTIAL_FREE_PERIODS* = 2.int256 + BOMB_EXPONENTIAL_PERIOD* = 100_000.i256 + BOMB_EXPONENTIAL_FREE_PERIODS* = 2.i256 - BLOCK_REWARD* = 5.int256 * 2.int256 # denoms.ether + BLOCK_REWARD* = 5.i256 * 2.i256 # denoms.ether - UNCLE_DEPTH_PENALTY_FACTOR* = 8.int256 + UNCLE_DEPTH_PENALTY_FACTOR* = 8.i256 - MAX_UNCLE_DEPTH* = 6.int256 - MAX_UNCLES* = 2.int256 + MAX_UNCLE_DEPTH* = 6.i256 + MAX_UNCLES* = 2.i256 SECPK1_P*: Int256 = 2 ^ 256 - 2 ^ 32 - 977 SECPK1_N*: Int256 = "115792089237316195423570985008687907852837564279074904382605163141518161494337".initBigInt - SECPK1_A* = 0.int256 - SECPK1_B* = 7.int256 - SECPK1_Gx* = 0.int256 - SECPK1_Gy* = 0.int256 + SECPK1_A* = 0.i256 + SECPK1_B* = 7.i256 + SECPK1_Gx* = 0.i256 + 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" - GENESIS_BLOCK_NUMBER* = 0.int256 - GENESIS_DIFFICULTY* = 131_072.int256 - GENESIS_GAS_LIMIT* = 3_141_592.int256 + 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" @@ -186,7 +188,7 @@ let 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!" - GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20.int256 + GAS_MOD_EXP_QUADRATIC_DENOMINATOR* = 20.i256 - MAX_PREV_HEADER_DEPTH* = 256.int256 + MAX_PREV_HEADER_DEPTH* = 256.i256 diff --git a/src/db/state_db.nim b/src/db/state_db.nim new file mode 100644 index 000000000..743938a02 --- /dev/null +++ b/src/db/state_db.nim @@ -0,0 +1,43 @@ +import + strformat, tables, + ../constants, ../errors, ../validation, ../account, ../logging, bigints + +type + AccountStateDB* = ref object + db*: Table[cstring, Int256] + +proc newAccountStateDB*(db: Table[cstring, 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 = + # let rlpAccount = db.trie[address] + # if not rlpAccount.isNil: + # account = rlp.decode[Account](rlpAccount) + # account.mutable = true + # else: + # account = newAccount() + result = newAccount() # TODO + +proc setAccount(db: AccountStateDB, address: cstring, account: Account) = + # db.trie[address] = rlp.encode[Account](account) + discard # TODO + +# public + +proc getBalance*(db: AccountStateDB, address: cstring): Int256 = + validateCanonicalAddress(address, title="Storage Address") + let account = db.getAccount(address) + account.balance + +proc setBalance*(db: var AccountStateDB, address: cstring, 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) = + db.setBalance(address, db.getBalance(address) + delta) + diff --git a/src/logic/comparison.nim b/src/logic/comparison.nim index db0caf084..e8e6cb73c 100644 --- a/src/logic/comparison.nim +++ b/src/logic/comparison.nim @@ -18,22 +18,22 @@ quasiBoolean(orOp, `or`, nonzero=true) # Bitwise Or quasiBoolean(xorOp, `xor`, nonzero=true) # Bitwise XOr -# proc iszero*(computation: var BaseComputation) = -# var value = computation.stack.popInt() +proc iszero*(computation: var BaseComputation) = + var value = computation.stack.popInt() -# var res = if value == 0: 1.Int256 else: 0.Int256 -# pushRes() + var res = if value == 0: 1.i256 else: 0.i256 + pushRes() -# proc notOp*(computation: var BaseComputation) = -# var value = computation.stack.popInt() +proc notOp*(computation: var BaseComputation) = + var value = computation.stack.popInt() -# var res = constants.UINT_256_MAX - value -# pushRes() + var res = constants.UINT_256_MAX - value + pushRes() -# proc byteOp*(computation: var BaseComputation) = -# # Bitwise And -# var (position, value) = computation.stack.popInt(2) +proc byteOp*(computation: var BaseComputation) = + # Bitwise And + var (position, value) = computation.stack.popInt(2) -# var res = if position >= 32.Int256: 0.Int256 else: (value div (256.Int256 ^ (31.Int256 - position))) mod 256 -# pushRes() + var res = if position >= 32.i256: 0.i256 else: (value div (256.i256.pow(31.i256 - position))) mod 256 + pushRes() diff --git a/src/logic/context.nim b/src/logic/context.nim new file mode 100644 index 000000000..d8f5fd2e4 --- /dev/null +++ b/src/logic/context.nim @@ -0,0 +1,158 @@ +import + ../constants, ../utils_numeric, ../computation, ../vm_state, ../account, ../db/state_db, ../validation, + .. / vm / [stack, message], .. / utils / [address, padding, bytes] + +proc balance*(computation: var BaseComputation) = + let address = forceBytesToAddress(computation.stack.popBinary) + var balance: Int256 + # TODO computation.vmState.stateDB(read_only=True): + # balance = db.getBalance(address) + # computation.stack.push(balance) + +proc origin*(computation: var BaseComputation) = + computation.stack.push(computation.msg.origin) + +proc address*(computation: var BaseComputation) = + computation.stack.push(computation.msg.storageAddress) + +proc caller*(computation: var BaseComputation) = + computation.stack.push(computation.msg.sender) + + +proc callValue*(computation: var BaseComputation) = + computation.stack.push(computation.msg.value) + +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 normalizedValue = paddedValue.lStrip(0.char) + computation.stack.push(normalizedValue) + + +proc callDataSize*(computation: var BaseComputation) = + let size = computation.msg.data.len + computation.stack.push(size) + +# def calldatacopy(computation): +# ( +# mem_start_position, +# calldata_start_position, +# size, +# ) = computation.stack.pop(num_items=3, type_hint=constants.UINT256) + +# computation.extend_memory(mem_start_position, size) + +# word_count = ceil32(size) // 32 +# copy_gas_cost = word_count * constants.GAS_COPY + +# computation.gas_meter.consume_gas(copy_gas_cost, reason="CALLDATACOPY fee") + +# value = computation.msg.data[calldata_start_position: calldata_start_position + size] +# padded_value = pad_right(value, size, b'\x00') + +# computation.memory.write(mem_start_position, size, padded_value) + + +# def codesize(computation): +# size = len(computation.code) +# computation.stack.push(size) + + +# def codecopy(computation): +# ( +# mem_start_position, +# code_start_position, +# size, +# ) = computation.stack.pop(num_items=3, type_hint=constants.UINT256) + +# computation.extend_memory(mem_start_position, size) + +# word_count = ceil32(size) // 32 +# copy_gas_cost = constants.GAS_COPY * word_count + +# computation.gas_meter.consume_gas( +# copy_gas_cost, +# reason="CODECOPY: word gas cost", +# ) + +# with computation.code.seek(code_start_position): +# code_bytes = computation.code.read(size) + +# padded_code_bytes = pad_right(code_bytes, size, b'\x00') + +# computation.memory.write(mem_start_position, size, padded_code_bytes) + + +# def gasprice(computation): +# computation.stack.push(computation.msg.gas_price) + + +# def extcodesize(computation): +# account = force_bytes_to_address(computation.stack.pop(type_hint=constants.BYTES)) +# with computation.vm_state.state_db(read_only=True) as state_db: +# code_size = len(state_db.get_code(account)) + +# computation.stack.push(code_size) + + +# def extcodecopy(computation): +# account = force_bytes_to_address(computation.stack.pop(type_hint=constants.BYTES)) +# ( +# mem_start_position, +# code_start_position, +# size, +# ) = computation.stack.pop(num_items=3, type_hint=constants.UINT256) + +# computation.extend_memory(mem_start_position, size) + +# word_count = ceil32(size) // 32 +# copy_gas_cost = constants.GAS_COPY * word_count + +# computation.gas_meter.consume_gas( +# copy_gas_cost, +# reason='EXTCODECOPY: word gas cost', +# ) + +# with computation.vm_state.state_db(read_only=True) as state_db: +# code = state_db.get_code(account) +# code_bytes = code[code_start_position:code_start_position + size] +# padded_code_bytes = pad_right(code_bytes, size, b'\x00') + +# computation.memory.write(mem_start_position, size, padded_code_bytes) + + +# def returndatasize(computation): +# size = len(computation.return_data) +# computation.stack.push(size) + + +# def returndatacopy(computation): +# ( +# mem_start_position, +# returndata_start_position, +# size, +# ) = computation.stack.pop(num_items=3, type_hint=constants.UINT256) + +# if returndata_start_position + size > len(computation.return_data): +# raise OutOfBoundsRead( +# "Return data length is not sufficient to satisfy request. Asked " +# "for data from index {0} to {1}. Return data is {2} bytes in " +# "length.".format( +# returndata_start_position, +# returndata_start_position + size, +# len(computation.return_data), +# ) +# ) + +# computation.extend_memory(mem_start_position, size) + +# word_count = ceil32(size) // 32 +# copy_gas_cost = word_count * constants.GAS_COPY + +# computation.gas_meter.consume_gas(copy_gas_cost, reason="RETURNDATACOPY fee") + +# value = computation.return_data[returndata_start_position: returndata_start_position + size] + +# computation.memory.write(mem_start_position, size, value) diff --git a/src/logic/sha3.nim b/src/logic/sha3.nim new file mode 100644 index 000000000..3b8b8798a --- /dev/null +++ b/src/logic/sha3.nim @@ -0,0 +1,12 @@ +import + ../constants, ../utils_numeric, .. / utils / [keccak, bytes], .. / vm / [stack, memory, gas_meter], ../computation, helpers, bigints + +proc sha3op*(computation: var BaseComputation) = + let (startPosition, size) = computation.stack.popInt(2) + computation.extendMemory(startPosition, size) + let sha3Bytes = computation.memory.read(startPosition, size) + let wordCount = sha3Bytes.len.i256.ceil32 div 32 + let gasCost = constants.GAS_SHA3_WORD * wordCount + computation.gasMeter.consumeGas(gasCost, reason="SHA3: word gas cost") + var res = keccak(cstring"") + pushRes() diff --git a/src/runner.nim b/src/runner.nim index f5a84f04a..502ed0e6b 100644 --- a/src/runner.nim +++ b/src/runner.nim @@ -2,29 +2,49 @@ import strformat, strutils, tables, macros, constants, bigints, errors, logging, vm_state, vm / [gas_meter, stack, code_stream, memory, message, value, gas_costs], db / chain, computation, opcode, opcode_values, utils / [header, address], - logic / [arithmetic, comparison] + logic / [arithmetic, comparison, sha3, context] var opcodes = initOpcodes: # arithmetic - Op.Add: GAS_VERY_LOW add - Op.Mul: GAS_LOW mul - Op.Sub: GAS_VERY_LOW sub - Op.Div: GAS_LOW divide - Op.SDiv: GAS_LOW sdiv - Op.Mod: GAS_LOW modulo - Op.SMod: GAS_LOW smod - Op.AddMod: GAS_MID addmod - Op.MulMod: GAS_MID mulmod - Op.Exp: expGasCost arithmetic.exp - Op.SignExtend: GAS_LOW signextend + Op.Add: GAS_VERY_LOW add + Op.Mul: GAS_LOW mul + Op.Sub: GAS_VERY_LOW sub + Op.Div: GAS_LOW divide + Op.SDiv: GAS_LOW sdiv + Op.Mod: GAS_LOW modulo + Op.SMod: GAS_LOW smod + Op.AddMod: GAS_MID addmod + Op.MulMod: GAS_MID mulmod + Op.Exp: expGasCost arithmetic.exp + Op.SignExtend: GAS_LOW signextend # comparison - Op.Lt: GAS_VERY_LOW lt - Op.Gt: GAS_VERY_LOW gt - Op.SLt: GAS_VERY_LOW slt - Op.SGt: GAS_VERY_LOW sgt - Op.Eq: GAS_VERY_LOW eq + Op.Lt: GAS_VERY_LOW lt + Op.Gt: GAS_VERY_LOW gt + Op.SLt: GAS_VERY_LOW slt + Op.SGt: GAS_VERY_LOW sgt + Op.Eq: GAS_VERY_LOW eq + Op.IsZero: GAS_VERY_LOW iszero + Op.And: GAS_VERY_LOW andOp + Op.Or: GAS_VERY_LOW orOp + Op.Xor: GAS_VERY_LOW xorOp + Op.Not: GAS_VERY_LOW notOp + Op.Byte: GAS_VERY_LOW byteOp + + + # sha3 + Op.SHA3: GAS_SHA3 sha3op + + + # context + Op.Address: GAS_BASE context.address + Op.Balance: GAS_COST_BALANCE balance + Op.Origin: GAS_BASE origin + Op.Caller: GAS_BASE caller + Op.CallValue: GAS_BASE callValue + Op.CallDataLoad: GAS_VERY_LOW callDataLoad + Op.CallDataSize: GAS_BASE callDataSize var mem = newMemory(pow(1024.int256, 2)) @@ -32,6 +52,7 @@ var to = toCanonicalAddress(cstring"0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6") var sender = toCanonicalAddress(cstring"0xcd1722f3947def4cf144679da39c4c32bdc35681") var code = cstring"" +var data: seq[byte] = @[] var msg = newMessage( 25.int256, @@ -39,7 +60,7 @@ var msg = newMessage( to, sender, 0.int256, - cstring"", + data, code, MessageOptions(depth: 1.int256)) diff --git a/src/utils/address.nim b/src/utils/address.nim index eb2b56799..3151a9ae5 100644 --- a/src/utils/address.nim +++ b/src/utils/address.nim @@ -1,6 +1,6 @@ -import strformat, strutils, encodings +import strformat, strutils, encodings, keccak, padding -proc toText(c: cstring): string = +proc toText*(c: cstring): string = ($c).convert(destEncoding="iso-8859-1") proc toCanonicalAddress*(address: string): string = @@ -8,9 +8,19 @@ proc toCanonicalAddress*(address: string): string = address # address.toNormalizedAddress.decodeHex -proc toCanonicalAddress*(address: cstring): string = +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: diff --git a/src/utils/bytes.nim b/src/utils/bytes.nim new file mode 100644 index 000000000..9a2954911 --- /dev/null +++ b/src/utils/bytes.nim @@ -0,0 +1,11 @@ +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 diff --git a/src/utils/keccak.nim b/src/utils/keccak.nim new file mode 100644 index 000000000..06fc6cf15 --- /dev/null +++ b/src/utils/keccak.nim @@ -0,0 +1,10 @@ +import + keccak_tiny, strutils + +template keccak*(value: string): cstring = + cstring($keccak_256(value)) + +template keccak*(value: cstring): cstring = + ($value).keccak + + \ No newline at end of file diff --git a/src/utils/padding.nim b/src/utils/padding.nim index 87eae2866..c9813d33c 100644 --- a/src/utils/padding.nim +++ b/src/utils/padding.nim @@ -15,6 +15,16 @@ proc pad(value: cstring, size: int, with: cstring, left: bool): cstring = 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 template padLeft*(value: cstring, size: int, with: cstring): cstring = pad(value, size, with, true) @@ -34,3 +44,47 @@ template pad32*(value: cstring): cstring = 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) diff --git a/src/vm/memory.nim b/src/vm/memory.nim index 1b50b8f53..8c17721fe 100644 --- a/src/vm/memory.nim +++ b/src/vm/memory.nim @@ -1,11 +1,11 @@ import sequtils, bigints, - ../constants, ../errors, ../logging, ../validation, ../utils_numeric + ../constants, ../errors, ../logging, ../validation, ../utils_numeric, ../utils/bytes type Memory* = ref object logger*: Logger - bytes*: seq[byte] + bytes*: seq[byte] proc newMemory*: Memory = new(result) @@ -28,8 +28,11 @@ proc newMemory*(size: Int256): Memory = result = newMemory() result.extend(0.int256, size) -proc read*(self: var Memory; startPosition: Int256; size: Int256): cstring = - return cstring"" +proc read*(memory: var Memory, startPosition: Int256, size: Int256): seq[byte] = + result = memory.bytes[startPosition.getInt ..< (startPosition + size).getInt] -proc write*(self: var Memory, startPosition: Int256, size: Int256, value: cstring) = +proc write*(memory: var Memory, startPosition: Int256, size: Int256, value: seq[byte]) = echo value + +template write*(memory: var Memory, startPosition: Int256, size: Int256, value: cstring) = + memory.write(startPosition, size, value.toBytes) diff --git a/src/vm/message.nim b/src/vm/message.nim index 0ae67eb69..3ec8d7eea 100644 --- a/src/vm/message.nim +++ b/src/vm/message.nim @@ -22,7 +22,7 @@ type to*: cstring sender*: cstring value*: Int256 - data*: cstring + data*: seq[byte] code*: cstring internalOrigin: cstring internalCodeAddress: cstring @@ -71,7 +71,7 @@ proc newMessage*( to: cstring, sender: cstring, value: Int256, - data: cstring, + data: seq[byte], code: cstring, options: MessageOptions = newMessageOptions()): Message =