123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 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. *)(* *)(*****************************************************************************)openAlpha_context(** {2 Definition and initialization of [validate_operation_info] and
[validate_operation_state]}
These live in memory during the validation of a block, or until a
change of head block in mempool mode; they are never put in the
storage. *)(** Static information used to validate manager operations. *)typemanager_info={hard_storage_limit_per_operation:Z.t;hard_gas_limit_per_operation:Gas.Arith.integral;}letinit_manager_infoctxt={hard_storage_limit_per_operation=Constants.hard_storage_limit_per_operationctxt;hard_gas_limit_per_operation=Constants.hard_gas_limit_per_operationctxt;}(** State used and modified when validating manager operations. *)typemanager_state={managers_seen:Operation_hash.tSignature.Public_key_hash.Map.t;(** To enforce the one-operation-per manager-per-block restriction
(1M). The operation hash lets us indicate the conflicting
operation in the {!Manager_restriction} error.
Note that as part of {!validate_operation_state}, this map
lives in memory. It is not explicitly bounded here, however:
- In block validation mode, it is bounded by the number of
manager operations allowed in the block.
- In mempool mode, bounding the number of operations in this
map is the responsability of the mempool. (E.g. the plugin used
by Octez has a [max_prechecked_manager_operations] parameter to
ensure this.) *)remaining_block_gas:Gas.Arith.fp;}letinit_manager_statectxt={managers_seen=Signature.Public_key_hash.Map.empty;remaining_block_gas=Gas.Arith.fp(Constants.hard_gas_limit_per_blockctxt);}(* If you add a new mode, please make sure that it has a way to bound
the size of the map {!recfield:managers_seen}. *)typemode=Block|Mempooltypevalidate_operation_info={ctxt:t;(** The context at the beginning of the block. *)mode:mode;chain_id:Chain_id.t;(** Needed for signature checks. *)current_level:Level.t;manager_info:manager_info;}typevalidate_operation_state={manager_state:manager_state}letinit_validate_operation_infoctxtmodechain_id={ctxt;mode;chain_id;current_level=Level.currentctxt;manager_info=init_manager_infoctxt;}letinit_validate_operation_statectxt={manager_state=init_manager_statectxt}letinit_info_and_statectxtmodechain_id=letvi=init_validate_operation_infoctxtmodechain_idinletvs=init_validate_operation_statectxtin(vi,vs)(* See mli file. *)typestamp=Operation_validated_stampmoduleManager=structtypeerror+=|Manager_restrictionofSignature.Public_key_hash.t*Operation_hash.t|Inconsistent_sources|Inconsistent_counters|Incorrect_reveal_position|Insufficient_gas_for_manager|Gas_quota_exceeded_init_deserialize|Tx_rollup_feature_disabled|Sc_rollup_feature_disabledlet()=register_error_kind`Temporary~id:"validate_operation.manager_restriction"~title:"Manager restriction"~description:"An operation with the same manager has already been validated in the \
current block."~pp:(funppf(d,hash)->Format.fprintfppf"Manager %a already has the operation %a in the current block."Signature.Public_key_hash.ppdOperation_hash.pphash)Data_encoding.(obj2(req"manager"Signature.Public_key_hash.encoding)(req"hash"Operation_hash.encoding))(function|Manager_restriction(manager,hash)->Some(manager,hash)|_->None)(fun(manager,hash)->Manager_restriction(manager,hash));letinconsistent_sources_description="The operation batch includes operations from different sources."inregister_error_kind`Permanent~id:"validate_operation.inconsistent_sources"~title:"Inconsistent sources in operation batch"~description:inconsistent_sources_description~pp:(funppf()->Format.fprintfppf"%s"inconsistent_sources_description)Data_encoding.empty(functionInconsistent_sources->Some()|_->None)(fun()->Inconsistent_sources);letinconsistent_counters_description="Inconsistent counters in operation. Counters of an operation must be \
successive."inregister_error_kind`Permanent~id:"validate_operation.inconsistent_counters"~title:"Inconsistent counters in operation"~description:inconsistent_counters_description~pp:(funppf()->Format.fprintfppf"%s"inconsistent_counters_description)Data_encoding.empty(functionInconsistent_counters->Some()|_->None)(fun()->Inconsistent_counters);letincorrect_reveal_description="Incorrect reveal operation position in batch: only allowed in first \
position."inregister_error_kind`Permanent~id:"validate_operation.incorrect_reveal_position"~title:"Incorrect reveal position"~description:incorrect_reveal_description~pp:(funppf()->Format.fprintfppf"%s"incorrect_reveal_description)Data_encoding.empty(functionIncorrect_reveal_position->Some()|_->None)(fun()->Incorrect_reveal_position);register_error_kind`Permanent~id:"validate_operation.insufficient_gas_for_manager"~title:"Not enough gas for initial manager cost"~description:(Format.asprintf"Gas limit is too low to cover the initial cost of manager \
operations: at least %a gas required."Gas.pp_costMichelson_v1_gas.Cost_of.manager_operation)Data_encoding.empty(functionInsufficient_gas_for_manager->Some()|_->None)(fun()->Insufficient_gas_for_manager);letgas_deserialize_description="Gas limit was not high enough to deserialize the transaction parameters \
or origination script code or initial storage etc., making the \
operation impossible to parse within the provided gas bounds."inregister_error_kind`Permanent~id:"validate_operation.gas_quota_exceeded_init_deserialize"~title:"Not enough gas for initial deserialization of script expressions"~description:gas_deserialize_description~pp:(funppf()->Format.fprintfppf"%s"gas_deserialize_description)Data_encoding.empty(functionGas_quota_exceeded_init_deserialize->Some()|_->None)(fun()->Gas_quota_exceeded_init_deserialize);register_error_kind`Permanent~id:"validate_operation.tx_rollup_is_disabled"~title:"Tx rollup is disabled"~description:"Cannot originate a tx rollup as it is disabled."~pp:(funppf()->Format.fprintfppf"Cannot apply a tx rollup operation as it is disabled. This feature \
will be enabled in a future proposal")Data_encoding.unit(functionTx_rollup_feature_disabled->Some()|_->None)(fun()->Tx_rollup_feature_disabled);letscoru_disabled_description="Smart contract rollups will be enabled in a future proposal."inregister_error_kind`Permanent~id:"validate_operation.sc_rollup_disabled"~title:"Smart contract rollups are disabled"~description:scoru_disabled_description~pp:(funppf()->Format.fprintfppf"%s"scoru_disabled_description)Data_encoding.unit(functionSc_rollup_feature_disabled->Some()|_->None)(fun()->Sc_rollup_feature_disabled)(** State that simulates changes from individual operations that have
an effect on future operations inside the same batch. *)typebatch_state={balance:Tez.t;(** Remaining balance in the contract, used to simulate the
payment of fees by each operation in the batch. *)is_allocated:bool;(** Track whether the contract is still allocated. Indeed,
previous operations' fee payment may empty the contract and
this may deallocate the contract.
TODO: https://gitlab.com/tezos/tezos/-/issues/3209 Change
empty account cleanup mechanism to avoid the need for this
field. *)remaining_block_gas:Gas.Arith.fp;(** In Block_validation mode, this is what remains of the block gas
quota after subtracting the gas_limit of all previously
validated operations in the block. In Mempool mode, only
previous gas for previous operations in the same batch has been
subtracted from the block quota. Cf
{!maybe_update_remaining_block_gas}:
[vs.manager_state.remaining_block_gas] is updated only in
Block_validation mode. *)}(** Check a few simple properties of the batch, and return the
initial {!batch_state} and the contract public key.
Invariants checked:
- All operations in a batch have the same source.
- The source's contract is allocated.
- The counters in a batch are successive, and the first of them
is the source's next expected counter.
- A batch contains at most one Reveal operation that must occur
in first position.
- The source's public key has been revealed (either before the
considered batch, or during its first operation).
Note that currently, the [op] batch contains only one signature,
so all operations in the batch are required to originate from the
same manager. This may change in the future, in order to allow
several managers to group-sign a sequence of operations. *)letcheck_sanity_and_find_public_keyvivs(contents_list:_Kind.managercontents_list)=letopenResult_syntaxinletcheck_source_and_counter~expected_source~source~previous_counter~counter=let*()=error_unless(Signature.Public_key_hash.equalexpected_sourcesource)Inconsistent_sourcesinerror_unlessCompare.Z.(Z.succprevious_counter=counter)Inconsistent_countersinletreccheck_batch_tail_sanity:typekind.public_key_hash->counter->kindKind.managercontents_list->unittzresult=funexpected_sourceprevious_counter->function|Single(Manager_operation{operation=Reveal_key;_})->errorIncorrect_reveal_position|Cons(Manager_operation{operation=Reveal_key;_},_res)->errorIncorrect_reveal_position|Single(Manager_operation{source;counter;_})->check_source_and_counter~expected_source~source~previous_counter~counter|Cons(Manager_operation{source;counter;_},rest)->letopenResult_syntaxinlet*()=check_source_and_counter~expected_source~source~previous_counter~counterincheck_batch_tail_sanitysourcecounterrestinletcheck_batch:typekind.kindKind.managercontents_list->(public_key_hash*public_keyoption*counter)tzresult=funcontents_list->matchcontents_listwith|Single(Manager_operation{source;operation=Revealkey;counter;_})->ok(source,Somekey,counter)|Single(Manager_operation{source;counter;_})->ok(source,None,counter)|Cons(Manager_operation{source;operation=Revealkey;counter;_},rest)->check_batch_tail_sanitysourcecounterrest>>?fun()->ok(source,Somekey,counter)|Cons(Manager_operation{source;counter;_},rest)->check_batch_tail_sanitysourcecounterrest>>?fun()->ok(source,None,counter)inletopenLwt_result_syntaxinlet*?source,revealed_key,first_counter=check_batchcontents_listinlet*balance=Contract.check_allocated_and_get_balancevi.ctxtsourceinlet*()=Contract.check_counter_incrementvi.ctxtsourcefirst_counterinlet*pk=matchrevealed_keywith|Somepk->returnpk|None->Contract.get_manager_keyvi.ctxtsourceinletinitial_batch_state={balance;(* Initial contract allocation is ensured by the success of
the call to {!Contract.check_allocated_and_get_balance}
above. *)is_allocated=true;remaining_block_gas=vs.manager_state.remaining_block_gas;}inreturn(initial_batch_state,pk)letcheck_gas_limit_and_consume_from_block_gasvi~remaining_block_gas~gas_limit=(matchvi.modewith|Block->funres->res|Mempool->(* [Gas.check_limit_and_consume_from_block_gas] will only
raise a "temporary" error, however when
{!validate_operation} is called on a batch in isolation
(like e.g. in the mempool) it must "refuse" operations
whose total gas limit (the sum of the [gas_limit]s of each
operation) is already above the block limit. We add the
"permanent" error [Gas.Gas_limit_too_high] on top of the
trace to this effect. *)record_traceGas.Gas_limit_too_high)(Gas.check_limit_and_consume_from_block_gas~hard_gas_limit_per_operation:vi.manager_info.hard_gas_limit_per_operation~remaining_block_gas~gas_limit)letcheck_storage_limitvistorage_limit=error_unlessCompare.Z.(storage_limit<=vi.manager_info.hard_storage_limit_per_operation&&storage_limit>=Z.zero)Fees.Storage_limit_too_highletassert_tx_rollup_feature_enabledvi=letopenResult_syntaxinlet*sunset=Raw_level.of_int32(Constants.tx_rollup_sunset_levelvi.ctxt)inerror_unless(Constants.tx_rollup_enablevi.ctxt&&Raw_level.(vi.current_level.level<sunset))Tx_rollup_feature_disabledletassert_sc_rollup_feature_enabledvi=error_unless(Constants.sc_rollup_enablevi.ctxt)Sc_rollup_feature_disabledletassert_dal_feature_enabledvi=error_unless(Constants.dal_enablevi.ctxt)Dal_errors.Dal_feature_disabledletconsume_decoding_gasctxtlexpr=record_traceGas_quota_exceeded_init_deserialize@@(* Fail early if the operation does not have enough gas to
cover the deserialization cost. We always consider the full
deserialization cost, independently from the internal state
of the lazy_expr. Otherwise we might risk getting different
results if the operation has already been deserialized
before (e.g. when retrieved in JSON format). Note that the
lazy_expr is not actually decoded here; its deserialization
cost is estimated from the size of its bytes. *)Script.consume_decoding_gasctxtlexprletvalidate_tx_rollup_submit_batchviremaining_gascontent=letopenResult_syntaxinlet*()=assert_tx_rollup_feature_enabledviinletsize_limit=Constants.tx_rollup_hard_size_limit_per_messagevi.ctxtinlet_message,message_size=Tx_rollup_message.make_batchcontentinlet*cost=Tx_rollup_gas.hash_costmessage_sizeinlet*remaining_gas=Gas.consume_fromremaining_gascostinlet*()=error_unlessCompare.Int.(message_size<=size_limit)Tx_rollup_errors.Message_size_exceeds_limitinreturnremaining_gasletvalidate_tx_rollup_dispatch_ticketsviremaining_gasoperation=letopenResult_syntaxinlet*()=assert_tx_rollup_feature_enabledviinlet(Tx_rollup_dispatch_tickets{tickets_info;message_result_path;_})=operationinletConstants.Parametric.{max_messages_per_inbox;max_withdrawals_per_batch;_}=Constants.tx_rollupvi.ctxtinlet*()=Tx_rollup_errors.check_path_depth`Commitment(Tx_rollup_commitment.Merkle.path_depthmessage_result_path)~count_limit:max_messages_per_inboxinlet*()=error_whenCompare.List_length_with.(tickets_info=0)Tx_rollup_errors.No_withdrawals_to_dispatchinlet*()=error_whenCompare.List_length_with.(tickets_info>max_withdrawals_per_batch)Tx_rollup_errors.Too_many_withdrawalsinrecord_traceGas_quota_exceeded_init_deserialize(List.fold_left_e(funremaining_gasTx_rollup_reveal.{contents;ty;_}->let*remaining_gas=Script.consume_decoding_gasremaining_gascontentsinScript.consume_decoding_gasremaining_gasty)remaining_gastickets_info)letvalidate_tx_rollup_rejectionvioperation=letopenResult_syntaxinlet*()=assert_tx_rollup_feature_enabledviinlet(Tx_rollup_rejection{message_path;message_result_path;previous_message_result_path;_})=operationinletConstants.Parametric.{max_messages_per_inbox;_}=Constants.tx_rollupvi.ctxtinlet*()=Tx_rollup_errors.check_path_depth`Inbox(Tx_rollup_inbox.Merkle.path_depthmessage_path)~count_limit:max_messages_per_inboxinlet*()=Tx_rollup_errors.check_path_depth`Commitment(Tx_rollup_commitment.Merkle.path_depthmessage_result_path)~count_limit:max_messages_per_inboxinTx_rollup_errors.check_path_depth`Commitment(Tx_rollup_commitment.Merkle.path_depthprevious_message_result_path)~count_limit:max_messages_per_inboxletvalidate_contents(typekind)vibatch_state(contents:kindKind.managercontents)=letopenLwt_result_syntaxinlet(Manager_operation{source;fee;counter=_;operation;gas_limit;storage_limit})=contentsinlet*?remaining_block_gas=check_gas_limit_and_consume_from_block_gasvi~remaining_block_gas:batch_state.remaining_block_gas~gas_limitinlet*?remaining_gas=record_traceInsufficient_gas_for_manager(Gas.consume_from(Gas.Arith.fpgas_limit)Michelson_v1_gas.Cost_of.manager_operation)inlet*?()=check_storage_limitvistorage_limitinlet*?()=(* {!Contract.must_be_allocated} has already been called while
initializing [batch_state]. This checks that the contract has
not been emptied by spending fees for previous operations in
the batch. *)error_unlessbatch_state.is_allocated(Contract_storage.Empty_implicit_contractsource)inlet*?(_remaining_gas:Gas.Arith.fp)=letopenResult_syntaxinmatchoperationwith|Revealpk->let*()=Contract.check_public_keypksourceinreturnremaining_gas|Transaction{parameters;_}->consume_decoding_gasremaining_gasparameters|Origination{script;_}->let*remaining_gas=consume_decoding_gasremaining_gasscript.codeinconsume_decoding_gasremaining_gasscript.storage|Register_global_constant{value}->consume_decoding_gasremaining_gasvalue|Delegation_|Set_deposits_limit_|Increase_paid_storage_->returnremaining_gas|Tx_rollup_origination->let*()=assert_tx_rollup_feature_enabledviinreturnremaining_gas|Tx_rollup_submit_batch{content;_}->validate_tx_rollup_submit_batchviremaining_gascontent|Tx_rollup_commit_|Tx_rollup_return_bond_|Tx_rollup_finalize_commitment_|Tx_rollup_remove_commitment_->let*()=assert_tx_rollup_feature_enabledviinreturnremaining_gas|Transfer_ticket{contents;ty;_}->let*()=assert_tx_rollup_feature_enabledviinlet*remaining_gas=consume_decoding_gasremaining_gascontentsinconsume_decoding_gasremaining_gasty|Tx_rollup_dispatch_tickets_->validate_tx_rollup_dispatch_ticketsviremaining_gasoperation|Tx_rollup_rejection_->let*()=validate_tx_rollup_rejectionvioperationinreturnremaining_gas|Sc_rollup_originate_|Sc_rollup_add_messages_|Sc_rollup_cement_|Sc_rollup_publish_|Sc_rollup_refute_|Sc_rollup_timeout_|Sc_rollup_execute_outbox_message_->let*()=assert_sc_rollup_feature_enabledviinreturnremaining_gas|Sc_rollup_recover_bond_->(* TODO: https://gitlab.com/tezos/tezos/-/issues/3063
Should we successfully precheck Sc_rollup_recover_bond and any
(simple) Sc rollup operation, or should we add some some checks to make
the operations Branch_delayed if they cannot be successfully
prechecked? *)let*()=assert_sc_rollup_feature_enabledviinreturnremaining_gas|Sc_rollup_dal_slot_subscribe_->let*()=assert_sc_rollup_feature_enabledviinlet*()=assert_dal_feature_enabledviinreturnremaining_gas|Dal_publish_slot_header{slot}->let*()=Dal_apply.validate_publish_slot_headervi.ctxtslotinreturnremaining_gasinlet*balance,is_allocated=Contract.simulate_spendingvi.ctxt~balance:batch_state.balance~amount:feesourceinreturn{remaining_block_gas;balance;is_allocated}(** This would be [fold_left_es (validate_contents vi) batch_state
contents_list] if [contents_list] were an ordinary [list]. *)letrecvalidate_contents_list:typekind.validate_operation_info->batch_state->kindKind.managercontents_list->batch_statetzresultLwt.t=funvibatch_statecontents_list->letopenLwt_result_syntaxinmatchcontents_listwith|Singlecontents->validate_contentsvibatch_statecontents|Cons(contents,tail)->let*batch_state=validate_contentsvibatch_statecontentsinvalidate_contents_listvibatch_statetail(** Return the new value that [remaining_block_gas] should have in
[validate_operation_state] after the validation of a manager
operation:
- In [Block] (ie. block validation or block full construction)
mode, this value is [batch_state.remaining_block_gas], in which
the gas from the validated operation has been subtracted.
- In [Mempool] mode, the [remaining_block_gas] in
[validate_operation_state] should remain unchanged. Indeed, we
only want each batch to not exceed the block limit individually,
without taking other operations into account. *)letmaybe_update_remaining_block_gasvivsbatch_state=matchvi.modewith|Block->batch_state.remaining_block_gas|Mempool->vs.manager_state.remaining_block_gasletvalidate_manager_operationvivssourceoph(typekind)(operation:kindKind.manageroperation)=letopenLwt_result_syntaxinlet*?()=(* One-operation-per-manager-per-block restriction (1M).
We want to check 1M before we call
{!Contract.check_counter_increment} in
{!check_batch_sanity_and_find_public_key}. Indeed, if a block
contains two operations from the same manager, it is more
relevant to fail the second one with {!Manager_restriction}
than with {!Contract_storage.Counter_in_the_future}. *)matchSignature.Public_key_hash.Map.findsourcevs.manager_state.managers_seenwith|None->Result.return_unit|Someconflicting_oph->error(Manager_restriction(source,conflicting_oph))inletcontents_list=operation.protocol_data.contentsinlet*batch_state,source_pk=check_sanity_and_find_public_keyvivscontents_listinlet*batch_state=validate_contents_listvibatch_statecontents_listinlet*?()=Operation.check_signaturesource_pkvi.chain_idoperationinletmanagers_seen=Signature.Public_key_hash.Map.addsourceophvs.manager_state.managers_seeninletremaining_block_gas=maybe_update_remaining_block_gasvivsbatch_stateinreturn{manager_state={managers_seen;remaining_block_gas}}endletvalidate_operation(vi:validate_operation_info)(vs:validate_operation_state)oph(typekind)(operation:kindoperation)=letopenLwt_result_syntaxinlet*vs=matchoperation.protocol_data.contentswith|Single(Manager_operation{source;_})->Manager.validate_manager_operationvivssourceophoperation|Cons(Manager_operation{source;_},_)->Manager.validate_manager_operationvivssourceophoperation|Single(Preendorsement_)|Single(Endorsement_)|Single(Dal_slot_availability_)|Single(Seed_nonce_revelation_)|Single(Vdf_revelation_)|Single(Proposals_)|Single(Ballot_)|Single(Activate_account_)|Single(Double_preendorsement_evidence_)|Single(Double_endorsement_evidence_)|Single(Double_baking_evidence_)|Single(Failing_noop_)->(* TODO: https://gitlab.com/tezos/tezos/-/issues/2603
There is no separate validation phase for non-manager
operations yet: all checks are currently done during
application in {!Apply}.
When the validation of other operations is implemented, we
should also update
{!TMP_for_plugin.precheck_manager__do_nothing_on_non_manager_op}
(if has not been removed yet). *)returnvsinreturn(vs,Operation_validated_stamp)moduleTMP_for_plugin=structtype'ashould_check_signature=|Check_signatureof'aoperation|Skip_signature_checkletprecheck_managervivscontents_listshould_check_signature=letopenLwt_result_syntaxinletopenManagerinlet*batch_state,source_pk=check_sanity_and_find_public_keyvivscontents_listinlet*_batch_state=validate_contents_listvibatch_statecontents_listinlet*?()=matchshould_check_signaturewith|Check_signatureoperation->Operation.check_signaturesource_pkvi.chain_idoperation|Skip_signature_check->ok()inreturnOperation_validated_stampletprecheck_manager__do_nothing_on_non_manager_opctxtchain_id(typekind)(contents_list:kindcontents_list)should_check_signature=lethandle_manager(typea)(contents_list:aKind.managercontents_list)=letvi,vs=init_info_and_statectxtMempoolchain_idinprecheck_managervivscontents_listshould_check_signatureinmatchcontents_listwith|Single(Manager_operation_)->handle_managercontents_list|Cons(Manager_operation_,_)->handle_managercontents_list|Single(Preendorsement_)|Single(Endorsement_)|Single(Dal_slot_availability_)|Single(Seed_nonce_revelation_)|Single(Vdf_revelation_)|Single(Proposals_)|Single(Ballot_)|Single(Activate_account_)|Single(Double_preendorsement_evidence_)|Single(Double_endorsement_evidence_)|Single(Double_baking_evidence_)|Single(Failing_noop_)->(* TODO: https://gitlab.com/tezos/tezos/-/issues/2603
This should be updated when {!validate_operation} is
implemented on non-manager operations. (Alternatively, this
function might be removed first:
https://gitlab.com/tezos/tezos/-/issues/3245) *)returnOperation_validated_stampend