def returndatacopy(computation: ComputationAPI) -> None: ( mem_start_position, returndata_start_position, size, ) = computation.stack_pop_ints(3) if returndata_start_position + size > len(computation.return_data): raise OutOfBoundsRead( "Return data length is not sufficient to satisfy request. Asked " f"for data from index {returndata_start_position} " f"to {returndata_start_position + size}. " f"Return data is {len(computation.return_data)} bytes in length.") computation.extend_memory(mem_start_position, size) word_count = ceil32(size) // 32 copy_gas_cost = word_count * constants.GAS_COPY computation.consume_gas(copy_gas_cost, reason="RETURNDATACOPY fee") value = computation.return_data[ returndata_start_position:returndata_start_position + size] computation.memory_write(mem_start_position, size, value)
def mstore(computation: ComputationAPI) -> None: start_position = computation.stack_pop1_int() value = computation.stack_pop1_bytes() padded_value = value.rjust(32, b'\x00') normalized_value = padded_value[-32:] computation.extend_memory(start_position, 32) computation.memory_write(start_position, 32, normalized_value)
def calldatacopy(computation: ComputationAPI) -> None: ( mem_start_position, calldata_start_position, size, ) = computation.stack_pop_ints(3) computation.extend_memory(mem_start_position, size) word_count = ceil32(size) // 32 copy_gas_cost = word_count * constants.GAS_COPY computation.consume_gas(copy_gas_cost, reason="CALLDATACOPY fee") value = computation.msg.data_as_bytes[ calldata_start_position:calldata_start_position + size] padded_value = value.ljust(size, b'\x00') computation.memory_write(mem_start_position, size, padded_value)
def codecopy(computation: ComputationAPI) -> None: ( mem_start_position, code_start_position, size, ) = computation.stack_pop_ints(3) computation.extend_memory(mem_start_position, size) word_count = ceil32(size) // 32 copy_gas_cost = constants.GAS_COPY * word_count computation.consume_gas( copy_gas_cost, reason="CODECOPY: word gas cost", ) with computation.code.seek(code_start_position): code_bytes = computation.code.read(size) padded_code_bytes = code_bytes.ljust(size, b'\x00') computation.memory_write(mem_start_position, size, padded_code_bytes)
def extcodecopy(computation: ComputationAPI) -> None: account = force_bytes_to_address(computation.stack_pop1_bytes()) ( mem_start_position, code_start_position, size, ) = computation.stack_pop_ints(3) computation.extend_memory(mem_start_position, size) word_count = ceil32(size) // 32 copy_gas_cost = constants.GAS_COPY * word_count computation.consume_gas( copy_gas_cost, reason='EXTCODECOPY: word gas cost', ) code = computation.state.get_code(account) code_bytes = code[code_start_position:code_start_position + size] padded_code_bytes = code_bytes.ljust(size, b'\x00') computation.memory_write(mem_start_position, size, padded_code_bytes)
def __call__(self, computation: ComputationAPI) -> None: computation.consume_gas( self.gas_cost, reason=self.mnemonic, ) ( gas, value, to, sender, code_address, memory_input_start_position, memory_input_size, memory_output_start_position, memory_output_size, should_transfer_value, is_static, ) = self.get_call_params(computation) computation.extend_memory(memory_input_start_position, memory_input_size) computation.extend_memory(memory_output_start_position, memory_output_size) call_data = computation.memory_read(memory_input_start_position, memory_input_size) # # Message gas allocation and fees # child_msg_gas, child_msg_gas_fee = self.compute_msg_gas( computation, gas, to, value) computation.consume_gas(child_msg_gas_fee, reason=self.mnemonic) # Pre-call checks sender_balance = computation.state.get_balance( computation.msg.storage_address) insufficient_funds = should_transfer_value and sender_balance < value stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT if insufficient_funds or stack_too_deep: computation.return_data = b'' if insufficient_funds: err_message = f"Insufficient Funds: have: {sender_balance} | need: {value}" elif stack_too_deep: err_message = "Stack Limit Reached" else: raise Exception("Invariant: Unreachable code path") self.logger.debug2( "%s failure: %s", self.mnemonic, err_message, ) computation.return_gas(child_msg_gas) computation.stack_push_int(0) else: if code_address: code = computation.state.get_code(code_address) else: code = computation.state.get_code(to) child_msg_kwargs = { 'gas': child_msg_gas, 'value': value, 'to': to, 'data': call_data, 'code': code, 'code_address': code_address, 'should_transfer_value': should_transfer_value, 'is_static': is_static, } if sender is not None: child_msg_kwargs['sender'] = sender # TODO: after upgrade to py3.6, use a TypedDict and try again child_msg = computation.prepare_child_message( **child_msg_kwargs) # type: ignore child_computation = computation.apply_child_computation(child_msg) if child_computation.is_error: computation.stack_push_int(0) else: computation.stack_push_int(1) if not child_computation.should_erase_return_data: actual_output_size = min(memory_output_size, len(child_computation.output)) computation.memory_write( memory_output_start_position, actual_output_size, child_computation.output[:actual_output_size], ) if child_computation.should_return_gas: computation.return_gas(child_computation.get_gas_remaining())