def create_contract(ext, msg): #print('CREATING WITH GAS', msg.gas) sender = decode_hex(msg.sender) if len(msg.sender) == 40 else msg.sender if ext.tx_origin != msg.sender: ext._block.increment_nonce(msg.sender) nonce = utils.encode_int(ext._block.get_nonce(msg.sender) - 1) msg.to = mk_contract_address(sender, nonce) b = ext.get_balance(msg.to) if b > 0: ext.set_balance(msg.to, b) ext._block.set_nonce(msg.to, 0) ext._block.set_code(msg.to, b'') ext._block.reset_storage(msg.to) msg.is_create = True # assert not ext.get_code(msg.to) code = msg.data.extract_all() msg.data = vm.CallData([], 0, 0) res, gas, dat = _apply_msg(ext, msg, code) assert utils.is_numeric(gas) if res: if not len(dat): return 1, gas, msg.to gcost = len(dat) * opcodes.GCONTRACTBYTE if gas >= gcost: gas -= gcost else: dat = [] #print('CONTRACT CREATION OOG', 'have', gas, 'want', gcost) log_msg.debug('CONTRACT CREATION OOG', have=gas, want=gcost) ext._block.set_code(msg.to, b''.join(map(ascii_chr, dat))) return 1, gas, msg.to else: return 0, gas, b''
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 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 create_contract(ext, msg): log_msg.debug('CONTRACT CREATION') #print('CREATING WITH GAS', msg.gas) sender = decode_hex(msg.sender) if len(msg.sender) == 40 else msg.sender code = msg.data.extract_all() if ext._block.number >= ext._block.config['METROPOLIS_FORK_BLKNUM']: msg.to = mk_metropolis_contract_address(msg.sender, code) if ext.get_code(msg.to): if ext.get_nonce(msg.to) >= 2 ** 40: ext.set_nonce(msg.to, (ext.get_nonce(msg.to) + 1) % 2 ** 160) msg.to = normalize_address((ext.get_nonce(msg.to) - 1) % 2 ** 160) else: ext.set_nonce(msg.to, (big_endian_to_int(msg.to) + 2) % 2 ** 160) msg.to = normalize_address((ext.get_nonce(msg.to) - 1) % 2 ** 160) else: if ext.tx_origin != msg.sender: ext._block.increment_nonce(msg.sender) nonce = utils.encode_int(ext._block.get_nonce(msg.sender) - 1) msg.to = mk_contract_address(sender, nonce) b = ext.get_balance(msg.to) if b > 0: ext.set_balance(msg.to, b) ext._block.set_nonce(msg.to, 0) ext._block.set_code(msg.to, b'') ext._block.reset_storage(msg.to) msg.is_create = True # assert not ext.get_code(msg.to) msg.data = vm.CallData([], 0, 0) snapshot = ext._block.snapshot() res, gas, dat = _apply_msg(ext, msg, code) assert utils.is_numeric(gas) if res: if not len(dat): return 1, gas, msg.to gcost = len(dat) * opcodes.GCONTRACTBYTE if gas >= gcost: gas -= gcost else: dat = [] log_msg.debug('CONTRACT CREATION OOG', have=gas, want=gcost, block_number=ext._block.number) if ext._block.number >= ext._block.config['HOMESTEAD_FORK_BLKNUM']: ext._block.revert(snapshot) return 0, 0, b'' ext._block.set_code(msg.to, b''.join(map(ascii_chr, dat))) return 1, gas, msg.to else: return 0, gas, b''
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 _safe_call(self): log.debug('create native contract instance called') assert len(self._msg.sender) == 20 assert len(self._msg.data.extract_all()) >= 4 # get native contract nc_address = registry.native_contract_address_prefix + self._msg.data.extract_all( )[:4] if nc_address not in registry: return 0, self._msg.gas, b'' native_contract = registry[nc_address].im_self # get new contract address if self._ext.tx_origin != self._msg.sender: self._ext._block.increment_nonce(self._msg.sender) nonce = utils.encode_int( self._ext._block.get_nonce(self._msg.sender) - 1) self._msg.to = registry.mk_instance_address(native_contract, self._msg.sender, nonce) assert not self._ext.get_balance(self._msg.to) # must be none existant # value was initially added to this contract's address, we need to transfer success = self._ext._block.transfer_value(self.address, self._msg.to, self._msg.value) assert success assert not self._ext.get_balance(self.address) # call new instance with additional data self._msg.is_create = True self._msg.data = vm.CallData(self._msg.data.data[4:], 0, 0) res, gas, dat = registry[self._msg.to](self._ext, self._msg) assert gas >= 0 log.debug('created native contract instance', template=nc_address.encode('hex'), instance=self._msg.to.encode('hex')) return res, gas, memoryview(self._msg.to).tolist()
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 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 create_contract(ext, msg): log_msg.debug('CONTRACT CREATION') code = msg.data.extract_all() if ext.tx_origin != msg.sender: ext.increment_nonce(msg.sender) if ext.post_constantinople_hardfork() and msg.sender == null_address: msg.to = utils.mk_contract_address(msg.sender, 0) # msg.to = sha3(msg.sender + code)[12:] else: nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1) msg.to = utils.mk_contract_address(msg.sender, nonce) if ext.post_metropolis_hardfork() and ( 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'' b = ext.get_balance(msg.to) if b > 0: ext.set_balance(msg.to, b) 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 if ext.post_spurious_dragon_hardfork() else 0) 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 or not ext.post_spurious_dragon_hardfork()): gas -= gcost else: dat = [] log_msg.debug( 'CONTRACT CREATION FAILED', have=gas, want=gcost, block_number=ext.block_number) if ext.post_homestead_hardfork(): 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 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