diff --git a/ethereum/blocks.py b/ethereum/blocks.py index fa3cba371..587130fbf 100644 --- a/ethereum/blocks.py +++ b/ethereum/blocks.py @@ -964,7 +964,7 @@ def reset_storage(self, address): for k in self.caches[CACHE_KEY]: self.set_and_journal(CACHE_KEY, k, 0) - def get_storage_data(self, address, index): + def get_storage_bytes(self, address, index): """Get a specific item in the storage of an account. :param address: the address of the account (binary or hex string) @@ -980,9 +980,16 @@ def get_storage_data(self, address, index): key = utils.zpad(utils.coerce_to_bytes(index), 32) storage = self.get_storage(address).get(key) if storage: - return rlp.decode(storage, big_endian_int) + return rlp.decode(storage) + else: + return '' + + def get_storage_data(self, address, index): + bytez = self.get_storage_bytes(address, index) + if len(bytez) >= 32: + return big_endian_to_int(bytez[-32:]) else: - return 0 + return big_endian_to_int(bytez) * (1 << (8 * (32 - len(bytes)))) def set_storage_data(self, address, index, value): """Set a specific item in the storage of an account. diff --git a/ethereum/opcodes.py b/ethereum/opcodes.py index 2d275885e..d5f168f3f 100644 --- a/ethereum/opcodes.py +++ b/ethereum/opcodes.py @@ -61,6 +61,9 @@ 0xa2: ['LOG2', 4, 0, 1125], 0xa3: ['LOG3', 5, 0, 1500], 0xa4: ['LOG4', 6, 0, 1875], + 0xe1: ['SLOADBYTES', 3, 0, 50], + 0xe2: ['SSTOREBYTES', 3, 0, 0], + 0xe3: ['SSIZE', 1, 1, 50], 0xf0: ['CREATE', 3, 1, 32000], 0xf1: ['CALL', 7, 1, 40], 0xf2: ['CALLCODE', 7, 1, 40], @@ -85,10 +88,7 @@ GDEFAULT = 1 GMEMORY = 3 GQUADRATICMEMDENOM = 512 # 1 gas per 512 quadwords -GSTORAGEREFUND = 15000 -GSTORAGEKILL = 5000 -GSTORAGEMOD = 5000 -GSTORAGEADD = 20000 + GEXPONENTBYTE = 10 # cost of EXP exponent per byte GCOPY = 3 # cost to copy one 32 byte word GCONTRACTBYTE = 200 # one byte of code in contract creation @@ -111,3 +111,15 @@ GCALLNEWACCOUNT = 25000 GSUICIDEREFUND = 24000 + +GSTORAGEBASE = 2500 +GSTORAGEBYTESTORAGE = 250 +GSTORAGEBYTECHANGE = 40 +GSTORAGEMIN = 2500 +GSSIZE = 50 +GSLOADBYTES = 50 + +GSTORAGEREFUND = 15000 +GSTORAGEKILL = 5000 +GSTORAGEMOD = 5000 +GSTORAGEADD = 20000 diff --git a/ethereum/processblock.py b/ethereum/processblock.py index 2f5d0bc1b..f7fb5a20f 100644 --- a/ethereum/processblock.py +++ b/ethereum/processblock.py @@ -230,6 +230,7 @@ def __init__(self, block, tx): self.set_nonce = block.set_nonce self.set_storage_data = block.set_storage_data self.get_storage_data = block.get_storage_data + self.get_storage_bytes = block.get_storage_bytes self.log_storage = lambda x: block.account_to_dict(x)['storage'] self.add_suicide = lambda x: block.suicides.append(x) self.add_refund = lambda x: \ diff --git a/ethereum/vm.py b/ethereum/vm.py index e8af89b83..5d99ea589 100644 --- a/ethereum/vm.py +++ b/ethereum/vm.py @@ -12,7 +12,7 @@ import time from ethereum.slogging import get_logger from rlp.utils import encode_hex, ascii_chr -from ethereum.utils import to_string +from ethereum.utils import to_string, encode_int, zpad log_log = get_logger('eth.vm.log') log_vm_exit = get_logger('eth.vm.exit') @@ -431,7 +431,7 @@ def vm_execute(ext, msg, code): return vm_exception('OUT OF GAS') compustate.gas -= gascost ext.add_refund(refund) # adds neg gascost as a refund if below zero - ext.set_storage_data(msg.to, s0, s1) + ext.set_storage_data(msg.to, s0, zpad(encode_int(s1), 32)) elif op == 'JUMP': compustate.pc = stk.pop() opnew = processed_code[compustate.pc][0] if \ @@ -452,6 +452,37 @@ def vm_execute(ext, msg, code): stk.append(len(mem)) elif op == 'GAS': stk.append(compustate.gas) # AFTER subtracting cost 1 + elif 0xe0 <= opcode < 0xef: + if not ext.post_metropolis_hardfork(): + return vm_exception('OPCODE RANGE INACTIVE', opcode=opcode) + if op == 'SLOADBYTES': + key, mstart, msize = stk.pop(), stk.pop(), stk.pop() + bytez = map(ord, ext.get_storage_bytes(msg.to, key)) + if not mem_extend(mem, compustate, op, mstart, min(msize, mstart + len(bytez))): + return vm_exception('OOG EXTENDING MEMORY') + for i in range(min(msize, len(bytez))): + mem[mstart + i] = bytez[i] + stk.append(ext.get_storage_data(msg.to, stk.pop())) + elif op == 'SSTOREBYTES': + key, mstart, msize = stk.pop(), stk.pop(), stk.pop() + if not mem_extend(mem, compustate, op, mstart, min(msize, mstart + len(bytez))): + return vm_exception('OOG EXTENDING MEMORY') + prev_adjbyte_count = len(ext.get_storage_data(msg.to, key)) + if prev_adjbyte_count >= 0: + prev_adjbyte_count += 32 + post_adjbyte_count = msize + (32 if msize else 0) + gas_cost = opcodes.GSTORAGEBASE + opcodes.GSTORAGEBYTESTORAGE * \ + (post_adjbyte_count - prev_adjbyte_count) + opcodes.GSTORAGEBYTECHANGE * post_adjbyte_count + gas_payment = max(opcodes.GSTORAGEMIN, gas_cost) + refund = gas_payment - gas_cost + if compustate.gas < gas_payment: + return vm_exception('OUT OF GAS') + compustate.gas -= gas_payment + data = ''.join(map(chr, mem[mstart: mstart + msize])) + ext.set_storage_data(msg.to, data) + ext.add_refund(refund) + elif op == 'SSIZE': + stk.append(len(ext.get_storage_bytes(msg.to, stk.pop()))) elif op[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc += pushnum