Raw File
tx_rollup_state_repr.mli
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2022 Marigold <contact@marigold.dev>                        *)
(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com>                *)
(* Copyright (c) 2022 Oxhead Alpha <info@oxhead-alpha.com>                   *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

(** The state of a transaction rollup is a set of variables whose values vary
    in time, as the rollup progresses. *)
type t

(** [initial_state pre_allocated_storage] returns the initial state of
    a transaction rollup (after its origination) with
    [pre_allocated_storage] bytes of storage already paid for. *)
val initial_state : pre_allocated_storage:Z.t -> t

val encoding : t Data_encoding.t

val pp : Format.formatter -> t -> unit

(** [update_burn_per_byte state ~elapsed ~factor ~final_size
    ~hard_limit] updates the cost per byte to be paid for each message
    submitted to the rollup.  This is done by computing a moving
    average for [factor] snapshots. Each snapshot being the size of the
    total messages for the rollup. Hence each snapshot contributes to
    [1/(1 + factor)] to the average.

    It may happen that the rollup does not receive any message for
    some period of time. The parameter [elapsed] allows that to be taken
    into account. If [elapsed=n] with [n>=1] it is similar as if
    [update_burn_per_byte] was called [n] times with [final_size=0].

    Once the exponential moving average [ema] is computed, we use the
    [hard limit] to know whether the cost per byte should be updated:

    1. If [ema <= 80] then the cost per byte is decreased

    2. If [80 < ema <= 90] then the cost per byte is stable

    3. If [90 < ema] then the cost ber byte is increased

    The rationale behind this mechanics is to adapt the cost of a
    transactional rollup depending on its activity. This can be used
    to prevent from some spamming attacks. *)
val update_burn_per_byte :
  t -> elapsed:int -> factor:int -> final_size:int -> hard_limit:int -> t

(** [burn_cost ~limit state size] computes the burn to be paid to submit
    [size] bytes in the inbox of the transactional rollup.

    Returns [Tx_rollup_submit_batch_burn_exceeded] if the (computed) burn
    exceeds [limit].
*)
val burn_cost : limit:Tez_repr.t option -> t -> int -> Tez_repr.t tzresult

(** [has_valid_commitment_at state level] returns [true] iff there is
    a valid commitment for [level] in the layer-1 storage.

    On the contrary, if there is not commitment for [level] in the
    layer-1 storage, or if there exists an orphan commitment (that is,
    a commitment which has been rejected, or with one of its ancestors
    that has been rejected) at [level], this function returns
    [false]. *)
val has_valid_commitment_at : t -> Tx_rollup_level_repr.t -> bool

(** [uncommitted_inboxes_count state] returns the number of inboxes
    the rollup current has in the storage which did not receive a
    commitment yet. *)
val uncommitted_inboxes_count : t -> int

(** [commitments_count t] returns the number of commitment still in
    the layer-1 context. *)
val commitments_count : t -> int

(** [inboxes_count state] returns the number of inboxes the rollup
    current has in the storage. *)
val inboxes_count : t -> int

(** [next_commitment_to_finalize state] returns the rollup level of
    the next commitment to be finalized. *)
val next_commitment_to_finalize : t -> Tx_rollup_level_repr.t option

(** [next_commitment_to_remove state] returns the rollup level of the
    next commitment to be removed from the layer-1 context. *)
val next_commitment_to_remove : t -> Tx_rollup_level_repr.t option

(** [finalized_commitment_oldest_level state] returns the rollup level
    of the oldest finalized commitment. *)
val finalized_commitment_oldest_level : t -> Tx_rollup_level_repr.t option

(** [next_commitment_level current_level state] returns the expected
    level of the next valid commitment.

    This function can return the error [No_uncommitted_inbox] if
    there is no inbox awaiting a commitment. *)
val next_commitment_level :
  t -> Raw_level_repr.t -> Tx_rollup_level_repr.t tzresult

(** [next_commitment_predecessor state] returns the expected
    predecessor hash of the next valid commitment. *)
val next_commitment_predecessor : t -> Tx_rollup_commitment_repr.Hash.t option

(** [record_inbox_creation state level] updates the state of a rollup
    to take into account the creation of of a new inbox at the given
    Tezos [level], and returns the rollup level to associate to this
    inbox and the number of bytes allocated for the inbox.

    This function may return an [Internal_error] iff an inbox has
    already been created at a level greater (or equal) than
    [level]. It is the responsibility of the caller to avoid that. *)
val record_inbox_creation :
  t -> Raw_level_repr.t -> (t * Tx_rollup_level_repr.t * Z.t) tzresult

(** [record_inbox_deletion state level] updates [state] to take into
    account the deletion of the inbox stored at Tezos [level] from the
    storage.

    This function returns an [Internal_error] iff there is no inbox
    in the storage of the layer-1, or if [level] is not the oldest
    level of rollup. *)
val record_inbox_deletion : t -> Tx_rollup_level_repr.t -> t tzresult

(** [record_commitment_creation state level] updates [state] to take
    into account the creation of a commitment at a given Tezos
    [level].

    This function returns an [Internal_error] if [level] is not the
    successor level of the current commitment head, or if [level] is
    greater than the inbox head. *)
val record_commitment_creation :
  t -> Tx_rollup_level_repr.t -> Tx_rollup_commitment_repr.Hash.t -> t tzresult

(** [record_commitment_rejection state level pred_hash] updates
    [state] to take into account the fact that the commitment for the
    inbox at [level] has been rejected.

    The caller is expected to provide the predecessor hash the next
    valid commitment needs to use. It can be omitted under two
    circumstances: if [level = root], or if the commitment identified
    by [pred_hash] is no longer in the layer-1 context. *)
val record_commitment_rejection :
  t ->
  Tx_rollup_level_repr.t ->
  Tx_rollup_commitment_repr.Hash.t option ->
  t tzresult

(** [record_commitment_deletion state level msg_hash commitment_hash]
    updates [state] to take into account the deletion of a commitment
    at a given rollup [level], and of given [commitment_hash] and
    whose last message commitment is [msg_hash].

    This function returns an [Internal_error] if [level] is not the
    commitment tail, that is the oldest finalized commitment. *)
val record_commitment_deletion :
  t ->
  Tx_rollup_level_repr.t ->
  Tx_rollup_commitment_repr.Hash.t ->
  Tx_rollup_message_result_hash_repr.t ->
  t tzresult

(** [finalized_commitments_range state] returns the window of finalized
    commitments that have not yet been cleaned out

    This function returns an [Internal_error] if the state is inconsistent,
    which should not be possible. *)
val finalized_commitments_range :
  t -> (Tx_rollup_level_repr.t * Tx_rollup_level_repr.t) option

(** [check_level_can_be_rejected state level] raises
    [Cannot_reject_level] iff there does not exist a commitment at
    [level] that is not yet finalized. *)
val check_level_can_be_rejected : t -> Tx_rollup_level_repr.t -> unit tzresult

(** [last_removed_commitment_hashes state] returns two hashes
    associated to the last removed commitment: the message result
    hash and the last commitment hash. *)
val last_removed_commitment_hashes :
  t ->
  (Tx_rollup_message_result_hash_repr.t * Tx_rollup_commitment_repr.Hash.t)
  option

(** [head_levels state] returns the level of the last inbox which has
    been created in the layer-1 context, along with the Tezos level at
    which said inbox has been created. *)
val head_levels : t -> (Tx_rollup_level_repr.t * Raw_level_repr.t) option

(** [adjust_storage_allocation state ~delta] accounts for a change in
    [delta] number of bytes used storage space by a transaction rollup.

    A positive [delta] indicates that the occupied storage of the
    rollup increased. A negative [delta] indicates that the
    occupied storage of the rollup decreased.

    Along with an updated state, a diff of storage space
    is returned. The diff is
    [max(0, allocated_storage - (occupied_storage + delta))].
    That is, 0 if no new storage was allocated, and the number of bytes
    allocated otherwise.

    This function returns [Tx_rollup_errors.Internal_error] if
    submitted [delta] would make [occupied_storage] negative. *)
val adjust_storage_allocation : t -> delta:Z.t -> (t * Z.t) tzresult

module Internal_for_tests : sig
  (** [make] returns a state for tests *)
  val make :
    ?burn_per_byte:Tez_repr.t ->
    ?inbox_ema:int ->
    ?last_removed_commitment_hashes:
      Tx_rollup_message_result_hash_repr.t * Tx_rollup_commitment_repr.Hash.t ->
    ?finalized_commitments:Tx_rollup_level_repr.t * Tx_rollup_level_repr.t ->
    ?unfinalized_commitments:Tx_rollup_level_repr.t * Tx_rollup_level_repr.t ->
    ?uncommitted_inboxes:Tx_rollup_level_repr.t * Tx_rollup_level_repr.t ->
    ?commitment_newest_hash:Tx_rollup_commitment_repr.Hash.t ->
    ?tezos_head_level:Raw_level_repr.t ->
    ?occupied_storage:Z.t ->
    ?commitments_watermark:Tx_rollup_level_repr.t ->
    allocated_storage:Z.t ->
    unit ->
    t

  val get_inbox_ema : t -> int

  val get_occupied_storage : t -> Z.t

  val set_occupied_storage : Z.t -> t -> t

  val get_allocated_storage : t -> Z.t

  val set_allocated_storage : Z.t -> t -> t

  val reset_commitments_watermark : t -> t

  val get_commitments_watermark : t -> Tx_rollup_level_repr.t option
end
back to top