def compute_eip150_msg_gas(*, computation: ComputationAPI, gas: int, extra_gas: int, value: int, mnemonic: str, callstipend: int) -> Tuple[int, int]: if computation.get_gas_remaining() < extra_gas: # It feels wrong to raise an OutOfGas exception outside of GasMeter, # but I don't see an easy way around it. raise OutOfGas(f"Out of gas: Needed {extra_gas}" f" - Remaining {computation.get_gas_remaining()}" f" - Reason: {mnemonic}") gas = min( gas, max_child_gas_eip150(computation.get_gas_remaining() - extra_gas)) total_fee = gas + extra_gas child_msg_gas = gas + (callstipend if value else 0) return child_msg_gas, total_fee
def __call__(self, computation: ComputationAPI) -> None: stack_data = self.get_stack_data(computation) gas_cost = self.get_gas_cost(stack_data) computation.consume_gas(gas_cost, reason=self.mnemonic) computation.extend_memory(stack_data.memory_start, stack_data.memory_length) storage_address_balance = computation.state.get_balance(computation.msg.storage_address) insufficient_funds = storage_address_balance < stack_data.endowment stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT if insufficient_funds or stack_too_deep: computation.stack_push_int(0) computation.return_data = b'' if insufficient_funds: self.logger.debug2( "%s failure: %s", self.mnemonic, f"Insufficient Funds: {storage_address_balance} < {stack_data.endowment}" ) elif stack_too_deep: self.logger.debug2("%s failure: %s", self.mnemonic, "Stack limit reached") else: raise RuntimeError("Invariant: error must be insufficient funds or stack too deep") return call_data = computation.memory_read_bytes( stack_data.memory_start, stack_data.memory_length ) create_msg_gas = self.max_child_gas_modifier( computation.get_gas_remaining() ) computation.consume_gas(create_msg_gas, reason=self.mnemonic) contract_address = self.generate_contract_address(stack_data, call_data, computation) is_collision = computation.state.has_code_or_nonce(contract_address) if is_collision: computation.stack_push_int(0) computation.return_data = b'' self.logger.debug2( "Address collision while creating contract: %s", encode_hex(contract_address), ) return child_msg = computation.prepare_child_message( gas=create_msg_gas, to=constants.CREATE_CONTRACT_ADDRESS, value=stack_data.endowment, data=b'', code=call_data, create_address=contract_address, ) self.apply_create_message(computation, child_msg)
def make_frontier_receipt(base_header: BlockHeaderAPI, transaction: SignedTransactionAPI, computation: ComputationAPI) -> ReceiptAPI: # Reusable for other forks # This skips setting the state root (set to 0 instead). The logic for making a state root # lives in the FrontierVM, so that state merkelization at each receipt is skipped at Byzantium+. logs = [ Log(address, topics, data) for address, topics, data in computation.get_log_entries() ] gas_remaining = computation.get_gas_remaining() gas_refund = computation.get_gas_refund() tx_gas_used = (transaction.gas - gas_remaining) - min( gas_refund, (transaction.gas - gas_remaining) // 2, ) gas_used = base_header.gas_used + tx_gas_used receipt = Receipt( state_root=ZERO_HASH32, gas_used=gas_used, logs=logs, ) return receipt
def finalize_gas_used(cls, transaction: SignedTransactionAPI, computation: ComputationAPI) -> int: gas_remaining = computation.get_gas_remaining() consumed_gas = transaction.gas - gas_remaining gross_refund = computation.get_gas_refund() net_refund = cls.calculate_net_gas_refund(consumed_gas, gross_refund) return consumed_gas - net_refund
def finalize_computation(self, transaction: SignedTransactionAPI, computation: ComputationAPI) -> ComputationAPI: # Self Destruct Refunds num_deletions = len(computation.get_accounts_for_deletion()) if num_deletions: computation.refund_gas(REFUND_SELFDESTRUCT * num_deletions) # Gas Refunds gas_remaining = computation.get_gas_remaining() gas_refunded = computation.get_gas_refund() gas_used = transaction.gas - gas_remaining gas_refund = min(gas_refunded, gas_used // 2) gas_refund_amount = (gas_refund + gas_remaining) * transaction.gas_price if gas_refund_amount: self.vm_state.logger.debug2( 'TRANSACTION REFUND: %s -> %s', gas_refund_amount, encode_hex(computation.msg.sender), ) # self.vm_state.delta_balance(computation.msg.sender, gas_refund_amount) ### DAEJUN changed ### # Miner Fees # transaction_fee = \ # (transaction.gas - gas_remaining - gas_refund) * transaction.gas_price transaction_fee = 0 self.vm_state.logger.debug2( 'TRANSACTION FEE: %s -> %s', transaction_fee, encode_hex(self.vm_state.coinbase), ) self.vm_state.delta_balance(self.vm_state.coinbase, transaction_fee) # Process Self Destructs for account, _ in computation.get_accounts_for_deletion(): # TODO: need to figure out how we prevent multiple selfdestructs from # the same account and if this is the right place to put this. self.vm_state.logger.debug2('DELETING ACCOUNT: %s', encode_hex(account)) # TODO: this balance setting is likely superflous and can be # removed since `delete_account` does this. self.vm_state.set_balance(account, 0) self.vm_state.delete_account(account) return computation
def __call__(self, computation: ComputationAPI) -> None: stack_data = self.get_stack_data(computation) gas_cost = self.get_gas_cost(stack_data) computation.consume_gas(gas_cost, reason=self.mnemonic) computation.extend_memory(stack_data.memory_start, stack_data.memory_length) insufficient_funds = computation.state.get_balance( computation.msg.storage_address) < stack_data.endowment stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT if insufficient_funds or stack_too_deep: computation.stack_push_int(0) return call_data = computation.memory_read_bytes(stack_data.memory_start, stack_data.memory_length) create_msg_gas = self.max_child_gas_modifier( computation.get_gas_remaining()) computation.consume_gas(create_msg_gas, reason=self.mnemonic) contract_address = self.generate_contract_address( stack_data, call_data, computation) is_collision = computation.state.has_code_or_nonce(contract_address) if is_collision: self.logger.debug2( "Address collision while creating contract: %s", encode_hex(contract_address), ) computation.stack_push_int(0) return child_msg = computation.prepare_child_message( gas=create_msg_gas, to=constants.CREATE_CONTRACT_ADDRESS, value=stack_data.endowment, data=b'', code=call_data, create_address=contract_address, ) self.apply_create_message(computation, child_msg)
def finalize_computation(self, transaction: SignedTransactionAPI, computation: ComputationAPI) -> ComputationAPI: transaction_context = self.vm_state.get_transaction_context(transaction) gas_remaining = computation.get_gas_remaining() gas_used = transaction.gas - gas_remaining gas_refund = self.calculate_gas_refund(computation, gas_used) gas_refund_amount = (gas_refund + gas_remaining) * transaction_context.gas_price if gas_refund_amount: self.vm_state.logger.debug2( 'TRANSACTION REFUND: %s -> %s', gas_refund_amount, encode_hex(computation.msg.sender), ) self.vm_state.delta_balance(computation.msg.sender, gas_refund_amount) # Miner Fees gas_used = transaction.gas - gas_remaining - gas_refund transaction_fee = gas_used * self.vm_state.get_tip(transaction) self.vm_state.logger.debug2( 'TRANSACTION FEE: %s -> %s', transaction_fee, encode_hex(self.vm_state.coinbase), ) self.vm_state.delta_balance(self.vm_state.coinbase, transaction_fee) # Process Self Destructs for account, _ in computation.get_accounts_for_deletion(): # TODO: need to figure out how we prevent multiple selfdestructs from # the same account and if this is the right place to put this. self.vm_state.logger.debug2('DELETING ACCOUNT: %s', encode_hex(account)) # TODO: this balance setting is likely superflous and can be # removed since `delete_account` does this. self.vm_state.set_balance(account, 0) self.vm_state.delete_account(account) return computation
def before_computation(cls, computation: ComputationAPI, next_opcode: int, next_opcode_fn: Any): logger.info("Entering pre_computation with opcode {o}".format(o=next_opcode_fn.mnemonic)) head = ChangeChainLink(TableWidgetEnum.OPCODES, pre_computation=[computation.code.pc - 1], post_computation=[]) chain = ChangeChain(head) chain.add_link(stack_effects.get(next_opcode)) stack = computation._stack.values lkp = cls.storage_lookup.get(computation.msg.storage_address) # Big if construct that determines which opcode has which causer and has which effect. This could be # simplified a bit, however it was not possible to determine the effects statically like in stack effects, # since the changes in memory and storage are dependent on the varying contents of memory, stack and storage, # instead of the only the opcode as it is the case with the stack. if next_opcode == SSTORE: slot = get_stack_content(stack, 1)[0] if lkp is None: index = 0 else: index = lkp.get(slot) if index is None: index = len(lkp) chain.add_link(ChangeChainLink(TableWidgetEnum.STORAGE, [], [index])) elif next_opcode == SLOAD: slot = get_stack_content(stack, 1)[0] if lkp is None or lkp.get(slot) is None: cls.set_storage.emit(computation.msg.storage_address, slot, "0x00") cls.storage_lock.acquire(True) # refresh because the main storage should have set this in the meantime lkp = cls.storage_lookup.get(computation.msg.storage_address) logger.info("Trying to SLOAD storage key -> index: {k} -> {i}".format(k=slot, i=lkp.get(slot))) chain.add_link(ChangeChainLink(TableWidgetEnum.STORAGE, [lkp.get(slot)], [])) elif next_opcode == MSTORE or next_opcode == MSTORE8: offset = get_stack_content(stack, 1)[0] chain.add_link( ChangeChainLink(TableWidgetEnum.MEMORY, [], [int(offset, 16) / 32]) ) elif next_opcode == MLOAD: offset = get_stack_content(stack, 1)[0] chain.add_link( ChangeChainLink(TableWidgetEnum.MEMORY, [int(offset, 16) / 32], []) ) elif next_opcode == SHA3 or (LOG0 <= next_opcode <= LOG4) \ or next_opcode == RETURN or next_opcode == REVERT: arr = get_stack_content(stack, 2) print(arr) offset = arr[0] length = arr[1] chain.add_link(ChangeChainLink(TableWidgetEnum.MEMORY, list(range(int(int(offset, 16) / 32), int(int(length, 16) / 32) + 1)), []) ) elif next_opcode == CALLDATACOPY or next_opcode == CODECOPY \ or next_opcode == RETURNDATACOPY: arr = get_stack_content(stack, 3) offset = arr[0] length = arr[2] chain.add_link(ChangeChainLink(TableWidgetEnum.MEMORY, [], [] if "0x" == length or "0x" == offset else list( range(int(int(offset, 16) / 32), int(int(length, 16) / 32) + 1))) ) elif next_opcode == EXTCODECOPY: arr = get_stack_content(stack, 4) offset = arr[1] length = arr[3] chain.add_link(ChangeChainLink(TableWidgetEnum.MEMORY, [], list(range(int(int(offset, 16) / 32), int(int(length, 16) / 32) + 1))) ) elif next_opcode == CREATE or next_opcode == CREATE2: arr = get_stack_content(stack, 3) print(f"CREATE {arr}") offset = arr[1] length = arr[2] chain.add_link(ChangeChainLink(TableWidgetEnum.MEMORY, list(range(int(int(offset, 16) / 32), int(int(length, 16) / 32) + 1)), []) ) elif next_opcode == CALL or next_opcode == CALLCODE: arr = get_stack_content(stack, 7) print(f"CALL {arr}") argoffset = arr[3] arglength = arr[4] retOffset = arr[5] retLength = arr[6] chain.add_link( ChangeChainLink(TableWidgetEnum.MEMORY, list(range(int(int(argoffset, 16) / 32), int(int(arglength, 16) / 32) + 1)), list(range(int(int(retOffset, 16) / 32), int(int(retLength, 16) / 32) + 1))) ) elif next_opcode == DELEGATECALL or next_opcode == STATICCALL: arr = get_stack_content(stack, 6) print(f"DELEGATECALL {arr}") argoffset = arr[2] arglength = arr[3] retOffset = arr[4] retLength = arr[5] chain.add_link( ChangeChainLink(TableWidgetEnum.MEMORY, list(range(int(int(argoffset, 16) / 32), int(int(arglength, 16) / 32) + 1)), list(range(int(int(retOffset, 16) / 32), int(int(retLength, 16) / 32) + 1))) ) cls.add_chain.emit(chain) cls.pre_computation.emit(computation.get_gas_remaining(), computation.code.pc) if cls.debug_mode: cls.step_lock.acquire(True)