def memory_gas_cost(size_in_bytes: int) -> int: size_in_words = ceil32(size_in_bytes) // 32 linear_cost = size_in_words * GAS_MEMORY quadratic_cost = size_in_words**2 // GAS_MEMORY_QUADRATIC_DENOMINATOR total_cost = linear_cost + quadratic_cost return total_cost
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 identity(computation: ComputationAPI) -> ComputationAPI: word_count = ceil32(len(computation.msg.data)) // 32 gas_fee = constants.GAS_IDENTITY + word_count * constants.GAS_IDENTITYWORD computation.consume_gas(gas_fee, reason="Identity Precompile") computation.output = computation.msg.data_as_bytes return computation
def sha256(computation: ComputationAPI) -> ComputationAPI: word_count = ceil32(len(computation.msg.data)) // 32 gas_fee = constants.GAS_SHA256 + word_count * constants.GAS_SHA256WORD computation.consume_gas(gas_fee, reason="SHA256 Precompile") input_bytes = computation.msg.data hash = hashlib.sha256(input_bytes).digest() computation.output = hash return computation
def ripemd160(computation: ComputationAPI) -> ComputationAPI: word_count = ceil32(len(computation.msg.data)) // 32 gas_fee = constants.GAS_RIPEMD160 + word_count * constants.GAS_RIPEMD160WORD computation.consume_gas(gas_fee, reason="RIPEMD160 Precompile") # TODO: this only works if openssl is installed. hash = hashlib.new('ripemd160', computation.msg.data).digest() padded_hash = pad32(hash) computation.output = padded_hash return computation
def extend_memory(self, start_position: int, size: int) -> None: validate_uint256(start_position, title="Memory start position") validate_uint256(size, title="Memory size") before_size = ceil32(len(self._memory)) after_size = ceil32(start_position + size) before_cost = memory_gas_cost(before_size) after_cost = memory_gas_cost(after_size) if self.logger.show_debug2: self.logger.debug2( "MEMORY: size (%s -> %s) | cost (%s -> %s)", before_size, after_size, before_cost, after_cost, ) self._memory.extend(start_position, size)
def sha3(computation: ComputationAPI) -> None: start_position, size = computation.stack_pop_ints(2) computation.extend_memory(start_position, size) sha3_bytes = computation.memory_read_bytes(start_position, size) word_count = ceil32(len(sha3_bytes)) // 32 gas_cost = constants.GAS_SHA3WORD * word_count computation.consume_gas(gas_cost, reason="SHA3: word gas cost") result = keccak(sha3_bytes) computation.stack_push_bytes(result)
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 extend(self, start_position: int, size: int) -> None: if size == 0: return new_size = ceil32(start_position + size) if new_size <= len(self): return size_to_extend = new_size - len(self) try: self._bytes.extend(itertools.repeat(0, size_to_extend)) except BufferError: # we can't extend the buffer (which might involve relocating it) if a # memoryview (which stores a pointer into the buffer) has been created by # read() and not released. Callers of read() will never try to write to the # buffer so we're not missing anything by making a new buffer and forgetting # about the old one. We're keeping too much memory around but this is still a # net savings over having read() return a new bytes() object every time. self._bytes = self._bytes + bytearray(size_to_extend)
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 get_gas_cost(self, data: CreateOpcodeStackData) -> int: return constants.GAS_CREATE + constants.GAS_SHA3WORD * ceil32(data.memory_length) // 32