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 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 apply_message(state, msg=None, **kwargs): if msg is None: msg = vm.Message(**kwargs) else: assert not kwargs ext = VMExt(state, transactions.Transaction(0, 0, 21000, b'', 0, b'')) result, gas_remained, data = apply_msg(ext, msg) return bytearray_to_bytestr(data) if result else None
def call_validation_code(state, validation_code_addr, msg_hash, signature): """Call validationCodeAddr on the main shard with 200000 gas, 0 value, the block_number concatenated with the sigIndex'th signature as input data gives output 1. """ dummy_addr = b'\xff' * 20 data = msg_hash + signature msg = vm.Message(dummy_addr, validation_code_addr, 0, 200000, data) result = apply_message(state.ephemeral_clone(), msg) if result is None: raise MessageFailed() return bool(utils.big_endian_to_int(result))
def call_casper(state, fun, args=[], gas=1000000, value=0): ct = get_casper_ct() abidata = vm.CallData([utils.safe_ord(x) for x in ct.encode(fun, args)]) msg = vm.Message(casper_config['METROPOLIS_ENTRY_POINT'], casper_config['CASPER_ADDR'], value, gas, abidata) o = apply_const_message(state, msg) if o: # print 'cc', fun, args, ct.decode(fun, o)[0] return ct.decode(fun, o)[0] else: return None
def run(self, sender=None, to=None, code=None, gas=None): sender = normalize_address(sender) if sender else normalize_address(zpad('sender', 20)) to = normalize_address(to) if to else normalize_address(zpad('receiver', 20)) code = scan_bin(code) if code else '' gas = scan_int(gas) if gas else 10000000000000 msg = vm.Message(sender, to, gas=gas) ext = VMExt(self.state, Transaction(0, 0, 21000, b'', 0, b'')) result, gas_remained, data = _apply_msg(ext, msg, code) return bytearray_to_bytestr(data) if result else None
def call(self, to, data='', **kargs): assert set(kargs.keys()).issubset(set(('value', ))) value = kargs.get('value', 0) data = vm.CallData(memoryview(data).tolist()) msg = vm.Message(self.address, to, value, self.gas, data, self.msg_depth + 1, code_address=to) success, self.gas, out = self._ext.msg(msg) assert success # FIXME return ''.join(chr(x) for x in out)
def call_msg(state, ct, func, args, sender_addr, to, value=0, startgas=STARTGAS): abidata = vm.CallData( [utils.safe_ord(x) for x in ct.encode_function_call(func, args)]) msg = vm.Message(sender_addr, to, value, startgas, abidata) result = apply_message(state, msg) if result is None: raise MessageFailed("Msg failed") if result is False: return result if result == b'': return None o = ct.decode(func, result) return o[0] if len(o) == 1 else o
def run_vm_test(params, mode, profiler=None): pre = params['pre'] exek = params['exec'] env = params['env'] if 'previousHash' not in env: env['previousHash'] = encode_hex(db_env.config['GENESIS_PREVHASH']) assert set(env.keys()) == set([ 'currentGasLimit', 'currentTimestamp', 'previousHash', 'currentCoinbase', 'currentDifficulty', 'currentNumber' ]) # setup env header = blocks.BlockHeader( prevhash=decode_hex(env['previousHash']), number=parse_int_or_hex(env['currentNumber']), coinbase=decode_hex(env['currentCoinbase']), difficulty=parse_int_or_hex(env['currentDifficulty']), gas_limit=parse_int_or_hex(env['currentGasLimit']), timestamp=parse_int_or_hex(env['currentTimestamp'])) blk = blocks.Block(header, env=db_env) # setup state for address, h in list(pre.items()): assert len(address) == 40 address = decode_hex(address) assert set(h.keys()) == set(['code', 'nonce', 'balance', 'storage']) blk.set_nonce(address, parse_int_or_hex(h['nonce'])) blk.set_balance(address, parse_int_or_hex(h['balance'])) blk.set_code(address, decode_hex(h['code'][2:])) for k, v in h['storage'].items(): blk.set_storage_data(address, utils.big_endian_to_int(decode_hex(k[2:])), utils.big_endian_to_int(decode_hex(v[2:]))) # execute transactions sender = decode_hex(exek['caller']) # a party that originates a call recvaddr = decode_hex(exek['address']) nonce = blk._get_acct_item(sender, 'nonce') gasprice = parse_int_or_hex(exek['gasPrice']) startgas = parse_int_or_hex(exek['gas']) value = parse_int_or_hex(exek['value']) data = decode_hex(exek['data'][2:]) # bypass gas check in tx initialization by temporarily increasing startgas num_zero_bytes = str_to_bytes(data).count(ascii_chr(0)) num_non_zero_bytes = len(data) - num_zero_bytes intrinsic_gas = (opcodes.GTXCOST + opcodes.GTXDATAZERO * num_zero_bytes + opcodes.GTXDATANONZERO * num_non_zero_bytes) startgas += intrinsic_gas tx = transactions.Transaction(nonce=nonce, gasprice=gasprice, startgas=startgas, to=recvaddr, value=value, data=data) tx.startgas -= intrinsic_gas tx.sender = sender # capture apply_message calls apply_message_calls = [] orig_apply_msg = pb.apply_msg ext = pb.VMExt(blk, tx) def msg_wrapper(msg): hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append( dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=encode_hex(msg.to), data=b'0x' + hexdata)) return 1, msg.gas, b'' def create_wrapper(msg): sender = decode_hex(msg.sender) if \ len(msg.sender) == 40 else msg.sender nonce = utils.encode_int(ext._block.get_nonce(msg.sender)) addr = utils.sha3(rlp.encode([sender, nonce]))[12:] hexdata = encode_hex(msg.data.extract_all()) apply_message_calls.append( dict(gasLimit=to_string(msg.gas), value=to_string(msg.value), destination=b'', data=b'0x' + hexdata)) return 1, msg.gas, addr ext.msg = msg_wrapper ext.create = create_wrapper def blkhash(n): if n >= ext.block_number or n < ext.block_number - 256: return b'' else: return utils.sha3(to_string(n)) ext.block_hash = blkhash msg = vm.Message(tx.sender, tx.to, tx.value, tx.startgas, vm.CallData([safe_ord(x) for x in tx.data])) code = decode_hex(exek['code'][2:]) time_pre = time.time() if profiler: profiler.enable() success, gas_remained, output = vm.vm_execute(ext, msg, code) if profiler: profiler.disable() pb.apply_msg = orig_apply_msg blk.commit_state() for s in blk.suicides: blk.del_account(s) time_post = time.time() """ generally expected that the test implementer will read env, exec and pre then check their results against gas, logs, out, post and callcreates. If an exception is expected, then latter sections are absent in the test. Since the reverting of the state is not part of the VM tests. """ params2 = copy.deepcopy(params) if success: params2['callcreates'] = apply_message_calls params2['out'] = b'0x' + encode_hex(b''.join(map(ascii_chr, output))) params2['gas'] = to_string(gas_remained) params2['logs'] = [log.to_dict() for log in blk.logs] params2['post'] = blk.to_dict(with_state=True)['state'] if mode == FILL: return params2 elif mode == VERIFY: if not success: assert 'post' not in params, 'failed, but expected to succeed' params1 = copy.deepcopy(params) shouldbe, reallyis = params1.get('post', None), params2.get('post', None) compare_post_states(shouldbe, reallyis) def normalize_value(k, p): if k in p: if k == 'gas': return parse_int_or_hex(p[k]) elif k == 'callcreates': return list(map(callcreate_standard_form, p[k])) else: return utils.to_string(k) return None for k in ['pre', 'exec', 'env', 'callcreates', 'out', 'gas', 'logs']: shouldbe = normalize_value(k, params1) reallyis = normalize_value(k, params2) if shouldbe != reallyis: raise Exception("Mismatch: " + k + ':\n shouldbe %r\n reallyis %r' % (shouldbe, reallyis)) elif mode == TIME: return time_post - time_pre
def apply_transaction(block, tx, cb=None, validate=True): eth_call = False def dummy_cb(*args, **kwargs): pass if cb is None: cb = dummy_cb eth_call = True if validate: validate_transaction(block, tx) log_tx.debug('TX NEW', tx_dict=tx.log_dict()) # start transacting ################# block.increment_nonce(tx.sender) intrinsic_gas = intrinsic_gas_used(tx) if block.number >= block.config['HOMESTEAD_FORK_BLKNUM']: assert tx.s * 2 < transactions.secpk1n if not tx.to or tx.to == CREATE_CONTRACT_ADDRESS: intrinsic_gas += opcodes.CREATE[3] if tx.startgas < intrinsic_gas: raise InsufficientStartGas(rp('startgas', tx.startgas, intrinsic_gas)) # buy startgas if validate: assert block.get_balance(tx.sender) >= tx.startgas * tx.gasprice block.delta_balance(tx.sender, -tx.startgas * tx.gasprice) message_gas = tx.startgas - intrinsic_gas message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data)) message = vm.Message(tx.sender, tx.to, tx.value, message_gas, message_data, code_address=tx.to) # MESSAGE ext = CapVMExt(block, tx, cb) if tx.to and tx.to != CREATE_CONTRACT_ADDRESS: result, gas_remained, data = ext._msg(message) log_tx.debug('_res_', result=result, gas_remained=gas_remained, data=data) else: # CREATE result, gas_remained, data = create_contract(ext, message) assert utils.is_numeric(gas_remained) log_tx.debug('_create_', result=result, gas_remained=gas_remained, data=data) assert gas_remained >= 0 log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained, data=data) if not result: # 0 = OOG failure in both cases log_tx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) block.gas_used += tx.startgas block.delta_balance(block.coinbase, tx.gasprice * tx.startgas) output = b'' success = 0 else: log_tx.debug('TX SUCCESS', data=data) gas_used = tx.startgas - gas_remained block.refunds += len(set(block.suicides)) * opcodes.GSUICIDEREFUND if block.refunds > 0: log_tx.debug('Refunding', gas_refunded=min(block.refunds, gas_used // 2)) gas_remained += min(block.refunds, gas_used // 2) gas_used -= min(block.refunds, gas_used // 2) block.refunds = 0 # sell remaining gas block.delta_balance(tx.sender, tx.gasprice * gas_remained) block.delta_balance(block.coinbase, tx.gasprice * gas_used) block.gas_used += gas_used if tx.to: output = b''.join(map(ascii_chr, data)) else: output = data success = 1 block.commit_state() suicides = block.suicides block.suicides = [] for s in suicides: block.ether_delta -= block.get_balance(s) block.set_balance(s, 0) block.del_account(s) block.add_transaction_to_list(tx) block.logs = [] return success, output
def mk_hash_setting_message(data): return vm.Message(sender=system_addr, to=blockhash_addr, value=0, gas=1000000, data=data)
def send_msg_transfer_value(mainchain_state, shard_state, shard_id, tx): urs_addr = get_urs_contract(shard_id)['addr'] log_rctx.debug("Begin: urs.balance={}, tx.to.balance={}".format( shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to))) receipt_id = tx.r # we should deduct the startgas of this message in advance, because # the message may be possibly a contract, not only a normal value transfer. value = tx.value - tx.gasprice * tx.startgas log_rctx.debug( "value={}, tx.value={}, tx.gasprice={}, tx.startgas={}".format( value, tx.value, tx.gasprice, tx.startgas)) if value <= 0: return False, None # start transactioning if not send_msg_add_used_receipt(shard_state, shard_id, receipt_id): return False, None receipt_sender_hex = call_valmgr(mainchain_state, 'get_receipts__sender', [receipt_id]) receipt_data = call_valmgr(mainchain_state, 'get_receipts__data', [receipt_id]) msg_data = (b'00' * 12) + utils.parse_as_bin(receipt_sender_hex) + receipt_data msg = vm.Message(urs_addr, tx.to, value, tx.startgas - tx.intrinsic_gas_used, msg_data) env_tx = Transaction(0, tx.gasprice, tx.startgas, b'', 0, b'') env_tx._sender = urs_addr ext = VMExt(shard_state, env_tx) log_rctx.debug( "before apply_msg: urs_addr.balance={}, tx.to.balance={}".format( shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to))) # even if `transfer_value` in `apply_msg` fails, no error occurs. # it seems no raise in apply_msg result, gas_remained, data = apply_msg(ext, msg) log_rctx.debug( "after apply_msg: urs_addr.balance={}, tx.to.balance={}".format( shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to))) assert gas_remained >= 0 gas_used = tx.startgas - gas_remained # Transaction failed if not result: log_rctx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) shard_state.gas_used += tx.startgas shard_state.delta_balance(tx.to, tx.gasprice * gas_remained) shard_state.delta_balance(shard_state.block_coinbase, tx.gasprice * gas_used) output = b'' success = 0 # Transaction success else: log_rctx.debug('TX SUCCESS', data=data) shard_state.refunds += len(set( shard_state.suicides)) * opcodes.GSUICIDEREFUND if shard_state.refunds > 0: log_rctx.debug('Refunding', gas_refunded=min(shard_state.refunds, gas_used // 2)) gas_remained += min(shard_state.refunds, gas_used // 2) gas_used -= min(shard_state.refunds, gas_used // 2) shard_state.refunds = 0 # sell remaining gas shard_state.delta_balance(tx.to, tx.gasprice * gas_remained) log_rctx.debug("gas_remained={}, gasprice={}".format( gas_remained, tx.gasprice)) log_rctx.debug("End: urs.balance={}, tx.to.balance={}".format( shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to))) shard_state.delta_balance(shard_state.block_coinbase, tx.gasprice * gas_used) shard_state.gas_used += gas_used if tx.to: output = utils.bytearray_to_bytestr(data) else: output = data success = 1 # Clear suicides suicides = shard_state.suicides shard_state.suicides = [] for s in suicides: shard_state.set_balance(s, 0) shard_state.del_account(s) # Pre-Metropolis: commit state after every tx if not shard_state.is_METROPOLIS() and not SKIP_MEDSTATES: shard_state.commit() # Construct a receipt r = mk_receipt(shard_state, success, shard_state.logs) _logs = list(shard_state.logs) shard_state.logs = [] shard_state.add_receipt(r) shard_state.set_param('bloom', shard_state.bloom | r.bloom) shard_state.set_param('txindex', shard_state.txindex + 1) return success, output
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 apply_transaction(block, tx): validate_transaction(block, tx) log_tx.debug('TX NEW', tx_dict=tx.log_dict()) # start transacting ################# block.increment_nonce(tx.sender) # print block.get_nonce(tx.sender), '@@@' # buy startgas assert block.get_balance(tx.sender) >= tx.startgas * tx.gasprice block.delta_balance(tx.sender, -tx.startgas * tx.gasprice) message_gas = tx.startgas - intrinsic_gas_used(tx) message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data)) message = vm.Message(tx.sender, tx.to, tx.value, message_gas, message_data, code_address=tx.to) # MESSAGE ext = VMExt(block, tx) if tx.to and tx.to != CREATE_CONTRACT_ADDRESS: result, gas_remained, data = apply_msg(ext, message) log_tx.debug('_res_', result=result, gas_remained=gas_remained, data=data) else: # CREATE result, gas_remained, data = create_contract(ext, message) assert utils.is_numeric(gas_remained) log_tx.debug('_create_', result=result, gas_remained=gas_remained, data=data) assert gas_remained >= 0 log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained, data=data) if not result: # 0 = OOG failure in both cases log_tx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) block.gas_used += tx.startgas block.delta_balance(block.coinbase, tx.gasprice * tx.startgas) output = b'' success = 0 else: log_tx.debug('TX SUCCESS', data=data) gas_used = tx.startgas - gas_remained block.refunds += len(set(block.suicides)) * opcodes.GSUICIDEREFUND if block.refunds > 0: log_tx.debug('Refunding', gas_refunded=min(block.refunds, gas_used // 2)) gas_remained += min(block.refunds, gas_used // 2) gas_used -= min(block.refunds, gas_used // 2) block.refunds = 0 # sell remaining gas block.delta_balance(tx.sender, tx.gasprice * gas_remained) block.delta_balance(block.coinbase, tx.gasprice * gas_used) block.gas_used += gas_used if tx.to: output = b''.join(map(ascii_chr, data)) else: output = data success = 1 block.commit_state() suicides = block.suicides block.suicides = [] for s in suicides: block.ether_delta -= block.get_balance(s) block.set_balance(s, 0) block.del_account(s) block.add_transaction_to_list(tx) block.logs = [] return success, output
def apply_transaction(state, tx): state.logs = [] state.suicides = [] state.refunds = 0 validate_transaction(state, tx) intrinsic_gas = tx.intrinsic_gas_used if state.is_HOMESTEAD(): assert tx.s * 2 < transactions.secpk1n if not tx.to or tx.to == CREATE_CONTRACT_ADDRESS: intrinsic_gas += opcodes.CREATE[3] if tx.startgas < intrinsic_gas: raise InsufficientStartGas( rp(tx, 'startgas', tx.startgas, intrinsic_gas)) log_tx.debug('TX NEW', txdict=tx.to_dict()) # start transacting ################# if tx.sender != null_address: state.increment_nonce(tx.sender) # buy startgas assert state.get_balance(tx.sender) >= tx.startgas * tx.gasprice state.delta_balance(tx.sender, -tx.startgas * tx.gasprice) message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data)) message = vm.Message( tx.sender, tx.to, tx.value, tx.startgas - intrinsic_gas, message_data, code_address=tx.to) # MESSAGE ext = VMExt(state, tx) if tx.to != b'': result, gas_remained, data = apply_msg(ext, message) else: # CREATE result, gas_remained, data = create_contract(ext, message) assert gas_remained >= 0 log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained, data=data) gas_used = tx.startgas - gas_remained # Transaction failed if not result: log_tx.debug('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) state.delta_balance(tx.sender, tx.gasprice * gas_remained) state.delta_balance(state.block_coinbase, tx.gasprice * gas_used) output = b'' success = 0 # Transaction success else: log_tx.debug('TX SUCCESS', data=data) state.refunds += len(set(state.suicides)) * opcodes.GSUICIDEREFUND if state.refunds > 0: log_tx.debug( 'Refunding', gas_refunded=min( state.refunds, gas_used // 2)) gas_remained += min(state.refunds, gas_used // 2) gas_used -= min(state.refunds, gas_used // 2) state.refunds = 0 # sell remaining gas state.delta_balance(tx.sender, tx.gasprice * gas_remained) state.delta_balance(state.block_coinbase, tx.gasprice * gas_used) if tx.to: output = bytearray_to_bytestr(data) else: output = data success = 1 state.gas_used += gas_used # Clear suicides suicides = state.suicides state.suicides = [] for s in suicides: state.set_balance(s, 0) state.del_account(s) # Pre-Metropolis: commit state after every tx if not state.is_METROPOLIS() and not SKIP_MEDSTATES: state.commit() # Construct a receipt r = mk_receipt(state, success, state.logs) _logs = list(state.logs) state.logs = [] state.add_receipt(r) state.set_param('bloom', state.bloom | r.bloom) state.set_param('txindex', state.txindex + 1) return success, output