Raw File
main.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.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.                                                 *)
(*                                                                           *)
(*****************************************************************************)

(* Tezos Protocol Implementation - Protocol Signature Instance *)

type block_header_data = Alpha_context.Block_header.protocol_data

type block_header = Alpha_context.Block_header.t = {
  shell : Block_header.shell_header;
  protocol_data : block_header_data;
}

let block_header_data_encoding =
  Alpha_context.Block_header.protocol_data_encoding

type block_header_metadata = Apply_results.block_metadata

let block_header_metadata_encoding = Apply_results.block_metadata_encoding

type operation_data = Alpha_context.packed_protocol_data =
  | Operation_data :
      'kind Alpha_context.Operation.protocol_data
      -> operation_data

let operation_data_encoding = Alpha_context.Operation.protocol_data_encoding

type operation_receipt = Apply_results.packed_operation_metadata =
  | Operation_metadata :
      'kind Apply_results.operation_metadata
      -> operation_receipt
  | No_operation_metadata : operation_receipt

let operation_receipt_encoding = Apply_results.operation_metadata_encoding

let operation_data_and_receipt_encoding =
  Apply_results.operation_data_and_metadata_encoding

type operation = Alpha_context.packed_operation = {
  shell : Operation.shell_header;
  protocol_data : operation_data;
}

let acceptable_pass = Alpha_context.Operation.acceptable_pass

let max_block_length = Alpha_context.Block_header.max_header_length

let max_operation_data_length =
  Alpha_context.Constants.max_operation_data_length

let validation_passes =
  let open Alpha_context.Constants in
  Updater.
    [
      (* 2048 endorsements *)
      {max_size = 2048 * 2048; max_op = Some 2048};
      (* 32k of voting operations *)
      {max_size = 32 * 1024; max_op = None};
      (* revelations, wallet activations and denunciations *)
      {
        max_size = max_anon_ops_per_block * 1024;
        max_op = Some max_anon_ops_per_block;
      };
      (* 512kB *)
      {max_size = 512 * 1024; max_op = None};
    ]

let rpc_services =
  Alpha_services.register () ;
  Services_registration.get_rpc_services ()

type validation_state = Validate.validation_state

type application_state = Apply.application_state

let init_allowed_consensus_operations ctxt ~endorsement_level
    ~preendorsement_level =
  let open Lwt_result_syntax in
  let open Alpha_context in
  let* ctxt = Delegate.prepare_stake_distribution ctxt in
  let* ctxt, allowed_endorsements, allowed_preendorsements =
    if Level.(endorsement_level = preendorsement_level) then
      let* ctxt, slots =
        Baking.endorsing_rights_by_first_slot ctxt endorsement_level
      in
      let consensus_operations = slots in
      return (ctxt, consensus_operations, consensus_operations)
    else
      let* ctxt, endorsements =
        Baking.endorsing_rights_by_first_slot ctxt endorsement_level
      in
      let* ctxt, preendorsements =
        Baking.endorsing_rights_by_first_slot ctxt preendorsement_level
      in
      return (ctxt, endorsements, preendorsements)
  in
  let ctxt =
    Consensus.initialize_consensus_operation
      ctxt
      ~allowed_endorsements
      ~allowed_preendorsements
  in
  return ctxt

(** Circumstances and relevant information for [begin_validation] and
    [begin_application] below. *)
type mode =
  | Application of block_header
  | Partial_validation of block_header
  | Construction of {
      predecessor_hash : Block_hash.t;
      timestamp : Time.t;
      block_header_data : block_header_data;
    }
  | Partial_construction of {
      predecessor_hash : Block_hash.t;
      timestamp : Time.t;
    }

let prepare_ctxt ctxt mode ~(predecessor : Block_header.shell_header) =
  let open Lwt_result_syntax in
  let open Alpha_context in
  let level, timestamp =
    match mode with
    | Application block_header | Partial_validation block_header ->
        (block_header.shell.level, block_header.shell.timestamp)
    | Construction {timestamp; _} | Partial_construction {timestamp; _} ->
        (Int32.succ predecessor.level, timestamp)
  in
  let* ctxt, migration_balance_updates, migration_operation_results =
    prepare ctxt ~level ~predecessor_timestamp:predecessor.timestamp ~timestamp
  in
  let*? predecessor_raw_level = Raw_level.of_int32 predecessor.level in
  let predecessor_level = Level.from_raw ctxt predecessor_raw_level in
  (* During block (full or partial) application or full construction,
     endorsements must be for [predecessor_level] and preendorsements,
     if any, for the block's level. In the mempool (partial
     construction), only consensus operations for [predecessor_level]
     (that is, head's level) are allowed (except for grandparent
     endorsements, which are handled differently). *)
  let preendorsement_level =
    match mode with
    | Application _ | Partial_validation _ | Construction _ ->
        Level.current ctxt
    | Partial_construction _ -> predecessor_level
  in
  let* ctxt =
    init_allowed_consensus_operations
      ctxt
      ~endorsement_level:predecessor_level
      ~preendorsement_level
  in
  Dal_apply.initialisation ~level:predecessor_level ctxt >>=? fun ctxt ->
  return
    ( ctxt,
      migration_balance_updates,
      migration_operation_results,
      predecessor_level,
      predecessor_raw_level )

let begin_validation ctxt chain_id mode ~predecessor =
  let open Lwt_result_syntax in
  let open Alpha_context in
  let* ( ctxt,
         _migration_balance_updates,
         _migration_operation_results,
         predecessor_level,
         _predecessor_raw_level ) =
    prepare_ctxt ctxt ~predecessor mode
  in
  let predecessor_timestamp = predecessor.timestamp in
  let predecessor_fitness = predecessor.fitness in
  match mode with
  | Application block_header ->
      let*? fitness = Fitness.from_raw block_header.shell.fitness in
      Validate.begin_application
        ctxt
        chain_id
        ~predecessor_level
        ~predecessor_timestamp
        block_header
        fitness
  | Partial_validation block_header ->
      let*? fitness = Fitness.from_raw block_header.shell.fitness in
      Validate.begin_partial_validation
        ctxt
        chain_id
        ~predecessor_level
        ~predecessor_timestamp
        block_header
        fitness
  | Construction {predecessor_hash; timestamp; block_header_data} ->
      let*? predecessor_round = Fitness.round_from_raw predecessor_fitness in
      let*? round =
        Round.round_of_timestamp
          (Constants.round_durations ctxt)
          ~predecessor_timestamp
          ~predecessor_round
          ~timestamp
      in
      Validate.begin_full_construction
        ctxt
        chain_id
        ~predecessor_level
        ~predecessor_round
        ~predecessor_timestamp
        ~predecessor_hash
        round
        block_header_data.contents
  | Partial_construction _ ->
      let*? predecessor_round = Fitness.round_from_raw predecessor_fitness in
      let*? grandparent_round =
        Fitness.predecessor_round_from_raw predecessor_fitness
      in
      return
        (Validate.begin_partial_construction
           ctxt
           chain_id
           ~predecessor_level
           ~predecessor_round
           ~grandparent_round)

let validate_operation = Validate.validate_operation

let finalize_validation = Validate.finalize_block

type error += Cannot_apply_in_partial_validation

let () =
  register_error_kind
    `Permanent
    ~id:"main.begin_application.cannot_apply_in_partial_validation"
    ~title:"cannot_apply_in_partial_validation"
    ~description:
      "Cannot instantiate an application state using the 'Partial_validation' \
       mode."
    ~pp:(fun ppf () ->
      Format.fprintf
        ppf
        "Cannot instantiate an application state using the \
         'Partial_validation' mode.")
    Data_encoding.(empty)
    (function Cannot_apply_in_partial_validation -> Some () | _ -> None)
    (fun () -> Cannot_apply_in_partial_validation)

let begin_application ctxt chain_id mode ~predecessor =
  let open Lwt_result_syntax in
  let open Alpha_context in
  let* ( ctxt,
         migration_balance_updates,
         migration_operation_results,
         predecessor_level,
         predecessor_raw_level ) =
    prepare_ctxt ctxt ~predecessor mode
  in
  let predecessor_timestamp = predecessor.timestamp in
  let predecessor_fitness = predecessor.fitness in
  match mode with
  | Application block_header ->
      Apply.begin_application
        ctxt
        chain_id
        ~migration_balance_updates
        ~migration_operation_results
        ~predecessor_fitness
        block_header
  | Partial_validation _ -> tzfail Cannot_apply_in_partial_validation
  | Construction {predecessor_hash; timestamp; block_header_data; _} ->
      let*? predecessor_round = Fitness.round_from_raw predecessor_fitness in
      Apply.begin_full_construction
        ctxt
        chain_id
        ~migration_balance_updates
        ~migration_operation_results
        ~predecessor_timestamp
        ~predecessor_level
        ~predecessor_round
        ~predecessor_hash
        ~timestamp
        block_header_data.contents
  | Partial_construction {predecessor_hash; _} ->
      Apply.begin_partial_construction
        ctxt
        chain_id
        ~migration_balance_updates
        ~migration_operation_results
        ~predecessor_level:predecessor_raw_level
        ~predecessor_hash
        ~predecessor_fitness

let apply_operation = Apply.apply_operation

let finalize_application = Apply.finalize_block

let compare_operations (oph1, op1) (oph2, op2) =
  Alpha_context.Operation.compare (oph1, op1) (oph2, op2)

let init chain_id ctxt block_header =
  let level = block_header.Block_header.level in
  let timestamp = block_header.timestamp in
  let predecessor = block_header.predecessor in
  let typecheck (ctxt : Alpha_context.context) (script : Alpha_context.Script.t)
      =
    let allow_forged_in_storage =
      false
      (* There should be no forged value in bootstrap contracts. *)
    in
    Script_ir_translator.parse_script
      ctxt
      ~elab_conf:Script_ir_translator_config.(make ~legacy:true ())
      ~allow_forged_in_storage
      script
    >>=? fun (Ex_script (Script parsed_script), ctxt) ->
    Script_ir_translator.extract_lazy_storage_diff
      ctxt
      Optimized
      parsed_script.storage_type
      parsed_script.storage
      ~to_duplicate:Script_ir_translator.no_lazy_storage_id
      ~to_update:Script_ir_translator.no_lazy_storage_id
      ~temporary:false
    >>=? fun (storage, lazy_storage_diff, ctxt) ->
    Script_ir_translator.unparse_data
      ctxt
      Optimized
      parsed_script.storage_type
      storage
    >|=? fun (storage, ctxt) ->
    let storage = Alpha_context.Script.lazy_expr storage in
    (({script with storage}, lazy_storage_diff), ctxt)
  in
  (* The cache must be synced at the end of block validation, so we do
     so here for the first block in a protocol where `finalize_block`
     is not called. *)
  Alpha_context.Raw_level.of_int32 level >>?= fun raw_level ->
  let init_fitness =
    Alpha_context.Fitness.create_without_locked_round
      ~level:raw_level
      ~round:Alpha_context.Round.zero
      ~predecessor_round:Alpha_context.Round.zero
  in
  Alpha_context.prepare_first_block
    chain_id
    ~typecheck
    ~level
    ~timestamp
    ~predecessor
    ctxt
  >>=? fun ctxt ->
  let cache_nonce =
    Alpha_context.Cache.cache_nonce_from_block_header
      block_header
      ({
         payload_hash = Block_payload_hash.zero;
         payload_round = Alpha_context.Round.zero;
         liquidity_baking_toggle_vote = Alpha_context.Liquidity_baking.LB_pass;
         seed_nonce_hash = None;
         proof_of_work_nonce =
           Bytes.make Constants_repr.proof_of_work_nonce_size '0';
       }
        : Alpha_context.Block_header.contents)
  in
  Alpha_context.Cache.Admin.sync ctxt cache_nonce >>= fun ctxt ->
  return
    (Alpha_context.finalize ctxt (Alpha_context.Fitness.to_raw init_fitness))

let value_of_key ~chain_id:_ ~predecessor_context:ctxt ~predecessor_timestamp
    ~predecessor_level:pred_level ~predecessor_fitness:_ ~predecessor:_
    ~timestamp =
  let level = Int32.succ pred_level in
  Alpha_context.prepare ctxt ~level ~predecessor_timestamp ~timestamp
  >>=? fun (ctxt, _, _) -> return (Apply.value_of_key ctxt)

module Mempool = struct
  include Mempool_validation

  let init ctxt chain_id ~head_hash ~(head : Block_header.shell_header) =
    let open Lwt_result_syntax in
    let open Alpha_context in
    let* ( ctxt,
           _migration_balance_updates,
           _migration_operation_results,
           head_level,
           _head_raw_level ) =
      (* We use Partial_construction to factorize the [prepare_ctxt]. *)
      prepare_ctxt
        ctxt
        (Partial_construction
           {predecessor_hash = head_hash; timestamp = head.timestamp})
        ~predecessor:head
    in
    let*? predecessor_round = Fitness.round_from_raw head.fitness in
    let*? grandparent_round = Fitness.predecessor_round_from_raw head.fitness in
    return
      (init
         ctxt
         chain_id
         ~predecessor_level:head_level
         ~predecessor_round
         ~predecessor_hash:head_hash
         ~grandparent_round)
end

(* Vanity nonce: 6455568396538124 *)
back to top