Exemplo n.º 1
0
def log_XX(computation: BaseComputation, topic_count: int) -> None:
    if topic_count < 0 or topic_count > 4:
        raise TypeError("Invalid log topic size.  Must be 0, 1, 2, 3, or 4")

    mem_start_position, size = computation.stack_pop_ints(2)

    if not topic_count:
        topics: Tuple[int, ...] = ()
    elif topic_count > 1:
        topics = computation.stack_pop_ints(topic_count)
    else:
        topics = (computation.stack_pop1_int(), )

    data_gas_cost = constants.GAS_LOGDATA * size
    topic_gas_cost = constants.GAS_LOGTOPIC * topic_count
    total_gas_cost = data_gas_cost + topic_gas_cost

    computation.consume_gas(
        total_gas_cost,
        reason="Log topic and data gas cost",
    )

    computation.extend_memory(mem_start_position, size)
    log_data = computation.memory_read_bytes(mem_start_position, size)

    computation.add_log_entry(
        account=computation.msg.storage_address,
        topics=topics,
        data=log_data,
    )
Exemplo n.º 2
0
def log_XX(computation: BaseComputation, topic_count: int) -> None:
    if topic_count < 0 or topic_count > 4:
        raise TypeError("Invalid log topic size.  Must be 0, 1, 2, 3, or 4")

    mem_start_position, size = computation.stack_pop(
        num_items=2, type_hint=constants.UINT256)

    if not topic_count:
        topics = []  # type: List[int]
    elif topic_count > 1:
        topics = computation.stack_pop(num_items=topic_count,
                                       type_hint=constants.UINT256)
    else:
        topics = [
            computation.stack_pop(num_items=topic_count,
                                  type_hint=constants.UINT256)
        ]

    data_gas_cost = constants.GAS_LOGDATA * size
    topic_gas_cost = constants.GAS_LOGTOPIC * topic_count
    total_gas_cost = data_gas_cost + topic_gas_cost

    computation.consume_gas(
        total_gas_cost,
        reason="Log topic and data gas cost",
    )

    computation.extend_memory(mem_start_position, size)
    log_data = computation.memory_read_bytes(mem_start_position, size)

    computation.add_log_entry(
        account=computation.msg.storage_address,
        topics=topics,
        data=log_data,
    )
Exemplo n.º 3
0
def extcodecopy(computation: BaseComputation) -> None:
    account = force_bytes_to_address(
        computation.stack_pop(type_hint=constants.BYTES))
    (
        mem_start_position,
        code_start_position,
        size,
    ) = computation.stack_pop(num_items=3, type_hint=constants.UINT256)

    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.account_db.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)
Exemplo n.º 4
0
def ecrecover(computation: BaseComputation) -> BaseComputation:
    computation.consume_gas(constants.GAS_ECRECOVER, reason="ECRecover Precompile")
    raw_message_hash = computation.msg.data[:32]
    message_hash = pad32r(raw_message_hash)

    v_bytes = pad32r(computation.msg.data[32:64])
    v = big_endian_to_int(v_bytes)

    r_bytes = pad32r(computation.msg.data[64:96])
    r = big_endian_to_int(r_bytes)

    s_bytes = pad32r(computation.msg.data[96:128])
    s = big_endian_to_int(s_bytes)

    try:
        validate_lt_secpk1n(r, title="ECRecover: R")
        validate_lt_secpk1n(s, title="ECRecover: S")
        validate_lte(v, 28, title="ECRecover: V")
        validate_gte(v, 27, title="ECRecover: V")
    except ValidationError:
        return computation

    canonical_v = v - 27

    try:
        signature = keys.Signature(vrs=(canonical_v, r, s))
        public_key = signature.recover_public_key_from_msg_hash(message_hash)
    except BadSignature:
        return computation

    address = public_key.to_canonical_address()
    padded_address = pad32(address)

    computation.output = padded_address
    return computation
Exemplo n.º 5
0
def returndatacopy(computation: BaseComputation) -> None:
    (
        mem_start_position,
        returndata_start_position,
        size,
    ) = computation.stack_pop(num_items=3, type_hint=constants.UINT256)

    if returndata_start_position + size > len(computation.return_data):
        raise OutOfBoundsRead(
            "Return data length is not sufficient to satisfy request.  Asked "
            "for data from index {0} to {1}.  Return data is {2} bytes in "
            "length.".format(
                returndata_start_position,
                returndata_start_position + size,
                len(computation.return_data),
            )
        )

    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)
Exemplo n.º 6
0
def ecpairing(
        computation: BaseComputation,
        gas_cost_base: int = constants.GAS_ECPAIRING_BASE,
        gas_cost_per_point: int = constants.GAS_ECPAIRING_PER_POINT) -> BaseComputation:

    if len(computation.msg.data) % 192:
        # data length must be an exact multiple of 192
        raise VMError("Invalid ECPAIRING parameters")

    num_points = len(computation.msg.data) // 192
    gas_fee = gas_cost_base + num_points * gas_cost_per_point

    computation.consume_gas(gas_fee, reason='ECPAIRING Precompile')

    try:
        result = _ecpairing(computation.msg.data)
    except ValidationError:
        raise VMError("Invalid ECPAIRING parameters")

    if result is True:
        computation.output = pad32(b'\x01')
    elif result is False:
        computation.output = pad32(b'\x00')
    else:
        raise Exception("Invariant: unreachable code path")
    return computation
Exemplo n.º 7
0
def returndatacopy(computation: BaseComputation) -> 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)
Exemplo n.º 8
0
def selfdestruct_eip150(computation: BaseComputation) -> None:
    beneficiary = force_bytes_to_address(computation.stack_pop1_bytes())
    if not computation.state.account_exists(beneficiary):
        computation.consume_gas(
            constants.GAS_SELFDESTRUCT_NEWACCOUNT,
            reason=mnemonics.SELFDESTRUCT,
        )
    _selfdestruct(computation, beneficiary)
Exemplo n.º 9
0
def identity(computation: BaseComputation) -> BaseComputation:
    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
Exemplo n.º 10
0
def net_sstore(gas_schedule: NetSStoreGasSchedule,
               computation: BaseComputation) -> None:
    slot, value = computation.stack_pop_ints(2)

    current_value = computation.state.get_storage(
        address=computation.msg.storage_address,
        slot=slot,
    )

    original_value = computation.state.get_storage(
        address=computation.msg.storage_address, slot=slot, from_journal=False)

    gas_refund = 0

    if current_value == value:
        gas_cost = gas_schedule.base
    else:
        if original_value == current_value:
            if original_value == 0:
                gas_cost = gas_schedule.create
            else:
                gas_cost = gas_schedule.update

                if value == 0:
                    gas_refund += gas_schedule.remove_refund
        else:
            gas_cost = gas_schedule.base

            if original_value != 0:
                if current_value == 0:
                    gas_refund -= gas_schedule.remove_refund
                if value == 0:
                    gas_refund += gas_schedule.remove_refund

            if original_value == value:
                if original_value == 0:
                    gas_refund += (gas_schedule.create - gas_schedule.base)
                else:
                    gas_refund += (gas_schedule.update - gas_schedule.base)

    computation.consume_gas(
        gas_cost,
        reason="SSTORE: {}[{}] -> {} (current: {} / original: {})".format(
            encode_hex(computation.msg.storage_address),
            slot,
            value,
            current_value,
            original_value,
        ))

    if gas_refund:
        computation.refund_gas(gas_refund)

    computation.state.set_storage(
        address=computation.msg.storage_address,
        slot=slot,
        value=value,
    )
Exemplo n.º 11
0
def sha256(computation: BaseComputation) -> BaseComputation:
    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
Exemplo n.º 12
0
def sstore_eip1283(computation: BaseComputation) -> None:
    slot, value = computation.stack_pop_ints(2)

    current_value = computation.state.get_storage(
        address=computation.msg.storage_address,
        slot=slot,
    )

    original_value = computation.state.get_storage(
        address=computation.msg.storage_address, slot=slot, from_journal=False)

    gas_refund = 0

    if current_value == value:
        gas_cost = constants.GAS_SSTORE_EIP1283_NOOP
    else:
        if original_value == current_value:
            if original_value == 0:
                gas_cost = constants.GAS_SSTORE_EIP1283_INIT
            else:
                gas_cost = constants.GAS_SSTORE_EIP1283_CLEAN

                if value == 0:
                    gas_refund += constants.GAS_SSTORE_EIP1283_CLEAR_REFUND
        else:
            gas_cost = constants.GAS_SSTORE_EIP1283_NOOP

            if original_value != 0:
                if current_value == 0:
                    gas_refund -= constants.GAS_SSTORE_EIP1283_CLEAR_REFUND
                if value == 0:
                    gas_refund += constants.GAS_SSTORE_EIP1283_CLEAR_REFUND

            if original_value == value:
                if original_value == 0:
                    gas_refund += constants.GAS_SSTORE_EIP1283_RESET_CLEAR_REFUND
                else:
                    gas_refund += constants.GAS_SSTORE_EIP1283_RESET_REFUND

    computation.consume_gas(
        gas_cost,
        reason="SSTORE: {0}[{1}] -> {2} (current: {3} / original: {4})".format(
            encode_hex(computation.msg.storage_address),
            slot,
            value,
            current_value,
            original_value,
        ))

    if gas_refund:
        computation.refund_gas(gas_refund)

    computation.state.set_storage(
        address=computation.msg.storage_address,
        slot=slot,
        value=value,
    )
Exemplo n.º 13
0
    def __call__(self, computation: BaseComputation) -> 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.account_db.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(0)
            return

        call_data = computation.memory_read(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.account_db.account_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(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,
        )

        child_computation = computation.apply_child_computation(child_msg)

        if child_computation.is_error:
            computation.stack_push(0)
        else:
            computation.stack_push(contract_address)
        computation.return_gas(child_computation.get_gas_remaining())
Exemplo n.º 14
0
def ripemd160(computation: BaseComputation) -> BaseComputation:
    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
Exemplo n.º 15
0
def selfdestruct_eip161(computation: BaseComputation) -> None:
    beneficiary = force_bytes_to_address(
        computation.stack_pop(type_hint=constants.BYTES))
    is_dead = (not computation.state.account_db.account_exists(beneficiary)
               or computation.state.account_db.account_is_empty(beneficiary))
    if is_dead and computation.state.account_db.get_balance(
            computation.msg.storage_address):
        computation.consume_gas(
            constants.GAS_SELFDESTRUCT_NEWACCOUNT,
            reason=mnemonics.SELFDESTRUCT,
        )
    _selfdestruct(computation, beneficiary)
Exemplo n.º 16
0
def sha3(computation: BaseComputation) -> 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)
Exemplo n.º 17
0
def blake2b_fcompress(computation: BaseComputation) -> BaseComputation:
    try:
        parameters = extract_blake2b_parameters(computation.msg.data_as_bytes)
    except ValidationError as exc:
        raise VMError(
            f"Blake2b input parameter validation failure: {exc}") from exc

    num_rounds = parameters[0]
    gas_cost = GAS_COST_PER_ROUND * num_rounds

    computation.consume_gas(
        gas_cost, reason=f"Blake2b Compress Precompile w/ {num_rounds} rounds")

    computation.output = _do_compression(*parameters)
    return computation
Exemplo n.º 18
0
def ecmul(computation: BaseComputation) -> BaseComputation:
    computation.consume_gas(constants.GAS_ECMUL, reason='ECMUL Precompile')

    try:
        result = _ecmull(computation.msg.data)
    except ValidationError:
        raise VMError("Invalid ECMUL parameters")

    result_x, result_y = result
    result_bytes = b''.join((
        pad32(int_to_big_endian(result_x.n)),
        pad32(int_to_big_endian(result_y.n)),
    ))
    computation.output = result_bytes
    return computation
Exemplo n.º 19
0
def ecadd(
        computation: BaseComputation,
        gas_cost: int = constants.GAS_ECADD) -> BaseComputation:

    computation.consume_gas(gas_cost, reason='ECADD Precompile')

    try:
        result = _ecadd(computation.msg.data_as_bytes)
    except ValidationError:
        raise VMError("Invalid ECADD parameters")

    result_x, result_y = result
    result_bytes = b''.join((
        pad32(int_to_big_endian(result_x.n)),
        pad32(int_to_big_endian(result_y.n)),
    ))
    computation.output = result_bytes
    return computation
Exemplo n.º 20
0
def modexp(computation: BaseComputation) -> BaseComputation:
    """
    https://github.com/ethereum/EIPs/pull/198
    """
    gas_fee = _compute_modexp_gas_fee(computation.msg.data)
    computation.consume_gas(gas_fee, reason='MODEXP Precompile')

    result = _modexp(computation.msg.data)

    _, _, modulus_length = _extract_lengths(computation.msg.data)

    # Modulo 0 is undefined, return zero
    # https://math.stackexchange.com/questions/516251/why-is-n-mod-0-undefined
    result_bytes = b'' if modulus_length == 0 else zpad_left(
        int_to_big_endian(result), to_size=modulus_length)

    computation.output = result_bytes
    return computation
Exemplo n.º 21
0
def sstore(computation: BaseComputation) -> None:
    slot, value = computation.stack_pop(num_items=2,
                                        type_hint=constants.UINT256)

    current_value = computation.state.account_db.get_storage(
        address=computation.msg.storage_address,
        slot=slot,
    )

    is_currently_empty = not bool(current_value)
    is_going_to_be_empty = not bool(value)

    if is_currently_empty:
        gas_refund = 0
    elif is_going_to_be_empty:
        gas_refund = constants.REFUND_SCLEAR
    else:
        gas_refund = 0

    if is_currently_empty and is_going_to_be_empty:
        gas_cost = constants.GAS_SRESET
    elif is_currently_empty:
        gas_cost = constants.GAS_SSET
    elif is_going_to_be_empty:
        gas_cost = constants.GAS_SRESET
    else:
        gas_cost = constants.GAS_SRESET

    computation.consume_gas(gas_cost,
                            reason="SSTORE: {0}[{1}] -> {2} ({3})".format(
                                encode_hex(computation.msg.storage_address),
                                slot,
                                value,
                                current_value,
                            ))

    if gas_refund:
        computation.refund_gas(gas_refund)

    computation.state.account_db.set_storage(
        address=computation.msg.storage_address,
        slot=slot,
        value=value,
    )
Exemplo n.º 22
0
def calldatacopy(computation: BaseComputation) -> 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)
Exemplo n.º 23
0
def sstore(computation: BaseComputation) -> None:
    slot, value = computation.stack_pop_ints(2)

    current_value = computation.state.get_storage(
        address=computation.msg.storage_address,
        slot=slot,
    )

    is_currently_empty = not bool(current_value)
    is_going_to_be_empty = not bool(value)

    if is_currently_empty:
        gas_refund = 0
    elif is_going_to_be_empty:
        gas_refund = constants.REFUND_SCLEAR
    else:
        gas_refund = 0

    if is_currently_empty and is_going_to_be_empty:
        gas_cost = constants.GAS_SRESET
    elif is_currently_empty:
        gas_cost = constants.GAS_SSET
    elif is_going_to_be_empty:
        gas_cost = constants.GAS_SRESET
    else:
        gas_cost = constants.GAS_SRESET

    computation.consume_gas(
        gas_cost,
        reason=(f"SSTORE: {encode_hex(computation.msg.storage_address)}"
                f"[{slot}] -> {value} ({current_value})"))

    if gas_refund:
        computation.refund_gas(gas_refund)

    computation.state.set_storage(
        address=computation.msg.storage_address,
        slot=slot,
        value=value,
    )
Exemplo n.º 24
0
def exp(computation: BaseComputation, gas_per_byte: int) -> None:
    """
    Exponentiation
    """
    base, exponent = computation.stack_pop_ints(2)

    bit_size = exponent.bit_length()
    byte_size = ceil8(bit_size) // 8

    if exponent == 0:
        result = 1
    elif base == 0:
        result = 0
    else:
        result = pow(base, exponent, constants.UINT_256_CEILING)

    computation.consume_gas(
        gas_per_byte * byte_size,
        reason="EXP: exponent bytes",
    )

    computation.stack_push_int(result)
Exemplo n.º 25
0
def codecopy(computation: BaseComputation) -> 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)
Exemplo n.º 26
0
    def __call__(self, computation: BaseComputation) -> 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.account_db.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 = "Insufficient Funds: have: {0} | need: {1}".format(
                    sender_balance,
                    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(0)
        else:
            if code_address:
                code = computation.state.account_db.get_code(code_address)
            else:
                code = computation.state.account_db.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

            child_msg = computation.prepare_child_message(**child_msg_kwargs)

            child_computation = computation.apply_child_computation(child_msg)

            if child_computation.is_error:
                computation.stack_push(0)
            else:
                computation.stack_push(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())
Exemplo n.º 27
0
def net_sstore(gas_schedule: NetSStoreGasSchedule,
               computation: BaseComputation) -> int:
    """
    :return slot: where the new value was stored
    """
    slot, value = computation.stack_pop_ints(2)

    current_value = computation.state.get_storage(
        address=computation.msg.storage_address,
        slot=slot,
    )

    original_value = computation.state.get_storage(
        address=computation.msg.storage_address, slot=slot, from_journal=False)

    gas_refund = 0

    if current_value == value:
        gas_cost = gas_schedule.sload_gas
    else:
        if original_value == current_value:
            if original_value == 0:
                gas_cost = gas_schedule.sstore_set_gas
            else:
                gas_cost = gas_schedule.sstore_reset_gas

                if value == 0:
                    gas_refund += gas_schedule.sstore_clears_schedule
        else:
            gas_cost = gas_schedule.sload_gas

            if original_value != 0:
                if current_value == 0:
                    gas_refund -= gas_schedule.sstore_clears_schedule
                if value == 0:
                    gas_refund += gas_schedule.sstore_clears_schedule

            if original_value == value:
                if original_value == 0:
                    gas_refund += (gas_schedule.sstore_set_gas -
                                   gas_schedule.sload_gas)
                else:
                    gas_refund += (gas_schedule.sstore_reset_gas -
                                   gas_schedule.sload_gas)

    computation.consume_gas(
        gas_cost,
        reason=
        (f"SSTORE: {encode_hex(computation.msg.storage_address)}"
         f"[{slot}] -> {value} (current: {current_value} / original: {original_value})"
         ))

    if gas_refund:
        computation.refund_gas(gas_refund)

    computation.state.set_storage(
        address=computation.msg.storage_address,
        slot=slot,
        value=value,
    )
    return slot