def test_ecrecover(): s = tester.state() c = s.abi_contract(ecrecover_code) priv = utils.sha3('some big long brainwallet password') pub = bitcoin.privtopub(priv) msghash = utils.sha3('the quick brown fox jumps over the lazy dog') pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msghash, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) V = utils.safe_ord(signature[64]) + 27 R = big_endian_to_int(signature[0:32]) S = big_endian_to_int(signature[32:64]) assert bitcoin.ecdsa_raw_verify(msghash, (V, R, S), pub) addr = utils.big_endian_to_int(utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) assert utils.big_endian_to_int(utils.privtoaddr(priv)) == addr result = c.test_ecrecover(utils.big_endian_to_int(msghash), V, R, S) assert result == addr
def preprocess_vmcode(code): o = [] jumps = {} # Round 1: Locate all JUMP, JUMPI, JUMPDEST, STOP, RETURN, INVALID locs opcodez = copy.deepcopy([opcodes.get(safe_ord(c), ['INVALID', 0, 0, 1]) + [safe_ord(c)] for c in code]) for i, o in enumerate(opcodez): if o[0] in filter1: jumps[i] = True chunks = {} chunks["code"] = [safe_ord(x) for x in code] c = [] h, reqh, gascost = 0, 0, 0 i = 0 laststart, lastjumpable = 0, 0 while i < len(opcodez): o = opcodez[i] # [op, ins, outs, gas, opcode, pos, push?] o.append(laststart + i) c.append(o) reqh = max(reqh, h + o[1]) h += o[1] - o[2] gascost += o[3] if i in jumps: chunks[laststart] = {"reqh": reqh, "deltah": -h, "gascost": gascost, "opdata": c, "ops": list(enumerate([x[4] for x in c])), "start": laststart, "jumpable": lastjumpable, "end": i + 1} c = [] laststart = i + 1 lastjumpable = 1 if o[0] == 'JUMPDEST' else 0 h, reqh, gascost = 0, 0, 0 if 0x60 <= o[4] < 0x80: v = 0 for j in range(i + 1, i + o[4] - 0x5e): v = (v << 8) + opcodez[j][4] o.append(v) i += o[4] - 0x5f i += 1 chunks[laststart] = {"reqh": reqh, "deltah": -h, "gascost": gascost, "opdata": c, "ops": list(enumerate([x[4] for x in c])), "start": laststart, "jumpable": lastjumpable, "end": i + 1} return chunks
def proc_sha256(ext, msg): # print('sha256 proc', msg.gas) OP_GAS = opcodes.GSHA256BASE + \ (utils.ceil32(msg.data.size) // 32) * opcodes.GSHA256WORD gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] d = msg.data.extract_all() o = [safe_ord(x) for x in bitcoin.bin_sha256(d)] return 1, msg.gas - gas_cost, o
def proc_ripemd160(ext, msg): # print('ripemd160 proc', msg.gas) OP_GAS = opcodes.GRIPEMD160BASE + \ (utils.ceil32(msg.data.size) // 32) * opcodes.GRIPEMD160WORD gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] d = msg.data.extract_all() o = [0] * 12 + [safe_ord(x) for x in bitcoin.ripemd.RIPEMD160(d).digest()] return 1, msg.gas - gas_cost, o
def sign_eth(rawhash, priv): pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) v = utils.safe_ord(signature[64]) + 27 r = utils.big_endian_to_int(signature[0:32]) s = utils.big_endian_to_int(signature[32:64]) return (v, r, s)
def OP_EXTCODECOPY(): addr, s1, s2, s3 = stk.pop(), stk.pop(), stk.pop(), stk.pop() extcode = block.get_code(utils.coerce_addr_to_hex(addr)) or '' if not mem_extend(mem, msgtop.compustate, '', s1, s3): return drop(OUT_OF_GAS) for i in range(s3): if s2 + i < len(extcode): mem[s1 + i] = safe_ord(extcode[s2 + i]) else: mem[s1 + i] = 0
def verify(msghash: bytes, signature, public_key): """Verify that data has been signed with Etheruem private key. :param signature: :return: """ V = utils.safe_ord(signature[64]) + 27 R = big_endian_to_int(signature[0:32]) S = big_endian_to_int(signature[32:64]) return bitcoin.ecdsa_raw_verify(msghash, (V, R, S), public_key)
def call_casper(state, fun, args=[], gas=1000000, value=0): ct = get_casper_ct() abidata = vm.CallData([utils.safe_ord(x) for x in ct.encode(fun, args)]) msg = vm.Message(casper_config['METROPOLIS_ENTRY_POINT'], casper_config['CASPER_ADDR'], value, gas, abidata) o = apply_const_message(state, msg) if o: # print 'cc', fun, args, ct.decode(fun, o)[0] return ct.decode(fun, o)[0] else: return None
def proc_ripemd160(ext, msg): # print('ripemd160 proc', msg.gas) OP_GAS = opcodes.GRIPEMD160BASE + \ (utils.ceil32(msg.data.size) // 32) * opcodes.GRIPEMD160WORD gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] d = msg.data.extract_all() o = [0] * 12 + [ safe_ord(x) for x in pybitcoin.ripemd.RIPEMD160(d).digest() ] return 1, msg.gas - gas_cost, o
def ec_add(data: List[int]) -> List[int]: bytes_data = bytearray(data) x1 = extract32(bytes_data, 0) y1 = extract32(bytes_data, 32) x2 = extract32(bytes_data, 64) y2 = extract32(bytes_data, 96) p1 = validate_point(x1, y1) p2 = validate_point(x2, y2) if p1 is False or p2 is False: return [] o = bn128.normalize(bn128.add(p1, p2)) return [safe_ord(x) for x in (encode_int32(o[0].n) + encode_int32(o[1].n))]
def preprocess_code(code): o = 0 i = 0 pushcache = {} code = code + b'\x00' * 32 while i < len(code) - 32: codebyte = safe_ord(code[i]) if codebyte == 0x5b: o |= 1 << i if 0x60 <= codebyte <= 0x7f: pushcache[i] = utils.big_endian_to_int(code[i + 1:i + codebyte - 0x5e]) i += codebyte - 0x5e else: i += 1 return o, pushcache
def sign(data: bytes, private_key_seed_ascii: str, hash_function=bitcoin.bin_sha256): """Sign data using Ethereum private key. :param private_key_seed_ascii: Private key seed as ASCII string """ msghash = hash_function(data) priv = utils.sha3(private_key_seed_ascii) pub = bitcoin.privtopub(priv) # Based on ethereum/tesrt_contracts.py test_ecrecover pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize(pk.ecdsa_sign_recoverable(msghash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) v = utils.safe_ord(signature[64]) + 27 r_bytes = signature[0:32] r = big_endian_to_int(r_bytes) s_bytes = signature[32:64] s = big_endian_to_int(s_bytes) # Make sure we use bytes data and zero padding stays # good across different systems r_hex = binascii.hexlify(r_bytes).decode("ascii") s_hex = binascii.hexlify(s_bytes).decode("ascii") # Convert to Etheruem address format addr = utils.big_endian_to_int(utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) # Return various bits about signing so it's easier to debug return { "signature": signature, "v": v, "r": r, "s": s, "r_bytes": r_bytes, "s_bytes": s_bytes, "r_hex": "0x" + r_hex, "s_hex": "0x" + s_hex, "address_bitcoin": addr, "address_ethereum": get_ethereum_address_from_private_key(priv), "public_key": pub, "hash": msghash, "payload": binascii.hexlify(bytes([v] + list(r_bytes)+ list(s_bytes,))) }
def proc_ecrecover(ext, msg): #print('ecrecover proc', msg.gas) OP_GAS = opcodes.GECRECOVER gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] b = [0] * 32 msg.data.extract_copy(b, 0, 0, 32) h = ''.join([ascii_chr(x) for x in b]) v = msg.data.extract32(32) r = msg.data.extract32(64) s = msg.data.extract32(96) if r >= bitcoin.N or s >= bitcoin.P or v < 27 or v > 28: return 1, msg.gas - opcodes.GECRECOVER, [0] * 32 pub = bitcoin.encode_pubkey(bitcoin.ecdsa_raw_recover(h, (v, r, s)), 'bin') o = [0] * 12 + [safe_ord(x) for x in utils.sha3(pub[1:])[-20:]] return 1, msg.gas - gas_cost, o
def decompress(data): o = b'' i = 0 while i < len(data): if int_to_bytes(data[i]) == b'\xfe': if i == len(data) - 1: raise Exception("Invalid encoding, \\xfe at end") elif int_to_bytes(data[i + 1]) == b'\x00': o += b'\xfe' elif int_to_bytes(data[i + 1]) == b'\x01': o += NULLSHA3 else: o += b'\x00' * safe_ord(data[i + 1]) i += 1 else: o += int_to_bytes(data[i]) i += 1 return o
def proc_ecmul(ext, msg): if not ext.post_metropolis_hardfork(): return 1, msg.gas, [] import py_ecc.optimized_bn128 as bn128 FQ = bn128.FQ print('ecmul proc', msg.gas) if msg.gas < opcodes.GECMUL: return 0, 0, [] x = msg.data.extract32(0) y = msg.data.extract32(32) m = msg.data.extract32(64) p = validate_point(x, y) if p is False: return 0, 0, [] o = bn128.normalize(bn128.multiply(p, m)) return (1, msg.gas - opcodes.GECMUL, [ safe_ord(c) for c in (encode_int32(o[0].n) + encode_int32(o[1].n)) ])
def add_signature_to_transaction(tx, signature): if isinstance(tx, str): tx = decode_transaction(tx) encode_at_end = True else: encode_at_end = False if isinstance(signature, str): signature = data_decoder(signature) tx.v = safe_ord(signature[64]) + 27 tx.r = big_endian_to_int(signature[0:32]) tx.s = big_endian_to_int(signature[32:64]) if encode_at_end: return encode_transaction(tx) return tx
def decompress(data): o = b'' i = 0 while i < len(data): if data[i:i + 1] == b'\xfe': if i == len(data) - 1: raise Exception("Invalid encoding, \\xfe at end") elif data[i + 1:i + 2] == b'\x00': o += b'\xfe' elif data[i + 1:i + 2] == b'\x01': o += NULLSHA3 else: o += b'\x00' * safe_ord(data[i + 1]) i += 1 else: o += data[i:i + 1] i += 1 return o
def sign(data: bytes, private_key_seed_ascii: str, hash_function=bitcoin.bin_sha256): """Sign data using Ethereum private key. :param private_key_seed_ascii: Private key seed as ASCII string """ msghash = hash_function(data) priv = utils.sha3(private_key_seed_ascii) pub = bitcoin.privtopub(priv) # Based on ethereum/tesrt_contracts.py test_ecrecover pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msghash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) v = utils.safe_ord(signature[64]) + 27 r_bytes = signature[0:32] r = big_endian_to_int(r_bytes) s_bytes = signature[32:64] s = big_endian_to_int(s_bytes) #: XXX Ethereum wants its address in different format addr = utils.big_endian_to_int( utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) return { "signature": signature, "v": v, "r": r, "s": s, "r_bytes": r_bytes, "s_bytes": s_bytes, "address_bitcoin": addr, "address_ethereum": eth_privtoaddr(priv), "public_key": pub, "hash": msghash, "payload": bytes([v] + list(r_bytes) + list(s_bytes, )) }
def ecrecover(msg, signature, address=None): """ Returns None on failure, returns the recovered address on success. If address is provided: Returns True if the recovered address matches it, otherwise False. """ rawhash = sha3(msg) if isinstance(signature, str): signature = data_decoder(signature) if len(signature) >= 65: v = safe_ord(signature[64]) r = big_endian_to_int(signature[0:32]) s = big_endian_to_int(signature[32:64]) else: if address: return False else: return None # check if ethereum signature, and adjust the recovery id accordingly if v == 27 or v == 28: v -= 27 pk = PublicKey(flags=ALL_FLAGS) pk.public_key = pk.ecdsa_recover( rawhash, pk.ecdsa_recoverable_deserialize( zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32), v), raw=True) pub = pk.serialize(compressed=False) recaddr = data_encoder(sha3(pub[1:])[-20:]) if address: if not address.startswith("0x"): recaddr = recaddr[2:] return recaddr == address return recaddr
def proc_ecrecover(ext, msg): # print('ecrecover proc', msg.gas) OP_GAS = opcodes.GECRECOVER gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] message_hash_bytes = [0] * 32 msg.data.extract_copy(message_hash_bytes, 0, 0, 32) message_hash = b''.join(map(ascii_chr, message_hash_bytes)) # TODO: This conversion isn't really necessary. # TODO: Invesitage if the check below is really needed. v = msg.data.extract32(32) r = msg.data.extract32(64) s = msg.data.extract32(96) if r >= bitcoin.N or s >= bitcoin.N or v < 27 or v > 28: return 1, msg.gas - opcodes.GECRECOVER, [] signature_bytes = [0] * 64 msg.data.extract_copy(signature_bytes, 0, 64, 32) msg.data.extract_copy(signature_bytes, 32, 96, 32) signature = b''.join(map(ascii_chr, signature_bytes)) pk = PublicKey(flags=ALL_FLAGS) try: pk.public_key = pk.ecdsa_recover( message_hash, pk.ecdsa_recoverable_deserialize( signature, v - 27 ), raw=True ) except Exception: # Recovery failed return 1, msg.gas - gas_cost, [] pub = pk.serialize(compressed=False) o = [0] * 12 + [safe_ord(x) for x in utils.sha3(pub[1:])[-20:]] return 1, msg.gas - gas_cost, o
def call_msg(state, ct, func, args, sender_addr, to, value=0, startgas=STARTGAS): abidata = vm.CallData( [utils.safe_ord(x) for x in ct.encode_function_call(func, args)]) msg = vm.Message(sender_addr, to, value, startgas, abidata) result = apply_message(state, msg) if result is None: raise MessageFailed("Msg failed") if result is False: return result if result == b'': return None o = ct.decode(func, result) return o[0] if len(o) == 1 else o
def proc_ecadd(ext, msg): if not ext.post_metropolis_hardfork(): return 1, msg.gas, [] import py_ecc.optimized_bn128 as bn128 FQ = bn128.FQ print('ecadd proc:', msg.gas) if msg.gas < opcodes.GECADD: return 0, 0, [] x1 = msg.data.extract32(0) y1 = msg.data.extract32(32) x2 = msg.data.extract32(64) y2 = msg.data.extract32(96) p1 = validate_point(x1, y1) p2 = validate_point(x2, y2) if p1 is False or p2 is False: return 0, 0, [] o = bn128.normalize(bn128.add(p1, p2)) return 1, msg.gas - \ opcodes.GECADD, [safe_ord(x) for x in ( encode_int32(o[0].n) + encode_int32(o[1].n))]
def sign(data: bytes, private_key_seed_ascii: str, hash_function=bitcoin.bin_sha256): """Sign data using Ethereum private key. :param private_key_seed_ascii: Private key seed as ASCII string """ msghash = hash_function(data) priv = utils.sha3(private_key_seed_ascii) pub = bitcoin.privtopub(priv) # Based on ethereum/tesrt_contracts.py test_ecrecover pk = PrivateKey(priv, raw=True) signature = pk.ecdsa_recoverable_serialize(pk.ecdsa_sign_recoverable(msghash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) v = utils.safe_ord(signature[64]) + 27 r_bytes = signature[0:32] r = big_endian_to_int(r_bytes) s_bytes = signature[32:64] s = big_endian_to_int(s_bytes) #: XXX Ethereum wants its address in different format addr = utils.big_endian_to_int(utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:]) return { "signature": signature, "v": v, "r": r, "s": s, "r_bytes": r_bytes, "s_bytes": s_bytes, "address_bitcoin": addr, "address_ethereum": eth_privtoaddr(priv), "public_key": pub, "hash": msghash, "payload": bytes([v] + list(r_bytes)+ list(s_bytes,)) }
def proc_ecrecover(ext, msg): # print('ecrecover proc', msg.gas) OP_GAS = opcodes.GECRECOVER gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] message_hash_bytes = [0] * 32 msg.data.extract_copy(message_hash_bytes, 0, 0, 32) message_hash = b''.join(map(ascii_chr, message_hash_bytes)) # TODO: This conversion isn't really necessary. # TODO: Invesitage if the check below is really needed. v = msg.data.extract32(32) r = msg.data.extract32(64) s = msg.data.extract32(96) if r >= bitcoin.N or s >= bitcoin.N or v < 27 or v > 28: return 1, msg.gas - opcodes.GECRECOVER, [] signature_bytes = [0] * 64 msg.data.extract_copy(signature_bytes, 0, 64, 32) msg.data.extract_copy(signature_bytes, 32, 96, 32) signature = b''.join(map(ascii_chr, signature_bytes)) pk = PublicKey(flags=ALL_FLAGS) try: pk.public_key = pk.ecdsa_recover(message_hash, pk.ecdsa_recoverable_deserialize( signature, v - 27), raw=True) except Exception: # Recovery failed return 1, msg.gas - gas_cost, [] pub = pk.serialize(compressed=False) o = [0] * 12 + [safe_ord(x) for x in utils.sha3(pub[1:])[-20:]] return 1, msg.gas - gas_cost, o
def sign(self, key): """Sign this transaction with a private key. A potentially already existing signature would be overridden. """ if key in (0, '', b'\x00' * 32, '0' * 64): raise InvalidTransaction("Zero privkey cannot sign") rawhash = utils.sha3(rlp.encode(self, UnsignedTransaction)) if len(key) == 64: # we need a binary key key = encode_privkey(key, 'bin') pk = PrivateKey(key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True)) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) self.v = utils.safe_ord(signature[64]) + 27 self.r = big_endian_to_int(signature[0:32]) self.s = big_endian_to_int(signature[32:64]) self.sender = utils.privtoaddr(key) return self
def proc_modexp(ext, msg): if not ext.post_metropolis_hardfork(): return 1, msg.gas, [] print('modexp proc', msg.gas) baselen = msg.data.extract32(0) explen = msg.data.extract32(32) modlen = msg.data.extract32(64) first_exp_bytes = msg.data.extract32(96 + baselen) >> (8 * max(32 - explen, 0)) bitlength = -1 while first_exp_bytes: bitlength += 1 first_exp_bytes >>= 1 adjusted_explen = max(bitlength, 0) + 8 * max(explen - 32, 0) gas_cost = (mult_complexity(max(modlen, baselen)) * max(adjusted_explen, 1)) // opcodes.GMODEXPQUADDIVISOR print(baselen, explen, modlen, 'expected gas cost', gas_cost) if msg.gas < gas_cost: return 0, 0, [] if baselen == 0: return 1, msg.gas - gas_cost, [0] * modlen if modlen == 0: return 1, msg.gas - gas_cost, [] base = bytearray(baselen) msg.data.extract_copy(base, 0, 96, baselen) exp = bytearray(explen) msg.data.extract_copy(exp, 0, 96 + baselen, explen) mod = bytearray(modlen) msg.data.extract_copy(mod, 0, 96 + baselen + explen, modlen) if utils.big_endian_to_int(mod) == 0: return 1, msg.gas - gas_cost, [0] * modlen o = pow(utils.big_endian_to_int(base), utils.big_endian_to_int(exp), utils.big_endian_to_int(mod)) return 1, msg.gas - \ gas_cost, [ safe_ord(x) for x in utils.zpad( utils.int_to_big_endian(o), modlen)]
def sign(self, key): """Sign this transaction with a private key. A potentially already existing signature would be overridden. """ if key in (0, '', b'\x00' * 32, '0' * 64): raise InvalidTransaction("Zero privkey cannot sign") rawhash = utils.sha3(rlp.encode(self, UnsignedTransaction)) if len(key) == 64: # we need a binary key key = encode_privkey(key, 'bin') pk = PrivateKey(key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(rawhash, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) self.v = utils.safe_ord(signature[64]) + 27 self.r = big_endian_to_int(signature[0:32]) self.s = big_endian_to_int(signature[32:64]) self.sender = utils.privtoaddr(key) return self
def run_vm_test(params, mode, profiler=None): pre = params['pre'] exek = params['exec'] env = params['env'] if 'previousHash' not in env: env['previousHash'] = encode_hex(db_env.config['GENESIS_PREVHASH']) assert set(env.keys()) == set([ 'currentGasLimit', 'currentTimestamp', 'previousHash', 'currentCoinbase', 'currentDifficulty', 'currentNumber' ]) # setup env header = blocks.BlockHeader( prevhash=decode_hex(env['previousHash']), number=parse_int_or_hex(env['currentNumber']), coinbase=decode_hex(env['currentCoinbase']), difficulty=parse_int_or_hex(env['currentDifficulty']), gas_limit=parse_int_or_hex(env['currentGasLimit']), timestamp=parse_int_or_hex(env['currentTimestamp'])) blk = blocks.Block(header, env=db_env) # setup state for address, h in list(pre.items()): assert len(address) == 40 address = decode_hex(address) assert set(h.keys()) == set(['code', 'nonce', 'balance', 'storage']) blk.set_nonce(address, parse_int_or_hex(h['nonce'])) blk.set_balance(address, parse_int_or_hex(h['balance'])) blk.set_code(address, decode_hex(h['code'][2:])) for k, v in h['storage'].items(): blk.set_storage_data(address, utils.big_endian_to_int(decode_hex(k[2:])), utils.big_endian_to_int(decode_hex(v[2:]))) # execute transactions sender = decode_hex(exek['caller']) # a party that originates a call recvaddr = decode_hex(exek['address']) nonce = blk._get_acct_item(sender, 'nonce') gasprice = parse_int_or_hex(exek['gasPrice']) startgas = parse_int_or_hex(exek['gas']) value = parse_int_or_hex(exek['value']) data = decode_hex(exek['data'][2:]) # bypass gas check in tx initialization by temporarily increasing startgas num_zero_bytes = str_to_bytes(data).count(ascii_chr(0)) num_non_zero_bytes = len(data) - num_zero_bytes intrinsic_gas = (opcodes.GTXCOST + opcodes.GTXDATAZERO * num_zero_bytes + opcodes.GTXDATANONZERO * num_non_zero_bytes) startgas += intrinsic_gas tx = transactions.Transaction(nonce=nonce, gasprice=gasprice, startgas=startgas, to=recvaddr, value=value, data=data) tx.startgas -= intrinsic_gas tx.sender = sender # capture apply_message calls apply_message_calls = [] orig_apply_msg = pb.apply_msg ext = pb.VMExt(blk, tx) def msg_wrapper(msg): hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append( dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=encode_hex(msg.to), data=b'0x' + hexdata)) return 1, msg.gas, b'' def create_wrapper(msg): sender = decode_hex(msg.sender) if \ len(msg.sender) == 40 else msg.sender nonce = utils.encode_int(ext._block.get_nonce(msg.sender)) addr = utils.sha3(rlp.encode([sender, nonce]))[12:] hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append( dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=b'', data=b'0x' + hexdata)) return 1, msg.gas, addr ext.msg = msg_wrapper ext.create = create_wrapper def blkhash(n): if n >= ext.block_number or n < ext.block_number - 256: return b'' else: return utils.sha3(to_string(n)) ext.block_hash = blkhash msg = vm.Message(tx.sender, tx.to, tx.value, tx.startgas, vm.CallData([safe_ord(x) for x in tx.data])) code = decode_hex(exek['code'][2:]) time_pre = time.time() if profiler: profiler.enable() success, gas_remained, output = vm.vm_execute(ext, msg, code) if profiler: profiler.disable() pb.apply_msg = orig_apply_msg blk.commit_state() for s in blk.suicides: blk.del_account(s) time_post = time.time() """ generally expected that the test implementer will read env, exec and pre then check their results against gas, logs, out, post and callcreates. If an exception is expected, then latter sections are absent in the test. Since the reverting of the state is not part of the VM tests. """ params2 = copy.deepcopy(params) if success: params2['callcreates'] = apply_message_calls params2['out'] = b'0x' + encode_hex(b''.join(map(ascii_chr, output))) params2['gas'] = to_string(gas_remained) params2['logs'] = [log.to_dict() for log in blk.logs] params2['post'] = blk.to_dict(with_state=True)['state'] if mode == FILL: return params2 elif mode == VERIFY: if not success: assert 'post' not in params, 'failed, but expected to succeed' params1 = copy.deepcopy(params) shouldbe, reallyis = params1.get('post', None), params2.get('post', None) compare_post_states(shouldbe, reallyis) def normalize_value(k, p): if k in p: if k == 'gas': return parse_int_or_hex(p[k]) elif k == 'callcreates': return list(map(callcreate_standard_form, p[k])) else: return utils.to_string(k) return None for k in ['pre', 'exec', 'env', 'callcreates', 'out', 'gas', 'logs']: shouldbe = normalize_value(k, params1) reallyis = normalize_value(k, params2) if shouldbe != reallyis: raise Exception("Mismatch: " + k + ':\n shouldbe %r\n reallyis %r' % (shouldbe, reallyis)) elif mode == TIME: return time_post - time_pre
def bloom_bits(val): h = utils.sha3(val) return [bits_in_number(1 << ((safe_ord(h[i + 1]) + (safe_ord(h[i]) << 8)) & 2047)) for i in range(0, BUCKETS_PER_VAL * 2, 2)]
def bloom_insert(bloom, val): h = utils.sha3(val) # print('bloom_insert', bloom_bits(val), repr(val)) for i in range(0, BUCKETS_PER_VAL * 2, 2): bloom |= 1 << ((safe_ord(h[i + 1]) + (safe_ord(h[i]) << 8)) & 2047) return bloom
def test_multiarg_code(): s = tester.state() c = s.abi_contract(multiarg_code) o = c.kall([1, 2, 3], 4, [5, 6, 7], b"doge", 8) assert o == [862541, safe_ord('d') + safe_ord('o') + safe_ord('g'), 4]
def vm_execute(ext, msg, code): # precompute trace flag # if we trace vm, we're in slow mode anyway trace_vm = log_vm_op.is_active('trace') compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory processed_code = preprocess_code(code) s = time.time() op = None steps = 0 _prevop = None # for trace only while True: # print('op: ', op, time.time() - s) # s = time.time() # stack size limit error if compustate.pc not in processed_code: return vm_exception('INVALID START POINT') _data = processed_code[compustate.pc] gas, min_stack, max_stack, compustate.pc = _data[:4] ops = _data[4:] # out of gas error if gas > compustate.gas: return vm_exception('OUT OF GAS') # insufficient stack error if not (min_stack <= len(compustate.stack) <= max_stack): return vm_exception('INCOMPATIBLE STACK LENGTH', min_stack=min_stack, have=len(compustate.stack), max_stack=max_stack) # Apply operation compustate.gas -= gas for op in ops: if trace_vm: """ This diverges from normal logging, as we use the logging namespace only to decide which features get logged in 'eth.vm.op' i.e. tracing can not be activated by activating a sub like 'eth.vm.op.stack' """ trace_data = {} trace_data['stack'] = list( map(to_string, list(compustate.stack))) if _prevop in (op_MLOAD, op_MSTORE, op_MSTORE8, op_SHA3, op_CALL, op_CALLCODE, op_CREATE, op_CALLDATACOPY, op_CODECOPY, op_EXTCODECOPY): if len(compustate.memory) < 1024: trace_data['memory'] = \ b''.join([encode_hex(ascii_chr(x)) for x in compustate.memory]) else: trace_data['sha3memory'] = \ encode_hex(utils.sha3(''.join([ascii_chr(x) for x in compustate.memory]))) if _prevop in (op_SSTORE, op_SLOAD) or steps == 0: trace_data['storage'] = ext.log_storage(msg.to) # trace_data['gas'] = to_string(compustate.gas + fee) trace_data['inst'] = op trace_data['pc'] = to_string(compustate.pc - 1) if steps == 0: trace_data['depth'] = msg.depth trace_data['address'] = msg.to trace_data['op'] = op trace_data['steps'] = steps # if op[:4] == 'PUSH': # trace_data['pushvalue'] = pushval log_vm_op.trace('vm', **trace_data) steps += 1 _prevop = op # Invalid operation if op == INVALID: return vm_exception('INVALID OP', opcode=op) # Valid operations if op < 0x10: if op == op_STOP: return peaceful_exit('STOP', compustate.gas, []) elif op == op_ADD: stk.append((stk.pop() + stk.pop()) & TT256M1) elif op == op_SUB: stk.append((stk.pop() - stk.pop()) & TT256M1) elif op == op_MUL: stk.append((stk.pop() * stk.pop()) & TT256M1) elif op == op_DIV: s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 // s1) elif op == op_MOD: s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 % s1) elif op == op_SDIV: s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) elif op == op_SMOD: s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) elif op == op_ADDMOD: s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 + s1) % s2 if s2 else 0) elif op == op_MULMOD: s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 * s1) % s2 if s2 else 0) elif op == op_EXP: base, exponent = stk.pop(), stk.pop() # fee for exponent is dependent on its bytes # calc n bytes to represent exponent nbytes = len(utils.encode_int(exponent)) expfee = nbytes * opcodes.GEXPONENTBYTE if compustate.gas < expfee: compustate.gas = 0 return vm_exception('OOG EXPONENT') compustate.gas -= expfee stk.append(pow(base, exponent, TT256)) elif op == op_SIGNEXTEND: s0, s1 = stk.pop(), stk.pop() if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): stk.append(s1 | (TT256 - (1 << testbit))) else: stk.append(s1 & ((1 << testbit) - 1)) else: stk.append(s1) elif op < 0x20: if op == op_LT: stk.append(1 if stk.pop() < stk.pop() else 0) elif op == op_GT: stk.append(1 if stk.pop() > stk.pop() else 0) elif op == op_SLT: s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == op_SGT: s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(1 if s0 > s1 else 0) elif op == op_EQ: stk.append(1 if stk.pop() == stk.pop() else 0) elif op == op_ISZERO: stk.append(0 if stk.pop() else 1) elif op == op_AND: stk.append(stk.pop() & stk.pop()) elif op == op_OR: stk.append(stk.pop() | stk.pop()) elif op == op_XOR: stk.append(stk.pop() ^ stk.pop()) elif op == op_NOT: stk.append(TT256M1 - stk.pop()) elif op == op_BYTE: s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 // 256**(31 - s0)) % 256) elif op < 0x40: if op == op_SHA3: s0, s1 = stk.pop(), stk.pop() compustate.gas -= opcodes.GSHA3WORD * \ (utils.ceil32(s1) // 32) if compustate.gas < 0: return vm_exception('OOG PAYING FOR SHA3') if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[s0:s0 + s1])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == op_ADDRESS: stk.append(utils.coerce_to_int(msg.to)) elif op == op_BALANCE: addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(ext.get_balance(addr)) elif op == op_ORIGIN: stk.append(utils.coerce_to_int(ext.tx_origin)) elif op == op_CALLER: stk.append(utils.coerce_to_int(msg.sender)) elif op == op_CALLVALUE: stk.append(msg.value) elif op == op_CALLDATALOAD: stk.append(msg.data.extract32(stk.pop())) elif op == op_CALLDATASIZE: stk.append(msg.data.size) elif op == op_CALLDATACOPY: mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') msg.data.extract_copy(mem, mstart, dstart, size) elif op == op_CODESIZE: stk.append(len(code)) elif op == op_CODECOPY: start, s1, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s1 + i < len(code): mem[start + i] = utils.safe_ord(code[s1 + i]) else: mem[start + i] = 0 elif op == op_GASPRICE: stk.append(ext.tx_gasprice) elif op == op_EXTCODESIZE: addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(len(ext.get_code(addr) or b'')) elif op == op_EXTCODECOPY: addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) start, s2, size = stk.pop(), stk.pop(), stk.pop() extcode = ext.get_code(addr) or b'' assert utils.is_string(extcode) if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s2 + i < len(extcode): mem[start + i] = utils.safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 elif op < 0x50: if op == op_BLOCKHASH: stk.append( utils.big_endian_to_int(ext.block_hash(stk.pop()))) elif op == op_COINBASE: stk.append(utils.big_endian_to_int(ext.block_coinbase)) elif op == op_TIMESTAMP: stk.append(ext.block_timestamp) elif op == op_NUMBER: stk.append(ext.block_number) elif op == op_DIFFICULTY: stk.append(ext.block_difficulty) elif op == op_GASLIMIT: stk.append(ext.block_gas_limit) elif op < 0x60: if op == op_POP: stk.pop() elif op == op_MLOAD: s0 = stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') data = 0 for c in mem[s0:s0 + 32]: data = (data << 8) + c stk.append(data) elif op == op_MSTORE: s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') v = s1 for i in range(31, -1, -1): mem[s0 + i] = v % 256 v //= 256 elif op == op_MSTORE8: s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 1): return vm_exception('OOG EXTENDING MEMORY') mem[s0] = s1 % 256 elif op == op_SLOAD: stk.append(ext.get_storage_data(msg.to, stk.pop())) elif op == op_SSTORE: s0, s1 = stk.pop(), stk.pop() if ext.get_storage_data(msg.to, s0): gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL refund = 0 if s1 else opcodes.GSTORAGEREFUND else: gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD refund = 0 if compustate.gas < gascost: return vm_exception('OUT OF GAS') compustate.gas -= gascost # adds neg gascost as a refund if below zero ext.add_refund(refund) ext.set_storage_data(msg.to, s0, s1) elif op == op_JUMP: compustate.pc = stk.pop() opnew = processed_code[compustate.pc][4] if \ compustate.pc in processed_code else op_STOP if opnew != op_JUMPDEST: return vm_exception('BAD JUMPDEST') elif op == op_JUMPI: s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 opnew = processed_code[compustate.pc][4] if \ compustate.pc in processed_code else op_STOP if opnew != op_JUMPDEST: return vm_exception('BAD JUMPDEST') elif op == op_PC: stk.append(compustate.pc - 1) elif op == op_MSIZE: stk.append(len(mem)) elif op == op_GAS: stk.append(compustate.gas) # AFTER subtracting cost 1 elif op_PUSH1 <= (op & 255) <= op_PUSH32: # Hide push value in high-order bytes of op stk.append(op >> 8) elif op_DUP1 <= op <= op_DUP16: depth = op - op_DUP1 + 1 stk.append(stk[-depth]) elif op_SWAP1 <= op <= op_SWAP16: depth = op - op_SWAP1 + 1 temp = stk[-depth - 1] stk[-depth - 1] = stk[-1] stk[-1] = temp elif op_LOG0 <= op <= op_LOG4: """ 0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4) b. Logs are kept track of during tx execution exactly the same way as suicides (except as an ordered list, not a set). Each log is in the form [address, [topic1, ... ], data] where: * address is what the ADDRESS opcode would output * data is mem[MEMSTART: MEMSTART + MEMSZ] * topics are as provided by the opcode c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN]. """ depth = op - op_LOG0 mstart, msz = stk.pop(), stk.pop() topics = [stk.pop() for x in range(depth)] compustate.gas -= msz * opcodes.GLOGBYTE if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[mstart:mstart + msz])) ext.log(msg.to, topics, data) log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data))) # print('LOG', msg.to, topics, list(map(ord, data))) elif op == op_CREATE: value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') if ext.get_balance(msg.to) >= value and msg.depth < 1024: cd = CallData(mem, mstart, msz) create_msg = Message(msg.to, b'', value, compustate.gas, cd, msg.depth + 1) o, gas, addr = ext.create(create_msg) if o: stk.append(utils.coerce_to_int(addr)) compustate.gas = gas else: stk.append(0) compustate.gas = 0 else: stk.append(0) elif op == op_CALL: gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \ (value > 0) * opcodes.GCALLVALUETRANSFER submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas + extra_gas) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == op_CALLCODE: gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas + extra_gas) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == op_RETURN: s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1]) elif op == op_SUICIDE: to = utils.encode_int(stk.pop()) to = ((b'\x00' * (32 - len(to))) + to)[12:] xfer = ext.get_balance(msg.to) ext.set_balance(to, ext.get_balance(to) + xfer) ext.set_balance(msg.to, 0) ext.add_suicide(msg.to) # print('suiciding %s %s %d' % (msg.to, to, xfer)) return 1, compustate.gas, []
def vm_execute(ext, msg, code): # precompute trace flag # if we trace vm, we're in slow mode anyway trace_vm = log_vm_op.is_active('trace') compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory if code in code_cache: processed_code = code_cache[code] else: processed_code = preprocess_code(code) code_cache[code] = processed_code # print(processed_code.keys(), code) codelen = len(code) s = time.time() steps = 0 _prevop = None # for trace only while compustate.pc in processed_code: ops, minstack, maxstack, totgas, nextpos = processed_code[ compustate.pc] if len(compustate.stack) < minstack: return vm_exception('INSUFFICIENT STACK') if len(compustate.stack) > maxstack: return vm_exception('STACK SIZE LIMIT EXCEEDED') if totgas > compustate.gas: return vm_exception('OUT OF GAS %d %d' % (totgas, compustate.gas)) jumped = False compustate.gas -= totgas compustate.pc = nextpos # Invalid operation; can only come at the end of a chunk if ops[-1][0] == 'INVALID': return vm_exception('INVALID OP', opcode=ops[-1][1]) for op, opcode, pushval in ops: if trace_vm: """ This diverges from normal logging, as we use the logging namespace only to decide which features get logged in 'eth.vm.op' i.e. tracing can not be activated by activating a sub like 'eth.vm.op.stack' """ trace_data = {} trace_data['stack'] = list( map(to_string, list(compustate.stack))) if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL', 'CALLCODE', 'CREATE', 'CALLDATACOPY', 'CODECOPY', 'EXTCODECOPY'): if len(compustate.memory) < 1024: trace_data['memory'] = \ ''.join([encode_hex(ascii_chr(x)) for x in compustate.memory]) else: trace_data['sha3memory'] = \ encode_hex(utils.sha3(b''.join([ascii_chr(x) for x in compustate.memory]))) if _prevop in ('SSTORE', ) or steps == 0: trace_data['storage'] = ext.log_storage(msg.to) trace_data['gas'] = to_string(compustate.gas + totgas) trace_data['inst'] = opcode trace_data['pc'] = to_string(compustate.pc - 1) if steps == 0: trace_data['depth'] = msg.depth trace_data['address'] = msg.to trace_data['steps'] = steps trace_data['depth'] = msg.depth if op[:4] == 'PUSH': trace_data['pushvalue'] = pushval log_vm_op.trace('vm', op=op, **trace_data) steps += 1 _prevop = op # Valid operations # Pushes first because they are very frequent if 0x60 <= opcode <= 0x7f: # compustate.pc += opcode - 0x5f # Move 1 byte forward for # 0x60, up to 32 bytes for 0x7f stk.append(pushval) elif opcode < 0x10: if op == 'STOP': return peaceful_exit('STOP', compustate.gas, []) elif op == 'ADD': stk.append((stk.pop() + stk.pop()) & TT256M1) elif op == 'SUB': stk.append((stk.pop() - stk.pop()) & TT256M1) elif op == 'MUL': stk.append((stk.pop() * stk.pop()) & TT256M1) elif op == 'DIV': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 // s1) elif op == 'MOD': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 % s1) elif op == 'SDIV': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) elif op == 'SMOD': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) elif op == 'ADDMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 + s1) % s2 if s2 else 0) elif op == 'MULMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 * s1) % s2 if s2 else 0) elif op == 'EXP': base, exponent = stk.pop(), stk.pop() # fee for exponent is dependent on its bytes # calc n bytes to represent exponent nbytes = len(utils.encode_int(exponent)) expfee = nbytes * opcodes.GEXPONENTBYTE if ext.post_clearing_hardfork(): expfee += opcodes.EXP_SUPPLEMENTAL_GAS * nbytes if compustate.gas < expfee: compustate.gas = 0 return vm_exception('OOG EXPONENT') compustate.gas -= expfee stk.append(pow(base, exponent, TT256)) elif op == 'SIGNEXTEND': s0, s1 = stk.pop(), stk.pop() if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): stk.append(s1 | (TT256 - (1 << testbit))) else: stk.append(s1 & ((1 << testbit) - 1)) else: stk.append(s1) elif opcode < 0x20: if op == 'LT': stk.append(1 if stk.pop() < stk.pop() else 0) elif op == 'GT': stk.append(1 if stk.pop() > stk.pop() else 0) elif op == 'SLT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == 'SGT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed( stk.pop()) stk.append(1 if s0 > s1 else 0) elif op == 'EQ': stk.append(1 if stk.pop() == stk.pop() else 0) elif op == 'ISZERO': stk.append(0 if stk.pop() else 1) elif op == 'AND': stk.append(stk.pop() & stk.pop()) elif op == 'OR': stk.append(stk.pop() | stk.pop()) elif op == 'XOR': stk.append(stk.pop() ^ stk.pop()) elif op == 'NOT': stk.append(TT256M1 - stk.pop()) elif op == 'BYTE': s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 // 256**(31 - s0)) % 256) elif opcode < 0x40: if op == 'SHA3': s0, s1 = stk.pop(), stk.pop() compustate.gas -= opcodes.GSHA3WORD * \ (utils.ceil32(s1) // 32) if compustate.gas < 0: return vm_exception('OOG PAYING FOR SHA3') if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') data = bytearray_to_bytestr(mem[s0:s0 + s1]) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': if ext.post_anti_dos_hardfork(): if not eat_gas(compustate, opcodes.BALANCE_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(ext.get_balance(addr)) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(ext.tx_origin)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': stk.append(msg.data.extract32(stk.pop())) elif op == 'CALLDATASIZE': stk.append(msg.data.size) elif op == 'CALLDATACOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') msg.data.extract_copy(mem, mstart, dstart, size) elif op == 'CODESIZE': stk.append(len(code)) elif op == 'CODECOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if dstart + i < len(code): mem[mstart + i] = utils.safe_ord(code[dstart + i]) else: mem[mstart + i] = 0 elif op == 'RETURNDATACOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') if dstart + size > len(compustate.last_returned): return vm_exception('RETURNDATACOPY out of range') mem[mstart:mstart + size] = compustate.last_returned elif op == 'RETURNDATASIZE': stk.append(len(compustate.last_returned)) elif op == 'GASPRICE': stk.append(ext.tx_gasprice) elif op == 'EXTCODESIZE': if ext.post_anti_dos_hardfork(): if not eat_gas(compustate, opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(len(ext.get_code(addr) or b'')) elif op == 'EXTCODECOPY': if ext.post_anti_dos_hardfork(): if not eat_gas(compustate, opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) start, s2, size = stk.pop(), stk.pop(), stk.pop() extcode = ext.get_code(addr) or b'' assert utils.is_string(extcode) if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s2 + i < len(extcode): mem[start + i] = utils.safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 elif opcode < 0x50: if op == 'BLOCKHASH': if ext.post_metropolis_hardfork() and False: bh_addr = ext.blockhash_store stk.append(ext.get_storage_data(bh_addr, stk.pop())) else: stk.append( utils.big_endian_to_int(ext.block_hash(stk.pop()))) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(ext.block_coinbase)) elif op == 'TIMESTAMP': stk.append(ext.block_timestamp) elif op == 'NUMBER': stk.append(ext.block_number) elif op == 'DIFFICULTY': stk.append(ext.block_difficulty) elif op == 'GASLIMIT': stk.append(ext.block_gas_limit) elif opcode < 0x60: if op == 'POP': stk.pop() elif op == 'MLOAD': s0 = stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') stk.append(utils.bytes_to_int(mem[s0:s0 + 32])) elif op == 'MSTORE': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') mem[s0:s0 + 32] = utils.encode_int32(s1) elif op == 'MSTORE8': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 1): return vm_exception('OOG EXTENDING MEMORY') mem[s0] = s1 % 256 elif op == 'SLOAD': if ext.post_anti_dos_hardfork(): if not eat_gas(compustate, opcodes.SLOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") stk.append(ext.get_storage_data(msg.to, stk.pop())) elif op == 'SSTORE': s0, s1 = stk.pop(), stk.pop() if msg.static: return vm_exception( 'Cannot SSTORE inside a static context') if ext.get_storage_data(msg.to, s0): gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL refund = 0 if s1 else opcodes.GSTORAGEREFUND else: gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD refund = 0 if compustate.gas < gascost: return vm_exception('OUT OF GAS') compustate.gas -= gascost # adds neg gascost as a refund if below zero ext.add_refund(refund) ext.set_storage_data(msg.to, s0, s1) elif op == 'JUMP': compustate.pc = stk.pop() opnew = code[ compustate.pc] if compustate.pc < codelen else 0 jumped = True if opnew != JUMPDEST: return vm_exception('BAD JUMPDEST') elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 opnew = code[ compustate.pc] if compustate.pc < codelen else 0 jumped = True if opnew != JUMPDEST: return vm_exception('BAD JUMPDEST') elif op == 'PC': stk.append(compustate.pc - 1) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(compustate.gas) # AFTER subtracting cost 1 elif op[:3] == 'DUP': # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for # 0x8f stk.append(stk[0x7f - opcode]) elif op[:4] == 'SWAP': # 0x8e - opcode is a negative number, -2 for 0x90 ... -17 for # 0x9f temp = stk[0x8e - opcode] stk[0x8e - opcode] = stk[-1] stk[-1] = temp elif op[:3] == 'LOG': """ 0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4) b. Logs are kept track of during tx execution exactly the same way as suicides (except as an ordered list, not a set). Each log is in the form [address, [topic1, ... ], data] where: * address is what the ADDRESS opcode would output * data is mem[MEMSTART: MEMSTART + MEMSZ] * topics are as provided by the opcode c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN]. """ depth = int(op[3:]) mstart, msz = stk.pop(), stk.pop() topics = [stk.pop() for x in range(depth)] compustate.gas -= msz * opcodes.GLOGBYTE if msg.static: return vm_exception('Cannot LOG inside a static context') if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') data = bytearray_to_bytestr(mem[mstart:mstart + msz]) ext.log(msg.to, topics, data) log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data))) # print('LOG', msg.to, topics, list(map(ord, data))) elif op == 'CREATE': value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') if msg.static: return vm_exception( 'Cannot CREATE inside a static context') if ext.get_balance(msg.to) >= value and msg.depth < MAX_DEPTH: cd = CallData(mem, mstart, msz) ingas = compustate.gas if ext.post_anti_dos_hardfork(): ingas = all_but_1n(ingas, opcodes.CALL_CHILD_LIMIT_DENOM) create_msg = Message(msg.to, b'', value, ingas, cd, msg.depth + 1) o, gas, addr = ext.create(create_msg) if o: stk.append(utils.coerce_to_int(addr)) else: stk.append(0) compustate.gas = compustate.gas - ingas + gas else: stk.append(0) elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'): # Pull arguments from the stack if op in ('CALL', 'CALLCODE'): gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() else: gas, to, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() value = 0 # Static context prohibition if msg.static and value > 0: return vm_exception( 'Cannot make a non-zero-value call inside a static context' ) # Expand memory if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') to = utils.int_to_addr(to) # Extra gas costs based on hard fork-dependent factors extra_gas = (not ext.account_exists(to)) * (op == 'CALL') * (value > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT + \ (value > 0) * opcodes.GCALLVALUETRANSFER + \ ext.post_anti_dos_hardfork() * opcodes.CALL_SUPPLEMENTAL_GAS # Compute child gas limit if ext.post_anti_dos_hardfork(): if compustate.gas < extra_gas: return vm_exception('OUT OF GAS', needed=extra_gas) gas = min( gas, all_but_1n(compustate.gas - extra_gas, opcodes.CALL_CHILD_LIMIT_DENOM)) else: if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas + extra_gas) submsg_gas = gas + opcodes.GSTIPEND * (value > 0) # Verify that there is sufficient balance and depth if ext.get_balance(msg.to) < value or msg.depth >= MAX_DEPTH: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) else: # Subtract gas from parent compustate.gas -= (gas + extra_gas) assert compustate.gas >= 0 cd = CallData(mem, meminstart, meminsz) # Generate the message if op == 'CALL': call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=msg.static) elif ext.post_homestead_hardfork( ) and op == 'DELEGATECALL': call_msg = Message(msg.sender, msg.to, msg.value, submsg_gas, cd, msg.depth + 1, code_address=to, transfers_value=False, static=msg.static) elif op == 'DELEGATECALL': return vm_exception('OPCODE INACTIVE') elif op == 'CALLCODE': call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=msg.static) elif op == 'STATICCALL': call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=True) else: raise Exception("Lolwut") # Get result result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) # Set output memory for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] compustate.gas += gas compustate.last_returned = bytearray(data) elif op == 'RETURN': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1]) elif op == 'REVERT': if not ext.post_metropolis_hardfork(): return vm_exception('Opcode not yet enabled') s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return revert(compustate.gas, mem[s0:s0 + s1]) elif op == 'SUICIDE': if msg.static: return vm_exception( 'Cannot SUICIDE inside a static context') to = utils.encode_int(stk.pop()) to = ((b'\x00' * (32 - len(to))) + to)[12:] xfer = ext.get_balance(msg.to) if ext.post_anti_dos_hardfork(): extra_gas = opcodes.SUICIDE_SUPPLEMENTAL_GAS + \ (not ext.account_exists( to)) * (xfer > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT if not eat_gas(compustate, extra_gas): return vm_exception("OUT OF GAS") ext.set_balance(to, ext.get_balance(to) + xfer) ext.set_balance(msg.to, 0) ext.add_suicide(msg.to) log_msg.debug('SUICIDING', addr=utils.checksum_encode(msg.to), to=utils.checksum_encode(to), xferring=xfer) return 1, compustate.gas, [] # assert utils.is_numeric(compustate.gas) # this is slow! # for a in stk: # assert is_numeric(a), (op, stk) # assert a >= 0 and a < 2**256, (a, op, stk) # if not jumped: # assert compustate.pc == nextpos # compustate.pc = nextpos if compustate.pc >= codelen: return peaceful_exit('CODE OUT OF RANGE', compustate.gas, []) return vm_exception('INVALID JUMP')
def test_multiarg_code(): s = tester.state() c = s.abi_contract(multiarg_code) o = c.kall([1, 2, 3], 4, [5, 6, 7], "doge", 8) assert o == [862541, safe_ord('d') + safe_ord('o') + safe_ord('g'), 4]
def vm_execute(ext, msg, code): # precompute trace flag # if we trace vm, we're in slow mode anyway trace_vm = log_vm_op.is_active('trace') compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory processed_code = preprocess_code(code) codelen = len(processed_code) op = None steps = 0 _prevop = None # for trace only while 1: # stack size limit error if compustate.pc >= codelen: return peaceful_exit('CODE OUT OF RANGE', compustate.gas, []) op, in_args, out_args, fee, opcode, pushval = \ processed_code[compustate.pc] # out of gas error if fee > compustate.gas: return vm_exception('OUT OF GAS') # empty stack error if in_args > len(compustate.stack): return vm_exception('INSUFFICIENT STACK', op=op, needed=to_string(in_args), available=to_string(len(compustate.stack))) if len(compustate.stack) - in_args + out_args > 1024: return vm_exception('STACK SIZE LIMIT EXCEEDED', op=op, pre_height=to_string(len(compustate.stack))) # Apply operation compustate.gas -= fee compustate.pc += 1 if trace_vm: """ This diverges from normal logging, as we use the logging namespace only to decide which features get logged in 'eth.vm.op' i.e. tracing can not be activated by activating a sub like 'eth.vm.op.stack' """ trace_data = {} trace_data['stack'] = list(map(to_string, list(compustate.stack))) if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL', 'CALLCODE', 'CREATE', 'CALLDATACOPY', 'CODECOPY', 'EXTCODECOPY'): if len(compustate.memory) < 1024: trace_data['memory'] = \ b''.join([encode_hex(ascii_chr(x)) for x in compustate.memory]) else: trace_data['sha3memory'] = \ encode_hex(utils.sha3(''.join([ascii_chr(x) for x in compustate.memory]))) if _prevop in ('SSTORE', 'SLOAD') or steps == 0: trace_data['storage'] = ext.log_storage(msg.to) trace_data['gas'] = to_string(compustate.gas + fee) trace_data['inst'] = opcode trace_data['pc'] = to_string(compustate.pc - 1) if steps == 0: trace_data['depth'] = msg.depth trace_data['address'] = msg.to trace_data['op'] = op trace_data['steps'] = steps if op[:4] == 'PUSH': trace_data['pushvalue'] = pushval log_vm_op.trace('vm', **trace_data) steps += 1 _prevop = op # Invalid operation if op == 'INVALID': return vm_exception('INVALID OP', opcode=opcode) # Valid operations if opcode < 0x10: if op == 'STOP': return peaceful_exit('STOP', compustate.gas, []) elif op == 'ADD': stk.append((stk.pop() + stk.pop()) & TT256M1) elif op == 'SUB': stk.append((stk.pop() - stk.pop()) & TT256M1) elif op == 'MUL': stk.append((stk.pop() * stk.pop()) & TT256M1) elif op == 'DIV': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 // s1) elif op == 'MOD': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 % s1) elif op == 'SDIV': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) elif op == 'SMOD': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) elif op == 'ADDMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 + s1) % s2 if s2 else 0) elif op == 'MULMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 * s1) % s2 if s2 else 0) elif op == 'EXP': base, exponent = stk.pop(), stk.pop() # fee for exponent is dependent on its bytes # calc n bytes to represent exponent nbytes = len(utils.encode_int(exponent)) expfee = nbytes * opcodes.GEXPONENTBYTE if compustate.gas < expfee: compustate.gas = 0 return vm_exception('OOG EXPONENT') compustate.gas -= expfee stk.append(pow(base, exponent, TT256)) elif op == 'SIGNEXTEND': s0, s1 = stk.pop(), stk.pop() if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): stk.append(s1 | (TT256 - (1 << testbit))) else: stk.append(s1 & ((1 << testbit) - 1)) else: stk.append(s1) elif opcode < 0x20: if op == 'LT': stk.append(1 if stk.pop() < stk.pop() else 0) elif op == 'GT': stk.append(1 if stk.pop() > stk.pop() else 0) elif op == 'SLT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == 'SGT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(1 if s0 > s1 else 0) elif op == 'EQ': stk.append(1 if stk.pop() == stk.pop() else 0) elif op == 'ISZERO': stk.append(0 if stk.pop() else 1) elif op == 'AND': stk.append(stk.pop() & stk.pop()) elif op == 'OR': stk.append(stk.pop() | stk.pop()) elif op == 'XOR': stk.append(stk.pop() ^ stk.pop()) elif op == 'NOT': stk.append(TT256M1 - stk.pop()) elif op == 'BYTE': s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 // 256 ** (31 - s0)) % 256) elif opcode < 0x40: if op == 'SHA3': s0, s1 = stk.pop(), stk.pop() compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32) if compustate.gas < 0: return vm_exception('OOG PAYING FOR SHA3') if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[s0: s0 + s1])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': # EIP150: Increase the gas cost of BALANCE to 400 if ext.post_anti_dos_hardfork: if not eat_gas(compustate, opcodes.BALANCE_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2 ** 160) stk.append(ext.get_balance(addr)) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(ext.tx_origin)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': stk.append(msg.data.extract32(stk.pop())) elif op == 'CALLDATASIZE': stk.append(msg.data.size) elif op == 'CALLDATACOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') msg.data.extract_copy(mem, mstart, dstart, size) elif op == 'CODESIZE': stk.append(len(processed_code)) elif op == 'CODECOPY': start, s1, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s1 + i < len(processed_code): mem[start + i] = processed_code[s1 + i][4] else: mem[start + i] = 0 elif op == 'GASPRICE': stk.append(ext.tx_gasprice) elif op == 'EXTCODESIZE': # EIP150: Increase the gas cost of EXTCODESIZE to 700 if ext.post_anti_dos_hardfork: if not eat_gas(compustate, opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2 ** 160) stk.append(len(ext.get_code(addr) or b'')) elif op == 'EXTCODECOPY': # EIP150: Increase the base gas cost of EXTCODECOPY to 700 if ext.post_anti_dos_hardfork: if not eat_gas(compustate, opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") addr = utils.coerce_addr_to_hex(stk.pop() % 2 ** 160) start, s2, size = stk.pop(), stk.pop(), stk.pop() extcode = ext.get_code(addr) or b'' assert utils.is_string(extcode) if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s2 + i < len(extcode): mem[start + i] = utils.safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 elif opcode < 0x50: if op == 'BLOCKHASH': stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop()))) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(ext.block_coinbase)) elif op == 'TIMESTAMP': stk.append(ext.block_timestamp) elif op == 'NUMBER': stk.append(ext.block_number) elif op == 'DIFFICULTY': stk.append(ext.block_difficulty) elif op == 'GASLIMIT': stk.append(ext.block_gas_limit) elif opcode < 0x60: if op == 'POP': stk.pop() elif op == 'MLOAD': s0 = stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[s0: s0 + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') v = s1 for i in range(31, -1, -1): mem[s0 + i] = v % 256 v //= 256 elif op == 'MSTORE8': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 1): return vm_exception('OOG EXTENDING MEMORY') mem[s0] = s1 % 256 elif op == 'SLOAD': # EIP150: Increase the gas cost of SLOAD to 200 if ext.post_anti_dos_hardfork: if not eat_gas(compustate, opcodes.SLOAD_SUPPLEMENTAL_GAS): return vm_exception("OUT OF GAS") stk.append(ext.get_storage_data(msg.to, stk.pop())) elif op == 'SSTORE': s0, s1 = stk.pop(), stk.pop() if ext.get_storage_data(msg.to, s0): gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL refund = 0 if s1 else opcodes.GSTORAGEREFUND else: gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD refund = 0 if compustate.gas < gascost: 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) elif op == 'JUMP': compustate.pc = stk.pop() opnew = processed_code[compustate.pc][0] if \ compustate.pc < len(processed_code) else 'STOP' if opnew != 'JUMPDEST': return vm_exception('BAD JUMPDEST') elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 opnew = processed_code[compustate.pc][0] if \ compustate.pc < len(processed_code) else 'STOP' if opnew != 'JUMPDEST': return vm_exception('BAD JUMPDEST') elif op == 'PC': stk.append(compustate.pc - 1) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(compustate.gas) # AFTER subtracting cost 1 elif op[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc += pushnum stk.append(pushval) elif op[:3] == 'DUP': depth = int(op[3:]) stk.append(stk[-depth]) elif op[:4] == 'SWAP': depth = int(op[4:]) temp = stk[-depth - 1] stk[-depth - 1] = stk[-1] stk[-1] = temp elif op[:3] == 'LOG': """ 0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4) b. Logs are kept track of during tx execution exactly the same way as suicides (except as an ordered list, not a set). Each log is in the form [address, [topic1, ... ], data] where: * address is what the ADDRESS opcode would output * data is mem[MEMSTART: MEMSTART + MEMSZ] * topics are as provided by the opcode c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN]. """ depth = int(op[3:]) mstart, msz = stk.pop(), stk.pop() topics = [stk.pop() for x in range(depth)] compustate.gas -= msz * opcodes.GLOGBYTE if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[mstart: mstart + msz])) ext.log(msg.to, topics, data) log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data))) # print('LOG', msg.to, topics, list(map(ord, data))) elif op == 'CREATE': value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') if ext.get_balance(msg.to) >= value and msg.depth < 1024: cd = CallData(mem, mstart, msz) ingas = compustate.gas # EIP150(1b) CREATE only provides all but one 64th of the # parent gas to the child call if ext.post_anti_dos_hardfork: ingas = max_call_gas(ingas) create_msg = Message(msg.to, b'', value, ingas, cd, msg.depth + 1) o, gas, addr = ext.create(create_msg) if o: stk.append(utils.coerce_to_int(addr)) compustate.gas -= (ingas - gas) else: stk.append(0) compustate.gas -= ingas else: stk.append(0) elif op == 'CALL': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \ (value > 0) * opcodes.GCALLVALUETRANSFER + \ ext.post_anti_dos_hardfork * opcodes.CALL_SUPPLEMENTAL_GAS # ^ EIP150 Increase the gas cost of CALL to 700 if ext.post_anti_dos_hardfork: # EIP150(1b) if a call asks for more gas than all but one 64th of # the maximum allowed amount, call with all but one 64th of the # maximum allowed amount of gas if compustate.gas < extra_gas: return vm_exception('OUT OF GAS', needed=extra_gas) gas = min(gas, max_call_gas(compustate.gas - extra_gas)) else: if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas + extra_gas) submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == 'CALLCODE' or op == 'DELEGATECALL': if op == 'CALLCODE': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() else: gas, to, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() value = 0 if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER + \ ext.post_anti_dos_hardfork * opcodes.CALL_SUPPLEMENTAL_GAS # ^ EIP150 Increase the gas cost of CALLCODE, DELEGATECALL to 700 if ext.post_anti_dos_hardfork: # EIP150(1b) if a call asks for more gas than all but one 64th of # the maximum allowed amount, call with all but one 64th of the # maximum allowed amount of gas if compustate.gas < extra_gas: return vm_exception('OUT OF GAS', needed=extra_gas) gas = min(gas, max_call_gas(compustate.gas - extra_gas)) else: if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas + extra_gas) submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] cd = CallData(mem, meminstart, meminsz) if ext.post_homestead_hardfork and op == 'DELEGATECALL': call_msg = Message(msg.sender, msg.to, msg.value, submsg_gas, cd, msg.depth + 1, code_address=to, transfers_value=False) elif op == 'DELEGATECALL': return vm_exception('OPCODE INACTIVE') else: call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == 'RETURN': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return peaceful_exit('RETURN', compustate.gas, mem[s0: s0 + s1]) elif op == 'SUICIDE': to = utils.encode_int(stk.pop()) to = ((b'\x00' * (32 - len(to))) + to)[12:] if ext.post_anti_dos_hardfork: # EIP150 Increase the gas cost of SUICIDE to 5000 extra_gas = opcodes.SUICIDE_SUPPLEMENTAL_GAS + \ (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT # ^ EIP150(1c) If SUICIDE hits a newly created account, it # triggers an additional gas cost of 25000 (similar to CALLs) if not eat_gas(compustate, extra_gas): return vm_exception("OUT OF GAS") xfer = ext.get_balance(msg.to) ext.set_balance(to, ext.get_balance(to) + xfer) ext.set_balance(msg.to, 0) ext.add_suicide(msg.to) # print('suiciding %s %s %d' % (msg.to, to, xfer)) return 1, compustate.gas, []
def run_vm_test(params, mode, profiler=None): pre = params['pre'] exek = params['exec'] env = params['env'] if 'previousHash' not in env: env['previousHash'] = encode_hex(db_env.config['GENESIS_PREVHASH']) assert set(env.keys()) == set(['currentGasLimit', 'currentTimestamp', 'previousHash', 'currentCoinbase', 'currentDifficulty', 'currentNumber']) # setup env header = blocks.BlockHeader( prevhash=decode_hex(env['previousHash']), number=parse_int_or_hex(env['currentNumber']), coinbase=decode_hex(env['currentCoinbase']), difficulty=parse_int_or_hex(env['currentDifficulty']), gas_limit=parse_int_or_hex(env['currentGasLimit']), timestamp=parse_int_or_hex(env['currentTimestamp'])) blk = blocks.Block(header, env=db_env) # setup state for address, h in list(pre.items()): assert len(address) == 40 address = decode_hex(address) assert set(h.keys()) == set(['code', 'nonce', 'balance', 'storage']) blk.set_nonce(address, parse_int_or_hex(h['nonce'])) blk.set_balance(address, parse_int_or_hex(h['balance'])) blk.set_code(address, decode_hex(h['code'][2:])) for k, v in h['storage'].items(): blk.set_storage_data(address, utils.big_endian_to_int(decode_hex(k[2:])), utils.big_endian_to_int(decode_hex(v[2:]))) # execute transactions sender = decode_hex(exek['caller']) # a party that originates a call recvaddr = decode_hex(exek['address']) nonce = blk._get_acct_item(sender, 'nonce') gasprice = parse_int_or_hex(exek['gasPrice']) startgas = parse_int_or_hex(exek['gas']) value = parse_int_or_hex(exek['value']) data = decode_hex(exek['data'][2:]) # bypass gas check in tx initialization by temporarily increasing startgas num_zero_bytes = str_to_bytes(data).count(ascii_chr(0)) num_non_zero_bytes = len(data) - num_zero_bytes intrinsic_gas = (opcodes.GTXCOST + opcodes.GTXDATAZERO * num_zero_bytes + opcodes.GTXDATANONZERO * num_non_zero_bytes) startgas += intrinsic_gas tx = transactions.Transaction(nonce=nonce, gasprice=gasprice, startgas=startgas, to=recvaddr, value=value, data=data) tx.startgas -= intrinsic_gas tx.sender = sender # capture apply_message calls apply_message_calls = [] orig_apply_msg = pb.apply_msg ext = pb.VMExt(blk, tx) def msg_wrapper(msg): hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append(dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=encode_hex(msg.to), data=b'0x' + hexdata)) return 1, msg.gas, b'' def create_wrapper(msg): sender = decode_hex(msg.sender) if \ len(msg.sender) == 40 else msg.sender nonce = utils.encode_int(ext._block.get_nonce(msg.sender)) addr = utils.sha3(rlp.encode([sender, nonce]))[12:] hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append(dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=b'', data=b'0x' + hexdata)) return 1, msg.gas, addr ext.msg = msg_wrapper ext.create = create_wrapper def blkhash(n): if n >= ext.block_number or n < ext.block_number - 256: return b'' else: return utils.sha3(to_string(n)) ext.block_hash = blkhash msg = vm.Message(tx.sender, tx.to, tx.value, tx.startgas, vm.CallData([safe_ord(x) for x in tx.data])) code = decode_hex(exek['code'][2:]) time_pre = time.time() if profiler: profiler.enable() success, gas_remained, output = vm.vm_execute(ext, msg, code) if profiler: profiler.disable() pb.apply_msg = orig_apply_msg blk.commit_state() for s in blk.suicides: blk.del_account(s) time_post = time.time() """ generally expected that the test implementer will read env, exec and pre then check their results against gas, logs, out, post and callcreates. If an exception is expected, then latter sections are absent in the test. Since the reverting of the state is not part of the VM tests. """ params2 = copy.deepcopy(params) if success: params2['callcreates'] = apply_message_calls params2['out'] = b'0x' + encode_hex(b''.join(map(ascii_chr, output))) params2['gas'] = to_string(gas_remained) params2['logs'] = [log.to_dict() for log in blk.logs] params2['post'] = blk.to_dict(with_state=True)['state'] if mode == FILL: return params2 elif mode == VERIFY: if not success: assert 'post' not in params, 'failed, but expected to succeed' params1 = copy.deepcopy(params) shouldbe, reallyis = params1.get('post', None), params2.get('post', None) compare_post_states(shouldbe, reallyis) def normalize_value(k, p): if k in p: if k == 'gas': return parse_int_or_hex(p[k]) elif k == 'callcreates': return list(map(callcreate_standard_form, p[k])) else: return utils.to_string(k) return None for k in ['pre', 'exec', 'env', 'callcreates', 'out', 'gas', 'logs']: shouldbe = normalize_value(k, params1) reallyis = normalize_value(k, params2) if shouldbe != reallyis: raise Exception("Mismatch: " + k + ':\n shouldbe %r\n reallyis %r' % (shouldbe, reallyis)) elif mode == TIME: return time_post - time_pre
def apply_transaction(block, tx): validate_transaction(block, tx) log_tx.debug('TX NEW', tx_dict=tx.log_dict()) # start transacting ################# block.increment_nonce(tx.sender) # print block.get_nonce(tx.sender), '@@@' # buy startgas assert block.get_balance(tx.sender) >= tx.startgas * tx.gasprice block.delta_balance(tx.sender, -tx.startgas * tx.gasprice) message_gas = tx.startgas - intrinsic_gas_used(tx) message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data)) message = vm.Message(tx.sender, tx.to, tx.value, message_gas, message_data, code_address=tx.to) # MESSAGE ext = VMExt(block, tx) if tx.to and tx.to != CREATE_CONTRACT_ADDRESS: result, gas_remained, data = apply_msg(ext, message) log_tx.debug('_res_', result=result, gas_remained=gas_remained, data=data) else: # CREATE result, gas_remained, data = create_contract(ext, message) assert utils.is_numeric(gas_remained) log_tx.debug('_create_', result=result, gas_remained=gas_remained, data=data) assert gas_remained >= 0 log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained, data=data) if not result: # 0 = OOG failure in both cases log_tx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) block.gas_used += tx.startgas block.delta_balance(block.coinbase, tx.gasprice * tx.startgas) output = b'' success = 0 else: log_tx.debug('TX SUCCESS', data=data) gas_used = tx.startgas - gas_remained block.refunds += len(set(block.suicides)) * opcodes.GSUICIDEREFUND if block.refunds > 0: log_tx.debug('Refunding', gas_refunded=min(block.refunds, gas_used // 2)) gas_remained += min(block.refunds, gas_used // 2) gas_used -= min(block.refunds, gas_used // 2) block.refunds = 0 # sell remaining gas block.delta_balance(tx.sender, tx.gasprice * gas_remained) block.delta_balance(block.coinbase, tx.gasprice * gas_used) block.gas_used += gas_used if tx.to: output = b''.join(map(ascii_chr, data)) else: output = data success = 1 block.commit_state() suicides = block.suicides block.suicides = [] for s in suicides: block.ether_delta -= block.get_balance(s) block.set_balance(s, 0) block.del_account(s) block.add_transaction_to_list(tx) block.logs = [] return success, output
def apply_msg(block, tx, msg, code): msg = Message(msg.sender, msg.to, msg.value, msg.gas, bytearray_to_32s( [safe_ord(x) for x in msg.data]), len(msg.data)) # print '### applying msg ###' callstack = [] msgtop, mem, stk, ops, index = None, None, None, [], [None] done = [] # Does anything special need to be done after this code chunk? special = [None, None, None, None, None, None] # To be called immediately when a new message is initiated def initialize(msg, code): # print 'init', extract_bytes(msg.data, 0, msg.databytes), msg.gas, # msg.sender, block.get_nonce(msg.sender) callstack.append(msg) # Transfer value, instaquit if not enough o = block.transfer_value(msg.sender, msg.to, msg.value) if not o: msg.callback(1, msg.gas, []) return msg.snapshot = block.snapshot() msg.compustate = Compustate(gas=msg.gas) if code in code_cache: msg.processed_code = code_cache[code] else: msg.processed_code = preprocess_vmcode(code) code_cache[code] = msg.processed_code initialize(msg, code) # To be called immediately when a message returns def drop(output, outputlength=0): special[0] = 'drop' special[1] = output special[2] = outputlength # The actual drop function, called by chunk finalizing code way # at the bottom below def drop2(o, l=0): # print 'dropping', extract_bytes(o, 0, l) if len(callstack) > 1: if o == OUT_OF_GAS: block.revert(msgtop.snapshot) msgtop.callback(0, msgtop.compustate.gas, [], 0) else: msgtop.callback(1, msgtop.compustate.gas, o, l) callstack.pop() else: if o == OUT_OF_GAS: block.revert(msgtop.snapshot) done.extend([0, msgtop.compustate.gas, []]) else: done.extend([1, msgtop.compustate.gas, extract_bytes(o, 0, l)]) # Generates callback functions for messages that create contracts def contract_callback_factory(): def cb(res, gas, dat, databytes): if res: b = extract_bytes(dat, 0, databytes) block.set_code(callstack[-1].to, ''.join([ascii_chr(x) for x in b])) res = utils.coerce_to_int(callstack[-1].to) else: if tx.sender != callstack[-1].sender: block.decrement_nonce(callstack[-1].sender) block.del_account(callstack[-1].to) callstack[-2].compustate.stack.append(res) callstack[-2].compustate.gas = gas callstack[-1].callback = cb # Generates callback functions for normal messages. Take the output start # and end bytes to put return data into parent memory def callback_factory(memoutstart, memoutsz): def cb(res, gas, dat, databytes): if res == 0: callstack[-2].compustate.stack.append(0) else: callstack[-2].compustate.stack.append(1) callstack[-2].compustate.gas += gas if len(dat) * 32 < memoutsz: dat += [0] * (memoutsz - (len(dat) // 32) + 1) copy32(dat, callstack[-2].compustate.memory, 0, memoutstart, databytes) callstack[-1].callback = cb # Functions to calculate the current amount of gas left and # the current PC def gaz(): g = msgtop.compustate.gas for i in range(index[0]): g -= code_chunk['opdata'][i][3] return g gas = gaz def pc(): return code_chunk["opdata"][index[0]][5] # Functions to handle the individual operations def OP_STOP(): drop([]) def OP_ADD(): stk.append((stk.pop() + stk.pop()) & TT256M1) def OP_SUB(): stk.append((stk.pop() - stk.pop()) & TT256M1) def OP_MUL(): stk.append((stk.pop() * stk.pop()) & TT256M1) def OP_DIV(): s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 // s1) def OP_MOD(): s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 % s1) def OP_SDIV(): s0, s1 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) def OP_SMOD(): s0, s1 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) def OP_INVALID(): drop([]) def OP_PUSHN(): stk.append(code_chunk['opdata'][index[0]][6]) def OP_DUPN(): stk.append(stk[-(ops[index[0]][1] - 0x7f)]) def OP_SWAPN(): depth = ops[index[0]][1] - 0x8f temp = stk[-depth - 1] stk[-depth - 1] = stk[-1] stk[-1] = temp def OP_CREATE(): value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, msgtop.compustate, '', mstart, msz): return drop(OUT_OF_GAS) if block.get_balance(msgtop.to) >= value: sender = decode_hex(msgtop.to) if len(msgtop.to) == 40 else msgtop.to block.increment_nonce(msgtop.to) data = [0] * ((msz >> 5) + 1) copy32(mem, data, mstart, 0, msz) create_msg = Message(msgtop.to, '', value, gaz() - 100, data, msz) msgtop.compustate.gas -= gaz() - 100 nonce = utils.encode_int(block.get_nonce(msgtop.to) - 1) create_msg.to = encode_hex(utils.sha3(rlp.encode([sender, nonce]))[12:]) special[0] = 'create' special[1] = create_msg special[2] = ''.join([ascii_chr(x) for x in extract_bytes(data, 0, msz)]) else: stk.append(0) def OP_CALL(): subgas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if max(meminstart + meminsz, memoutstart + memoutsz) > len(mem) << 5: if not mem_extend(mem, msgtop.compustate, '', meminstart, meminsz) or \ not mem_extend(mem, msgtop.compustate, '', memoutstart, memoutsz) or \ msgtop.compustate.gas < subgas: return drop(OUT_OF_GAS) msgtop.compustate.gas -= subgas if block.get_balance(msgtop.to) >= value: data = [0] * ((meminsz >> 5) + 1) copy32(mem, data, meminstart, 0, meminsz) to = utils.int_to_addr(to) call_msg = Message(msgtop.to, to, value, subgas, data, meminsz) special[0] = 'call' special[1] = call_msg special[2] = block.get_code(to) special[3] = memoutstart special[4] = memoutsz else: stk.append(0) def OP_RETURN(): s0, s1 = stk.pop(), stk.pop() if s1: if not mem_extend(mem, msgtop.compustate, '', s0, s1): return drop(OUT_OF_GAS) o = [0] * ((s1 >> 5) + 1) copy32(mem, o, s0, 0, s1) drop(o, s1) def OP_CALLCODE(): subgas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, msgtop.compustate, '', meminstart, meminsz) or \ not mem_extend(mem, msgtop.compustate, '', memoutstart, memoutsz): return drop(OUT_OF_GAS) if msgtop.compustate.gas < subgas: return drop(out_of_gas_exception('subcall gas', gas, msgtop.compustate, '')) msgtop.compustate.gas -= subgas data = [0] * ((meminsz >> 5) + 1) copy32(mem, data, meminstart, 0, meminsz) call_msg = Message(msgtop.to, msgtop.to, value, subgas, data, meminsz) special[0] = 'call' special[1] = call_msg special[2] = block.get_code(utils.int_to_addr(to)) special[3] = memoutstart special[4] = memoutsz def OP_SUICIDE(): to = utils.encode_int(stk.pop()) to = encode_hex((('\x00' * (32 - len(to))) + to)[12:]) block.transfer_value(msgtop.to, to, block.get_balance(msgtop.to)) block.suicides.append(msgtop.to) drop([]) def OP_EXP(): stk.append(pow(stk.pop(), stk.pop(), TT256)) def OP_NEG(): stk.append(-stk.pop() & TT256M1) def OP_LT(): stk.append(1 if stk.pop() < stk.pop() else 0) def OP_GT(): stk.append(1 if stk.pop() > stk.pop() else 0) def OP_SLT(): s0, s1 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(1 if s0 < s1 else 0) def OP_SGT(): s0, s1 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(1 if s0 > s1 else 0) def OP_EQ(): stk.append(1 if stk.pop() == stk.pop() else 0) def OP_NOT(): stk.append(0 if stk.pop() else 1) def OP_AND(): stk.append(stk.pop() & stk.pop()) def OP_OR(): stk.append(stk.pop() | stk.pop()) def OP_XOR(): stk.append(stk.pop() ^ stk.pop()) def OP_BYTE(): s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 // 256 ** (31 - s0)) % 256) def OP_ADDMOD(): s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 + s1) % s2 if s2 else 0) def OP_MULMOD(): s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 * s1) % s2 if s2 else 0) def OP_SHA3(): s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, msgtop.compustate, '', s0, s1): return drop(OUT_OF_GAS) data = ''.join([ascii_chr(x) for x in mem[s0: s0 + s1]]) stk.append(utils.big_endian_to_int(utils.sha3(data))) def OP_ADDRESS(): stk.append(utils.coerce_to_int(msg.to)) def OP_BALANCE(): stk.append(block.get_balance(utils.coerce_addr_to_hex(stk.pop()))) def OP_ORIGIN(): stk.append(utils.coerce_to_int(tx.sender)) def OP_CALLER(): stk.append(utils.coerce_to_int(msg.sender)) def OP_CALLVALUE(): stk.append(msg.value) def OP_CALLDATALOAD(): s0 = stk.pop() if s0 >= msgtop.databytes: stk.append(0) else: stk.append(load32(msgtop.data, s0)) def OP_CALLDATASIZE(): stk.append(msgtop.databytes) def OP_CALLDATACOPY(): s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, msgtop.compustate, '', s0, s2): return drop(OUT_OF_GAS) if s1 + s2 > (len(msgtop.data) << 5): msgtop.data.extend([0] * (((s1 + s2) >> 5) - len(msgtop.data) + 1)) copy32(msgtop.data, mem, s1, s0, s2) def OP_CODESIZE(): stk.append(len(msgtop.processed_code)) def OP_CODECOPY(): s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() if s2: if not mem_extend(mem, msgtop.compustate, '', s0, s2): return drop(OUT_OF_GAS) copy_chunk = msgtop.processed_code["code"][s1: s1 + s2] copy_chunk += [0] * (s2 - len(copy_chunk)) set_bytes(mem, s0, copy_chunk) def OP_GASPRICE(): stk.append(tx.gasprice) def OP_EXTCODESIZE(): stk.append(len(block.get_code(utils.coerce_addr_to_hex(stk.pop())) or '')) def OP_EXTCODECOPY(): addr, s1, s2, s3 = stk.pop(), stk.pop(), stk.pop(), stk.pop() extcode = block.get_code(utils.coerce_addr_to_hex(addr)) or '' if not mem_extend(mem, msgtop.compustate, '', s1, s3): return drop(OUT_OF_GAS) for i in range(s3): if s2 + i < len(extcode): mem[s1 + i] = safe_ord(extcode[s2 + i]) else: mem[s1 + i] = 0 def OP_PREVHASH(): stk.append(utils.big_endian_to_int(block.prevhash)) def OP_COINBASE(): stk.append(utils.big_endian_to_int(decode_hex(block.coinbase))) def OP_TIMESTAMP(): stk.append(block.timestamp) def OP_NUMBER(): stk.append(block.number) def OP_DIFFICULTY(): stk.append(block.difficulty) def OP_GASLIMIT(): stk.append(block.gas_limit) def OP_POP(): stk.pop() def OP_MLOAD(): s0 = stk.pop() if (s0 + 32) > len(mem) << 5: if not mem_extend(mem, msgtop.compustate, '', s0, 32): return drop(OUT_OF_GAS) if not s0 % 32: stk.append(mem[s0 >> 5]) else: stk.append((mem[s0 >> 5] << (8 * (s0 % 32))) & TT256M1 + mem[s0 >> 5 + 1] >> (256 - 8 * (s0 % 32))) def OP_MSTORE(): s0, s1 = stk.pop(), stk.pop() if (s0 + 32) > len(mem) << 5: if not mem_extend(mem, msgtop.compustate, '', s0, 32): return drop(OUT_OF_GAS) if not s0 % 32: mem[s0 >> 5] = s1 else: mem[s0 >> 5] = mem[s0 >> 5] & ( TT256 - (TT256 >> (8 * (s0 % 32)))) + s1 >> (8 * (s0 % 32)) mem[s0 >> 5 + 1] = mem[s0 >> 5 + 1] & ((TT256 >> (8 * (s0 % 32))) - 1) + (s1 << (256 - 8 * (s0 % 32))) & TT256M1 def OP_MSTORE8(): s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, msgtop.compustate, '', s0, 1): return drop(OUT_OF_GAS) a = mem[s0 >> 5] mem[s0 >> 5] = (a ^ (a & ((1 << (256 - 8 * (s0 % 32))) * 255))) + \ s1 << (256 - 8 * (s0 % 32)) def OP_SLOAD(): stk.append(block.get_storage_data(msgtop.to, stk.pop())) def OP_SSTORE(): s0, s1 = stk.pop(), stk.pop() pre_occupied = GSTORAGE if block.get_storage_data(msgtop.to, s0) else 0 post_occupied = GSTORAGE if s1 else 0 gascost = GSTORAGE + post_occupied - pre_occupied if msgtop.compustate.gas < gascost: return drop(out_of_gas_exception('sstore trie expansion', gascost, msgtop.compustate, '')) msgtop.compustate.gas -= gascost block.set_storage_data(msgtop.to, s0, s1) def OP_JUMP(): special[0] = 'jump' special[1] = stk.pop() + 1 op = msgtop.processed_code.get(special[1], {"jumpable": None}) if not op["jumpable"]: return drop([]) msgtop.compustate.gas -= 1 def OP_JUMPI(): s0, s1 = stk.pop(), stk.pop() if s1: special[0] = 'jump' special[1] = s0 + 1 op = msgtop.processed_code.get(special[1], {"jumpable": None}) if not op["jumpable"]: return drop([]) msgtop.compustate.gas -= 1 def OP_PC(): stk.append(pc()) def OP_MSIZE(): stk.append(len(mem) << 5) def OP_GAS(): stk.append(gas() - 1) def OP_JUMPDEST(): pass op_map = [ # 0x00 OP_STOP, OP_ADD, OP_MUL, OP_SUB, OP_DIV, OP_SDIV, OP_MOD, OP_SMOD, OP_EXP, OP_NEG, OP_LT, OP_GT, OP_SLT, OP_SGT, OP_EQ, OP_NOT, # 0x10 OP_AND, OP_OR, OP_XOR, OP_BYTE, OP_ADDMOD, OP_MULMOD, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, # 0x20 OP_SHA3, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, # 0x30: OP_ADDRESS, OP_BALANCE, OP_ORIGIN, OP_CALLER, OP_CALLVALUE, OP_CALLDATALOAD, OP_CALLDATASIZE, OP_CALLDATACOPY, OP_CODESIZE, OP_CODECOPY, OP_GASPRICE, OP_EXTCODESIZE, OP_EXTCODECOPY, OP_INVALID, OP_INVALID, OP_INVALID, # 0x40: OP_PREVHASH, OP_COINBASE, OP_TIMESTAMP, OP_NUMBER, OP_DIFFICULTY, OP_GASLIMIT, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, # 0x50 OP_POP, OP_INVALID, OP_INVALID, OP_MLOAD, OP_MSTORE, OP_MSTORE8, OP_SLOAD, OP_SSTORE, OP_JUMP, OP_JUMPI, OP_PC, OP_MSIZE, OP_GAS, OP_JUMPDEST, OP_INVALID, OP_INVALID, ] + [OP_PUSHN] * 32 + [OP_DUPN] * 16 + [OP_SWAPN] * 16 + [OP_INVALID] * 80 + [ OP_CREATE, OP_CALL, OP_CALLCODE, OP_RETURN, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_INVALID, OP_SUICIDE ] # Main loop while not done: msgtop = callstack[-1] stk = msgtop.compustate.stack mem = msgtop.compustate.memory # print msgtop.processed_code if msgtop.compustate.pc not in msgtop.processed_code: drop2([]) continue code_chunk = msgtop.processed_code[msgtop.compustate.pc] # insufficient stack or base gas if len(stk) < code_chunk["reqh"] or \ msgtop.compustate.gas < code_chunk["gascost"]: drop2([]) ops = code_chunk["ops"] index = [0] # Run a code chunk for (index[0], op) in ops: op_map[op]() msgtop.compustate.gas -= code_chunk["gascost"] # insufficient extra gas if msgtop.compustate.gas < 0: drop2(out_of_gas_exception( 'surcharges', code_chunk["gascost"], msgtop.compustate, ops)) msgtop.compustate.pc = code_chunk["end"] # If we need to jump, return or call if special[0] is not None: if special[0] == 'drop': drop2(special[1], special[2]) elif special[0] == 'jump': msgtop.compustate.pc = special[1] elif special[0] == 'create': initialize(special[1], special[2]) contract_callback_factory() elif special[0] == 'call': initialize(special[1], special[2]) callback_factory(special[3], special[4]) special[0] = None return done
def apply_transaction(block, tx): validate_transaction(block, tx) # print(block.get_nonce(tx.sender), '@@@') def rp(what, actual, target): return "%r: %r actual:%r target:%r" % (tx, what, actual, target) intrinsic_gas = tx.intrinsic_gas_used if block.number >= block.config["HOMESTEAD_FORK_BLKNUM"]: assert tx.s * 2 < transactions.secpk1n if not tx.to or tx.to == CREATE_CONTRACT_ADDRESS: intrinsic_gas += opcodes.CREATE[3] if tx.startgas < intrinsic_gas: raise InsufficientStartGas(rp("startgas", tx.startgas, intrinsic_gas)) log_tx.debug("TX NEW", tx_dict=tx.log_dict()) # start transacting ################# block.increment_nonce(tx.sender) # buy startgas assert block.get_balance(tx.sender) >= tx.startgas * tx.gasprice block.delta_balance(tx.sender, -tx.startgas * tx.gasprice) message_gas = tx.startgas - intrinsic_gas message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data)) message = vm.Message(tx.sender, tx.to, tx.value, message_gas, message_data, code_address=tx.to) # MESSAGE ext = VMExt(block, tx) if tx.to and tx.to != CREATE_CONTRACT_ADDRESS: result, gas_remained, data = apply_msg(ext, message) log_tx.debug("_res_", result=result, gas_remained=gas_remained, data=lazy_safe_encode(data)) else: # CREATE result, gas_remained, data = create_contract(ext, message) assert utils.is_numeric(gas_remained) log_tx.debug("_create_", result=result, gas_remained=gas_remained, data=lazy_safe_encode(data)) assert gas_remained >= 0 log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained, data=lazy_safe_encode(data)) if not result: # 0 = OOG failure in both cases log_tx.debug("TX FAILED", reason="out of gas", startgas=tx.startgas, gas_remained=gas_remained) block.gas_used += tx.startgas block.delta_balance(block.coinbase, tx.gasprice * tx.startgas) output = b"" success = 0 else: log_tx.debug("TX SUCCESS", data=lazy_safe_encode(data)) gas_used = tx.startgas - gas_remained block.refunds += len(set(block.suicides)) * opcodes.GSUICIDEREFUND if block.refunds > 0: log_tx.debug("Refunding", gas_refunded=min(block.refunds, gas_used // 2)) gas_remained += min(block.refunds, gas_used // 2) gas_used -= min(block.refunds, gas_used // 2) block.refunds = 0 # sell remaining gas block.delta_balance(tx.sender, tx.gasprice * gas_remained) block.delta_balance(block.coinbase, tx.gasprice * gas_used) block.gas_used += gas_used if tx.to: output = b"".join(map(ascii_chr, data)) else: output = data success = 1 block.commit_state() suicides = block.suicides block.suicides = [] for s in suicides: block.ether_delta -= block.get_balance(s) block.set_balance(s, 0) block.del_account(s) block.add_transaction_to_list(tx) block.logs = [] return success, output
def apply_transaction(state, tx): state.logs = [] state.suicides = [] state.refunds = 0 validate_transaction(state, tx) intrinsic_gas = tx.intrinsic_gas_used if state.is_HOMESTEAD(): assert tx.s * 2 < transactions.secpk1n if not tx.to or tx.to == CREATE_CONTRACT_ADDRESS: intrinsic_gas += opcodes.CREATE[3] if tx.startgas < intrinsic_gas: raise InsufficientStartGas( rp(tx, 'startgas', tx.startgas, intrinsic_gas)) log_tx.debug('TX NEW', txdict=tx.to_dict()) # start transacting ################# if tx.sender != null_address: state.increment_nonce(tx.sender) # buy startgas assert state.get_balance(tx.sender) >= tx.startgas * tx.gasprice state.delta_balance(tx.sender, -tx.startgas * tx.gasprice) message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data)) message = vm.Message( tx.sender, tx.to, tx.value, tx.startgas - intrinsic_gas, message_data, code_address=tx.to) # MESSAGE ext = VMExt(state, tx) if tx.to != b'': result, gas_remained, data = apply_msg(ext, message) else: # CREATE result, gas_remained, data = create_contract(ext, message) assert gas_remained >= 0 log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained, data=data) gas_used = tx.startgas - gas_remained # Transaction failed if not result: log_tx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) state.delta_balance(tx.sender, tx.gasprice * gas_remained) state.delta_balance(state.block_coinbase, tx.gasprice * gas_used) output = b'' success = 0 # Transaction success else: log_tx.debug('TX SUCCESS', data=data) state.refunds += len(set(state.suicides)) * opcodes.GSUICIDEREFUND if state.refunds > 0: log_tx.debug( 'Refunding', gas_refunded=min( state.refunds, gas_used // 2)) gas_remained += min(state.refunds, gas_used // 2) gas_used -= min(state.refunds, gas_used // 2) state.refunds = 0 # sell remaining gas state.delta_balance(tx.sender, tx.gasprice * gas_remained) state.delta_balance(state.block_coinbase, tx.gasprice * gas_used) if tx.to: output = bytearray_to_bytestr(data) else: output = data success = 1 state.gas_used += gas_used # Clear suicides suicides = state.suicides state.suicides = [] for s in suicides: state.set_balance(s, 0) state.del_account(s) # Pre-Metropolis: commit state after every tx if not state.is_METROPOLIS() and not SKIP_MEDSTATES: state.commit() # Construct a receipt r = mk_receipt(state, success, state.logs) _logs = list(state.logs) state.logs = [] state.add_receipt(r) state.set_param('bloom', state.bloom | r.bloom) state.set_param('txindex', state.txindex + 1) return success, output
def apply_transaction(block, tx, cb=None, validate=True): eth_call = False def dummy_cb(*args, **kwargs): pass if cb is None: cb = dummy_cb eth_call = True if validate: validate_transaction(block, tx) log_tx.debug('TX NEW', tx_dict=tx.log_dict()) # start transacting ################# block.increment_nonce(tx.sender) intrinsic_gas = intrinsic_gas_used(tx) if block.number >= block.config['HOMESTEAD_FORK_BLKNUM']: assert tx.s * 2 < transactions.secpk1n if not tx.to or tx.to == CREATE_CONTRACT_ADDRESS: intrinsic_gas += opcodes.CREATE[3] if tx.startgas < intrinsic_gas: raise InsufficientStartGas(rp('startgas', tx.startgas, intrinsic_gas)) # buy startgas if validate: assert block.get_balance(tx.sender) >= tx.startgas * tx.gasprice block.delta_balance(tx.sender, -tx.startgas * tx.gasprice) message_gas = tx.startgas - intrinsic_gas message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data)) message = vm.Message(tx.sender, tx.to, tx.value, message_gas, message_data, code_address=tx.to) # MESSAGE ext = CapVMExt(block, tx, cb) if tx.to and tx.to != CREATE_CONTRACT_ADDRESS: result, gas_remained, data = ext._msg(message) log_tx.debug('_res_', result=result, gas_remained=gas_remained, data=data) else: # CREATE result, gas_remained, data = create_contract(ext, message) assert utils.is_numeric(gas_remained) log_tx.debug('_create_', result=result, gas_remained=gas_remained, data=data) assert gas_remained >= 0 log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained, data=data) if not result: # 0 = OOG failure in both cases log_tx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) block.gas_used += tx.startgas block.delta_balance(block.coinbase, tx.gasprice * tx.startgas) output = b'' success = 0 else: log_tx.debug('TX SUCCESS', data=data) gas_used = tx.startgas - gas_remained block.refunds += len(set(block.suicides)) * opcodes.GSUICIDEREFUND if block.refunds > 0: log_tx.debug('Refunding', gas_refunded=min(block.refunds, gas_used // 2)) gas_remained += min(block.refunds, gas_used // 2) gas_used -= min(block.refunds, gas_used // 2) block.refunds = 0 # sell remaining gas block.delta_balance(tx.sender, tx.gasprice * gas_remained) block.delta_balance(block.coinbase, tx.gasprice * gas_used) block.gas_used += gas_used if tx.to: output = b''.join(map(ascii_chr, data)) else: output = data success = 1 block.commit_state() suicides = block.suicides block.suicides = [] for s in suicides: block.ether_delta -= block.get_balance(s) block.set_balance(s, 0) block.del_account(s) block.add_transaction_to_list(tx) block.logs = [] return success, output
def vm_execute(ext, msg, code): # precompute trace flag # if we trace vm, we're in slow mode anyway trace_vm = log_vm_op.is_active('trace') compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory if code in code_cache: processed_code = code_cache[code] else: processed_code = preprocess_code(code) code_cache[code] = processed_code s = time.time() op = None steps = 0 _prevop = None # for trace only while 1: # print('op: ', op, time.time() - s) # s = time.time() # stack size limit error if compustate.pc not in processed_code: return vm_exception('INVALID START POINT') _data = processed_code[compustate.pc] gas, min_stack, max_stack, compustate.pc = _data[:4] ops = _data[4:] # out of gas error if gas > compustate.gas: return vm_exception('OUT OF GAS') # insufficient stack error if not (min_stack <= len(compustate.stack) <= max_stack): return vm_exception('INCOMPATIBLE STACK LENGTH', min_stack=min_stack, have=len(compustate.stack), max_stack=max_stack) # Apply operation compustate.gas -= gas for op in ops: if trace_vm: """ This diverges from normal logging, as we use the logging namespace only to decide which features get logged in 'eth.vm.op' i.e. tracing can not be activated by activating a sub like 'eth.vm.op.stack' """ trace_data = {} trace_data['stack'] = list(map(to_string, list(compustate.stack))) if _prevop in (op_MLOAD, op_MSTORE, op_MSTORE8, op_SHA3, op_CALL, op_CALLCODE, op_CREATE, op_CALLDATACOPY, op_CODECOPY, op_EXTCODECOPY): if len(compustate.memory) < 1024: trace_data['memory'] = \ b''.join([encode_hex(ascii_chr(x)) for x in compustate.memory]) else: trace_data['sha3memory'] = \ encode_hex(utils.sha3(''.join([ascii_chr(x) for x in compustate.memory]))) if _prevop in (op_SSTORE, op_SLOAD) or steps == 0: trace_data['storage'] = ext.log_storage(msg.to) # trace_data['gas'] = to_string(compustate.gas + fee) trace_data['inst'] = op trace_data['pc'] = to_string(compustate.pc - 1) if steps == 0: trace_data['depth'] = msg.depth trace_data['address'] = msg.to trace_data['op'] = op trace_data['steps'] = steps # if op[:4] == 'PUSH': # trace_data['pushvalue'] = pushval log_vm_op.trace('vm', **trace_data) steps += 1 _prevop = op # Invalid operation if op == INVALID: return vm_exception('INVALID OP', opcode=op) # Valid operations if op < 0x10: if op == op_STOP: return peaceful_exit('STOP', compustate.gas, []) elif op == op_ADD: stk.append((stk.pop() + stk.pop()) & TT256M1) elif op == op_SUB: stk.append((stk.pop() - stk.pop()) & TT256M1) elif op == op_MUL: stk.append((stk.pop() * stk.pop()) & TT256M1) elif op == op_DIV: s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 // s1) elif op == op_MOD: s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 % s1) elif op == op_SDIV: s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) elif op == op_SMOD: s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) elif op == op_ADDMOD: s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 + s1) % s2 if s2 else 0) elif op == op_MULMOD: s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 * s1) % s2 if s2 else 0) elif op == op_EXP: base, exponent = stk.pop(), stk.pop() # fee for exponent is dependent on its bytes # calc n bytes to represent exponent nbytes = len(utils.encode_int(exponent)) expfee = nbytes * opcodes.GEXPONENTBYTE if compustate.gas < expfee: compustate.gas = 0 return vm_exception('OOG EXPONENT') compustate.gas -= expfee stk.append(pow(base, exponent, TT256)) elif op == op_SIGNEXTEND: s0, s1 = stk.pop(), stk.pop() if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): stk.append(s1 | (TT256 - (1 << testbit))) else: stk.append(s1 & ((1 << testbit) - 1)) else: stk.append(s1) elif op < 0x20: if op == op_LT: stk.append(1 if stk.pop() < stk.pop() else 0) elif op == op_GT: stk.append(1 if stk.pop() > stk.pop() else 0) elif op == op_SLT: s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == op_SGT: s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(1 if s0 > s1 else 0) elif op == op_EQ: stk.append(1 if stk.pop() == stk.pop() else 0) elif op == op_ISZERO: stk.append(0 if stk.pop() else 1) elif op == op_AND: stk.append(stk.pop() & stk.pop()) elif op == op_OR: stk.append(stk.pop() | stk.pop()) elif op == op_XOR: stk.append(stk.pop() ^ stk.pop()) elif op == op_NOT: stk.append(TT256M1 - stk.pop()) elif op == op_BYTE: s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 // 256 ** (31 - s0)) % 256) elif op < 0x40: if op == op_SHA3: s0, s1 = stk.pop(), stk.pop() compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32) if compustate.gas < 0: return vm_exception('OOG PAYING FOR SHA3') if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[s0: s0 + s1])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == op_ADDRESS: stk.append(utils.coerce_to_int(msg.to)) elif op == op_BALANCE: addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(ext.get_balance(addr)) elif op == op_ORIGIN: stk.append(utils.coerce_to_int(ext.tx_origin)) elif op == op_CALLER: stk.append(utils.coerce_to_int(msg.sender)) elif op == op_CALLVALUE: stk.append(msg.value) elif op == op_CALLDATALOAD: stk.append(msg.data.extract32(stk.pop())) elif op == op_CALLDATASIZE: stk.append(msg.data.size) elif op == op_CALLDATACOPY: mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') msg.data.extract_copy(mem, mstart, dstart, size) elif op == op_CODESIZE: stk.append(len(code)) elif op == op_CODECOPY: start, s1, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s1 + i < len(code): mem[start + i] = utils.safe_ord(code[s1 + i]) else: mem[start + i] = 0 elif op == op_GASPRICE: stk.append(ext.tx_gasprice) elif op == op_EXTCODESIZE: addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(len(ext.get_code(addr) or b'')) elif op == op_EXTCODECOPY: addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) start, s2, size = stk.pop(), stk.pop(), stk.pop() extcode = ext.get_code(addr) or b'' assert utils.is_string(extcode) if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s2 + i < len(extcode): mem[start + i] = utils.safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 elif op < 0x50: if op == op_BLOCKHASH: stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop()))) elif op == op_COINBASE: stk.append(utils.big_endian_to_int(ext.block_coinbase)) elif op == op_TIMESTAMP: stk.append(ext.block_timestamp) elif op == op_NUMBER: stk.append(ext.block_number) elif op == op_DIFFICULTY: stk.append(ext.block_difficulty) elif op == op_GASLIMIT: stk.append(ext.block_gas_limit) elif op < 0x60: if op == op_POP: stk.pop() elif op == op_MLOAD: s0 = stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') data = 0 for c in mem[s0: s0 + 32]: data = (data << 8) + c stk.append(data) elif op == op_MSTORE: s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') v = s1 for i in range(31, -1, -1): mem[s0 + i] = v % 256 v //= 256 elif op == op_MSTORE8: s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 1): return vm_exception('OOG EXTENDING MEMORY') mem[s0] = s1 % 256 elif op == op_SLOAD: stk.append(ext.get_storage_data(msg.to, stk.pop())) elif op == op_SSTORE: s0, s1 = stk.pop(), stk.pop() if ext.get_storage_data(msg.to, s0): gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL refund = 0 if s1 else opcodes.GSTORAGEREFUND else: gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD refund = 0 if compustate.gas < gascost: 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) elif op == op_JUMP: compustate.pc = stk.pop() opnew = processed_code[compustate.pc][4] if \ compustate.pc in processed_code else op_STOP if opnew != op_JUMPDEST: return vm_exception('BAD JUMPDEST') elif op == op_JUMPI: s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 opnew = processed_code[compustate.pc][4] if \ compustate.pc in processed_code else op_STOP if opnew != op_JUMPDEST: return vm_exception('BAD JUMPDEST') elif op == op_PC: stk.append(compustate.pc - 1) elif op == op_MSIZE: stk.append(len(mem)) elif op == op_GAS: stk.append(compustate.gas) # AFTER subtracting cost 1 elif op_PUSH1 <= (op & 255) <= op_PUSH32: # Hide push value in high-order bytes of op stk.append(op >> 8) elif op_DUP1 <= op <= op_DUP16: depth = op - op_DUP1 + 1 stk.append(stk[-depth]) elif op_SWAP1 <= op <= op_SWAP16: depth = op - op_SWAP1 + 1 temp = stk[-depth - 1] stk[-depth - 1] = stk[-1] stk[-1] = temp elif op_LOG0 <= op <= op_LOG4: """ 0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4) b. Logs are kept track of during tx execution exactly the same way as suicides (except as an ordered list, not a set). Each log is in the form [address, [topic1, ... ], data] where: * address is what the ADDRESS opcode would output * data is mem[MEMSTART: MEMSTART + MEMSZ] * topics are as provided by the opcode c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN]. """ depth = op - op_LOG0 mstart, msz = stk.pop(), stk.pop() topics = [stk.pop() for x in range(depth)] compustate.gas -= msz * opcodes.GLOGBYTE if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[mstart: mstart + msz])) ext.log(msg.to, topics, data) log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data))) # print('LOG', msg.to, topics, list(map(ord, data))) elif op == op_CREATE: value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') if ext.get_balance(msg.to) >= value and msg.depth < 1024: cd = CallData(mem, mstart, msz) create_msg = Message(msg.to, b'', value, compustate.gas, cd, msg.depth + 1) o, gas, addr = ext.create(create_msg) if o: stk.append(utils.coerce_to_int(addr)) compustate.gas = gas else: stk.append(0) compustate.gas = 0 else: stk.append(0) elif op == op_CALL: gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \ (value > 0) * opcodes.GCALLVALUETRANSFER submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas+extra_gas) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == op_CALLCODE: gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas+extra_gas) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == op_RETURN: s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return peaceful_exit('RETURN', compustate.gas, mem[s0: s0 + s1]) elif op == op_SUICIDE: to = utils.encode_int(stk.pop()) to = ((b'\x00' * (32 - len(to))) + to)[12:] xfer = ext.get_balance(msg.to) ext.set_balance(to, ext.get_balance(to) + xfer) ext.set_balance(msg.to, 0) ext.add_suicide(msg.to) # print('suiciding %s %s %d' % (msg.to, to, xfer)) return 1, compustate.gas, []
def test_multiarg_code(): c = tester.Chain() x = c.contract(multiarg_code, language='serpent') o = x.kall([1, 2, 3], 4, [5, 6, 7], b"doge", 8) assert o == [862541, safe_ord('d') + safe_ord('o') + safe_ord('g'), 4]
def vm_execute(ext, msg, code): # precompute trace flag # if we trace vm, we're in slow mode anyway trace_vm = log_vm_op.is_active('trace') compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory if code in code_cache: processed_code = code_cache[code] else: processed_code = preprocess_code(code) code_cache[code] = processed_code codelen = len(processed_code) s = time.time() op = None steps = 0 _prevop = None # for trace only while 1: # print('op: ', op, time.time() - s) # s = time.time() # stack size limit error if compustate.pc >= codelen: return peaceful_exit('CODE OUT OF RANGE', compustate.gas, []) op, in_args, out_args, fee, opcode, pushval = \ processed_code[compustate.pc] # out of gas error if fee > compustate.gas: return vm_exception('OUT OF GAS') # empty stack error if in_args > len(compustate.stack): return vm_exception('INSUFFICIENT STACK', op=op, needed=to_string(in_args), available=to_string(len(compustate.stack))) if len(compustate.stack) - in_args + out_args > 1024: return vm_exception('STACK SIZE LIMIT EXCEEDED', op=op, pre_height=to_string(len(compustate.stack))) # Apply operation compustate.gas -= fee compustate.pc += 1 if trace_vm: """ This diverges from normal logging, as we use the logging namespace only to decide which features get logged in 'eth.vm.op' i.e. tracing can not be activated by activating a sub like 'eth.vm.op.stack' """ trace_data = {} trace_data['stack'] = list(map(to_string, list(compustate.stack))) if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL', 'CALLCODE', 'CREATE', 'CALLDATACOPY', 'CODECOPY', 'EXTCODECOPY'): if len(compustate.memory) < 1024: trace_data['memory'] = \ b''.join([encode_hex(ascii_chr(x)) for x in compustate.memory]) else: trace_data['sha3memory'] = \ encode_hex(utils.sha3(''.join([ascii_chr(x) for x in compustate.memory]))) if _prevop in ('SSTORE', 'SLOAD') or steps == 0: trace_data['storage'] = ext.log_storage(msg.to) trace_data['gas'] = to_string(compustate.gas + fee) trace_data['inst'] = opcode trace_data['pc'] = to_string(compustate.pc - 1) if steps == 0: trace_data['depth'] = msg.depth trace_data['address'] = msg.to trace_data['op'] = op trace_data['steps'] = steps if op[:4] == 'PUSH': trace_data['pushvalue'] = pushval log_vm_op.trace('vm', **trace_data) steps += 1 _prevop = op # Invalid operation if op == 'INVALID': return vm_exception('INVALID OP', opcode=opcode) # Valid operations if opcode < 0x10: if op == 'STOP': return peaceful_exit('STOP', compustate.gas, []) elif op == 'ADD': stk.append((stk.pop() + stk.pop()) & TT256M1) elif op == 'SUB': stk.append((stk.pop() - stk.pop()) & TT256M1) elif op == 'MUL': stk.append((stk.pop() * stk.pop()) & TT256M1) elif op == 'DIV': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 // s1) elif op == 'MOD': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 % s1) elif op == 'SDIV': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) elif op == 'SMOD': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) elif op == 'ADDMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 + s1) % s2 if s2 else 0) elif op == 'MULMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 * s1) % s2 if s2 else 0) elif op == 'EXP': base, exponent = stk.pop(), stk.pop() # fee for exponent is dependent on its bytes # calc n bytes to represent exponent nbytes = len(utils.encode_int(exponent)) expfee = nbytes * opcodes.GEXPONENTBYTE if compustate.gas < expfee: compustate.gas = 0 return vm_exception('OOG EXPONENT') compustate.gas -= expfee stk.append(pow(base, exponent, TT256)) elif op == 'SIGNEXTEND': s0, s1 = stk.pop(), stk.pop() if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): stk.append(s1 | (TT256 - (1 << testbit))) else: stk.append(s1 & ((1 << testbit) - 1)) else: stk.append(s1) elif opcode < 0x20: if op == 'LT': stk.append(1 if stk.pop() < stk.pop() else 0) elif op == 'GT': stk.append(1 if stk.pop() > stk.pop() else 0) elif op == 'SLT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == 'SGT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(1 if s0 > s1 else 0) elif op == 'EQ': stk.append(1 if stk.pop() == stk.pop() else 0) elif op == 'ISZERO': stk.append(0 if stk.pop() else 1) elif op == 'AND': stk.append(stk.pop() & stk.pop()) elif op == 'OR': stk.append(stk.pop() | stk.pop()) elif op == 'XOR': stk.append(stk.pop() ^ stk.pop()) elif op == 'NOT': stk.append(TT256M1 - stk.pop()) elif op == 'BYTE': s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 // 256 ** (31 - s0)) % 256) elif opcode < 0x40: if op == 'SHA3': s0, s1 = stk.pop(), stk.pop() compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32) if compustate.gas < 0: return vm_exception('OOG PAYING FOR SHA3') if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[s0: s0 + s1])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(ext.get_balance(addr)) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(ext.tx_origin)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': stk.append(msg.data.extract32(stk.pop())) elif op == 'CALLDATASIZE': stk.append(msg.data.size) elif op == 'CALLDATACOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') msg.data.extract_copy(mem, mstart, dstart, size) elif op == 'CODESIZE': stk.append(len(processed_code)) elif op == 'CODECOPY': start, s1, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s1 + i < len(processed_code): mem[start + i] = processed_code[s1 + i][4] else: mem[start + i] = 0 elif op == 'GASPRICE': stk.append(ext.tx_gasprice) elif op == 'EXTCODESIZE': addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(len(ext.get_code(addr) or b'')) elif op == 'EXTCODECOPY': addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) start, s2, size = stk.pop(), stk.pop(), stk.pop() extcode = ext.get_code(addr) or b'' assert utils.is_string(extcode) if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s2 + i < len(extcode): mem[start + i] = utils.safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 elif opcode < 0x50: if op == 'BLOCKHASH': stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop()))) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(ext.block_coinbase)) elif op == 'TIMESTAMP': stk.append(ext.block_timestamp) elif op == 'NUMBER': stk.append(ext.block_number) elif op == 'DIFFICULTY': stk.append(ext.block_difficulty) elif op == 'GASLIMIT': stk.append(ext.block_gas_limit) elif opcode < 0x60: if op == 'POP': stk.pop() elif op == 'MLOAD': s0 = stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[s0: s0 + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') v = s1 for i in range(31, -1, -1): mem[s0 + i] = v % 256 v //= 256 elif op == 'MSTORE8': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 1): return vm_exception('OOG EXTENDING MEMORY') mem[s0] = s1 % 256 elif op == 'SLOAD': stk.append(ext.get_storage_data(msg.to, stk.pop())) elif op == 'SSTORE': s0, s1 = stk.pop(), stk.pop() if ext.get_storage_data(msg.to, s0): gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL refund = 0 if s1 else opcodes.GSTORAGEREFUND else: gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD refund = 0 if compustate.gas < gascost: 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) elif op == 'JUMP': compustate.pc = stk.pop() opnew = processed_code[compustate.pc][0] if \ compustate.pc < len(processed_code) else 'STOP' if opnew != 'JUMPDEST': return vm_exception('BAD JUMPDEST') elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 opnew = processed_code[compustate.pc][0] if \ compustate.pc < len(processed_code) else 'STOP' if opnew != 'JUMPDEST': return vm_exception('BAD JUMPDEST') elif op == 'PC': stk.append(compustate.pc - 1) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(compustate.gas) # AFTER subtracting cost 1 elif op[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc += pushnum stk.append(pushval) elif op[:3] == 'DUP': depth = int(op[3:]) stk.append(stk[-depth]) elif op[:4] == 'SWAP': depth = int(op[4:]) temp = stk[-depth - 1] stk[-depth - 1] = stk[-1] stk[-1] = temp elif op[:3] == 'LOG': """ 0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4) b. Logs are kept track of during tx execution exactly the same way as suicides (except as an ordered list, not a set). Each log is in the form [address, [topic1, ... ], data] where: * address is what the ADDRESS opcode would output * data is mem[MEMSTART: MEMSTART + MEMSZ] * topics are as provided by the opcode c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN]. """ depth = int(op[3:]) mstart, msz = stk.pop(), stk.pop() topics = [stk.pop() for x in range(depth)] compustate.gas -= msz * opcodes.GLOGBYTE if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') data = b''.join(map(ascii_chr, mem[mstart: mstart + msz])) ext.log(msg.to, topics, data) log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data))) # print('LOG', msg.to, topics, list(map(ord, data))) elif op == 'CREATE': value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') if ext.get_balance(msg.to) >= value and msg.depth < 1024: cd = CallData(mem, mstart, msz) create_msg = Message(msg.to, b'', value, compustate.gas, cd, msg.depth + 1) o, gas, addr = ext.create(create_msg) if o: stk.append(utils.coerce_to_int(addr)) compustate.gas = gas else: stk.append(0) compustate.gas = 0 else: stk.append(0) elif op == 'CALL': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \ (value > 0) * opcodes.GCALLVALUETRANSFER submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas+extra_gas) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == 'CALLCODE' or op == 'DELEGATECALL': if op == 'CALLCODE': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() else: gas, to, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() value = 0 if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER submsg_gas = gas + opcodes.GSTIPEND * (value > 0) if compustate.gas < gas + extra_gas: return vm_exception('OUT OF GAS', needed=gas+extra_gas) if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= (gas + extra_gas) to = utils.encode_int(to) to = ((b'\x00' * (32 - len(to))) + to)[12:] cd = CallData(mem, meminstart, meminsz) if ext.post_homestead_hardfork() and op == 'DELEGATECALL': call_msg = Message(msg.sender, msg.to, msg.value, submsg_gas, cd, msg.depth + 1, code_address=to, transfers_value=False) elif op == 'DELEGATECALL': return vm_exception('OPCODE INACTIVE') else: call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to) result, gas, data = ext.msg(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: compustate.gas -= (gas + extra_gas - submsg_gas) stk.append(0) elif op == 'RETURN': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return peaceful_exit('RETURN', compustate.gas, mem[s0: s0 + s1]) elif op == 'SUICIDE': to = utils.encode_int(stk.pop()) to = ((b'\x00' * (32 - len(to))) + to)[12:] xfer = ext.get_balance(msg.to) ext.set_balance(to, ext.get_balance(to) + xfer) ext.set_balance(msg.to, 0) ext.add_suicide(msg.to) # print('suiciding %s %s %d' % (msg.to, to, xfer)) return 1, compustate.gas, []