def trace(code, calldata=""): log_handlers = [ "eth.vm.op", "eth.vm.op.stack", "eth.vm.op.memory", "eth.vm.op.storage", ] output = StringIO() stream_handler = StreamHandler(output) for handler in log_handlers: log_vm_op = get_logger(handler) log_vm_op.setLevel("TRACE") log_vm_op.addHandler(stream_handler) addr = bytes.fromhex("0123456789ABCDEF0123456789ABCDEF01234567") state = State() ext = messages.VMExt(state, transactions.Transaction(0, 0, 21000, addr, 0, addr)) message = vm.Message(addr, addr, 0, 21000, calldata) vm.vm_execute(ext, message, util.safe_decode(code)) stream_handler.flush() ret = output.getvalue() lines = ret.split("\n") state_trace = [] for line in lines: m = re.search(r"pc=b\'(\d+)\'.*op=([A-Z0-9]+)", line) if m: pc = m.group(1) op = m.group(2) m = re.match(r".*stack=(\[.*?\])", line) if m: stackitems = re.findall(r"b\'(\d+)\'", m.group(1)) stack = "[" if len(stackitems): for i in range(0, len(stackitems) - 1): stack += hex(int(stackitems[i])) + ", " stack += hex(int(stackitems[-1])) stack += "]" else: stack = "[]" if re.match(r"^PUSH.*", op): val = re.search(r"pushvalue=(\d+)", line).group(1) pushvalue = hex(int(val)) state_trace.append({ "pc": pc, "op": op, "stack": stack, "pushvalue": pushvalue }) else: state_trace.append({"pc": pc, "op": op, "stack": stack}) return state_trace
def trace(code, address = "", calldata = ""): logHandlers = ['eth.vm.op', 'eth.vm.op.stack', 'eth.vm.op.memory', 'eth.vm.op.storage'] output = StringIO() streamHandler = StreamHandler(output) for handler in logHandlers: log_vm_op = get_logger(handler) log_vm_op.setLevel("TRACE") log_vm_op.addHandler(streamHandler) addr_from = codecs.decode('0123456789ABCDEF0123456789ABCDEF01234567', 'hex_codec') addr_to = safe_decode(address) state = State() ext = messages.VMExt(state, transactions.Transaction(0, 0, 21000, addr_from, 0, addr_to)) message = vm.Message(addr_from, addr_to, 0, 21000, calldata, code_address=addr_to) res, gas, dat = vm.vm_execute(ext, message, code) streamHandler.flush() # print(output.getvalue()) ret = output.getvalue() return ret
def _apply_msg(ext, msg, code): trace_msg = log_msg.is_active("trace") if trace_msg: log_msg.debug( "MSG APPLY", sender=encode_hex(msg.sender), to=encode_hex(msg.to), gas=msg.gas, value=msg.value, data=encode_hex(msg.data.extract_all()), ) if log_state.is_active("trace"): log_state.trace( "MSG PRE STATE SENDER", account=encode_hex(msg.sender), bal=ext.get_balance(msg.sender), state=ext.log_storage(msg.sender), ) log_state.trace( "MSG PRE STATE RECIPIENT", account=encode_hex(msg.to), bal=ext.get_balance(msg.to), state=ext.log_storage(msg.to), ) # log_state.trace('CODE', code=code) # Transfer value, instaquit if not enough snapshot = ext._block.snapshot() if msg.transfers_value: if not ext._block.transfer_value(msg.sender, msg.to, msg.value): log_msg.debug("MSG TRANSFER FAILED", have=ext.get_balance(msg.to), want=msg.value) return 1, msg.gas, [] # Main loop if msg.code_address in specials.specials: res, gas, dat = specials.specials[msg.code_address](ext, msg) else: res, gas, dat = vm.vm_execute(ext, msg, code) # gas = int(gas) # assert utils.is_numeric(gas) if trace_msg: log_msg.debug("MSG APPLIED", gas_remained=gas, sender=encode_hex(msg.sender), to=encode_hex(msg.to), data=dat) if log_state.is_active("trace"): log_state.trace( "MSG POST STATE SENDER", account=encode_hex(msg.sender), bal=ext.get_balance(msg.sender), state=ext.log_storage(msg.sender), ) log_state.trace( "MSG POST STATE RECIPIENT", account=encode_hex(msg.to), bal=ext.get_balance(msg.to), state=ext.log_storage(msg.to), ) if res == 0: log_msg.debug("REVERTING") ext._block.revert(snapshot) return res, gas, dat
def _apply_msg(ext, msg, code): trace_msg = log_msg.is_active('trace') if trace_msg: log_msg.debug("MSG APPLY", sender=encode_hex(msg.sender), to=encode_hex(msg.to), gas=msg.gas, value=msg.value, data=encode_hex(msg.data.extract_all())) if log_state.is_active('trace'): log_state.trace('MSG PRE STATE SENDER', account=msg.sender, bal=ext.get_balance(msg.sender), state=ext.log_storage(msg.sender)) log_state.trace('MSG PRE STATE RECIPIENT', account=msg.to, bal=ext.get_balance(msg.to), state=ext.log_storage(msg.to)) # log_state.trace('CODE', code=code) # Transfer value, instaquit if not enough snapshot = ext._block.snapshot() if not ext._block.transfer_value(msg.sender, msg.to, msg.value): log_msg.debug('MSG TRANSFER FAILED', have=ext.get_balance(msg.to), want=msg.value) return 1, msg.gas, [] # Main loop if msg.code_address in specials.specials: res, gas, dat = specials.specials[msg.code_address](ext, msg) else: res, gas, dat = vm.vm_execute(ext, msg, code) # gas = int(gas) # assert utils.is_numeric(gas) if trace_msg: log_msg.debug('MSG APPLIED', gas_remained=gas, sender=msg.sender, to=msg.to, data=dat) if log_state.is_active('trace'): log_state.trace('MSG PRE STATE SENDER', account=msg.sender, bal=ext.get_balance(msg.sender), state=ext.log_storage(msg.sender)) log_state.trace('MSG PRE STATE RECIPIENT', account=msg.to, bal=ext.get_balance(msg.to), state=ext.log_storage(msg.to)) if res == 0: log_msg.debug('REVERTING') ext._block.revert(snapshot) return res, gas, dat
def _apply_msg(ext, msg, code): trace_msg = log_msg.is_active('trace') if trace_msg: log_msg.debug("MSG APPLY", sender=encode_hex(msg.sender), to=encode_hex(msg.to), gas=msg.gas, value=msg.value, codelen=len(code), data=encode_hex(msg.data.extract_all()) if msg.data.size < 2500 else ("data<%d>" % msg.data.size), pre_storage=ext.log_storage(msg.to), static=msg.static, depth=msg.depth) # Transfer value, instaquit if not enough snapshot = ext.snapshot() if msg.transfers_value: if not ext.transfer_value(msg.sender, msg.to, msg.value): log_msg.debug('MSG TRANSFER FAILED', have=ext.get_balance(msg.to), want=msg.value) return 1, msg.gas, [] # Main loop if msg.code_address in ext.specials: res, gas, dat = ext.specials[msg.code_address](ext, msg) else: res, gas, dat = vm.vm_execute(ext, msg, code) if trace_msg: log_msg.debug('MSG APPLIED', gas_remained=gas, sender=encode_hex(msg.sender), to=encode_hex(msg.to), data=dat if len(dat) < 2500 else ("data<%d>" % len(dat)), post_storage=ext.log_storage(msg.to)) if res == 0: log_msg.debug('REVERTING') ext.revert(snapshot) return res, gas, dat
def _apply_msg(ext, msg, code): if log_msg.is_active('trace'): log_msg.debug("MSG APPLY", sender=encode_hex(msg.sender), to=encode_hex(msg.to), gas=msg.gas, value=msg.value, data=encode_hex(msg.data.extract_all())) if log_state.is_active('trace'): log_state.trace('MSG PRE STATE SENDER', account=msg.sender, bal=ext.get_balance(msg.sender), state=ext.log_storage(msg.sender)) log_state.trace('MSG PRE STATE RECIPIENT', account=msg.to, bal=ext.get_balance(msg.to), state=ext.log_storage(msg.to)) # Transfer value, instaquit if not enough snapshot = ext._block.snapshot() o = ext._block.transfer_value(msg.sender, msg.to, msg.value) if not o: log_msg.debug('MSG TRANSFER FAILED', have=ext.get_balance(msg.to), want=msg.value) return 1, msg.gas, [] # Main loop if msg.code_address in specials.specials: res, gas, dat = specials.specials[msg.code_address](ext, msg) else: res, gas, dat = vm.vm_execute(ext, msg, code) gas = int(gas) assert utils.is_numeric(gas) if log_msg.is_active('trace'): log_msg.debug('MSG APPLIED', result=o, gas_remained=gas, sender=msg.sender, to=msg.to, data=dat) if log_state.is_active('trace'): log_state.trace('MSG PRE STATE SENDER', account=msg.sender, bal=ext.get_balance(msg.sender), state=ext.log_storage(msg.sender)) log_state.trace('MSG PRE STATE RECIPIENT', account=msg.to, bal=ext.get_balance(msg.to), state=ext.log_storage(msg.to)) if res == 0: log_msg.debug('REVERTING') ext._block.revert(snapshot) return res, gas, dat
def generate_code(contracts): ext = VmExtBase() new_contracts = {} for contract in contracts: raw_code = bytes.fromhex(contract["code"][2:]) contructor_output = EthVM.vm_execute( ext, ConstructorMSG(contract["address"]), raw_code ) new_contracts[contract["address"]] = { "name": contract["name"], "address": contract["address"], "abi": contract["abi"], "code": '0x' + contructor_output[2].hex(), "storage": {} } for contract in ext.storage: storage = {} for key in ext.storage[contract]: storage[hex(key)] = hex(ext.storage[contract][key]) new_contracts[contract]["storage"] = storage return list(new_contracts.values())
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 trace(code, calldata=""): logHandlers = [ 'eth.vm.op', 'eth.vm.op.stack', 'eth.vm.op.memory', 'eth.vm.op.storage' ] output = StringIO() streamHandler = StreamHandler(output) for handler in logHandlers: log_vm_op = get_logger(handler) log_vm_op.setLevel("TRACE") log_vm_op.addHandler(streamHandler) addr = bytes.fromhex('0123456789ABCDEF0123456789ABCDEF01234567') state = State() ext = messages.VMExt(state, transactions.Transaction(0, 0, 21000, addr, 0, addr)) message = vm.Message(addr, addr, 0, 21000, calldata) res, gas, dat = vm.vm_execute(ext, message, util.safe_decode(code)) streamHandler.flush() ret = output.getvalue() lines = ret.split("\n") trace = [] for line in lines: m = re.search(r'pc=b\'(\d+)\'.*op=([A-Z0-9]+)', line) if m: pc = m.group(1) op = m.group(2) m = re.match(r'.*stack=(\[.*?\])', line) if (m): stackitems = re.findall(r'b\'(\d+)\'', m.group(1)) stack = "[" if (len(stackitems)): for i in range(0, len(stackitems) - 1): stack += hex(int(stackitems[i])) + ", " stack += hex(int(stackitems[-1])) stack += "]" else: stack = "[]" if (re.match(r'^PUSH.*', op)): val = re.search(r'pushvalue=(\d+)', line).group(1) pushvalue = hex(int(val)) trace.append({ 'pc': pc, 'op': op, 'stack': stack, 'pushvalue': pushvalue }) else: trace.append({'pc': pc, 'op': op, 'stack': stack}) return trace
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