diff --git a/src/lib_shell/prevalidation.ml b/src/lib_shell/prevalidation.ml index dc083748512baeea5acf9eb2be1425bd13f7b9bd..5fb8dae5f3449e606787950c44ce10b7359a09e7 100644 --- a/src/lib_shell/prevalidation.ml +++ b/src/lib_shell/prevalidation.ml @@ -134,11 +134,20 @@ module MakeAbstract type operation = protocol_operation Shell_operation.operation + module Manager = Signature.Public_key_hash + type t = { validation_info : Proto.Mempool.validation_info; + (** Static information needed by [Proto.Mempool.add_operation]. *) mempool : Proto.Mempool.t; + (** Protocol representation of currently valid operations. *) bounding_state : Bounding.state; + (** Representation of currently valid operations used to enforce + mempool bounds. *) + manager_map : protocol_operation Manager.Map.t; + (** Valid manager operations in the mempool, indexed by manager. *) filter_info : Filter.Mempool.filter_info; + (** Static information needed by [Filter.Mempool.pre_filter]. *) } let create_aux ?old_state chain_store head timestamp = @@ -161,8 +170,9 @@ module MakeAbstract | None -> Filter.Mempool.init context ~head | Some old_state -> Filter.Mempool.flush old_state.filter_info ~head in - return - {validation_info; mempool; bounding_state = Bounding.empty; filter_info} + let bounding_state = Bounding.empty in + let manager_map = Manager.Map.empty in + return {validation_info; mempool; bounding_state; manager_map; filter_info} let create chain_store ~head ~timestamp = create_aux chain_store head timestamp @@ -273,6 +283,38 @@ module MakeAbstract in return (mempool, bounding_state, replacements) + (* Update the [manager_map] by removing the [replacements] then + adding [op] if it is a manager operation. Non-manager operations + are ignored. + + Note that it is important to remove before adding, otherwise we + risk removing the newly added operation if it has just replaced + an operation from the same manager. + + [mempool_before] is the protocol's mempool representation before + calling [Proto.Mempool.add_operation], so that it still contains + the replaced operations. Indeed, it is used to retrieve these + operations from their hash. *) + let update_manager_map manager_map mempool_before op replacements = + let manager_map = + (* No need to call [Proto.Mempool.operations] when the list is empty. *) + if List.is_empty replacements then manager_map + else + let ops = Proto.Mempool.operations mempool_before in + let remove_replacement mmap (oph, _error_classification) = + match Operation_hash.Map.find oph ops with + | None -> (* This case should never happen. *) mmap + | Some op -> ( + match Filter.Mempool.find_manager op with + | None -> (* Not a manager operation: ignore it. *) mmap + | Some manager -> Manager.Map.remove manager mmap) + in + List.fold_left remove_replacement manager_map replacements + in + match Filter.Mempool.find_manager op.protocol with + | None -> (* Not a manager operation: ignore it. *) manager_map + | Some manager -> Manager.Map.add manager op.protocol manager_map + let add_operation_result state (filter_config, bounding_config) op : add_result tzresult Lwt.t = let open Lwt_result_syntax in @@ -295,9 +337,12 @@ module MakeAbstract valid_op in match res with - | Ok (mempool, bounding_state, replacement) -> - let state = {state with mempool; bounding_state} in - return (state, valid_op, `Prechecked, replacement) + | Ok (mempool, bounding_state, replacements) -> + let manager_map = + update_manager_map state.manager_map state.mempool op replacements + in + let state = {state with mempool; bounding_state; manager_map} in + return (state, valid_op, `Prechecked, replacements) | Error trace -> (* We convert any error from [check_conflict_and_bound] into an [add_result] here, rather than let [add_operation] below do diff --git a/src/lib_shell/shell_plugin.ml b/src/lib_shell/shell_plugin.ml index 7a7782a68a1b104b4fe63d52add2c57cee7d1e4f..bfadbb231fcef03f9408c329bc164ef8f6cce1be 100644 --- a/src/lib_shell/shell_plugin.ml +++ b/src/lib_shell/shell_plugin.ml @@ -56,6 +56,8 @@ module type FILTER = sig val conflict_handler : config -> Proto.Mempool.conflict_handler + val find_manager : Proto.operation -> Signature.Public_key_hash.t option + val fee_needed_to_overtake : op_to_overtake:Proto.operation -> candidate_op:Proto.operation -> @@ -94,6 +96,8 @@ module No_filter (Proto : Registered_protocol.T) : `Replace else `Keep + let find_manager _ = None + let fee_needed_to_overtake ~op_to_overtake:_ ~candidate_op:_ = None end end diff --git a/src/lib_shell/shell_plugin.mli b/src/lib_shell/shell_plugin.mli index 465ffe3946b551dcbbe24cf6a888ec514b4475de..735b40eceeff94909b2737457c6a7a6aaa0ec498 100644 --- a/src/lib_shell/shell_plugin.mli +++ b/src/lib_shell/shell_plugin.mli @@ -88,6 +88,10 @@ module type FILTER = sig implementation of this function relies). *) val conflict_handler : config -> Proto.Mempool.conflict_handler + (** If the operation is a manager operation, return its source, + otherwise return [None]. *) + val find_manager : Proto.operation -> Signature.Public_key_hash.t option + (** Compute the minimal fee (expressed in mutez) that [candidate_op] would need to have in order to be strictly greater than [op_to_overtake] according to {!Proto.compare_operations}. diff --git a/src/lib_shell/test/test_prevalidation.ml b/src/lib_shell/test/test_prevalidation.ml index 9d341078b880e9661d75483b4f80dc3b886a224d..6b6b51ca6120b347d26eb7717a57b3b3fdcbd7c0 100644 --- a/src/lib_shell/test/test_prevalidation.ml +++ b/src/lib_shell/test/test_prevalidation.ml @@ -31,6 +31,15 @@ Subject: Unit tests for {!Prevalidation.T} *) +(* Note: the logic of Prevalidation.t.manager_map is not tested here + because the mock protocol operations don't have a notion of + manager. However, it will serve to indicate the fees needed to + replace the previous operation in a manager conflict error, which + will be tested in tezt. *) +(* TODO: https://gitlab.com/tezos/tezos/-/issues/5197 + Update the comment above once the fees needed to replace are + implemented and tested. *) + let register_test ~title ~additional_tags = Test.register ~__FILE__ diff --git a/src/proto_016_PtMumbai/lib_plugin/mempool.ml b/src/proto_016_PtMumbai/lib_plugin/mempool.ml index 1bd7893fcbcf415b5fa5892821533aa704656fcf..2fe3a73d26ed07258f583e901a267aaf76c4b78f 100644 --- a/src/proto_016_PtMumbai/lib_plugin/mempool.ml +++ b/src/proto_016_PtMumbai/lib_plugin/mempool.ml @@ -616,6 +616,18 @@ let conflict_handler config : Mempool.conflict_handler = else if Operation.compare existing_operation new_operation < 0 then `Replace else `Keep +let find_manager {shell = _; protocol_data = Operation_data {contents; _}} = + match contents with + | Single (Manager_operation {source; _}) -> Some source + | Cons (Manager_operation {source; _}, _) -> Some source + | Single + ( Preendorsement _ | Endorsement _ | Dal_attestation _ | Proposals _ + | Ballot _ | Seed_nonce_revelation _ | Vdf_revelation _ + | Double_baking_evidence _ | Double_preendorsement_evidence _ + | Double_endorsement_evidence _ | Activate_account _ | Drain_delegate _ + | Failing_noop _ ) -> + None + let fee_needed_to_overtake ~op_to_overtake ~candidate_op = if is_manager_operation candidate_op && is_manager_operation op_to_overtake then diff --git a/src/proto_016_PtMumbai/lib_plugin/mempool.mli b/src/proto_016_PtMumbai/lib_plugin/mempool.mli index 4cafa6a597c0c8bab929820a8de29e9b7beac7d6..d7fe3212cb3775075a204d430a9da40138500bc0 100644 --- a/src/proto_016_PtMumbai/lib_plugin/mempool.mli +++ b/src/proto_016_PtMumbai/lib_plugin/mempool.mli @@ -103,6 +103,11 @@ val pre_filter : able to call {!Protocol.Alpha_context.Operation.compare}). *) val conflict_handler : config -> Protocol.Mempool.conflict_handler +(** If the operation is a manager operation, return its source, + otherwise return [None]. *) +val find_manager : + Protocol.Alpha_context.packed_operation -> Signature.Public_key_hash.t option + (** Compute the minimal fee (expressed in mutez) that [candidate_op] would need to have in order to be strictly greater than [op_to_overtake] according to {!Protocol.Alpha_context.Operation.compare}, when both diff --git a/src/proto_017_PtNairob/lib_plugin/mempool.ml b/src/proto_017_PtNairob/lib_plugin/mempool.ml index 1bd7893fcbcf415b5fa5892821533aa704656fcf..2fe3a73d26ed07258f583e901a267aaf76c4b78f 100644 --- a/src/proto_017_PtNairob/lib_plugin/mempool.ml +++ b/src/proto_017_PtNairob/lib_plugin/mempool.ml @@ -616,6 +616,18 @@ let conflict_handler config : Mempool.conflict_handler = else if Operation.compare existing_operation new_operation < 0 then `Replace else `Keep +let find_manager {shell = _; protocol_data = Operation_data {contents; _}} = + match contents with + | Single (Manager_operation {source; _}) -> Some source + | Cons (Manager_operation {source; _}, _) -> Some source + | Single + ( Preendorsement _ | Endorsement _ | Dal_attestation _ | Proposals _ + | Ballot _ | Seed_nonce_revelation _ | Vdf_revelation _ + | Double_baking_evidence _ | Double_preendorsement_evidence _ + | Double_endorsement_evidence _ | Activate_account _ | Drain_delegate _ + | Failing_noop _ ) -> + None + let fee_needed_to_overtake ~op_to_overtake ~candidate_op = if is_manager_operation candidate_op && is_manager_operation op_to_overtake then diff --git a/src/proto_017_PtNairob/lib_plugin/mempool.mli b/src/proto_017_PtNairob/lib_plugin/mempool.mli index 4cafa6a597c0c8bab929820a8de29e9b7beac7d6..d7fe3212cb3775075a204d430a9da40138500bc0 100644 --- a/src/proto_017_PtNairob/lib_plugin/mempool.mli +++ b/src/proto_017_PtNairob/lib_plugin/mempool.mli @@ -103,6 +103,11 @@ val pre_filter : able to call {!Protocol.Alpha_context.Operation.compare}). *) val conflict_handler : config -> Protocol.Mempool.conflict_handler +(** If the operation is a manager operation, return its source, + otherwise return [None]. *) +val find_manager : + Protocol.Alpha_context.packed_operation -> Signature.Public_key_hash.t option + (** Compute the minimal fee (expressed in mutez) that [candidate_op] would need to have in order to be strictly greater than [op_to_overtake] according to {!Protocol.Alpha_context.Operation.compare}, when both diff --git a/src/proto_alpha/lib_plugin/mempool.ml b/src/proto_alpha/lib_plugin/mempool.ml index be6587427d3b93ad1c1748338a1c113c5f2b7fe7..622bf9b7f1a44d8aab749687e0f4b746ed229e9d 100644 --- a/src/proto_alpha/lib_plugin/mempool.ml +++ b/src/proto_alpha/lib_plugin/mempool.ml @@ -618,6 +618,18 @@ let conflict_handler config : Mempool.conflict_handler = else if Operation.compare existing_operation new_operation < 0 then `Replace else `Keep +let find_manager {shell = _; protocol_data = Operation_data {contents; _}} = + match contents with + | Single (Manager_operation {source; _}) -> Some source + | Cons (Manager_operation {source; _}, _) -> Some source + | Single + ( Preendorsement _ | Endorsement _ | Dal_attestation _ | Proposals _ + | Ballot _ | Seed_nonce_revelation _ | Vdf_revelation _ + | Double_baking_evidence _ | Double_preendorsement_evidence _ + | Double_endorsement_evidence _ | Activate_account _ | Drain_delegate _ + | Failing_noop _ ) -> + None + let fee_needed_to_overtake ~op_to_overtake ~candidate_op = if is_manager_operation candidate_op && is_manager_operation op_to_overtake then diff --git a/src/proto_alpha/lib_plugin/mempool.mli b/src/proto_alpha/lib_plugin/mempool.mli index 4cafa6a597c0c8bab929820a8de29e9b7beac7d6..d7fe3212cb3775075a204d430a9da40138500bc0 100644 --- a/src/proto_alpha/lib_plugin/mempool.mli +++ b/src/proto_alpha/lib_plugin/mempool.mli @@ -103,6 +103,11 @@ val pre_filter : able to call {!Protocol.Alpha_context.Operation.compare}). *) val conflict_handler : config -> Protocol.Mempool.conflict_handler +(** If the operation is a manager operation, return its source, + otherwise return [None]. *) +val find_manager : + Protocol.Alpha_context.packed_operation -> Signature.Public_key_hash.t option + (** Compute the minimal fee (expressed in mutez) that [candidate_op] would need to have in order to be strictly greater than [op_to_overtake] according to {!Protocol.Alpha_context.Operation.compare}, when both