def to_transaction(tx_attr_dict): return Transaction( tx_attr_dict.nonce, tx_attr_dict.gasPrice, tx_attr_dict.gas, normalize_address(tx_attr_dict.to), tx_attr_dict.value, codecs.decode(tx_attr_dict.input.replace("0x", ""), 'hex'), tx_attr_dict.v, bytes_to_int(tx_attr_dict.r), bytes_to_int(tx_attr_dict.s))
def get_sender(hash, sig): v = sig[64] if v < 27: v += 27 r = u.bytes_to_int(sig[:32]) s = u.bytes_to_int(sig[32:64]) pub = u.ecrecover_to_pub(hash, v, r, s) return u.sha3(pub)[-20:]
def verify_merkle_proof(self, blockhash, tx, proof): h = get_sum_hash_of_tx(tx) for i in range(0, len(proof) - 1): if proof[i][0] == 'left': new_value = b''.join([h, proof[i][1]]) else: new_value = b''.join([proof[i][1], h]) new_sum = int_to_bytes( bytes_to_int(h[32:]) + bytes_to_int(proof[i][1][32:])).rjust( 8, b"\x00") h = b''.join([sha3(new_value), new_sum]) return h == proof[-1]
def transaction(tx_dict: dict) -> transactions.Transaction: t = transactions.Transaction( utils.parse_as_int(tx_dict['nonce']), utils.parse_as_int(tx_dict['gasPrice']), utils.parse_as_int(tx_dict['gas']), normalize_bytes(tx_dict['to'] or ''), utils.parse_as_int(tx_dict['value']), utils.decode_hex(tx_dict['input']), utils.parse_as_int(tx_dict['v']), utils.bytes_to_int(normalize_bytes(tx_dict['r'])), utils.bytes_to_int(normalize_bytes(tx_dict['s'])), ) if normalize_bytes(tx_dict['hash']) != t.hash: raise ValueError( "Tx hash does not match. Received invalid transaction?") return t
def block_header(block_dict: dict): b = block.BlockHeader( normalize_bytes(block_dict["parentHash"]), normalize_bytes(block_dict["sha3Uncles"]), utils.normalize_address(block_dict["miner"]), normalize_bytes(block_dict["stateRoot"]), normalize_bytes(block_dict["transactionsRoot"]), normalize_bytes(block_dict["receiptsRoot"]), utils.bytes_to_int(normalize_bytes(block_dict["logsBloom"])), utils.parse_as_int(block_dict['difficulty']), utils.parse_as_int(block_dict['number']), utils.parse_as_int(block_dict['gasLimit']), utils.parse_as_int(block_dict['gasUsed']), utils.parse_as_int(block_dict['timestamp']), normalize_bytes(block_dict["extraData"]), normalize_bytes(block_dict["mixHash"]), normalize_bytes(block_dict["nonce"]), ) if normalize_bytes(block_dict["hash"]) != b.hash: raise ValueError( """Blockhash does not match. Received invalid block header? {} vs {}""".format( str(normalize_bytes(block_dict["hash"])), str(b.hash))) return b
def verify_block(block_info): # verify the difficulty if utils.parse_as_int(block_info['difficulty']) < 10000000: return False if int(normalize_bytes(block_info["nonce"]).hex(), 16) * utils.parse_as_int(block_info['difficulty']) \ > pow(2, 256): return False # get block header from block info header = block.BlockHeader( normalize_bytes(block_info["parentHash"]), normalize_bytes(block_info["sha3Uncles"]), utils.normalize_address(block_info["miner"]), normalize_bytes(block_info["stateRoot"]), normalize_bytes(block_info["transactionsRoot"]), normalize_bytes(block_info["receiptsRoot"]), utils.bytes_to_int(normalize_bytes(block_info["logsBloom"])), utils.parse_as_int(block_info['difficulty']), utils.parse_as_int(block_info['number']), utils.parse_as_int(block_info['gasLimit']), utils.parse_as_int(block_info['gasUsed']), utils.parse_as_int(block_info['timestamp']), normalize_bytes(block_info["extraData"]), normalize_bytes(block_info["mixHash"]), normalize_bytes(block_info["nonce"]), ) # calculate the block hash # compare the block hash with trusted block hash if normalize_bytes(block_info.hash) != header.hash: return False else: return True
def construct_tree(db, nodes): if len(nodes) < 2: return nodes[0] remaining_nodes = [] for i in range(0, len(nodes), 2): if i+1 == len(nodes): remaining_nodes.append(nodes[i]) break new_value = b''.join([nodes[i], nodes[i+1]]) new_sum = int_to_bytes(bytes_to_int(nodes[i+1][32:]) + bytes_to_int(nodes[i][32:])).rjust(8, b"\x00") new_hash = b''.join([sha3(new_value), new_sum]) print('Left:', encode_hex(nodes[i]), 'parent:', encode_hex(new_hash)) print('Right:', encode_hex(nodes[i+1]), 'parent:', encode_hex(new_hash)) db.put(new_hash, new_value) remaining_nodes.append(new_hash) return construct_tree(db, remaining_nodes)
def rlp_transaction(tx_dict: dict): t = transactions.Transaction( utils.parse_as_int(tx_dict['nonce']), utils.parse_as_int(tx_dict['gasPrice']), utils.parse_as_int(tx_dict['gas']), normalize_bytes(tx_dict['to'] or ''), utils.parse_as_int(tx_dict['value']), utils.decode_hex(tx_dict['input']), utils.parse_as_int(tx_dict['v']), utils.bytes_to_int(normalize_bytes(tx_dict['r'])), utils.bytes_to_int(normalize_bytes(tx_dict['s'])), ) if normalize_bytes(tx_dict['hash']) != t.hash: print("False transaction hash") return None return rlp.encode(t)
def follow_path(node, total_offset, target): print(node) if is_json(node): return node left_sum = bytes_to_int(node[32:40]) if left_sum + total_offset > target: return follow_path(db.get(node[0:40]), total_offset, target) else: return follow_path(db.get(node[40:]), total_offset + left_sum, target)
def follow_path(node, total_offset, target): node_value = db.get(node) if is_json(node_value): return node_value left_sum = bytes_to_int(node_value[32:40]) if left_sum + total_offset > target: proof.insert(0, ('left', node_value[40:])) return follow_path(node_value[0:40], total_offset, target) else: proof.insert(0, ('right', node_value[0:40])) return follow_path(node_value[40:], total_offset + left_sum, target)
def rlp_transaction(tx_dict: dict): t = transactions.Transaction( utils.parse_as_int(tx_dict['nonce']), utils.parse_as_int(tx_dict['gasPrice']), utils.parse_as_int(tx_dict['gas']), normalize_bytes(tx_dict['to'] or ''), utils.parse_as_int(tx_dict['value']), utils.decode_hex(tx_dict['input']), utils.parse_as_int(tx_dict['v']), utils.bytes_to_int(normalize_bytes(tx_dict['r'])), utils.bytes_to_int(normalize_bytes(tx_dict['s'])), ) if normalize_bytes(tx_dict['hash']) != t.hash: raise ValueError( """Tx hash does not match. Received invalid transaction? hashes: {} {} nonce: {} gasPrice: {} gas: {} to: {} value: {} input: {} v: {} r: {} s: {} """.format( tx_dict['hash'], t.hash, utils.parse_as_int(tx_dict['nonce']), utils.parse_as_int(tx_dict['gasPrice']), utils.parse_as_int(tx_dict['gas']), normalize_bytes(tx_dict['to'] or ''), utils.parse_as_int(tx_dict['value']), utils.decode_hex(tx_dict['input']), utils.parse_as_int(tx_dict['v']), utils.bytes_to_int(normalize_bytes(tx_dict['r'])), utils.bytes_to_int(normalize_bytes(tx_dict['s'])), )) return rlp.encode(t)
def to_blockheader(block_attr_dict): return BlockHeader(bytes(block_attr_dict.parentHash), bytes(block_attr_dict.sha3Uncles), normalize_address(block_attr_dict.miner), bytes(block_attr_dict.stateRoot), bytes(block_attr_dict.transactionsRoot), bytes(block_attr_dict.receiptsRoot), bytes_to_int(bytes(block_attr_dict.logsBloom)), block_attr_dict.difficulty, block_attr_dict.number, block_attr_dict.gasLimit, block_attr_dict.gasUsed, block_attr_dict.timestamp, bytes(block_attr_dict.extraData), bytes(block_attr_dict.mixHash), bytes(block_attr_dict.nonce))
def generate_pure_ecrecover_LLL_source(address): return [ 'seq', [ 'return', [0], [ 'lll', [ 'seq', ['calldatacopy', 0, 0, 128], ['call', 3000, 1, 0, 0, 128, 0, 32], ['mstore', 0, ['eq', ['mload', 0], bytes_to_int(address)]], ['return', 0, 32] ], [0] ] ] ]
def format_LLL_source(address, expression): return [ 'seq', ['return', [0], ['lll', ['seq', expression, # impure_expression goes here ['calldatacopy', 0, 0, 128], ['call', 3000, 1, 0, 0, 128, 0, 32], ['mstore', 0, ['eq', ['mload', 0], bytes_to_int(address)]], ['return', 0, 32]], [0]]] ]
def receipt(receipt_dict: dict) -> messages.Receipt: logs = [ Log(utils.normalize_address(l['address']), [utils.parse_as_int(t.hex()) for t in l['topics']], utils.decode_hex(l['data'])) for l in receipt_dict['logs'] ] root = receipt_dict.get('root') if root: state_root = normalize_bytes(root) elif receipt_dict['status'] == 1: state_root = bytes([1]) else: state_root = bytes() r = messages.Receipt( state_root, utils.parse_as_int(receipt_dict['cumulativeGasUsed']), utils.bytes_to_int(normalize_bytes(receipt_dict['logsBloom'])), logs, ) return r
def main(): args = get_args() if args.verbose: logger.setLevel(logging.DEBUG) from_network = settings.NETWORK_CONFIG[args.source] web3_from = Web3(HTTPProvider(endpoint_uri=from_network['url'], request_kwargs={'timeout': settings.DEFAULT_REQUEST_TIMEOUT})) web3_from.middleware_stack.inject(geth_poa_middleware, layer=0) to_network = settings.NETWORK_CONFIG[args.dest] web3_to = Web3(HTTPProvider(endpoint_uri=to_network['url'], request_kwargs={'timeout': settings.DEFAULT_REQUEST_TIMEOUT})) block_to_relay = args.start latest_block = web3_from.eth.blockNumber bridge_contract = BridgeContract(to_network['peaceRelayAddress'], web3_to) while True: if block_to_relay < latest_block - settings.BLOCKS_OFFSET: logger.info('Relaying {}'.format(block_to_relay)) w3_block = dict(web3_from.eth.getBlock(block_to_relay)) poa_data = w3_block.get('proofOfAuthorityData') if poa_data: w3_block['extraData'] = poa_data block = merkle_proof.block_header(w3_block) raw_txn = bridge_contract.submit_block(bytes_to_int(block.hash), rlp.encode(block)) raw_txn['nonce'] = web3_to.eth.getTransactionCount(privtoaddr(settings.PRIVATE_KEY)) raw_txn['gas'] = 200000 signed_txn = web3_to.eth.account.signTransaction(raw_txn, private_key=settings.PRIVATE_KEY) txn_hash = encode_hex(web3_to.eth.sendRawTransaction(signed_txn.rawTransaction)) txn_info = web3_to.eth.getTransaction(txn_hash) while txn_info is None or txn_info.blockNumber is None: time.sleep(1) txn_info = web3_to.eth.getTransaction(txn_hash) logger.info('Successfully relayed block {} in txn {}'.format(block_to_relay, txn_hash)) block_to_relay += 1 else: time.sleep(1) latest_block = web3_from.eth.blockNumber
def block_header(block_dict: dict) -> block.BlockHeader: b = block.BlockHeader( normalize_bytes(block_dict['parentHash']), normalize_bytes(block_dict['sha3Uncles']), utils.normalize_address(block_dict['miner']), normalize_bytes(block_dict['stateRoot']), normalize_bytes(block_dict['transactionsRoot']), normalize_bytes(block_dict['receiptsRoot']), utils.bytes_to_int(normalize_bytes(block_dict['logsBloom'])), utils.parse_as_int(block_dict['difficulty']), utils.parse_as_int(block_dict['number']), utils.parse_as_int(block_dict['gasLimit']), utils.parse_as_int(block_dict['gasUsed']), utils.parse_as_int(block_dict['timestamp']), normalize_bytes(block_dict['extraData']), normalize_bytes(block_dict['mixHash']), normalize_bytes(block_dict['nonce']), ) if normalize_bytes(block_dict['hash']) != b.hash: raise ValueError( 'Blockhash does not match. Received invalid block header?') return b
def preprocess_code(code): assert isinstance(code, bytes) lencode = len(code) code = memoryview(code + b'\x00' * 32).tolist() outdict = {} cur_start = 0 ops = [] i = 0 stack = 0 minstack = 0 maxstack = 0 gascost = 0 while i < lencode: o = opcodes.opcodes.get(code[i], ['INVALID', 0, 0, 0]) if i > cur_start and o[0] in ('JUMPDEST', 'PC'): outdict[cur_start] = (ops, minstack, 1024 - maxstack, gascost, i) cur_start = i ops = [] minstack, maxstack, stack, gascost = 0, 0, 0, 0 ops.append([o[0], code[i], 0]) if o[0][:4] == 'PUSH': pushlen = int(o[0][4:]) ops[-1][2] = utils.bytes_to_int(code[i + 1:i + pushlen + 1]) i += pushlen minstack = max(o[1] - stack, minstack) stack += o[2] - o[1] maxstack = max(stack, maxstack) gascost += o[3] if o[0] in break_list: outdict[cur_start] = (ops, minstack, 1024 - maxstack, gascost, i + 1) cur_start = i + 1 ops = [] minstack, maxstack, stack, gascost = 0, 0, 0, 0 i += 1 if ops: outdict[cur_start] = (ops, minstack, 1024 - maxstack, gascost, i) return outdict
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')