https://gitlab.com/tezos/tezos
Raw File
Tip revision: b26a075ddf2a5818566d8b2bd75d847c1b89022d authored by satos on 13 December 2021, 15:02:15 UTC
Shell: offer exception-less functions in Hex
Tip revision: b26a075
operation_pool.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 Protocol
open Alpha_context

(* Should we use a better ordering ? *)

module OpSet = Set.Make (struct
  type t = packed_operation

  let compare = compare
end)

(* TODO refine this: unpack operations *)
type pool = {
  consensus : OpSet.t;
  votes : OpSet.t;
  anonymous : OpSet.t;
  managers : OpSet.t;
}

(* TODO refine this: unpack operations *)
type ordered_pool = {
  ordered_consensus : packed_operation list;
  ordered_votes : packed_operation list;
  ordered_anonymous : packed_operation list;
  ordered_managers : packed_operation list;
}

let ordered_pool_encoding =
  let open Data_encoding in
  conv
    (fun {ordered_consensus; ordered_votes; ordered_anonymous; ordered_managers} ->
      (ordered_consensus, ordered_votes, ordered_anonymous, ordered_managers))
    (fun (ordered_consensus, ordered_votes, ordered_anonymous, ordered_managers) ->
      {ordered_consensus; ordered_votes; ordered_anonymous; ordered_managers})
    (obj4
       (req "ordered_consensus" (list (dynamic_size Operation.encoding)))
       (req "ordered_votes" (list (dynamic_size Operation.encoding)))
       (req "ordered_payload" (list (dynamic_size Operation.encoding)))
       (req "ordered_payload" (list (dynamic_size Operation.encoding))))

type payload = {
  votes_payload : packed_operation list;
  anonymous_payload : packed_operation list;
  managers_payload : packed_operation list;
}

let empty_payload =
  {votes_payload = []; anonymous_payload = []; managers_payload = []}

let payload_encoding =
  let open Data_encoding in
  conv
    (fun {votes_payload; anonymous_payload; managers_payload} ->
      (votes_payload, anonymous_payload, managers_payload))
    (fun (votes_payload, anonymous_payload, managers_payload) ->
      {votes_payload; anonymous_payload; managers_payload})
    (obj3
       (req "votes_payload" (list (dynamic_size Operation.encoding)))
       (req "anonymous_payload" (list (dynamic_size Operation.encoding)))
       (req "managers_payload" (list (dynamic_size Operation.encoding))))

let pp_payload fmt {votes_payload; anonymous_payload; managers_payload} =
  Format.fprintf
    fmt
    "[votes: %d, anonymous: %d, managers: %d]"
    (List.length votes_payload)
    (List.length anonymous_payload)
    (List.length managers_payload)

let empty =
  {
    consensus = OpSet.empty;
    votes = OpSet.empty;
    anonymous = OpSet.empty;
    managers = OpSet.empty;
  }

let empty_ordered =
  {
    ordered_consensus = [];
    ordered_votes = [];
    ordered_anonymous = [];
    ordered_managers = [];
  }

let pp_pool fmt {consensus; votes; anonymous; managers} =
  Format.fprintf
    fmt
    "[consensus: %d, votes: %d, anonymous: %d, managers: %d]"
    (OpSet.cardinal consensus)
    (OpSet.cardinal votes)
    (OpSet.cardinal anonymous)
    (OpSet.cardinal managers)

let pp_ordered_pool fmt
    {ordered_consensus; ordered_votes; ordered_anonymous; ordered_managers} =
  Format.fprintf
    fmt
    "[consensus: %d, votes: %d, anonymous: %d, managers: %d]"
    (List.length ordered_consensus)
    (List.length ordered_votes)
    (List.length ordered_anonymous)
    (List.length ordered_managers)

(* Hypothesis : we suppose [List.length Protocol.Main.validation_passes = 4] *)
let consensus_index = 0

let votes_index = 1

let anonymous_index = 2

let managers_index = 3

let classify op =
  (* Hypothesis: acceptable passes returns a size at most 1 list  *)
  match Main.acceptable_passes op with
  | [pass] ->
      if pass = consensus_index then `Consensus
        (* TODO filter outdated consensus ops ? *)
      else if pass = votes_index then `Votes
      else if pass = anonymous_index then `Anonymous
      else if pass = managers_index then `Managers
      else `Bad
  | _ -> `Bad

let add_operation pool op =
  match classify op with
  | `Consensus ->
      let consensus = OpSet.add op pool.consensus in
      {pool with consensus}
  | `Votes ->
      let votes = OpSet.add op pool.votes in
      {pool with votes}
  | `Anonymous ->
      let anonymous = OpSet.add op pool.anonymous in
      {pool with anonymous}
  | `Managers ->
      let managers = OpSet.add op pool.managers in
      {pool with managers}
  | `Bad -> pool

let add_operations pool new_ops = List.fold_left add_operation pool new_ops

type consensus_filter = {
  level : int32;
  round : Round.t;
  payload_hash : Block_payload_hash.t;
}

(** From a pool of operations [operation_pool], the function filters
    out the endorsements that are different from the [current_level],
    the [current_round] or the optional [current_block_payload_hash],
    as well as preendorsements. *)
let filter_with_relevant_consensus_ops ~(endorsement_filter : consensus_filter)
    ~(preendorsement_filter : consensus_filter option) operation_set =
  OpSet.filter
    (fun {protocol_data; _} ->
      match (protocol_data, preendorsement_filter) with
      (* 1a. Remove preendorsements. *)
      | (Operation_data {contents = Single (Preendorsement _); _}, None) ->
          false
      (* 1b. Filter preendorsements. *)
      | ( Operation_data
            {
              contents =
                Single (Preendorsement {level; round; block_payload_hash; _});
              _;
            },
          Some
            {level = level'; round = round'; payload_hash = block_payload_hash'}
        ) ->
          Compare.Int32.(Raw_level.to_int32 level = level')
          && Round.(round = round')
          && Block_payload_hash.(block_payload_hash = block_payload_hash')
      (* 2. Filter endorsements. *)
      | ( Operation_data
            {
              contents =
                Single (Endorsement {level; round; block_payload_hash; _});
              _;
            },
          _ ) ->
          Compare.Int32.(Raw_level.to_int32 level = endorsement_filter.level)
          && Round.(round = endorsement_filter.round)
          && Block_payload_hash.(
               block_payload_hash = endorsement_filter.payload_hash)
      (* 3. Preserve all non-consensus operations. *)
      | _ -> true)
    operation_set

let unpack_preendorsement packed_preendorsement =
  let {shell; protocol_data = Operation_data data} = packed_preendorsement in
  match data with
  | {contents = Single (Preendorsement _); _} ->
      Some ({shell; protocol_data = data} : Kind.preendorsement Operation.t)
  | _ -> None

let unpack_endorsement packed_endorsement =
  let {shell; protocol_data = Operation_data data} = packed_endorsement in
  match data with
  | {contents = Single (Endorsement _); _} ->
      Some ({shell; protocol_data = data} : Kind.endorsement Operation.t)
  | _ -> None

let filter_preendorsements ops =
  List.filter_map
    (function
      | {
          shell = {branch};
          protocol_data =
            Operation_data
              ({contents = Single (Preendorsement _); _} as content);
          _;
        } ->
          Some
            ({shell = {branch}; protocol_data = content}
              : Kind.preendorsement operation)
      | _ -> None)
    ops

let filter_endorsements ops =
  List.filter_map
    (function
      | {
          shell = {branch};
          protocol_data =
            Operation_data ({contents = Single (Endorsement _); _} as content);
          _;
        } ->
          Some
            ({shell = {branch}; protocol_data = content}
              : Kind.endorsement operation)
      | _ -> None)
    ops

let pool_to_list_list {consensus; votes; anonymous; managers} =
  List.map OpSet.elements [consensus; votes; anonymous; managers]

let pool_of_list_list (ll : packed_operation list list) =
  List.fold_left add_operations empty ll

let ordered_to_list_list
    {ordered_consensus; ordered_votes; ordered_anonymous; ordered_managers} =
  [ordered_consensus; ordered_votes; ordered_anonymous; ordered_managers]

let ordered_of_list_list = function
  | [ordered_consensus; ordered_votes; ordered_anonymous; ordered_managers] ->
      Some
        {ordered_consensus; ordered_votes; ordered_anonymous; ordered_managers}
  | _ -> None

let payload_of_ordered_pool
    {ordered_votes; ordered_anonymous; ordered_managers; _} =
  {
    votes_payload = ordered_votes;
    anonymous_payload = ordered_anonymous;
    managers_payload = ordered_managers;
  }

let ordered_pool_of_payload ~consensus_operations
    {votes_payload; anonymous_payload; managers_payload} =
  {
    ordered_consensus = consensus_operations;
    ordered_votes = votes_payload;
    ordered_anonymous = anonymous_payload;
    ordered_managers = managers_payload;
  }

let extract_operations_of_list_list = function
  | [consensus; votes_payload; anonymous_payload; managers_payload] ->
      let (preendorsements, endorsements) =
        List.fold_left
          (fun ( (preendorsements : Kind.preendorsement Operation.t list),
                 (endorsements : Kind.endorsement Operation.t list) )
               packed_op ->
            let {shell; protocol_data = Operation_data data} = packed_op in
            match data with
            | {contents = Single (Preendorsement _); _} ->
                ({shell; protocol_data = data} :: preendorsements, endorsements)
            | {contents = Single (Endorsement _); _} ->
                (preendorsements, {shell; protocol_data = data} :: endorsements)
            | _ ->
                (* unreachable *)
                (preendorsements, endorsements))
          ([], [])
          consensus
        (* N.b. the order doesn't matter *)
      in
      let preendorsements =
        if preendorsements = [] then None else Some preendorsements
      in
      let payload = {votes_payload; anonymous_payload; managers_payload} in
      Some (preendorsements, endorsements, payload)
  | _ -> None

let filter_pool p {consensus; votes; anonymous; managers} =
  {
    consensus = OpSet.filter p consensus;
    votes = OpSet.filter p votes;
    anonymous = OpSet.filter p anonymous;
    managers = OpSet.filter p managers;
  }
back to top