Raw File
remove_zero_amount_ticket_migration_for_o.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2023 Marigold <contact@marigold.dev>                        *)
(*                                                                           *)
(* 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.                                                 *)
(*                                                                           *)
(*****************************************************************************)

(* Remove zero tickets from big map with id 5696. This big map is found in the
   storage of [KT1CnygLoKfJA66499U9ZQkL6ykUfzgruGfM]. The id can be looked up in
   https://tzkt.io/KT1CnygLoKfJA66499U9ZQkL6ykUfzgruGfM/storage/

   This procedure removes all keys with zero-amount tickets from the big-map.
   We have compiled a list of hashes corresponding to keys of values with
   zero-amount tickets. We also verify removal by pattern matching on the values
   to make sure they contain zero-amount ticket values.
*)

type error +=
  | Invalid_big_map_key of string
  | Not_zero_ticket of Script_expr_hash.t
  | Invalid_big_map_value of Script_expr_hash.t

let () =
  register_error_kind
    `Permanent
    ~id:"migration.invalid_big_map_key"
    ~title:"Invalid big map key as a script expression hash"
    ~description:
      "As part of the protocol migration, a big map key was expected to be \
       parsed successfully into a script expression hash"
    ~pp:(fun ppf key ->
      Format.fprintf ppf "Unable to parse %s as a script expression hash" key)
    Data_encoding.(obj1 (req "key" (string Plain)))
    (function Invalid_big_map_key key -> Some key | _ -> None)
    (fun key -> Invalid_big_map_key key) ;
  register_error_kind
    `Permanent
    ~id:"migration.not_zero_ticket_key"
    ~title:"A big map key is not mapping to any zero ticket"
    ~description:"the value mapped to a big map key has an unexpected shape"
    ~pp:(fun ppf key ->
      Format.fprintf
        ppf
        "%a does not map to a zero ticket, key not found"
        Script_expr_hash.pp
        key)
    Data_encoding.(obj1 (req "key" Script_expr_hash.encoding))
    (function Not_zero_ticket key -> Some key | _ -> None)
    (fun key -> Not_zero_ticket key) ;
  register_error_kind
    `Permanent
    ~id:"migration.zero_ticket_wrong_value"
    ~title:"A big map key is mapped to an unexpected value"
    ~description:
      "As part of the protocol migration, a big map key was expected to be \
       mapping into a value which is of unexpected shape"
    ~pp:(fun ppf key ->
      Format.fprintf
        ppf
        "%a does not map to a zero ticket, unexpected shape"
        Script_expr_hash.pp
        key)
    Data_encoding.(obj1 (req "key" Script_expr_hash.encoding))
    (function Invalid_big_map_value key -> Some key | _ -> None)
    (fun key -> Invalid_big_map_value key)

let remove_zero_ticket_entries ctxt =
  let open Lwt_result_syntax in
  let*? contract_to_clean =
    Contract_repr.of_b58check "KT1CnygLoKfJA66499U9ZQkL6ykUfzgruGfM"
  in
  let big_map_to_clean =
    Lazy_storage_kind.Big_map.Id.parse_z @@ Z.of_int 5696
  in
  let*? keys_to_evict =
    List.map_e
      (fun key ->
        match Script_expr_hash.of_b58check_opt key with
        | Some key -> ok key
        | None -> error (Invalid_big_map_key key))
      [
        "exprtXBtxJxCDEDETueKAFLL7r7vZtNEo1MHajpHba1djtGKqJzWd3";
        "exprtbuRhaGDS942BgZ1qFdD7HAKeBjPEqzRxgLQyWQ6HWxcaiLC2c";
        "exprtePxSLgrhJmTPZEePyFBmESLhaBUN1WodvLYy9xYhEYE6dKPLe";
        "exprtx9GaYz5Fy5ytiuYgSfJqeYqkxGgobust8U6dpCLaeZUMiitmg";
        "expru28t4XoyB61WuRQnExk3Kq8ssGv1ejgdo9XHxpTXoQjXTGw1Dg";
        "expru2fZALknjB4vJjmQBPkrs3dJZ5ytuzfmE9A7ScUk5opJiZQyiJ";
        "expru2riAFKURjHJ1vNpvsZGGw6z4wtTvbstXVuwQPj1MaTqKPeQ6z";
        "expruHoZDr8ioVhaAs495crYTprAYyC87CruEJ6HaS7diYV6qLARqQ";
        "expruMie2gfy5smMd81NtcvvWm4jD7ThUebw9hpF3N3apKVtxkVG9M";
        "expruc3QW7cdxrGurDJQa6k9QqMZjGkRDJahy2XNtBt9WQzC1yavJK";
        "exprud86wYL7inFCVHkF1Jcz8uMXVY7dnbzxVupyyknZjtDVmwoQTJ";
        "exprufYzeBTGn9733Ga8xEEmU4SsrSyDrzEip8V8hTBAG253T5zZQx";
        "exprum9tuHNvisMa3c372AFmCa27rmkbCGrhzMSprrxgJjzXhrKAag";
        "expruokt7oQ6dDHRvL4sURKUzfwJirR8FPHvpXwjgUD4KHhPWhDGbv";
        "expruom5ds2hVgjdTB877Fx3ZuWT5WUnw1H6kUZavVHcJFbCkcgo3x";
        "exprv2DPd1pV3GVSN2CgW7PPrAQUTuZAdeJphwToQrTNrxiJcWzvtX";
        "exprv65Czv5TnKyEWgBHjDztkCkc1FAVEPxZ3V3ocgvGjfXwjPLo8M";
        "exprv6S2KAvqAC18jDLYjaj1w9oc4ESdDGJkUZ63EpkqSTAz88cSYB";
        "exprvNg3VDBnhtTHvc75krAEYzz6vUMr3iU5jtLdxs83FbgTbZ9nFT";
        "exprvS7wNDHYKYZ19nj3ZUo7AAVMCDpTK3NNERFhqe5SJGCBL4pwFA";
      ]
  in
  let* initial_big_map_size =
    Storage.Big_map.Total_bytes.get ctxt big_map_to_clean
  in
  let* initial_storage_size =
    Storage.Contract.Used_storage_space.get ctxt contract_to_clean
  in
  let scan_and_remove (ctxt, storage_freed) key =
    let* ctxt, v = Storage.Big_map.Contents.get (ctxt, big_map_to_clean) key in
    let open Micheline in
    let open Michelson_v1_primitives in
    match root v with
    | Prim
        ( _,
          D_Pair,
          [
            _timestamp;
            Prim
              ( _,
                D_Pair,
                [_data; Prim (_, D_Pair, [_ticketer; Int (_, amount)], _)],
                _ );
          ],
          _ ) ->
        if Z.(equal amount zero) then
          let* ctxt, size_sub, is_evicted =
            Storage.Big_map.Contents.remove (ctxt, big_map_to_clean) key
          in
          if is_evicted then
            return (ctxt, Z.(add storage_freed (Z.of_int size_sub)))
          else tzfail @@ Invalid_big_map_value key
        else tzfail @@ Invalid_big_map_value key
    | _ -> tzfail @@ Invalid_big_map_value key
  in
  let* ctxt, storage_freed =
    List.fold_left_es scan_and_remove (ctxt, Z.zero) keys_to_evict
  in
  let new_big_map_size = Z.sub initial_big_map_size storage_freed in
  let* ctxt =
    Storage.Big_map.Total_bytes.update ctxt big_map_to_clean new_big_map_size
  in
  let new_storage_size = Z.sub initial_storage_size storage_freed in
  Storage.Contract.Used_storage_space.update
    ctxt
    contract_to_clean
    new_storage_size

let remove_zero_ticket_entries orig_ctxt =
  let open Lwt_syntax in
  let* res = remove_zero_ticket_entries orig_ctxt in
  match res with
  | Error e ->
      Logging.(
        log
          Warning
          "Error while removing zero tickets: %a"
          Error_monad.pp_trace
          e) ;
      return orig_ctxt
  | Ok ctxt -> return ctxt
back to top