Raw File
init_storage.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com>     *)
(* Copyright (c) 2019-2020 Nomadic Labs <contact@nomadic-labs.com>           *)
(* Copyright (c) 2021 DaiLambda, Inc. <contact@dailambda.jp>                 *)
(*                                                                           *)
(* 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.                                                 *)
(*                                                                           *)
(*****************************************************************************)

(*
  To add invoices, you can use a helper function like this one:

(** Invoice a contract at a given address with a given amount. Returns the
    updated context and a  balance update receipt (singleton list). The address
    must be a valid base58 hash, otherwise this is no-op and returns an empty
    receipts list.

    Do not fail if something goes wrong.
*)

let invoice_contract ctxt ~address ~amount_mutez =
  let open Lwt_result_syntax in
  match Tez_repr.of_mutez amount_mutez with
  | None -> Lwt.return (ctxt, [])
  | Some amount -> (
      let*! result =
        let*? recipient = Contract_repr.of_b58check address in
        Token.transfer
          ~origin:Protocol_migration
          ctxt
          `Invoice
          (`Contract recipient)
          amount
      in
      Lwt.return @@ match result with Ok res -> res | Error _ -> (ctxt, []))
*)

(*
  To patch code of legacy contracts you can add a helper function here and call
  it at the end of prepare_first_block.

  See !3730 for an example.
*)

let patch_script ctxt (address, hash, patched_code) =
  let open Lwt_result_syntax in
  let*? contract = Contract_repr.of_b58check address in
  let* ctxt, code_opt = Storage.Contract.Code.find ctxt contract in
  Logging.log Notice "Patching %s... " address ;
  match code_opt with
  | Some old_code ->
      let old_bin = Data_encoding.force_bytes old_code in
      let old_hash = Script_expr_hash.hash_bytes [old_bin] in
      if Script_expr_hash.equal old_hash hash then (
        let new_code = Script_repr.lazy_expr patched_code in
        let* ctxt, size_diff =
          Storage.Contract.Code.update ctxt contract new_code
        in
        Logging.log Notice "Contract %s successfully patched" address ;
        let size_diff = Z.of_int size_diff in
        let* prev_size =
          Storage.Contract.Used_storage_space.get ctxt contract
        in
        let new_size = Z.add prev_size size_diff in
        let* ctxt =
          Storage.Contract.Used_storage_space.update ctxt contract new_size
        in
        if Z.(gt size_diff zero) then
          let* prev_paid_size =
            Storage.Contract.Paid_storage_space.get ctxt contract
          in
          let paid_size = Z.add prev_paid_size size_diff in
          Storage.Contract.Paid_storage_space.update ctxt contract paid_size
        else return ctxt)
      else (
        Logging.log
          Error
          "Patching %s was skipped because its script does not have the \
           expected hash (expected: %a, found: %a)"
          address
          Script_expr_hash.pp
          hash
          Script_expr_hash.pp
          old_hash ;
        return ctxt)
  | None ->
      Logging.log
        Error
        "Patching %s was skipped because no script was found for it in the \
         context."
        address ;
      return ctxt

let migrate_already_denounced_from_Oxford ctxt =
  let open Lwt_syntax in
  let migrate_cycle ctxt cycle =
    let* ctxt =
      Storage.Already_denounced__Oxford.fold
        (ctxt, cycle)
        ~order:`Undefined
        ~init:ctxt
        ~f:(fun (level, delegate) {for_double_attesting; for_double_baking} ctxt
           ->
          Storage.Already_denounced.add
            (ctxt, cycle)
            ((level, Round_repr.zero), delegate)
            {
              for_double_preattesting = for_double_attesting;
              for_double_attesting;
              for_double_baking;
            })
    in
    Storage.Already_denounced__Oxford.clear (ctxt, cycle)
  in
  (* Since the max_slashing_period is 2, denunciations are only
     relevant if the misbehaviour happened in either the current cycle
     or the previous cycle. *)
  let current_cycle = (Level_storage.current ctxt).cycle in
  let* ctxt = migrate_cycle ctxt current_cycle in
  match Cycle_repr.pred current_cycle with
  | None -> return ctxt
  | Some previous_cycle -> migrate_cycle ctxt previous_cycle

(* This removes snapshots and moves the current [staking_balance] one level
   up. Same thing for active delegates with minimal stake but renames the key
   at the same time. *)
let migrate_staking_balance_and_active_delegates_for_p ctxt =
  let open Lwt_result_syntax in
  let* staking_balance_tree =
    Raw_context.get_tree ctxt ["staking_balance"; "current"]
  in
  let*! ctxt =
    Raw_context.add_tree ctxt ["staking_balance"] staking_balance_tree
  in
  let* active_delegates_tree =
    Raw_context.get_tree ctxt ["active_delegate_with_one_roll"; "current"]
  in
  let*! ctxt = Raw_context.remove ctxt ["active_delegate_with_one_roll"] in
  let*! ctxt =
    Raw_context.add_tree
      ctxt
      ["active_delegates_with_minimal_stake"]
      active_delegates_tree
  in
  return ctxt

(* This should clean most of the remaining fields [frozen_deposits].
   The field was removed in Oxford but cleaning it using [remove] was too
   costly as it iterated over all contracts.
   Instead we iterate over activate delegates.
   If there are remaining [frozen_deposits] once P activates, they can still be
   removed one by one in Q. *)
let clean_frozen_deposits_for_p ctxt =
  let open Lwt_result_syntax in
  let contracts_index = ["contracts"; "index"] in
  let* contracts_tree = Raw_context.get_tree ctxt contracts_index in
  let field = ["frozen_deposits"] in
  let*! contracts_tree =
    Storage.Stake.Active_delegates_with_minimal_stake.fold
      ctxt
      ~order:`Undefined
      ~init:contracts_tree
      ~f:(fun pkh contracts_tree ->
        let path = Contract_repr.Index.to_path (Implicit pkh) field in
        Raw_context.Tree.remove contracts_tree path)
  in
  Raw_context.update_tree ctxt contracts_index contracts_tree

let cleanup_values_for_protocol_p ctxt
    (previous_proto_constants : Constants_parametric_previous_repr.t option)
    level =
  let open Lwt_result_syntax in
  let preserved_cycles =
    let previous_proto_constants =
      match previous_proto_constants with
      | None ->
          (* Shouldn't happen *)
          failwith
            "Internal error: cannot read previous protocol constants in \
             context."
      | Some c -> c
    in
    previous_proto_constants.preserved_cycles
  in
  let consensus_rights_delay = Constants_storage.consensus_rights_delay ctxt in
  let new_cycle =
    let next_level = Raw_level_repr.succ level in
    let cycle_eras = Raw_context.cycle_eras ctxt in
    (Level_repr.level_from_raw ~cycle_eras next_level).cycle
  in
  let* ctxt =
    Stake_storage.cleanup_values_for_protocol_p
      ctxt
      ~preserved_cycles
      ~consensus_rights_delay
      ~new_cycle
  in
  let* ctxt =
    Delegate_sampler.cleanup_values_for_protocol_p
      ctxt
      ~preserved_cycles
      ~consensus_rights_delay
      ~new_cycle
  in
  return ctxt

(** Updates the total supply with refined estimation at the activation
    of P using measures from
    https://gitlab.com/tezos/tezos/-/merge_requests/11978.

    Remove me in Q. *)
let update_total_supply_for_p chain_id ctxt =
  let open Lwt_result_syntax in
  (* We only update the total supply for mainnet. *)
  if Chain_id.equal Constants_repr.mainnet_id chain_id then
    let* current_total_supply = Storage.Contract.Total_supply.get ctxt in
    let*? updated_total_supply =
      Tez_repr.(current_total_supply +? of_mutez_exn 16458634911983L)
    in
    let*! ctxt = Storage.Contract.Total_supply.add ctxt updated_total_supply in
    return ctxt
  else return ctxt

let prepare_first_block chain_id ctxt ~typecheck_smart_contract
    ~typecheck_smart_rollup ~level ~timestamp ~predecessor =
  let open Lwt_result_syntax in
  let* previous_protocol, previous_proto_constants, ctxt =
    Raw_context.prepare_first_block ~level ~timestamp chain_id ctxt
  in
  let parametric = Raw_context.constants ctxt in
  let*! ctxt =
    let*! ctxt =
      Raw_context.Cache.set_cache_layout
        ctxt
        (Constants_repr.cache_layout parametric)
    in
    Lwt.return (Raw_context.Cache.clear ctxt)
  in
  let* ctxt, balance_updates =
    match previous_protocol with
    | Genesis param ->
        (* This is the genesis protocol: initialise the state *)
        let*? level = Raw_level_repr.of_int32 level in
        let* ctxt =
          Storage.Tenderbake.First_level_of_protocol.init ctxt level
        in
        let* ctxt = Forbidden_delegates_storage.init_for_genesis ctxt in
        let*! ctxt = Storage.Contract.Total_supply.add ctxt Tez_repr.zero in
        let* ctxt = Storage.Block_round.init ctxt Round_repr.zero in
        let init_commitment (ctxt, balance_updates)
            Commitment_repr.{blinded_public_key_hash; amount} =
          let* ctxt, new_balance_updates =
            Token.transfer
              ctxt
              `Initial_commitments
              (`Collected_commitments blinded_public_key_hash)
              amount
          in
          return (ctxt, new_balance_updates @ balance_updates)
        in
        let* ctxt, commitments_balance_updates =
          List.fold_left_es init_commitment (ctxt, []) param.commitments
        in
        let* ctxt =
          Seed_storage.init ?initial_seed:param.constants.initial_seed ctxt
        in
        let* ctxt = Contract_storage.init ctxt in
        let* ctxt, bootstrap_balance_updates =
          Bootstrap_storage.init
            ctxt
            ~typecheck_smart_contract
            ~typecheck_smart_rollup
            ?no_reward_cycles:param.no_reward_cycles
            param.bootstrap_accounts
            param.bootstrap_contracts
            param.bootstrap_smart_rollups
        in
        let* ctxt = Delegate_cycles.init_first_cycles ctxt in
        let* ctxt =
          Vote_storage.init
            ctxt
            ~start_position:(Level_storage.current ctxt).level_position
        in
        let* ctxt = Vote_storage.update_listings ctxt in
        (* Must be called after other originations since it unsets the origination nonce. *)
        let* ctxt, operation_results =
          Liquidity_baking_migration.init
            ctxt
            ~typecheck:typecheck_smart_contract
        in
        let* ctxt =
          Storage.Pending_migration.Operation_results.init
            ctxt
            operation_results
        in
        let* ctxt = Sc_rollup_inbox_storage.init_inbox ~predecessor ctxt in
        let* ctxt = Adaptive_issuance_storage.init ctxt in
        return (ctxt, commitments_balance_updates @ bootstrap_balance_updates)
    | Oxford_018
    (* Please update [next_protocol] and [previous_protocol] in
       [tezt/lib_tezos/protocol.ml] when you update this value. *) ->
        (* TODO (#2704): possibly handle attestations for migration block (in bakers);
           if that is done, do not set Storage.Tenderbake.First_level_of_protocol.
           /!\ this storage is also use to add the smart rollup
               inbox migration message. see `sc_rollup_inbox_storage`. *)
        let*? level = Raw_level_repr.of_int32 level in
        let* ctxt =
          Storage.Tenderbake.First_level_of_protocol.update ctxt level
        in
        (* Migration of refutation games needs to be kept for each protocol. *)
        let* ctxt =
          Sc_rollup_refutation_storage.migrate_clean_refutation_games ctxt
        in
        (* Adaptive Issuance-related migrations from Oxford to P. *)
        (* We usually clear the table at the end of the cycle but the migration
           can happen in the middle of the cycle, so we clear it here.
           Possible consequence: the slashing history could be inconsistent with
           the pending denunciations, i.e., there could be unstaked_frozen_deposits
           that are not slashed whereas unstake_requests are slashed. *)
        let*! ctxt = Pending_denunciations_storage.clear ctxt in
        let*! ctxt = migrate_already_denounced_from_Oxford ctxt in
        let* ctxt = migrate_staking_balance_and_active_delegates_for_p ctxt in
        let* ctxt = clean_frozen_deposits_for_p ctxt in
        let*! ctxt = Raw_context.remove ctxt ["last_snapshot"] in
        let* ctxt =
          cleanup_values_for_protocol_p ctxt previous_proto_constants level
        in
        (* Update the percentage representation of the stored slashes *)
        let*! ctxt =
          Delegate_slashed_deposits_storage.update_slashing_storage_for_p ctxt
        in
        let* ctxt = update_total_supply_for_p chain_id ctxt in
        return (ctxt, [])
  in
  let* ctxt =
    List.fold_left_es patch_script ctxt Legacy_script_patches.addresses_to_patch
  in
  let*? balance_updates = Receipt_repr.group_balance_updates balance_updates in
  let*! ctxt =
    Storage.Pending_migration.Balance_updates.add ctxt balance_updates
  in
  if Constants_storage.adaptive_issuance_force_activation ctxt then
    let ctxt = Raw_context.set_adaptive_issuance_enable ctxt in
    let* ctxt =
      let current_cycle = (Level_storage.current ctxt).cycle in
      Storage.Adaptive_issuance.Activation.update ctxt (Some current_cycle)
    in
    return ctxt
  else return ctxt

let prepare ctxt ~level ~predecessor_timestamp ~timestamp =
  let open Lwt_result_syntax in
  let* ctxt =
    Raw_context.prepare
      ~level
      ~predecessor_timestamp
      ~timestamp
      ~adaptive_issuance_enable:false
      ctxt
  in
  let* ctxt = Adaptive_issuance_storage.set_adaptive_issuance_enable ctxt in
  Storage.Pending_migration.remove ctxt
back to top