Raw File
script_typed_ir_size.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2021 Nomadic Labs, <contact@nomadic-labs.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.                                                 *)
(*                                                                           *)
(*****************************************************************************)

open Alpha_context
open Script_typed_ir
include Cache_memory_helpers

let script_string_size s = Script_string.to_string s |> string_size

(* Memo-sizes are 16-bit integers *)
let sapling_memo_size_size = !!0

let (comparable_ty_size, ty_size) =
  let base {annot = _; size = _} = hh3w in
  let apply_comparable :
      type a. nodes_and_size -> a comparable_ty -> nodes_and_size =
   fun accu cty ->
    match cty with
    | Unit_key a -> ret_succ_adding accu (base a)
    | Int_key a -> ret_succ_adding accu (base a)
    | Nat_key a -> ret_succ_adding accu (base a)
    | Signature_key a -> ret_succ_adding accu (base a)
    | String_key a -> ret_succ_adding accu (base a)
    | Bytes_key a -> ret_succ_adding accu (base a)
    | Mutez_key a -> ret_succ_adding accu (base a)
    | Key_hash_key a -> ret_succ_adding accu (base a)
    | Key_key a -> ret_succ_adding accu (base a)
    | Timestamp_key a -> ret_succ_adding accu (base a)
    | Address_key a -> ret_succ_adding accu (base a)
    | Bool_key a -> ret_succ_adding accu (base a)
    | Chain_id_key a -> ret_succ_adding accu (base a)
    | Never_key a -> ret_succ_adding accu (base a)
    | Pair_key ((_ty1, _fa1), (_ty2, _fa2), a) ->
        ret_succ_adding accu @@ (base a +! hh6w)
    | Union_key ((_ty1, _fa1), (_ty2, _fa2), a) ->
        ret_succ_adding accu @@ (base a +! hh6w)
    | Option_key (_ty, a) -> ret_succ_adding accu @@ (base a +! word_size)
  and apply : type a. nodes_and_size -> a ty -> nodes_and_size =
   fun accu ty ->
    match ty with
    | Unit_t a -> ret_succ_adding accu @@ base a
    | Int_t a -> ret_succ_adding accu @@ base a
    | Nat_t a -> ret_succ_adding accu @@ base a
    | Signature_t a -> ret_succ_adding accu @@ base a
    | String_t a -> ret_succ_adding accu @@ base a
    | Bytes_t a -> ret_succ_adding accu @@ base a
    | Mutez_t a -> ret_succ_adding accu @@ base a
    | Key_hash_t a -> ret_succ_adding accu @@ base a
    | Key_t a -> ret_succ_adding accu @@ base a
    | Timestamp_t a -> ret_succ_adding accu @@ base a
    | Address_t a -> ret_succ_adding accu @@ base a
    | Bool_t a -> ret_succ_adding accu @@ base a
    | Operation_t a -> ret_succ_adding accu @@ base a
    | Chain_id_t a -> ret_succ_adding accu @@ base a
    | Never_t a -> ret_succ_adding accu @@ base a
    | Bls12_381_g1_t a -> ret_succ_adding accu @@ base a
    | Bls12_381_g2_t a -> ret_succ_adding accu @@ base a
    | Bls12_381_fr_t a -> ret_succ_adding accu @@ base a
    | Chest_key_t a -> ret_succ_adding accu @@ base a
    | Chest_t a -> ret_succ_adding accu @@ base a
    | Pair_t ((_ty1, _fa1, _va1), (_ty2, _fa2, _va2), a) ->
        ret_succ_adding accu @@ (base a +! hh8w)
    | Union_t ((_ty1, _fa1), (_ty2, _fa2), a) ->
        ret_succ_adding accu @@ (base a +! hh6w)
    | Lambda_t (_ty1, _ty2, a) ->
        ret_succ_adding accu @@ (base a +! (word_size *? 2))
    | Option_t (_ty, a) -> ret_succ_adding accu @@ (base a +! word_size)
    | List_t (_ty, a) -> ret_succ_adding accu @@ (base a +! word_size)
    | Set_t (_cty, a) -> ret_succ_adding accu @@ (base a +! word_size)
    | Map_t (_cty, _ty, a) ->
        ret_succ_adding accu @@ (base a +! (word_size *? 2))
    | Big_map_t (_cty, _ty, a) ->
        ret_succ_adding accu @@ (base a +! (word_size *? 2))
    | Contract_t (_ty, a) -> ret_succ_adding accu @@ (base a +! word_size)
    | Sapling_transaction_t (_m, a) ->
        ret_succ_adding accu @@ (base a +! sapling_memo_size_size +! word_size)
    | Sapling_state_t (_m, a) ->
        ret_succ_adding accu @@ (base a +! sapling_memo_size_size +! word_size)
    | Ticket_t (_cty, a) -> ret_succ_adding accu @@ (base a +! word_size)
  in
  let f = ({apply; apply_comparable} : nodes_and_size ty_traverse) in
  ( (fun cty -> comparable_ty_traverse cty zero f),
    fun ty -> ty_traverse ty zero f )

let stack_ty_size s =
  let apply : type a s. nodes_and_size -> (a, s) stack_ty -> nodes_and_size =
   fun accu s ->
    match s with
    | Bot_t -> ret_succ accu
    | Item_t (ty, _, _annot) -> ret_succ_adding (accu ++ ty_size ty) h3w
  in
  stack_ty_traverse s zero {apply}

let script_nat_size n = Script_int.to_zint n |> z_size

let script_int_size n = Script_int.to_zint n |> z_size

let signature_size = h3w +? Signature.size

let key_hash_size (x : Signature.public_key_hash) =
  h1w
  +? Signature.(
       match x with
       | Ed25519 _ -> Ed25519.Public_key_hash.size
       | Secp256k1 _ -> Secp256k1.Public_key_hash.size
       | P256 _ -> P256.Public_key_hash.size)

let public_key_size (x : public_key) =
  let ks = Signature.Public_key.size x in
  h1w +? ks

let mutez_size = h2w

let timestamp_size x = Script_timestamp.to_zint x |> z_size

let contract_size = Contract.in_memory_size

let address_size ((c, s) : address) = h2w +! contract_size c +! string_size s

let view_signature_size (View_signature {name; input_ty; output_ty}) =
  ret_adding
    (ty_size input_ty ++ ty_size output_ty)
    (h3w +! script_string_size name)

let script_expr_hash_size = Script_expr_hash.size

let peano_shape_proof =
  let scale = header_size +! h1w in
  fun k -> scale *? k

let stack_prefix_preservation_witness_size =
  let kinfo_size = h2w in
  let scale = header_size +! (h2w +! kinfo_size) in
  fun k -> scale *? k

let comb_gadt_witness_size = peano_shape_proof

let uncomb_gadt_witness_size = peano_shape_proof

let comb_get_gadt_witness_size = peano_shape_proof

let comb_set_gadt_witness_size = peano_shape_proof

let dup_n_gadt_witness_size = peano_shape_proof

let contract_size (arg_ty, address) =
  ret_adding (ty_size arg_ty) (h2w +! address_size address)

let sapling_state_size {Sapling.id; diff; memo_size = _} =
  h3w
  +! option_size (fun x -> z_size (Sapling.Id.unparse_to_z x)) id
  +! Sapling.diff_in_memory_size diff
  +! sapling_memo_size_size

let operation_size
    (operation :
      packed_internal_operation * Lazy_storage.diffs_item list option) =
  let (poi, diffs) = operation in
  ret_adding
    (Operation.packed_internal_operation_in_memory_size poi
    ++ option_size_vec Lazy_storage.diffs_in_memory_size diffs)
    h2w

let chain_id_size = h1w +? Chain_id.size

(* [contents] is handle by the recursion scheme in [value_size] *)
let ticket_size {ticketer; contents = _; amount} =
  h3w +! Contract.in_memory_size ticketer +! script_nat_size amount

let chest_size chest =
  (*
     type chest = {
       locked_value : locked_value;
       rsa_public : rsa_public;
       ciphertext : ciphertext;
     }
  *)
  let locked_value_size = 256 in
  let rsa_public_size = 256 in
  let ciphertext_size = Timelock.get_plaintext_size chest in
  h3w +? (locked_value_size + rsa_public_size + ciphertext_size)

let chest_key_size _ =
  (*
     type chest_key = {
       unlocked_value : unlocked_value;
       proof : time_lock_proof
     }
  *)
  let unlocked_value_size = 256 in
  let proof_size = 256 in
  h2w +? (unlocked_value_size + proof_size)

let view_size {input_ty; output_ty; view_code} =
  ret_adding
    (node_size input_ty ++ node_size output_ty ++ node_size view_code)
    h3w

let views_size views =
  SMap.fold
    (fun k view accu ->
      ret_adding (accu ++ view_size view) (script_string_size k +! h4w))
    views
    zero

let kinfo_size {iloc = _; kstack_ty = _} = h2w

(* The following mutually recursive functions are mostly
   tail-recursive and the only recursive call that is not a tailcall
   cannot be nested. (See [big_map_size].) For this reason, these
   functions should not trigger stack overflows. *)
let rec value_size :
    type a.
    count_lambda_nodes:bool ->
    nodes_and_size ->
    (a ty, a comparable_ty) union ->
    a ->
    nodes_and_size =
 fun ~count_lambda_nodes accu ty x ->
  let apply : type a. nodes_and_size -> a ty -> a -> nodes_and_size =
   fun accu ty x ->
    match ty with
    | Unit_t _ -> ret_succ accu
    | Int_t _ -> ret_succ_adding accu (script_int_size x)
    | Nat_t _ -> ret_succ_adding accu (script_nat_size x)
    | Signature_t _ -> ret_succ_adding accu signature_size
    | String_t _ -> ret_succ_adding accu (script_string_size x)
    | Bytes_t _ -> ret_succ_adding accu (bytes_size x)
    | Mutez_t _ -> ret_succ_adding accu mutez_size
    | Key_hash_t _ -> ret_succ_adding accu (key_hash_size x)
    | Key_t _ -> ret_succ_adding accu (public_key_size x)
    | Timestamp_t _ -> ret_succ_adding accu (timestamp_size x)
    | Address_t _ -> ret_succ_adding accu (address_size x)
    | Bool_t _ -> ret_succ accu
    | Pair_t (_, _, _) -> ret_succ_adding accu h2w
    | Union_t (_, _, _) -> ret_succ_adding accu h1w
    | Lambda_t (_, _, _) ->
        (lambda_size [@ocaml.tailcall]) ~count_lambda_nodes (ret_succ accu) x
    | Option_t (_, _) -> ret_succ_adding accu (option_size (fun _ -> !!0) x)
    | List_t (_, _) -> ret_succ_adding accu (h2w +! (h2w *? x.length))
    | Set_t (_, _) ->
        let module M = (val x) in
        let boxing_space = !!300 in
        ret_succ_adding accu (boxing_space +! (h4w *? M.size))
    | Map_t (_, _, _) ->
        let module M = (val x) in
        let boxing_space = !!300 in
        ret_succ_adding accu (boxing_space +! (h5w *? M.size))
    | Big_map_t (cty, ty', _) ->
        (big_map_size [@ocaml.tailcall])
          ~count_lambda_nodes
          (ret_succ accu)
          cty
          ty'
          x
    | Contract_t (_, _) -> ret_succ (accu ++ contract_size x)
    | Sapling_transaction_t (_, _) ->
        ret_succ_adding accu (Sapling.transaction_in_memory_size x)
    | Sapling_state_t (_, _) -> ret_succ_adding accu (sapling_state_size x)
    | Operation_t _ -> ret_succ (accu ++ operation_size x)
    | Chain_id_t _ -> ret_succ_adding accu chain_id_size
    | Never_t _ -> ( match x with _ -> .)
    (* Related to https://gitlab.com/dannywillems/ocaml-bls12-381/-/issues/56.
       Since the update to blst as a backend for bls12-381, size_in_bytes is not
       the correct value for the allocated memory.
       There is 1 word for the OCaml block header, 1 word for the C pointer and
       a certain number of words for the actual value of the algebraic object
       whose size is fixed and defined by the object itself.
       For G1, it allocates 3 C values of type blst_fp which is 48 bytes.
       For G2, it allocates 3 C values of type blst_fp2 which is 48 * 2 bytes.
       For Fr, it allocates 1 C value of type blst_fr which is 32 bytes.
    *)
    | Bls12_381_g1_t _ -> ret_succ_adding accu !!((2 * 8) + (3 * 48))
    | Bls12_381_g2_t _ -> ret_succ_adding accu !!((2 * 8) + (3 * 48 * 2))
    | Bls12_381_fr_t _ -> ret_succ_adding accu !!((2 * 8) + 32)
    | Ticket_t (_, _) -> ret_succ_adding accu (ticket_size x)
    | Chest_key_t _ -> ret_succ_adding accu (chest_key_size x)
    | Chest_t _ -> ret_succ_adding accu (chest_size x)
  in
  let apply_comparable :
      type a. nodes_and_size -> a comparable_ty -> a -> nodes_and_size =
   fun accu ty x ->
    match ty with
    | Unit_key _ -> ret_succ accu
    | Int_key _ -> ret_succ_adding accu (script_int_size x)
    | Nat_key _ -> ret_succ_adding accu (script_nat_size x)
    | Signature_key _ -> ret_succ_adding accu signature_size
    | String_key _ -> ret_succ_adding accu (script_string_size x)
    | Bytes_key _ -> ret_succ_adding accu (bytes_size x)
    | Mutez_key _ -> ret_succ_adding accu mutez_size
    | Key_hash_key _ -> ret_succ_adding accu (key_hash_size x)
    | Key_key _ -> ret_succ_adding accu (public_key_size x)
    | Timestamp_key _ -> ret_succ_adding accu (timestamp_size x)
    | Address_key _ -> ret_succ_adding accu (address_size x)
    | Bool_key _ -> ret_succ accu
    | Pair_key (_, _, _) -> ret_succ_adding accu h2w
    | Union_key (_, _, _) -> ret_succ_adding accu h1w
    | Option_key (_, _) -> ret_succ_adding accu (option_size (fun _ -> !!0) x)
    | Chain_id_key _ -> ret_succ_adding accu chain_id_size
    | Never_key _ -> ( match x with _ -> .)
  in
  value_traverse ty x accu {apply; apply_comparable}
 [@@coq_axiom_with_reason "unreachable expressions '.' not handled for now"]

and big_map_size :
    type a b.
    count_lambda_nodes:bool ->
    nodes_and_size ->
    a comparable_ty ->
    b ty ->
    (a, b) big_map ->
    nodes_and_size =
 fun ~count_lambda_nodes accu cty ty' {id; diff; key_type; value_type} ->
  (* [Map.bindings] cannot overflow and only consumes a
     logarithmic amount of stack. *)
  let diff_size =
    let map_size =
      Big_map_overlay.fold
        (fun _key_hash (key, value) accu ->
          let accu = ret_succ_adding accu !!script_expr_hash_size in
          (* The following recursive call cannot introduce a stack
             overflow because this would require a key of type
             big_map while big_map is not comparable. *)
          let accu = value_size ~count_lambda_nodes accu (R cty) key in
          match value with
          | None -> accu
          | Some value ->
              (value_size [@ocaml.tailcall])
                ~count_lambda_nodes
                accu
                (L ty')
                value)
        diff.map
        accu
    in

    ret_adding map_size h2w
  in
  let big_map_id_size s = z_size (Big_map.Id.unparse_to_z s) in
  let id_size = option_size big_map_id_size id in
  ret_adding
    (comparable_ty_size key_type ++ ty_size value_type ++ diff_size)
    (h4w +! id_size)

and lambda_size :
    type i o.
    count_lambda_nodes:bool -> nodes_and_size -> (i, o) lambda -> nodes_and_size
    =
 fun ~count_lambda_nodes accu (Lam (kdescr, node)) ->
  (* We assume that the nodes' size have already been counted if the
     lambda is not a toplevel lambda. *)
  let accu =
    ret_adding (accu ++ if count_lambda_nodes then node_size node else zero) h2w
  in
  (kdescr_size [@ocaml.tailcall]) ~count_lambda_nodes:false accu kdescr

and kdescr_size :
    type a s r f.
    count_lambda_nodes:bool ->
    nodes_and_size ->
    (a, s, r, f) kdescr ->
    nodes_and_size =
 fun ~count_lambda_nodes accu {kloc = _; kbef; kaft; kinstr} ->
  let accu =
    ret_adding (accu ++ stack_ty_size kbef ++ stack_ty_size kaft) h4w
  in
  (kinstr_size [@ocaml.tailcall]) ~count_lambda_nodes accu kinstr

and kinstr_size :
    type a s r f.
    count_lambda_nodes:bool ->
    nodes_and_size ->
    (a, s, r, f) kinstr ->
    nodes_and_size =
 fun ~count_lambda_nodes accu t ->
  let base kinfo = h2w +! kinfo_size kinfo in
  let apply :
      type a s r f. nodes_and_size -> (a, s, r, f) kinstr -> nodes_and_size =
   fun accu t ->
    match t with
    | IDrop (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IDup (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISwap (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IConst (kinfo, x, k) ->
        let accu = ret_succ_adding accu (base kinfo +! word_size) in
        (value_size [@ocaml.tailcall])
          ~count_lambda_nodes
          accu
          (L (stack_top_ty (kinfo_of_kinstr k).kstack_ty))
          x
    | ICons_pair (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ICar (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ICdr (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IUnpair (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ICons_some (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ICons_none (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IIf_none {kinfo; _} -> ret_succ_adding accu (base kinfo)
    | IOpt_map {kinfo; _} -> ret_succ_adding accu (base kinfo)
    | ICons_left (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ICons_right (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IIf_left {kinfo; _} -> ret_succ_adding accu (base kinfo)
    | ICons_list (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INil (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IIf_cons {kinfo; _} -> ret_succ_adding accu (base kinfo)
    | IList_map (kinfo, _, _) -> ret_succ_adding accu (base kinfo)
    | IList_iter (kinfo, _, _) -> ret_succ_adding accu (base kinfo)
    | IList_size (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IEmpty_set (kinfo, cty, _) ->
        ret_succ_adding
          (accu ++ comparable_ty_size cty)
          (base kinfo +! word_size)
    | ISet_iter (kinfo, _, _) -> ret_succ_adding accu (base kinfo)
    | ISet_mem (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISet_update (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISet_size (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IEmpty_map (kinfo, cty, _) ->
        ret_succ_adding
          (accu ++ comparable_ty_size cty)
          (base kinfo +! word_size)
    | IMap_map (kinfo, _, _) -> ret_succ_adding accu (base kinfo +! word_size)
    | IMap_iter (kinfo, _, _) -> ret_succ_adding accu (base kinfo +! word_size)
    | IMap_mem (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMap_get (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMap_update (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMap_get_and_update (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMap_size (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IEmpty_big_map (kinfo, cty, ty, _) ->
        ret_succ_adding
          (accu ++ comparable_ty_size cty ++ ty_size ty)
          (base kinfo +! (word_size *? 2))
    | IBig_map_mem (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IBig_map_get (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IBig_map_update (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IBig_map_get_and_update (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IConcat_string (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IConcat_string_pair (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISlice_string (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IString_size (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IConcat_bytes (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IConcat_bytes_pair (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISlice_bytes (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IBytes_size (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAdd_seconds_to_timestamp (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAdd_timestamp_to_seconds (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISub_timestamp_seconds (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IDiff_timestamps (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAdd_tez (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISub_tez (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISub_tez_legacy (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_teznat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_nattez (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IEdiv_teznat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IEdiv_tez (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IOr (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAnd (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IXor (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INot (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IIs_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INeg (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAbs_int (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IInt_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAdd_int (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAdd_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISub_int (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_int (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IEdiv_int (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IEdiv_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ILsl_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ILsr_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IOr_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAnd_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAnd_int_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IXor_nat (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INot_int (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IIf {kinfo; _} -> ret_succ_adding accu (base kinfo)
    | ILoop (kinfo, _, _) -> ret_succ_adding accu (base kinfo)
    | ILoop_left (kinfo, _, _) -> ret_succ_adding accu (base kinfo +! word_size)
    | IDip (kinfo, _, _) -> ret_succ_adding accu (base kinfo +! word_size)
    | IExec (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IApply (kinfo, ty, _) ->
        ret_succ_adding (accu ++ ty_size ty) (base kinfo +! word_size)
    | ILambda (kinfo, lambda, _) ->
        let accu = ret_succ_adding accu (base kinfo +! word_size) in
        (lambda_size [@ocaml.tailcall]) ~count_lambda_nodes accu lambda
    | IFailwith (kinfo, _, ty) ->
        ret_succ_adding (accu ++ ty_size ty) (base kinfo +! word_size)
    | ICompare (kinfo, cty, _) ->
        ret_succ_adding
          (accu ++ comparable_ty_size cty)
          (base kinfo +! word_size)
    | IEq (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INeq (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ILt (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IGt (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ILe (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IGe (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAddress (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IContract (kinfo, ty, s, _) ->
        ret_succ_adding
          (accu ++ ty_size ty)
          (base kinfo +! string_size s +! (word_size *? 2))
    | IView (kinfo, s, _) ->
        ret_succ_adding (accu ++ view_signature_size s) (base kinfo +! word_size)
    | ITransfer_tokens (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IImplicit_account (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ICreate_contract
        {kinfo; storage_type; arg_type; lambda; root_name = _; views; k = _} ->
        let accu =
          ret_succ_adding
            (accu ++ ty_size storage_type ++ ty_size arg_type
           ++ views_size views)
            (base kinfo +! (word_size *? 4))
        in
        (lambda_size [@ocaml.tailcall]) ~count_lambda_nodes accu lambda
    | ISet_delegate (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INow (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IBalance (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ILevel (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ICheck_signature (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IHash_key (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IPack (kinfo, ty, _) ->
        ret_succ_adding (accu ++ ty_size ty) (base kinfo +! word_size)
    | IUnpack (kinfo, ty, _) ->
        ret_succ_adding (accu ++ ty_size ty) (base kinfo +! word_size)
    | IBlake2b (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISha256 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISha512 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISource (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISender (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISelf (kinfo, ty, s, _) ->
        ret_succ_adding
          (accu ++ ty_size ty)
          (base kinfo +! (word_size *? 2) +! string_size s)
    | ISelf_address (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAmount (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISapling_empty_state (kinfo, _m, _) ->
        ret_succ_adding accu (base kinfo +! word_size +! sapling_memo_size_size)
    | ISapling_verify_update (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IDig (kinfo, n, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2)
          +! stack_prefix_preservation_witness_size n)
    | IDug (kinfo, n, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2)
          +! stack_prefix_preservation_witness_size n)
    | IDipn (kinfo, n, _, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2)
          +! stack_prefix_preservation_witness_size n)
    | IDropn (kinfo, n, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2)
          +! stack_prefix_preservation_witness_size n)
    | IChainId (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INever kinfo -> ret_succ_adding accu (kinfo_size kinfo)
    | IVoting_power (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ITotal_voting_power (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IKeccak (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISha3 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAdd_bls12_381_g1 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAdd_bls12_381_g2 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IAdd_bls12_381_fr (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_bls12_381_g1 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_bls12_381_g2 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_bls12_381_fr (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_bls12_381_z_fr (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IMul_bls12_381_fr_z (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IInt_bls12_381_fr (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INeg_bls12_381_g1 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INeg_bls12_381_g2 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | INeg_bls12_381_fr (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IPairing_check_bls12_381 (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IComb (kinfo, n, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2) +! comb_gadt_witness_size n)
    | IUncomb (kinfo, n, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2) +! uncomb_gadt_witness_size n)
    | IComb_get (kinfo, n, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2) +! comb_get_gadt_witness_size n)
    | IComb_set (kinfo, n, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2) +! comb_set_gadt_witness_size n)
    | IDup_n (kinfo, n, _, _) ->
        ret_succ_adding
          accu
          (base kinfo +! (word_size *? 2) +! dup_n_gadt_witness_size n)
    | ITicket (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IRead_ticket (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | ISplit_ticket (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IJoin_tickets (kinfo, cty, _) ->
        ret_succ_adding
          (accu ++ comparable_ty_size cty)
          (base kinfo +! word_size)
    | IOpen_chest (kinfo, _) -> ret_succ_adding accu (base kinfo)
    | IHalt kinfo -> ret_succ_adding accu (h1w +! kinfo_size kinfo)
    | ILog (_, _, _, _) ->
        (* This instruction is ignored because it is only used for testing. *)
        accu
  in
  kinstr_traverse t accu {apply}

let rec kinstr_extra_size : type a s r f. (a, s, r, f) kinstr -> nodes_and_size
    =
 fun t ->
  let ret_zero x = (Nodes.zero, x) in
  let apply :
      type a s r f. nodes_and_size -> (a, s, r, f) kinstr -> nodes_and_size =
   fun accu t ->
    let stack_prefix_preservation_witness_size n = ret_zero (!!24 *? n) in
    let dup_n_gadt_witness_size n = ret_zero (!!16 *? n) in
    let comb n = ret_zero (!!16 *? n) in
    let if_join k =
      let kinfo = Script_typed_ir.kinfo_of_kinstr k in
      stack_ty_size kinfo.kstack_ty
    in
    let self_size =
      match t with
      (* Op n *)
      | IDig (_, n, _, _) -> stack_prefix_preservation_witness_size n
      | IDug (_, n, _, _) -> stack_prefix_preservation_witness_size n
      | IDipn (_, n, _, _, _) -> stack_prefix_preservation_witness_size n
      | IDropn (_, n, _, _) -> stack_prefix_preservation_witness_size n
      | IComb (_, n, _, _) -> comb n
      | IUncomb (_, n, _, _) -> comb n
      | IComb_get (_, n, _, _) -> comb (n / 2)
      | IComb_set (_, n, _, _) -> comb (n / 2)
      | IDup_n (_, n, _, _) -> dup_n_gadt_witness_size n
      (* Whole stack types after conditionals and loops. *)
      | IIf {k; _} -> if_join k
      | IIf_cons {k; _} -> if_join k
      | IIf_none {k; _} -> if_join k
      | IIf_left {k; _} -> if_join k
      (* Every instruction whose elaboration uses [merge_types],
         [check_item_ty], [comparable_of_ty], or [ty_of_comparable_ty]
         to create a type that is embedded in the IR. *)
      | IJoin_tickets (_, _, k) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr k in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | ITicket (_, k) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr k in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | IRead_ticket (_, k) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr k in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | ICons_list (_, k) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr k in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | IMap_update (_, k) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr k in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | IMap_get_and_update (_, k) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr k in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | IBig_map_get_and_update (_, k) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr k in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | IApply (_, ty, _) -> ty_size ty
      | ICompare (_, ty, _) -> comparable_ty_size ty
      | IList_iter (_, body, _) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr body in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | IList_map (_, body, _) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr body in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | ISet_iter (_, body, _) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr body in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | IMap_map (_, body, _) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr body in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | IMap_iter (_, body, _) -> (
          let kinfo = Script_typed_ir.kinfo_of_kinstr body in
          match kinfo.kstack_ty with Item_t (ty, _, _) -> ty_size ty)
      | ILambda (_, lambda, _) -> lambda_extra_size lambda
      | ICreate_contract {lambda; _} -> lambda_extra_size lambda
      | _ -> zero
    in
    ret_succ (accu ++ self_size)
  in
  kinstr_traverse t zero {apply}

and lambda_extra_size : type i o. (i, o) lambda -> nodes_and_size =
 fun (Lam ({kinstr; _}, _)) -> kinstr_extra_size kinstr

let lambda_size lam =
  (*

      The following formula has been obtained through a regression
      over the corpus of mainnet contracts in Granada.

  *)
  let (lambda_nodes, lambda_size) =
    lambda_size ~count_lambda_nodes:true zero lam
  in
  let (lambda_extra_size_nodes, lambda_extra_size) = lambda_extra_size lam in
  let size = (lambda_size *? 157 /? 100) +! (lambda_extra_size *? 18 /? 100) in
  (Nodes.add lambda_nodes lambda_extra_size_nodes, size)

let kinstr_size kinstr =
  let (kinstr_extra_size_nodes, kinstr_extra_size) = kinstr_extra_size kinstr in
  let (kinstr_nodes, kinstr_size) =
    kinstr_size ~count_lambda_nodes:true zero kinstr
  in
  let size = (kinstr_size *? 157 /? 100) +! (kinstr_extra_size *? 18 /? 100) in
  (Nodes.add kinstr_nodes kinstr_extra_size_nodes, size)

let value_size ty x = value_size ~count_lambda_nodes:true zero (L ty) x
back to top