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 >= secp256k1n or s >= secp256k1n or v < 27 or v > 28: return 1, msg.gas - opcodes.GECRECOVER, [] try: pub = utils.ecrecover_to_pub(message_hash, v, r, s) except Exception as e: return 1, msg.gas - gas_cost, [] o = [0] * 12 + [safe_ord(x) for x in utils.sha3(pub)[-20:]] return 1, msg.gas - gas_cost, o
def mk_contract_address(sender, nonce, full_shard_key): if full_shard_key is not None: to_encode = [utils.normalize_address(sender), full_shard_key, nonce] else: # only happens for backward-compatible EVM tests to_encode = [utils.normalize_address(sender), nonce] return utils.sha3(rlp.encode(to_encode))[12:]
def _decode_keystore_json(jsondata, password): # Get key derivation function (kdf) and parameters kdfparams = jsondata["crypto"]["kdfparams"] # Compute derived key derivedkey = pbkdf2.PBKDF2(password, decode_hex(kdfparams["salt"]), kdfparams["c"], SHA256).read(kdfparams["dklen"]) assert len( derivedkey) >= 32, "Derived key must be at least 32 bytes long" # Get cipher and parameters and decrypt using AES cipherparams = jsondata["crypto"]["cipherparams"] enckey = derivedkey[:16] iv = int.from_bytes(decode_hex(cipherparams["iv"]), byteorder="big") ctr = Counter.new(128, initial_value=iv, allow_wraparound=True) encryptor = AES.new(enckey, AES.MODE_CTR, counter=ctr) ctext = decode_hex(jsondata["crypto"]["ciphertext"]) o = encryptor.decrypt(ctext) # Compare the provided MAC with a locally computed MAC mac1 = sha3(derivedkey[16:32] + ctext) mac2 = decode_hex(jsondata["crypto"]["mac"]) if mac1 != mac2: raise ValueError("MAC mismatch. Password incorrect?") return o
def vm_trace(ext, msg, compustate, opcode, pushcache, tracer=log_vm_op): """ 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' """ op, in_args, out_args, fee = opcodes.opcodes[opcode] trace_data = {} trace_data["stack"] = list(map(to_string, list(compustate.prev_stack))) if compustate.prev_prev_op in ( "MLOAD", "MSTORE", "MSTORE8", "SHA3", "CALL", "CALLCODE", "CREATE", "CALLDATACOPY", "CODECOPY", "EXTCODECOPY", ): if len(compustate.prev_memory) < 4096: trace_data["memory"] = "".join( [encode_hex(ascii_chr(x)) for x in compustate.prev_memory] ) else: trace_data["sha3memory"] = encode_hex( utils.sha3(b"".join([ascii_chr(x) for x in compustate.prev_memory])) ) if compustate.prev_prev_op in ("SSTORE",) or compustate.steps == 0: trace_data["storage"] = ext.log_storage(msg.to) trace_data["gas"] = to_string(compustate.prev_gas) trace_data["gas_cost"] = to_string(compustate.prev_gas - compustate.gas) trace_data["fee"] = fee trace_data["inst"] = opcode trace_data["pc"] = to_string(compustate.prev_pc) if compustate.steps == 0: trace_data["depth"] = msg.depth trace_data["address"] = msg.to trace_data["steps"] = compustate.steps trace_data["depth"] = msg.depth if op[:4] == "PUSH": print(repr(pushcache)) trace_data["pushvalue"] = pushcache[compustate.prev_pc] tracer.trace("vm", op=op, **trace_data) compustate.steps += 1 compustate.prev_prev_op = op
def _make_keystore_json(self, password): """ Generate the keystore json that follows the Version 3 specification: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#definition Uses pbkdf2 for password encryption, and AES-128-CTR as the cipher. """ # Get the hash function and default parameters kdfparams = { "prf": "hmac-sha256", "dklen": 32, "c": 262144, "salt": encode_hex(os.urandom(16)), } # Compute derived key derivedkey = pbkdf2.PBKDF2(password, decode_hex(kdfparams["salt"]), kdfparams["c"], SHA256).read(kdfparams["dklen"]) # Produce the encryption key and encrypt using AES enckey = derivedkey[:16] cipherparams = {"iv": encode_hex(os.urandom(16))} iv = int.from_bytes(decode_hex(cipherparams["iv"]), byteorder="big") ctr = Counter.new(128, initial_value=iv, allow_wraparound=True) encryptor = AES.new(enckey, AES.MODE_CTR, counter=ctr) c = encryptor.encrypt(self.identity.key) # Compute the MAC mac = sha3(derivedkey[16:32] + c) # Return the keystore json return { "crypto": { "cipher": "aes-128-ctr", "ciphertext": encode_hex(c), "cipherparams": cipherparams, "kdf": "pbkdf2", "kdfparams": kdfparams, "mac": encode_hex(mac), "version": 1, }, "id": self.uuid, "version": 3, }
def vm_trace(ext, msg, compustate, opcode, pushcache, tracer=log_vm_op): """ 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' """ op, in_args, out_args, fee = opcodes.opcodes[opcode] trace_data = {} trace_data['stack'] = list(map(to_string, list(compustate.prev_stack))) if compustate.prev_prev_op in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL', 'CALLCODE', 'CREATE', 'CALLDATACOPY', 'CODECOPY', 'EXTCODECOPY'): if len(compustate.prev_memory) < 4096: trace_data['memory'] = \ ''.join([encode_hex(ascii_chr(x)) for x in compustate.prev_memory]) else: trace_data['sha3memory'] = \ encode_hex(utils.sha3(b''.join([ascii_chr(x) for x in compustate.prev_memory]))) if compustate.prev_prev_op in ('SSTORE', ) or compustate.steps == 0: trace_data['storage'] = ext.log_storage(msg.to) trace_data['gas'] = to_string(compustate.prev_gas) trace_data['gas_cost'] = to_string(compustate.prev_gas - compustate.gas) trace_data['fee'] = fee trace_data['inst'] = opcode trace_data['pc'] = to_string(compustate.prev_pc) if compustate.steps == 0: trace_data['depth'] = msg.depth trace_data['address'] = msg.to trace_data['steps'] = compustate.steps trace_data['depth'] = msg.depth if op[:4] == 'PUSH': print(repr(pushcache)) trace_data['pushvalue'] = pushcache[compustate.prev_pc] tracer.trace('vm', op=op, **trace_data) compustate.steps += 1 compustate.prev_prev_op = op
def code(self, value): self.code_hash = utils.sha3(value) # Technically a db storage leak, but doesn't really matter; the only # thing that fails to get garbage collected is when code disappears due # to a suicide self.db.put(self.code_hash, value)
decode_hex, sha3, is_string, is_numeric, BigEndianInt, ) from quarkchain.evm import utils from quarkchain.evm import trie from quarkchain.evm.trie import Trie from quarkchain.evm.securetrie import SecureTrie from quarkchain.evm.config import Env from quarkchain.db import Db, OverlayDb from quarkchain.evm.common import FakeHeader import copy BLANK_HASH = utils.sha3(b"") BLANK_ROOT = utils.sha3rlp(b"") THREE = b"\x00" * 19 + b"\x03" def snapshot_form(val): if is_numeric(val): return str(val) elif is_string(val): return "0x" + encode_hex(val) STATE_DEFAULTS = { "txindex": 0, "gas_used": 0,
def mk_contract_address(sender, full_shard_key, nonce): return utils.sha3( rlp.encode([utils.normalize_address(sender), full_shard_key, nonce]))[12:]
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") # Initialize stack, memory, program counter, etc compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory # Compute jumpdest_mask, pushcache = preprocess_code(code) codelen = len(code) # For tracing purposes op = None _prevop = None steps = 0 while compustate.pc < codelen: opcode = safe_ord(code[compustate.pc]) # Invalid operation if opcode not in opcodes.opcodes: return vm_exception("INVALID OP", opcode=opcode) op, in_args, out_args, fee = opcodes.opcodes[opcode] # Apply operation if trace_vm: compustate.reset_prev() compustate.gas -= fee compustate.pc += 1 # Tracing 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) < 4096: 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 + 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["steps"] = steps trace_data["depth"] = msg.depth if op[:4] == "PUSH": trace_data["pushvalue"] = pushcache[compustate.pc - 1] log_vm_op.trace("vm", op=op, **trace_data) steps += 1 _prevop = op # out of gas error if compustate.gas < 0: 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)), ) # overfull stack error 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)), ) # Valid operations # Pushes first because they are very frequent if 0x60 <= opcode <= 0x7F: stk.append(pushcache[compustate.pc - 1]) # Move 1 byte forward for 0x60, up to 32 bytes for 0x7f compustate.pc += opcode - 0x5F # Arithmetic 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 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) # Comparisons 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 op == "SHL": s0, s1 = stk.pop(), stk.pop() stk.append(0 if s0 >= 256 else (s1 << s0) & TT256M1) elif op == "SHR": s0, s1 = stk.pop(), stk.pop() stk.append(0 if s0 >= 256 else s1 >> s0) elif op == "SAR": s0, s1 = stk.pop(), utils.to_signed(stk.pop()) if s0 >= 256: ret = 0 if s1 >= 0 else TT256M1 else: ret = (s1 >> s0) & TT256M1 stk.append(ret) # SHA3 and environment info 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": 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(codelen) 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 < codelen: mem[mstart + i] = 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[ dstart : dstart + size ] elif op == "RETURNDATASIZE": stk.append(len(compustate.last_returned)) 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] = safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 elif op == "EXTCODEHASH": addr = utils.coerce_addr_to_hex(stk.pop() % 2 ** 160) if not ext.account_exists(addr): stk.append(0) else: extcode = ext.get_code(addr) or b"" assert utils.is_string(extcode) stk.append(utils.big_endian_to_int(utils.sha3(extcode))) # Block info 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) # VM state manipulations 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": 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() if compustate.pc >= codelen or not ( (1 << compustate.pc) & jumpdest_mask ): return vm_exception("BAD JUMPDEST") elif op == "JUMPI": s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 if compustate.pc >= codelen or not ( (1 << compustate.pc) & jumpdest_mask ): 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 # DUPn (eg. DUP1: a b c -> a b c c, DUP3: a b c -> a b c a) elif op[:3] == "DUP": # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for 0x8f stk.append(stk[0x7F - opcode]) # SWAPn (eg. SWAP1: a b c d -> a b d c, SWAP3: a b c d -> d b c a) 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 # Logs (aka "events") 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)) ) # Create a new contract elif op in ("CREATE", "CREATE2"): salt = None if op == "CREATE": value, mstart, msz = stk.pop(), stk.pop(), stk.pop() else: # CREATE2 value, mstart, msz, salt = stk.pop(), stk.pop(), stk.pop(), stk.pop() salt = salt.to_bytes(32, byteorder="big") compustate.gas -= opcodes.GSHA3WORD * ceil(msz / 32) 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 ingas = all_but_1n(ingas, opcodes.CALL_CHILD_LIMIT_DENOM) create_msg = Message( msg.to, b"", value, ingas, cd, msg.depth + 1, # Used for calculating contract address to_full_shard_key=msg.to_full_shard_key, transfer_token_id=msg.transfer_token_id, gas_token_id=msg.gas_token_id, ) o, gas, data = ext.create(create_msg, salt) if o: stk.append(utils.coerce_to_int(data)) compustate.last_returned = bytearray(b"") else: stk.append(0) compustate.last_returned = bytearray(data) compustate.gas = compustate.gas - ingas + gas else: stk.append(0) compustate.last_returned = bytearray(b"") # Calls 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 and op == "CALL": 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 various factors extra_gas = 0 # Creating a new account if op == "CALL" and not ext.account_exists(to) and (value > 0): extra_gas += opcodes.GCALLNEWACCOUNT # Value transfer if value > 0: extra_gas += opcodes.GCALLVALUETRANSFER # Compute child gas limit 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), ) 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) compustate.last_returned = bytearray(b"") 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, # Used for calculating contract address to_full_shard_key=msg.to_full_shard_key, code_address=to, static=msg.static, transfer_token_id=msg.transfer_token_id, gas_token_id=msg.gas_token_id, ) elif op == "DELEGATECALL": call_msg = Message( msg.sender, msg.to, msg.value, submsg_gas, cd, msg.depth + 1, # Used for calculating contract address to_full_shard_key=msg.to_full_shard_key, code_address=to, transfers_value=False, static=msg.static, transfer_token_id=msg.transfer_token_id, gas_token_id=msg.gas_token_id, ) elif op == "STATICCALL": call_msg = Message( msg.to, to, value, submsg_gas, cd, msg.depth + 1, # Used for calculating contract address to_full_shard_key=msg.to_full_shard_key, code_address=to, static=True, transfer_token_id=msg.transfer_token_id, gas_token_id=msg.gas_token_id, ) elif op == "CALLCODE": call_msg = Message( msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, # Used for calculating contract address to_full_shard_key=msg.to_full_shard_key, code_address=to, static=msg.static, transfer_token_id=msg.transfer_token_id, gas_token_id=msg.gas_token_id, ) else: raise Exception("Lolwut") # Get result if call_msg.to == PROC_CURRENT_MNT_ID: msg.token_id_queried = True 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) # Return opcode 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]) # Revert opcode (Metropolis) elif op == "REVERT": 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]) # SUICIDE opcode (also called SELFDESTRUCT) 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) extra_gas = ( (not ext.account_exists(to)) * (xfer > 0) * 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 peaceful_exit("SUICIDED", compustate.gas, []) if trace_vm: compustate.reset_prev() return peaceful_exit("CODE OUT OF RANGE", compustate.gas, [])
def create_contract(ext, msg, contract_recipient=b"", salt=None): log_msg.debug("CONTRACT CREATION") if msg.transfer_token_id != ext.default_chain_token: # TODODLL calling smart contract with non QKC transfer_token_id is not supported return 0, msg.gas, b"" code = msg.data.extract_all() if ext.tx_origin != msg.sender: ext.increment_nonce(msg.sender) if contract_recipient != b"": # apply xshard deposit, where contract address has already been specified msg.to = contract_recipient elif salt is not None: # create2 msg.to = mk_contract_address2(msg.sender, salt, utils.sha3(code)) else: nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1) msg.to = mk_contract_address(msg.sender, nonce, msg.to_full_shard_key) if ext.get_nonce(msg.to) or len(ext.get_code(msg.to)): log_msg.debug("CREATING CONTRACT ON TOP OF EXISTING CONTRACT") return 0, 0, b"" if ext.account_exists(msg.to): ext.set_nonce(msg.to, 0) ext.set_code(msg.to, b"") ext.reset_storage(msg.to) msg.is_create = True # assert not ext.get_code(msg.to) msg.data = vm.CallData([], 0, 0) snapshot = ext.snapshot() ext.set_nonce(msg.to, 1) ext.reset_storage(msg.to) res, gas, dat = _apply_msg(ext, msg, code) log_msg.debug( "CONTRACT CREATION FINISHED", res=res, gas=gas, dat=dat if len(dat) < 2500 else ("data<%d>" % len(dat)), ) if res: if not len(dat): # ext.set_code(msg.to, b'') return 1, gas, msg.to gcost = len(dat) * opcodes.GCONTRACTBYTE if gas >= gcost and (len(dat) <= 24576): gas -= gcost else: log_msg.debug( "CONTRACT CREATION FAILED", have=gas, want=gcost, block_number=ext.block_number, ) ext.revert(snapshot) return 0, 0, b"" ext.set_code(msg.to, bytearray_to_bytestr(dat)) log_msg.debug("SETTING CODE", addr=encode_hex(msg.to), lendat=len(dat)) return 1, gas, msg.to else: ext.revert(snapshot) return 0, gas, dat
def mk_contract_address2(sender, salt: bytes, init_code_hash: bytes): return utils.sha3(b"\xff" + utils.normalize_address(sender) + salt + init_code_hash)[12:]
def mk_fake_header(blknum): if blknum not in fake_headers: fake_headers[blknum] = FakeHeader(sha3(to_string(blknum))) return fake_headers[blknum]
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') # Initialize stack, memory, program counter, etc compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory # Compute jumpdest_mask, pushcache = preprocess_code(code) codelen = len(code) # For tracing purposes op = None _prevop = None steps = 0 while compustate.pc < codelen: opcode = safe_ord(code[compustate.pc]) # Invalid operation if opcode not in opcodes.opcodes: return vm_exception('INVALID OP', opcode=opcode) if opcode in opcodes.opcodesMetropolis and not ext.post_metropolis_hardfork( ): return vm_exception('INVALID OP (not yet enabled)', opcode=opcode) op, in_args, out_args, fee = opcodes.opcodes[opcode] # Apply operation if trace_vm: compustate.reset_prev() compustate.gas -= fee compustate.pc += 1 # Tracing 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) < 4096: 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 + 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['steps'] = steps trace_data['depth'] = msg.depth if op[:4] == 'PUSH': trace_data['pushvalue'] = pushcache[compustate.pc - 1] log_vm_op.trace('vm', op=op, **trace_data) steps += 1 _prevop = op # out of gas error if compustate.gas < 0: 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))) # overfull stack error 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))) # Valid operations # Pushes first because they are very frequent if 0x60 <= opcode <= 0x7f: stk.append(pushcache[compustate.pc - 1]) # Move 1 byte forward for 0x60, up to 32 bytes for 0x7f compustate.pc += opcode - 0x5f # Arithmetic 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_spurious_dragon_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) # Comparisons 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) # SHA3 and environment info 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(codelen) 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 < codelen: mem[mstart + i] = 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[dstart:dstart + size] 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] = safe_ord(extcode[s2 + i]) else: mem[start + i] = 0 # Block info elif opcode < 0x50: if op == 'BLOCKHASH': if ext.post_constantinople_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) # VM state manipulations 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() if compustate.pc >= codelen or not ( (1 << compustate.pc) & jumpdest_mask): return vm_exception('BAD JUMPDEST') elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 if compustate.pc >= codelen or not ( (1 << compustate.pc) & jumpdest_mask): 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 # DUPn (eg. DUP1: a b c -> a b c c, DUP3: a b c -> a b c a) elif op[:3] == 'DUP': # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for 0x8f stk.append(stk[0x7f - opcode]) # SWAPn (eg. SWAP1: a b c d -> a b d c, SWAP3: a b c d -> d b c a) 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 # Logs (aka "events") 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))) # Create a new contract 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, data = ext.create(create_msg) if o: stk.append(utils.coerce_to_int(data)) compustate.last_returned = bytearray(b'') else: stk.append(0) compustate.last_returned = bytearray(data) compustate.gas = compustate.gas - ingas + gas else: stk.append(0) compustate.last_returned = bytearray(b'') # Calls 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 and op == 'CALL': 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 various factors extra_gas = 0 # Creating a new account if op == 'CALL' and not ext.account_exists(to) and ( value > 0 or not ext.post_spurious_dragon_hardfork()): extra_gas += opcodes.GCALLNEWACCOUNT # Value transfer if value > 0: extra_gas += opcodes.GCALLVALUETRANSFER # Cost increased from 40 to 700 in Tangerine Whistle if ext.post_anti_dos_hardfork(): extra_gas += 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) compustate.last_returned = bytearray(b'') 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 ext.post_metropolis_hardfork() and op == 'STATICCALL': call_msg = Message(msg.to, to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=True) elif op in ('DELEGATECALL', 'STATICCALL'): return vm_exception('OPCODE %s INACTIVE' % op) elif op == 'CALLCODE': call_msg = Message(msg.to, msg.to, value, submsg_gas, cd, msg.depth + 1, code_address=to, static=msg.static) 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) # Return opcode 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]) # Revert opcode (Metropolis) 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]) # SUICIDE opcode (also called SELFDESTRUCT) 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_spurious_dragon_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 peaceful_exit('SUICIDED', compustate.gas, []) if trace_vm: vm_trace(ext, msg, compustate, opcode, pushcache) if trace_vm: compustate.reset_prev() vm_trace(ext, msg, compustate, 0, None) return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])