Revision 50ad02fa2a56f1d3541a6d8b3c8a6a93160d4f54 authored by Albin Coquereau on 05 April 2024, 09:32:54 UTC, committed by Albin Coquereau on 05 April 2024, 09:32:54 UTC
1 parent 9a71363
prevalidator_classification.mli
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2021-2022 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 Shell_operation
(** Classifications which correspond to errors *)
type error_classification =
[ `Branch_delayed of tztrace
| `Branch_refused of tztrace
| `Refused of tztrace
| `Outdated of tztrace ]
(** Classification of an operation in the mempool *)
type classification = [`Validated | error_classification]
type 'protocol_data bounded_map
(** [map bounded_map] gets the underling map of the [bounded_map]. *)
val map :
'protocol_data bounded_map ->
('protocol_data operation * tztrace) Operation_hash.Map.t
(** [cardinal bounded_map] gets the cardinal of the underling map of the [bounded_map] *)
val cardinal : 'protocol_data bounded_map -> int
type parameters = {
map_size_limit : int;
on_discarded_operation : Operation_hash.t -> unit;
}
module Sized_map :
Tezos_base.Sized.SizedMap
with type 'a map := 'a Operation_hash.Map.t
and type key = Operation_hash.t
(** Invariants ensured by this module, provided that the caller does
not {!add} an operation which is already present in [t]:
- The field [in_mempool] is the set of all operation hashes
present in fields:
[refused; outdated; branch_refused; branch_delayed; validated].
- An operation cannot be at the same time in two of the fields:
[refused; outdated; branch_refused; branch_delayed; validated].
Note: unparsable operations are handled in a different way because
they cannot be handled as a [operation] since this
datatype requires an operation to be parsable. Hence, unparsable
operations are handled differently. In particular, unparsable
operations are removed on flush.
Note: We could always enforce these invariants by checking in
{!add} whether the operation is already present. However, this
would make the behavior of {!add} less predictable, so we do not
think this to be an improvement from the point of view of the
caller. *)
type 'protocol_data t = private {
parameters : parameters;
refused : 'protocol_data bounded_map;
outdated : 'protocol_data bounded_map;
branch_refused : 'protocol_data bounded_map;
branch_delayed : 'protocol_data bounded_map;
mutable validated : 'protocol_data operation Sized_map.t;
mutable unparsable : Operation_hash.Set.t;
mutable in_mempool :
('protocol_data operation * classification) Operation_hash.Map.t;
}
(** [create parameters] returns an empty {!t} whose bounded maps hold
at most [parameters.map_size_limit] values. The
[on_discarded_operation] is called when a new operation is added
and an old one is discarded because the limit was reached.
{!Invalid_argument} is raised if [ring_size] is [0] or less.
*)
val create : parameters -> 'protocol_data t
(** [is_empty t] returns [true] iff [t] doesn't contain any operation. *)
val is_empty : 'protocol_data t -> bool
(** [is_in_mempool oph classes] indicates whether [oph] is present
in field [in_mempool] of [classes]. It returns the corresponding
operation and its classification if present, and None otherwise. *)
val is_in_mempool :
Operation_hash.t ->
'protocol_data t ->
('protocol_data operation * classification) option
(** [is_known_unparsable oph] returns [true] if the [oph] is
associated to an operation which is known to be unparsable. [false]
otherwise. *)
val is_known_unparsable : Operation_hash.t -> 'protocol_data t -> bool
(** [remove oph classes] removes operation of hash [oph] from all
fields of [classes]. The function is [O(log n)] with [n] the number
of operations in the corresponding class.
If [oph] was found, its classification as well as the operation it
was bound to are returned. If [oph] was not found, [None]
is returned. *)
val remove :
Operation_hash.t ->
'protocol_data t ->
('protocol_data operation * classification) option
(** [add ~notify classification op classes] adds the operation [op]
classified as [classification] to the classifier [classes]. The
[classes.parameters.on_discarded_operation] callback is called for
any operation discarded in this process. Currently, an operation is
discarded in the following cases:
- the corresponding error class field is full. In that case, the
new operation is added to the class, and the removed one is
discarded.
- an operation is classified as [Refused].
Note that a [Refused] operation may thus be passed twice to
[on_discarded_operation]: as soon as it is added, and if it is
removed because the [classes.refused] bounded map is full.
As a summary:
- [Validated] operations are never discarded
- [Branch_refused] and [Branch_delayed] are discarded 0 or 1 time
(if the corresponding bounded_map is full)
- [Refused] is discarded 1 or 2 times (if the corresponding
bounded_map is full) *)
val add : classification -> 'protocol_data operation -> 'protocol_data t -> unit
(** [add_unparsable oph classes] adds [oph] as an unparsable
operation. [unparsable] operations are removed automatically by the
[recycle_operations] function. [on_discard_operation] is also
called on those operations. *)
val add_unparsable : Operation_hash.t -> 'protocol_data t -> unit
(** Functions to query data on a polymorphic block-like type ['block]. *)
type 'block block_tools = {
bhash : 'block -> Block_hash.t; (** The hash of a block *)
operations : 'block -> Operation.t list list;
(** The list of operations of a block ordered by their validation pass *)
all_operation_hashes : 'block -> Operation_hash.t list list;
(** The list of hashes of operations of a block ordered by their
validation pass. Could be implemented
using {!operations} but this lets an alternative implementation
to be provided. *)
}
(** A wrapper over chain-related modules, to make client code easier to
emulate, and hence to test *)
type 'block chain_tools = {
clear_or_cancel : Operation_hash.t -> unit;
(** Removes the operation from the distributed database. *)
inject_operation : Operation_hash.t -> Operation.t -> unit Lwt.t;
(** Puts the operation in the distributed database. Returns [false] if
the [hash] is already in the distributed database *)
new_blocks :
from_block:'block -> to_block:'block -> ('block * 'block list) Lwt.t;
(** [new_blocks ~from_block ~to_block] returns a pair [(ancestor,
path)], where [ancestor] is the common ancestor of [from_block]
and [to_block] and where [path] is the chain from [ancestor]
(excluded) to [to_block] (included).
@raise assert failure when the two provided blocks do not belong
to the same [chain]. *)
read_predecessor_opt : 'block -> 'block option Lwt.t;
(** [read_predecessor_opt block] returns
the direct predecessor of [block] or [None] if it cannot
be found. *)
}
(** [recycle_operations] returns the new pending operations when
a reorganisation or a head update occurs. Returned operations come from:
1. operations in [from_branch] that are NOT in [to_branch],
2. operations in the relevant classes of [classification]
3. operations the [pending] map
This function guarantees that the branch of all returned operations
is in [live_blocks] ([live_blocks] acts as a filter).
Operation which where included in [from_branch] and which are NOT in
[to_branch] need to be parsed again using the [parse] argument. If the
parsing fails those operations are just dropped. This may happen if those
operations comes from another protocol.
See also {!Internal_for_tests.handle_live_operations}. *)
val recycle_operations :
from_branch:'block ->
to_branch:'block ->
live_blocks:Block_hash.Set.t ->
classes:'protocol_data t ->
parse:(Operation_hash.t -> Operation.t -> 'protocol_data operation option) ->
pending:'protocol_data operation Operation_hash.Map.t ->
block_store:'block block_tools ->
chain:'block chain_tools ->
handle_branch_refused:bool ->
'protocol_data operation Operation_hash.Map.t Lwt.t
(**/**)
module Internal_for_tests : sig
val pp : Format.formatter -> 'protocol_data t -> unit
val bounded_map_pp : Format.formatter -> 'protocol_data bounded_map -> unit
(** Returns a deep copy of the input [t], so that mutating the one
doesn't affect the other. *)
val copy : 'protocol_data t -> 'protocol_data t
(** [set_of_bounded_map m] returns all the operation hashes in [m]. *)
val set_of_bounded_map : 'protocol_data bounded_map -> Operation_hash.Set.t
(** [pp_t_sizes t] prints the [map_size_limit] parameter of [t]
and the sizes of its fields (number of elements in the map and
in the ring of [bounded_map] / length of list / cardinal of set). *)
val pp_t_sizes : Format.formatter -> 'protocol_data t -> unit
(** Returns the pairs [(operation_hash, operation)] contained in [t].
Fields of [t] are included according to the value of the corresponding
named argument. *)
val to_map :
validated:bool ->
branch_delayed:bool ->
branch_refused:bool ->
refused:bool ->
outdated:bool ->
'protocol_data t ->
'protocol_data operation Operation_hash.Map.t
(** [flush classes ~handle_branch_refused] partially resets [classes]:
- fields [validated], [branch_delayed] and [unparsable] are emptied;
- field [branch_refused] is emptied iff [handle_branch_refused] is [true];
- field [refused] is left unchanged, to avoid revalidating operations that
will never be valid;
- field [outdated] is left unchanged.
Also updates field [in_mempool] to maintain the corresponding invariant
of {!t}. *)
val flush : 'protocol_data t -> handle_branch_refused:bool -> unit
(** [handle_live_operations chain_db from_branch to_branch is_branch_alive parse
old_mempool] returns the operations from:
1. [old_mempool],
2. [from_branch] that are NOT in [to_branch],
for the subset of these operations whose branch is up-to-date according
to [is_branch_alive] (operations that are not alive are cleared).
Returned operations can be considered pending afterwards and
are eligible to be propagated. On the other hand, all operations
from [ancestor] to [to_branch] are cleared and won't be propagated.
Most general case:
to_branch ┐
/ │
from_branch . ├ path
\ / │
. . ┘
\ /
ancestor
Increment the head case:
to_branch = path
|
from_branch = ancestor
*)
val handle_live_operations :
classes:'protocol_data t ->
block_store:'block block_tools ->
chain:'block chain_tools ->
from_branch:'block ->
to_branch:'block ->
is_branch_alive:(Block_hash.t -> bool) ->
parse:(Operation_hash.t -> Operation.t -> 'protocol_data operation option) ->
'protocol_data operation Operation_hash.Map.t ->
'protocol_data operation Operation_hash.Map.t Lwt.t
end
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...