diff --git a/docs/protocols/alpha.rst b/docs/protocols/alpha.rst index 889b6585514b5f8f7784922663c2cc107b3e4565..0e1a1219c16670ded21806a3c221e2799443360d 100644 --- a/docs/protocols/alpha.rst +++ b/docs/protocols/alpha.rst @@ -28,6 +28,14 @@ Breaking Changes application will fail so the operation will have no effect, but its fees will still be taken. (MR :gl:`!5506`) +- The one-operation-per-manager-per-block restriction (1M) is now + enforced in blocks. It was previously (optionally) enforced by the + prevalidator using the plugin mempool filters. This meant that a + baker could still include several operations from the same manager + in its own block. This is no longer possible: the application of a + block containing more than one operation from the same manager will + now fail. (MR :gl:`!5557`) + RPC Changes ----------- @@ -68,6 +76,9 @@ Bug Fixes - Emptying an implicit account does not cost extra-gas anymore. (MR :gl:`!5566`) +- The ``helpers/scripts/run_operation`` RPC now checks whether all + operations in a batch have the same source. (MR :gl:`!5557`) + Minor Changes ------------- @@ -97,3 +108,32 @@ Internal that the operation has enough gas for these deserializations (by consuming an estimated gas cost based on the bytes size: this has not changed). (MR :gl:`!5506`) + +- Split precheck into two parts: checks and effects. The checks part + is effect-free. The effects part consists of the modifications of + the context that happen regardless of whether the application of the + operation succeeds: take the fees, increment the account's counter, + and remove the operation's gas limit from the available block + gas. The checks part must ensure that the effects part cannot + fail. (MR :gl:`!5557`) + +- Move the checks part of precheck (see above) to a new file + ``validate_operation.ml``. The effects part remains in + ``apply_operation`` and is renamed to ``take_fees``. The new + ``Validate_operation.validate_operation`` function is called before + ``Apply.apply_operation`` in ``Main``. It stores its own state in + ``Main.validation_state`` and works with the context from the + beginning of the block (which is fine thanks to the 1M restriction). + For now, ``validate_operation`` does nothing for non-manager + operations, but we plan to extend it to all operations in the + future. (MR :gl:`!5557`) + +- Remove ``Main.check_manager_signature``. Instead, + ``Main.precheck_manager`` now takes an additional argument that + indicates whether it should check the signature. (MR :gl:`!5557`) + +- Add a type ``Validate_operation.stamp`` in order to guarantee that + an operation is always validated before it is applied. Indeed, a + value of this type may only be created in ``Validate_operation``, + and is required by ``Apply.apply_operation`` and a few other + functions in ``Apply``. (MR :gl:`!5557`) diff --git a/src/proto_alpha/lib_client/michelson_v1_error_reporter.ml b/src/proto_alpha/lib_client/michelson_v1_error_reporter.ml index 11ad9bda7b9be60eec5c3557b24e8bbd5b928dc8..993a5981d8dd2c38d79200fab9dab2f9e983747c 100644 --- a/src/proto_alpha/lib_client/michelson_v1_error_reporter.ml +++ b/src/proto_alpha/lib_client/michelson_v1_error_reporter.ml @@ -319,7 +319,8 @@ let report_errors ~details ~show_source ?parsed ppf errs = else Format.fprintf ppf "Ill typed contract." ; if rest <> [] then Format.fprintf ppf "@," ; print_trace (parsed_locations parsed) rest - | Environment.Ecoproto_error Apply.Gas_quota_exceeded_init_deserialize + | Environment.Ecoproto_error + Validate_operation.Manager.Gas_quota_exceeded_init_deserialize :: rest -> Format.fprintf ppf diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index b0651d5fe63d024333e228460a6ddc9f6146a6a4..86144e9985b5f9cee56b6ea14c9860b97e7361d8 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -819,29 +819,34 @@ module Scripts = struct let hash = Operation.hash {shell; protocol_data} in let ctxt = Origination_nonce.init ctxt hash in let payload_producer = Signature.Public_key_hash.zero in + Validate_operation.TMP_for_plugin + .precheck_manager__do_nothing_on_non_manager_op + ctxt + chain_id + protocol_data.contents + Skip_signature_check + >>=? fun op_validated_stamp -> match protocol_data.contents with | Single (Manager_operation _) as op -> - Apply.precheck_manager_contents_list ctxt op ~mempool_mode:true - >>=? fun (ctxt, prechecked_contents_list) -> - (* removed signature check here *) - Apply.apply_manager_contents_list + Apply.apply_manager_operation ctxt Optimized ~payload_producer chain_id - prechecked_contents_list - >|= fun (_ctxt, result) -> ok @@ ret result + ~mempool_mode:true + op_validated_stamp + op + >|=? fun (_ctxt, result) -> ret result | Cons (Manager_operation _, _) as op -> - Apply.precheck_manager_contents_list ctxt op ~mempool_mode:true - >>=? fun (ctxt, prechecked_contents_list) -> - (* removed signature check here *) - Apply.apply_manager_contents_list + Apply.apply_manager_operation ctxt Optimized ~payload_producer chain_id - prechecked_contents_list - >|= fun (_ctxt, result) -> ok @@ ret result + ~mempool_mode:true + op_validated_stamp + op + >|=? fun (_ctxt, result) -> ret result | _ -> let predecessor_level = match @@ -862,6 +867,7 @@ module Scripts = struct }) Optimized ~payload_producer + op_validated_stamp operation operation.protocol_data.contents >|=? fun (_ctxt, result) -> ret result diff --git a/src/proto_alpha/lib_plugin/mempool.ml b/src/proto_alpha/lib_plugin/mempool.ml index 4b021d26b0269fda12465b4352a98d75b053bd28..4a3163b6061d5f903ecdbf7b3aca2b73864ba520 100644 --- a/src/proto_alpha/lib_plugin/mempool.ml +++ b/src/proto_alpha/lib_plugin/mempool.ml @@ -980,18 +980,17 @@ let precheck_manager : ~gas_limit source -> let precheck_manager_and_check_signature ~on_success = - ( Main.precheck_manager validation_state contents >>=? fun () -> - let (raw_operation : t Kind.manager operation) = - Alpha_context.{shell; protocol_data} - in + let should_check_signature = if Compare.Int.(nb_successful_prechecks > 0) then - (* Signature succesfully checked at least once. *) - return_unit + (* Signature successfully checked at least once. *) + Validate_operation.TMP_for_plugin.Skip_signature_check else (* Signature probably never checked. *) - Main.check_manager_signature validation_state contents raw_operation ) + Validate_operation.TMP_for_plugin.Check_signature {shell; protocol_data} + in + Main.precheck_manager validation_state contents should_check_signature >|= function - | Ok () -> on_success + | Ok (_ : Validate_operation.stamp) -> on_success | Error err -> ( let err = Environment.wrap_tztrace err in match classify_trace err with diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index de874245fd88f9fed653ef5321b8dbd86b9abe19..f7e3ec286ce442057e2e5490e9f3248bf51969b0 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -212,6 +212,7 @@ "Baking", "Amendment", + "Validate_operation", "Apply", "Services_registration", diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index f1e852c2f6ee467715a1ba344823b7a02742db10..4b2d226b898c7ad33cb210c7de64be8dc17eb1d4 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -134,32 +134,6 @@ end module Ratio = Ratio_repr module Raw_level = Raw_level_repr module Cycle = Cycle_repr - -module Script = struct - include Michelson_v1_primitives - include Script_repr - - type consume_deserialization_gas = Always | When_needed - - let force_decode_in_context ~consume_deserialization_gas ctxt lexpr = - let gas_cost = - match consume_deserialization_gas with - | Always -> Script_repr.stable_force_decode_cost lexpr - | When_needed -> Script_repr.force_decode_cost lexpr - in - Raw_context.consume_gas ctxt gas_cost >>? fun ctxt -> - Script_repr.force_decode lexpr >|? fun v -> (v, ctxt) - - let force_bytes_in_context ctxt lexpr = - Raw_context.consume_gas ctxt (Script_repr.force_bytes_cost lexpr) - >>? fun ctxt -> - Script_repr.force_bytes lexpr >|? fun v -> (v, ctxt) - - let consume_decoding_gas ctxt lexpr = - let gas_cost = Script_repr.stable_force_decode_cost lexpr in - Raw_context.consume_gas ctxt gas_cost -end - module Fees = Fees_storage type public_key = Signature.Public_key.t @@ -203,14 +177,10 @@ end module Gas = struct include Gas_limit_repr - type error += Gas_limit_too_high = Raw_context.Gas_limit_too_high - type error += Block_quota_exceeded = Raw_context.Block_quota_exceeded type error += Operation_quota_exceeded = Raw_context.Operation_quota_exceeded - let check_limit_is_valid = Raw_context.check_gas_limit_is_valid - let set_limit = Raw_context.set_gas_limit let consume_limit_in_block = Raw_context.consume_gas_limit_in_block @@ -219,6 +189,24 @@ module Gas = struct let consume = Raw_context.consume_gas + let consume_from available_gas cost = + match raw_consume available_gas cost with + | Some remaining_gas -> ok remaining_gas + | None -> error Operation_quota_exceeded + + let check_limit_and_consume_from_block_gas + ~(hard_gas_limit_per_operation : Arith.integral) + ~(remaining_block_gas : Arith.fp) ~(gas_limit : Arith.integral) = + let open Result_syntax in + let* () = check_gas_limit ~hard_gas_limit_per_operation ~gas_limit in + let gas_limit_fp = Arith.fp gas_limit in + let* () = + error_unless + Arith.(gas_limit_fp <= remaining_block_gas) + Block_quota_exceeded + in + return (Arith.sub remaining_block_gas gas_limit_fp) + let remaining_operation_gas = Raw_context.remaining_operation_gas let update_remaining_operation_gas = @@ -238,6 +226,31 @@ module Gas = struct let cost_of_repr cost = cost end +module Script = struct + include Michelson_v1_primitives + include Script_repr + + type consume_deserialization_gas = Always | When_needed + + let force_decode_in_context ~consume_deserialization_gas ctxt lexpr = + let gas_cost = + match consume_deserialization_gas with + | Always -> Script_repr.stable_force_decode_cost lexpr + | When_needed -> Script_repr.force_decode_cost lexpr + in + Raw_context.consume_gas ctxt gas_cost >>? fun ctxt -> + Script_repr.force_decode lexpr >|? fun v -> (v, ctxt) + + let force_bytes_in_context ctxt lexpr = + Raw_context.consume_gas ctxt (Script_repr.force_bytes_cost lexpr) + >>? fun ctxt -> + Script_repr.force_bytes lexpr >|? fun v -> (v, ctxt) + + let consume_decoding_gas available_gas lexpr = + let gas_cost = Script_repr.stable_force_decode_cost lexpr in + Gas.consume_from available_gas gas_cost +end + module Level = struct include Level_repr include Level_storage diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 4b62e4fb4619181a7c010489dba16aed913df19a..76c8c35068bcc57087ef62b8850bf1ef2fb1d5ca 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -375,11 +375,6 @@ module Gas : sig val pp : Format.formatter -> t -> unit - (** [check_limit_is_valid ctxt limit] checks that the given gas - [limit] is well-formed, i.e., it does not exceed the hard gas - limit per operation as defined in [ctxt] and it is positive. *) - val check_limit_is_valid : context -> 'a Arith.t -> unit tzresult - (** [set_limit ctxt limit] returns a context with a given [limit] level of gas allocated for an operation. *) val set_limit : context -> 'a Arith.t -> context @@ -426,26 +421,46 @@ module Gas : sig val pp_cost : Format.formatter -> cost -> unit + type error += Operation_quota_exceeded (* `Temporary *) + (** [consume ctxt cost] subtracts [cost] to the current operation gas level in [ctxt]. This operation may fail with [Operation_quota_exceeded] if the operation gas level would go below zero. *) val consume : context -> cost -> context tzresult - type error += Operation_quota_exceeded (* `Temporary *) + (** [consume_from available_gas cost] subtracts [cost] from + [available_gas] and returns the remaining gas. - (** [consume_limit_in_block ctxt limit] consumes [limit] in - the current block gas level of the context. This operation may - fail with error [Block_quota_exceeded] if not enough gas remains - in the block. This operation may also fail with - [Gas_limit_too_high] if [limit] is greater than the allowed - limit for operation gas level. *) - val consume_limit_in_block : context -> 'a Arith.t -> context tzresult + @return [Error Operation_quota_exceeded] if the remaining gas + would fall below [0]. *) + val consume_from : Arith.fp -> cost -> Arith.fp tzresult type error += Block_quota_exceeded (* `Temporary *) type error += Gas_limit_too_high (* `Permanent *) + (** See {!Raw_context.consume_gas_limit_in_block}. *) + val consume_limit_in_block : context -> 'a Arith.t -> context tzresult + + (** Check that [gas_limit] is a valid operation gas limit (at most + [hard_gas_limit_per_operation] and nonnegative), then subtract it + from [remaining_block_gas] and return the difference. + + @return [Error Gas_limit_too_high] if [gas_limit] is greater + than [hard_gas_limit_per_operation] or negative. + + @return [Error Block_quota_exceeded] if [gas_limit] is greater + than [remaining_block_gas]. + + This function mimics {!consume_limit_in_block} but bypasses the + context. *) + val check_limit_and_consume_from_block_gas : + hard_gas_limit_per_operation:Arith.integral -> + remaining_block_gas:Arith.fp -> + gas_limit:Arith.integral -> + Arith.fp tzresult + (** The cost of free operation is [0]. *) val free : cost @@ -710,17 +725,16 @@ module Script : sig val force_bytes_in_context : context -> lazy_expr -> (bytes * context) tzresult - (** [consume_decoding_gas ctxt lexpr] substracts (a lower bound on) - the cost to deserialize [lexpr] from the current operation gas - level in [ctxt]. The cost does not depend on the internal state - of the lazy_expr. + (** [consume_decoding_gas available_gas lexpr] subtracts (a lower + bound on) the cost to deserialize [lexpr] from [available_gas]. + The cost does not depend on the internal state of the lazy_expr. - @return [Error Operation_quota_exceeded] if the operation gas - level would fall below [0]. + @return [Error Operation_quota_exceeded] if the remaining gas + would fall below [0]. This mimics the gas consuming part of {!force_decode_in_context} called with [consume_deserialization_gas:Always]. *) - val consume_decoding_gas : context -> lazy_expr -> context tzresult + val consume_decoding_gas : Gas.Arith.fp -> lazy_expr -> Gas.Arith.fp tzresult val unit_parameter : lazy_expr @@ -1615,10 +1629,15 @@ module Contract : sig val get_counter : context -> public_key_hash -> Z.t tzresult Lwt.t + (** See {Contract_storage.get_balance}. *) val get_balance : context -> t -> Tez.t tzresult Lwt.t val get_balance_carbonated : context -> t -> (context * Tez.t) tzresult Lwt.t + (** See {Contract_storage.check_allocated_and_get_balance}. *) + val check_allocated_and_get_balance : + context -> public_key_hash -> Tez.t tzresult Lwt.t + val fresh_contract_from_current_nonce : context -> (context * Contract_hash.t) tzresult @@ -1664,6 +1683,14 @@ module Contract : sig val check_counter_increment : context -> public_key_hash -> Z.t -> unit tzresult Lwt.t + (** See {Contract_storage.simulate_spending}. *) + val simulate_spending : + context -> + balance:Tez.t -> + amount:Tez.t -> + public_key_hash -> + (Tez.t * bool) tzresult Lwt.t + val raw_originate : context -> prepaid_bootstrap_storage:bool -> diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index c64a2c6464c209db0506c5dbed503b8527615009..5c8f1820772236ae22c3ac1be124f50c6016e6f6 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -94,12 +94,12 @@ type error += } | Set_deposits_limit_on_unregistered_delegate of Signature.Public_key_hash.t | Set_deposits_limit_too_high of {limit : Tez.t; max_limit : Tez.t} + | Error_while_taking_fees | Empty_transaction of Contract.t | Tx_rollup_feature_disabled | Tx_rollup_invalid_transaction_ticket_amount | Cannot_transfer_ticket_to_implicit | Sc_rollup_feature_disabled - | Inconsistent_counters | Wrong_voting_period of {expected : int32; provided : int32} | Internal_operation_replay of Apply_internal_results.packed_internal_contents | Invalid_denunciation of denunciation_kind @@ -121,12 +121,8 @@ type error += } | Invalid_activation of {pkh : Ed25519.Public_key_hash.t} | Multiple_revelation - | Gas_quota_exceeded_init_deserialize - | Insufficient_gas_for_manager - | Inconsistent_sources | Failing_noop_error | Zero_frozen_deposits of Signature.Public_key_hash.t - | Incorrect_reveal_position | Invalid_transfer_to_sc_rollup_from_implicit_account let () = @@ -472,6 +468,22 @@ let () = | Set_deposits_limit_too_high {limit; max_limit} -> Some (limit, max_limit) | _ -> None) (fun (limit, max_limit) -> Set_deposits_limit_too_high {limit; max_limit}) ; + + let error_while_taking_fees_description = + "There was an error while taking the fees, which should not happen and \ + means that the operation's validation was faulty." + in + register_error_kind + `Permanent + ~id:"operation.error_while_taking_fees" + ~title:"Error while taking the fees of a manager operation" + ~description:error_while_taking_fees_description + ~pp:(fun ppf () -> + Format.fprintf ppf "%s" error_while_taking_fees_description) + Data_encoding.unit + (function Error_while_taking_fees -> Some () | _ -> None) + (fun () -> Error_while_taking_fees) ; + register_error_kind `Branch ~id:"contract.empty_transaction" @@ -537,21 +549,6 @@ let () = (function Sc_rollup_feature_disabled -> Some () | _ -> None) (fun () -> Sc_rollup_feature_disabled) ; - register_error_kind - `Permanent - ~id:"operation.inconsistent_counters" - ~title:"Inconsistent counters in operation" - ~description: - "Inconsistent counters in operation. Counters of an operation must be \ - successive." - ~pp:(fun ppf () -> - Format.fprintf - ppf - "Inconsistent counters in operation. Counters of an operation must be \ - successive.") - Data_encoding.empty - (function Inconsistent_counters -> Some () | _ -> None) - (fun () -> Inconsistent_counters) ; register_error_kind `Temporary ~id:"operation.wrong_voting_period" @@ -715,43 +712,6 @@ let () = Data_encoding.empty (function Multiple_revelation -> Some () | _ -> None) (fun () -> Multiple_revelation) ; - register_error_kind - `Permanent - ~id:"gas_exhausted.init_deserialize" - ~title:"Not enough gas for initial deserialization of script expressions" - ~description: - "Gas limit was not high enough to deserialize the transaction parameters \ - or origination script code or initial storage, making the operation \ - impossible to parse within the provided gas bounds." - Data_encoding.empty - (function Gas_quota_exceeded_init_deserialize -> Some () | _ -> None) - (fun () -> Gas_quota_exceeded_init_deserialize) ; - register_error_kind - `Permanent - ~id:"operation.insufficient_gas_for_manager" - ~title:"Not enough gas for initial manager cost" - ~description: - (Format.asprintf - "Gas limit was not high enough to cover the initial cost of manager \ - operations. At least %a expected." - Gas.pp_cost - Michelson_v1_gas.Cost_of.manager_operation) - Data_encoding.empty - (function Insufficient_gas_for_manager -> Some () | _ -> None) - (fun () -> Insufficient_gas_for_manager) ; - register_error_kind - `Permanent - ~id:"operation.inconsistent_sources" - ~title:"Inconsistent sources in operation pack" - ~description: - "The operation pack includes operations from different sources." - ~pp:(fun ppf () -> - Format.pp_print_string - ppf - "The operation pack includes operations from different sources.") - Data_encoding.empty - (function Inconsistent_sources -> Some () | _ -> None) - (fun () -> Inconsistent_sources) ; register_error_kind `Permanent ~id:"operation.failing_noop" @@ -781,19 +741,6 @@ let () = Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) (function Zero_frozen_deposits delegate -> Some delegate | _ -> None) (fun delegate -> Zero_frozen_deposits delegate) ; - register_error_kind - `Permanent - ~id:"operations.incorrect_reveal_position" - ~title:"Incorrect reveal position" - ~description:"Incorrect reveal position in batch" - ~pp:(fun ppf () -> - Format.fprintf - ppf - "Incorrect reveal operation position in batch: only allowed in first \ - position") - Data_encoding.empty - (function Incorrect_reveal_position -> Some () | _ -> None) - (fun () -> Incorrect_reveal_position) ; register_error_kind `Permanent ~id:"operations.invalid_transfer_to_sc_rollup_from_implicit_account" @@ -815,14 +762,14 @@ open Apply_operation_result open Apply_internal_results let assert_tx_rollup_feature_enabled ctxt = + let open Result_syntax in let level = (Level.current ctxt).level in - Raw_level.of_int32 @@ Constants.tx_rollup_sunset_level ctxt >>?= fun sunset -> - fail_when Raw_level.(sunset <= level) Tx_rollup_feature_disabled - >>=? fun () -> - fail_unless (Constants.tx_rollup_enable ctxt) Tx_rollup_feature_disabled + let* sunset = Raw_level.of_int32 @@ Constants.tx_rollup_sunset_level ctxt in + let* () = error_when Raw_level.(sunset <= level) Tx_rollup_feature_disabled in + error_unless (Constants.tx_rollup_enable ctxt) Tx_rollup_feature_disabled let assert_sc_rollup_feature_enabled ctxt = - fail_unless (Constants.sc_rollup_enable ctxt) Sc_rollup_feature_disabled + error_unless (Constants.sc_rollup_enable ctxt) Sc_rollup_feature_disabled let update_script_storage_and_ticket_balances ctxt ~self storage lazy_storage_diff ticket_diffs operations = @@ -1007,7 +954,7 @@ let ex_ticket_size : let apply_transaction_to_tx_rollup ~ctxt ~parameters_ty ~parameters ~payer ~dst_rollup ~since = - assert_tx_rollup_feature_enabled ctxt >>=? fun () -> + assert_tx_rollup_feature_enabled ctxt >>?= fun () -> (* If the ticket deposit fails on L2 for some reason (e.g. [Balance_overflow] in the recipient), then it is returned to [payer]. As [payer] is implicit, it cannot own @@ -1215,7 +1162,7 @@ let apply_internal_manager_operation_content : parameters = _; unparsed_parameters = payload; } -> - assert_sc_rollup_feature_enabled ctxt >>=? fun () -> + assert_sc_rollup_feature_enabled ctxt >>?= fun () -> (* TODO: #3242 We could rather change the type of [source] in {!Script_type_ir.internal_operation}. Only originated accounts should @@ -1969,148 +1916,6 @@ let apply_internal_manager_operations ctxt mode ~payer ~chain_id ops = in apply ctxt [] ops -let precheck_manager_contents (type kind) ctxt (op : kind Kind.manager contents) - ~(only_batch : bool) : (context * Receipt.balance_updates) tzresult Lwt.t = - let[@coq_match_with_default] (Manager_operation - { - source; - fee; - counter; - operation; - gas_limit; - storage_limit; - }) = - op - in - (if only_batch then - (* Gas.consume_limit_in_block will only raise a "temporary" error, however - when the precheck 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_limits 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_trace Gas.Gas_limit_too_high - else fun errs -> errs) - @@ Gas.consume_limit_in_block ctxt gas_limit - >>?= fun ctxt -> - let ctxt = Gas.set_limit ctxt gas_limit in - record_trace - Insufficient_gas_for_manager - (Gas.consume ctxt Michelson_v1_gas.Cost_of.manager_operation) - >>?= fun ctxt -> - Fees.check_storage_limit ctxt ~storage_limit >>?= fun () -> - let source_contract = Contract.Implicit source in - Contract.must_be_allocated ctxt source_contract >>=? fun () -> - Contract.check_counter_increment ctxt source counter >>=? fun () -> - let consume_decoding_gas ctxt lexpr = - record_trace Gas_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_gas ctxt lexpr - in - (match operation with - | Reveal pk -> Contract.check_public_key pk source >>?= fun () -> return ctxt - | Transaction {parameters; _} -> - Lwt.return @@ consume_decoding_gas ctxt parameters - | Origination {script; _} -> - Lwt.return - ( consume_decoding_gas ctxt script.code >>? fun ctxt -> - consume_decoding_gas ctxt script.storage ) - | Register_global_constant {value} -> - Lwt.return @@ consume_decoding_gas ctxt value - | Delegation _ | Set_deposits_limit _ -> return ctxt - | Tx_rollup_origination -> - assert_tx_rollup_feature_enabled ctxt >|=? fun () -> ctxt - | Tx_rollup_submit_batch {content; _} -> - assert_tx_rollup_feature_enabled ctxt >>=? fun () -> - let size_limit = Constants.tx_rollup_hard_size_limit_per_message ctxt in - let _message, message_size = Tx_rollup_message.make_batch content in - Tx_rollup_gas.hash_cost message_size >>?= fun cost -> - Gas.consume ctxt cost >>?= fun ctxt -> - fail_unless - Compare.Int.(message_size <= size_limit) - Tx_rollup_errors.Message_size_exceeds_limit - >>=? fun () -> return ctxt - | Tx_rollup_commit _ | Tx_rollup_return_bond _ - | Tx_rollup_finalize_commitment _ | Tx_rollup_remove_commitment _ -> - assert_tx_rollup_feature_enabled ctxt >>=? fun () -> return ctxt - | Transfer_ticket {contents; ty; _} -> - assert_tx_rollup_feature_enabled ctxt >>=? fun () -> - Lwt.return - @@ ( consume_decoding_gas ctxt contents >>? fun ctxt -> - consume_decoding_gas ctxt ty ) - | Tx_rollup_dispatch_tickets {tickets_info; message_result_path; _} -> - assert_tx_rollup_feature_enabled ctxt >>=? fun () -> - let Constants.Parametric. - {max_messages_per_inbox; max_withdrawals_per_batch; _} = - Constants.tx_rollup ctxt - in - Tx_rollup_errors.check_path_depth - `Commitment - (Tx_rollup_commitment.Merkle.path_depth message_result_path) - ~count_limit:max_messages_per_inbox - >>?= fun () -> - error_when - Compare.List_length_with.(tickets_info = 0) - Tx_rollup_errors.No_withdrawals_to_dispatch - >>?= fun () -> - error_when - Compare.List_length_with.(tickets_info > max_withdrawals_per_batch) - Tx_rollup_errors.Too_many_withdrawals - >>?= fun () -> - record_trace Gas_quota_exceeded_init_deserialize - @@ List.fold_left_e - (fun ctxt Tx_rollup_reveal.{contents; ty; _} -> - Script.consume_decoding_gas ctxt contents >>? fun ctxt -> - Script.consume_decoding_gas ctxt ty) - ctxt - tickets_info - >>?= fun ctxt -> return ctxt - | Tx_rollup_rejection - {message_path; message_result_path; previous_message_result_path; _} -> - assert_tx_rollup_feature_enabled ctxt >>=? fun () -> - let Constants.Parametric.{max_messages_per_inbox; _} = - Constants.tx_rollup ctxt - in - Tx_rollup_errors.check_path_depth - `Inbox - (Tx_rollup_inbox.Merkle.path_depth message_path) - ~count_limit:max_messages_per_inbox - >>?= fun () -> - Tx_rollup_errors.check_path_depth - `Commitment - (Tx_rollup_commitment.Merkle.path_depth message_result_path) - ~count_limit:max_messages_per_inbox - >>?= fun () -> - Tx_rollup_errors.check_path_depth - `Commitment - (Tx_rollup_commitment.Merkle.path_depth previous_message_result_path) - ~count_limit:max_messages_per_inbox - >>?= fun () -> return ctxt - | Sc_rollup_originate _ | Sc_rollup_add_messages _ | Sc_rollup_cement _ - | Sc_rollup_publish _ | Sc_rollup_refute _ | Sc_rollup_timeout _ - | Sc_rollup_execute_outbox_message _ -> - assert_sc_rollup_feature_enabled ctxt >|=? fun () -> ctxt - | 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. *) - assert_sc_rollup_feature_enabled ctxt >|=? fun () -> ctxt - | Dal_publish_slot_header {slot} -> - Dal_apply.validate_publish_slot_header ctxt slot >>?= fun () -> - return ctxt) - >>=? fun ctxt -> - Contract.increment_counter ctxt source >>=? fun ctxt -> - Token.transfer ctxt (`Contract source_contract) `Block_fees fee - let burn_transaction_storage_fees ctxt trr ~storage_limit ~payer = match trr with | Transaction_to_contract_result payload -> @@ -2395,15 +2200,32 @@ let apply_manager_contents (type kind) ctxt mode chain_id | Error errors -> Lwt.return (Failure, Failed (manager_kind operation, errors), []) +(** An individual manager operation (either standalone or inside a + batch) together with the balance update corresponding to the + transfer of its fee. *) +type 'kind fees_updated_contents = { + contents : 'kind contents; + balance_updates : Receipt.balance_updates; +} + +type _ fees_updated_contents_list = + | FeesUpdatedSingle : + 'kind fees_updated_contents + -> 'kind fees_updated_contents_list + | FeesUpdatedCons : + 'kind Kind.manager fees_updated_contents + * 'rest Kind.manager fees_updated_contents_list + -> ('kind * 'rest) Kind.manager fees_updated_contents_list + let rec mark_skipped : type kind. payload_producer:Signature.Public_key_hash.t -> Level.t -> - kind Kind.manager prechecked_contents_list -> + kind Kind.manager fees_updated_contents_list -> kind Kind.manager contents_result_list = - fun ~payload_producer level prechecked_contents_list -> - match[@coq_match_with_default] prechecked_contents_list with - | PrecheckedSingle + fun ~payload_producer level fees_updated_contents_list -> + match[@coq_match_with_default] fees_updated_contents_list with + | FeesUpdatedSingle {contents = Manager_operation {operation; _}; balance_updates} -> Single_result (Manager_operation_result @@ -2412,7 +2234,7 @@ let rec mark_skipped : operation_result = Skipped (manager_kind operation); internal_operation_results = []; }) - | PrecheckedCons + | FeesUpdatedCons ({contents = Manager_operation {operation; _}; balance_updates}, rest) -> Cons_result ( Manager_operation_result @@ -2423,114 +2245,53 @@ let rec mark_skipped : }, mark_skipped ~payload_producer level rest ) -(** Check that counters are consistent, i.e. that they are successive within a - batch. Fail with a {b permanent} error otherwise. - TODO: https://gitlab.com/tezos/tezos/-/issues/2301 - Remove when format of operation is changed to save space. - *) -let check_counters_consistency contents_list = - let check_counter ~previous_counter counter = - match previous_counter with - | None -> return_unit - | Some previous_counter -> - let expected = Z.succ previous_counter in - if Compare.Z.(expected = counter) then return_unit - else fail Inconsistent_counters - in - let rec check_counters_rec : - type kind. - counter option -> kind Kind.manager contents_list -> unit tzresult Lwt.t = - fun previous_counter contents_list -> - match[@coq_match_with_default] contents_list with - | Single (Manager_operation {counter; _}) -> - check_counter ~previous_counter counter - | Cons (Manager_operation {counter; _}, rest) -> - check_counter ~previous_counter counter >>=? fun () -> - check_counters_rec (Some counter) rest - in - check_counters_rec None contents_list +(** Return balance updates for fees, and an updated context that + accounts for: + + - fees spending, + + - counter incrementation, -(** Returns an updated context, and a list of prechecked contents containing - balance updates for fees related to each manager operation in - [contents_list]. *) -let precheck_manager_contents_list ctxt contents_list ~mempool_mode = - let rec rec_precheck_manager_contents_list : + - consumption of each operation's [gas_limit] from the available + block gas. + + The {!type:Validate_operation.stamp} argument enforces that the + operation has already been validated by {!Validate_operation}. The + latter is responsible for ensuring that the operation is solvable, + i.e. its fees can be taken, i.e. [take_fees] cannot return an + error. *) +let take_fees ctxt (_ : Validate_operation.stamp) contents_list = + let open Lwt_result_syntax in + let rec take_fees_rec : type kind. context -> kind Kind.manager contents_list -> - (context * kind Kind.manager prechecked_contents_list) tzresult Lwt.t = + (context * kind Kind.manager fees_updated_contents_list) tzresult Lwt.t = fun ctxt contents_list -> + let contents_effects contents = + let (Manager_operation {source; fee; gas_limit; _}) = contents in + let*? ctxt = Gas.consume_limit_in_block ctxt gas_limit in + let* ctxt = Contract.increment_counter ctxt source in + let+ ctxt, balance_updates = + Token.transfer + ctxt + (`Contract (Contract.Implicit source)) + `Block_fees + fee + in + (ctxt, {contents; balance_updates}) + in match contents_list with | Single contents -> - precheck_manager_contents ctxt contents ~only_batch:mempool_mode - >>=? fun (ctxt, balance_updates) -> - return (ctxt, PrecheckedSingle {contents; balance_updates}) + let+ ctxt, fees_updated_contents = contents_effects contents in + (ctxt, FeesUpdatedSingle fees_updated_contents) | Cons (contents, rest) -> - precheck_manager_contents ctxt contents ~only_batch:mempool_mode - >>=? fun (ctxt, balance_updates) -> - rec_precheck_manager_contents_list ctxt rest - >>=? fun (ctxt, results_rest) -> - return (ctxt, PrecheckedCons ({contents; balance_updates}, results_rest)) + let* ctxt, fees_updated_contents = contents_effects contents in + let+ ctxt, result_rest = take_fees_rec ctxt rest in + (ctxt, FeesUpdatedCons (fees_updated_contents, result_rest)) in - let ctxt = if mempool_mode then Gas.reset_block_gas ctxt else ctxt in - check_counters_consistency contents_list >>=? fun () -> - rec_precheck_manager_contents_list ctxt contents_list - -let find_manager_public_key ctxt (op : _ Kind.manager contents_list) = - (* 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. *) - (* Invariants checked: - - - Reveal operations are only authorized in the first position element of a batch. - - - All sources in a batch must be equal. *) - (* Performs a sanity check and return the operation's (single) - source and a potential public key if the batch contains a reveal - operation in the head position. *) - let rec check_batch_tail_sanity : - type kind. - public_key_hash -> kind Kind.manager contents_list -> unit tzresult = - fun expected_source -> function - | Single (Manager_operation {operation = Reveal _key; _}) -> - error Incorrect_reveal_position - | Cons (Manager_operation {operation = Reveal _key; _}, _res) -> - error Incorrect_reveal_position - | Single (Manager_operation {source; _}) -> - error_unless - (Signature.Public_key_hash.equal expected_source source) - Inconsistent_sources - | Cons (Manager_operation {source; _}, rest) -> - error_unless - (Signature.Public_key_hash.equal expected_source source) - Inconsistent_sources - >>? fun () -> check_batch_tail_sanity source rest - in - let check_batch : - type kind. - kind Kind.manager contents_list -> - (public_key_hash * public_key option) tzresult = - fun op -> - match op with - | Single (Manager_operation {source; operation = Reveal key; _}) -> - ok (source, Some key) - | Single (Manager_operation {source; _}) -> ok (source, None) - | Cons (Manager_operation {source; operation = Reveal key; _}, rest) -> - check_batch_tail_sanity source rest >>? fun () -> ok (source, Some key) - | Cons (Manager_operation {source; _}, rest) -> - check_batch_tail_sanity source rest >>? fun () -> ok (source, None) - in - check_batch op >>?= fun (source, revealed_key) -> - Contract.must_be_allocated ctxt (Contract.Implicit source) >>=? fun () -> - match revealed_key with - | None -> Contract.get_manager_key ctxt source - | Some pk -> return pk - -let check_manager_signature ctxt chain_id (op : _ Kind.manager contents_list) - raw_operation = - find_manager_public_key ctxt op >>=? fun public_key -> - Lwt.return (Operation.check_signature public_key chain_id raw_operation) + let*! result = take_fees_rec ctxt contents_list in + Lwt.return (record_trace Error_while_taking_fees result) let rec apply_manager_contents_list_rec : type kind. @@ -2538,12 +2299,12 @@ let rec apply_manager_contents_list_rec : Script_ir_translator.unparsing_mode -> payload_producer:public_key_hash -> Chain_id.t -> - kind Kind.manager prechecked_contents_list -> + kind Kind.manager fees_updated_contents_list -> (success_or_failure * kind Kind.manager contents_result_list) Lwt.t = - fun ctxt mode ~payload_producer chain_id prechecked_contents_list -> + fun ctxt mode ~payload_producer chain_id fees_updated_contents_list -> let level = Level.current ctxt in - match[@coq_match_with_default] prechecked_contents_list with - | PrecheckedSingle {contents = Manager_operation _ as op; balance_updates} -> + match[@coq_match_with_default] fees_updated_contents_list with + | FeesUpdatedSingle {contents = Manager_operation _ as op; balance_updates} -> apply_manager_contents ctxt mode chain_id op >|= fun (ctxt_result, operation_result, internal_operation_results) -> let result = @@ -2551,7 +2312,7 @@ let rec apply_manager_contents_list_rec : {balance_updates; operation_result; internal_operation_results} in (ctxt_result, Single_result result) - | PrecheckedCons + | FeesUpdatedCons ({contents = Manager_operation _ as op; balance_updates}, rest) -> ( apply_manager_contents ctxt mode chain_id op >>= function | Failure, operation_result, internal_operation_results -> @@ -2875,19 +2636,36 @@ let validate_consensus_contents (type kind) ctxt chain_id return (ctxt, delegate_pkh, voting_power) let apply_manager_contents_list ctxt mode ~payload_producer chain_id - prechecked_contents_list = + fees_updated_contents_list = apply_manager_contents_list_rec ctxt mode ~payload_producer chain_id - prechecked_contents_list + fees_updated_contents_list >>= fun (ctxt_result, results) -> match ctxt_result with | Failure -> Lwt.return (ctxt (* backtracked *), mark_backtracked results) | Success ctxt -> Lazy_storage.cleanup_temporaries ctxt >|= fun ctxt -> (ctxt, results) +let apply_manager_operation ctxt mode ~payload_producer chain_id ~mempool_mode + op_validated_stamp contents_list = + let open Lwt_result_syntax in + let ctxt = if mempool_mode then Gas.reset_block_gas ctxt else ctxt in + let* ctxt, fees_updated_contents_list = + take_fees ctxt op_validated_stamp contents_list + in + let*! ctxt, contents_result_list = + apply_manager_contents_list + ctxt + mode + ~payload_producer + chain_id + fees_updated_contents_list + in + return (ctxt, contents_result_list) + let check_denunciation_age ctxt kind given_level = let max_slashing_period = Constants.max_slashing_period ctxt in let current_cycle = (Level.current ctxt).cycle in @@ -3062,7 +2840,7 @@ let validate_grand_parent_endorsement ctxt chain_id }) ) let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode - ~payload_producer (operation : kind operation) + ~payload_producer op_validated_stamp (operation : kind operation) (contents_list : kind contents_list) : (context * kind contents_result_list) tzresult Lwt.t = let mempool_mode = @@ -3207,37 +2985,27 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode | Single (Failing_noop _) -> (* Failing_noop _ always fails *) fail Failing_noop_error - | Single (Manager_operation _) as op -> - (* Use the initial context, the contract may be deleted by the - fee transfer in [precheck_manager_contents] *) - find_manager_public_key ctxt op >>=? fun public_key -> - precheck_manager_contents_list ctxt op ~mempool_mode - >>=? fun (ctxt, prechecked_contents_list) -> - Operation.check_signature public_key chain_id operation >>?= fun () -> - apply_manager_contents_list + | Single (Manager_operation _) -> + apply_manager_operation ctxt mode ~payload_producer chain_id - prechecked_contents_list - >|= ok - | Cons (Manager_operation _, _) as op -> - (* Use the initial context, the contract may be deleted by the - fee transfer in [precheck_manager_contents] *) - find_manager_public_key ctxt op >>=? fun public_key -> - precheck_manager_contents_list ctxt op ~mempool_mode - >>=? fun (ctxt, prechecked_contents_list) -> - Operation.check_signature public_key chain_id operation >>?= fun () -> - apply_manager_contents_list + ~mempool_mode + op_validated_stamp + contents_list + | Cons (Manager_operation _, _) -> + apply_manager_operation ctxt mode ~payload_producer chain_id - prechecked_contents_list - >|= ok + ~mempool_mode + op_validated_stamp + contents_list let apply_operation ctxt chain_id (apply_mode : apply_mode) mode - ~payload_producer hash operation = + ~payload_producer op_validated_stamp hash operation = let ctxt = Origination_nonce.init ctxt hash in let ctxt = record_operation ctxt hash operation in apply_contents_list @@ -3246,6 +3014,7 @@ let apply_operation ctxt chain_id (apply_mode : apply_mode) mode apply_mode mode ~payload_producer + op_validated_stamp operation operation.protocol_data.contents >|=? fun (ctxt, result) -> diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index f01e01ece70ad62ddc3ec455e6925059a4f4e886..eca510e5cb5c93781c494582e7044099ca5b7cb4 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -39,14 +39,10 @@ open Apply_internal_results type error += | Internal_operation_replay of packed_internal_contents - | Gas_quota_exceeded_init_deserialize - | Insufficient_gas_for_manager | Tx_rollup_feature_disabled | Tx_rollup_invalid_transaction_ticket_amount | Sc_rollup_feature_disabled - | Inconsistent_counters - | Incorrect_reveal_position - | Inconsistent_sources + | Empty_transaction of Contract.t val begin_partial_construction : context -> @@ -116,15 +112,35 @@ type apply_mode = grand_parent_round : Round.t; } +(** Apply an operation, i.e. update the given context in accordance + with the operation's semantic (or return an error if the operation + is not applicable). + + The {!type:Validate_operation.stamp} argument enforces that an + operation needs to be validated by {!Validate_operation} before it + can be applied. + + TODO: https://gitlab.com/tezos/tezos/-/issues/2603 + + Currently, {!Validate_operation.validate_operation} does nothing + on non-manager operations. The "validation" of these operations is + instead handled by [apply_operation], which may thus return an + error if the operation is ill-formed. Once [validate_operation] has + been extended to every kind of operation, [apply_operation] should + never return an error. + + See {!apply_manager_operation} for additional information on the + application of manager operations. *) val apply_operation : context -> Chain_id.t -> apply_mode -> Script_ir_translator.unparsing_mode -> payload_producer:public_key_hash -> - Operation_list_hash.elt -> + Validate_operation.stamp -> + Operation_hash.t -> 'a operation -> - (context * 'a operation_metadata, error trace) result Lwt.t + (context * 'a operation_metadata) tzresult Lwt.t type finalize_application_mode = | Finalize_full_construction of { @@ -146,39 +162,58 @@ val finalize_application : migration_balance_updates:Receipt.balance_updates -> (context * Fitness.t * block_metadata, error trace) result Lwt.t -val apply_manager_contents_list : - context -> - Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> - Chain_id.t -> - 'a Kind.manager prechecked_contents_list -> - (context * 'a Kind.manager contents_result_list) Lwt.t - +(** Similar to {!apply_operation}, but a few initial and final steps + are skipped. This function is called in [lib_plugin/RPC.ml]. *) val apply_contents_list : context -> Chain_id.t -> apply_mode -> Script_ir_translator.unparsing_mode -> payload_producer:public_key_hash -> + Validate_operation.stamp -> 'kind operation -> 'kind contents_list -> (context * 'kind contents_result_list) tzresult Lwt.t -(** [precheck_manager_contents_list validation_state contents_list] - Returns an updated context, and a list of prechecked contents - containing balance updates for fees related to each manager - operation in [contents_list] - - If [mempool_mode], the function checks whether the total gas limit - of this batch of operation is below the [gas_limit] of a block and - fails with a permanent error when above. Otherwise, the gas limit - of the batch is removed from the one of the block (when possible) - before moving on. *) -val precheck_manager_contents_list : +(** Update the context to reflect the application of a manager + operation. + + This function first updates the context to: + + - take the fees; + + - increment the account's counter; + + - decrease of the available block gas by operation's [gas_limit]. + + These updates are mandatory. In particular, taking the fees is + critically important. That's why [apply_manager_operation] takes a + [Validate_operation.stamp] argument, so that it may only be called + after having validated the operation by calling + {!Validate_operation}. Indeed, this module is responsible for + ensuring that the operation is solvable, i.e. that fees can be + taken, i.e. that the first stage of [apply_manager_operation] + cannot fail. If this stage fails nevertheless, the function returns + an error. + + The second stage of this function consists in applying all the + other effects, in accordance with the semantic of the operation's + kind. + + An error may happen during this second phase: in that case, the + function returns the context obtained at the end of the first + stage, and a [contents_result_list] that contains the error. This + means that the operation has no other effects than those described + above during the first phase. *) +val apply_manager_operation : context -> - 'kind Kind.manager contents_list -> + Script_ir_translator.unparsing_mode -> + payload_producer:public_key_hash -> + Chain_id.t -> mempool_mode:bool -> - (context * 'kind Kind.manager prechecked_contents_list) tzresult Lwt.t + Validate_operation.stamp -> + 'a Kind.manager contents_list -> + (context * 'a Kind.manager contents_result_list) tzresult Lwt.t (** [value_of_key ctxt k] builds a value identified by key [k] so that it can be put into the cache. *) @@ -192,25 +227,3 @@ val are_endorsements_required : (** Check if a block's endorsing power is at least the minim required. *) val check_minimum_endorsements : endorsing_power:int -> minimum:int -> unit tzresult - -(** [check_manager_signature validation_state op raw_operation] - The function starts by retrieving the public key hash [pkh] of the manager - operation. In case the operation is batched, the function also checks that - the sources are all the same. - Once the [pkh] is retrieved, the function looks for its associated public - key. For that, the manager operation is inspected to check if it contains - a public key revelation. If not, the public key is searched in the context. - - @return [Error Invalid_signature] if the signature check fails - @return [Error Unrevealed_manager_key] if the manager has not yet been - revealed - @return [Error Missing_manager_contract] if the key is not found in the - context - @return [Error Inconsistent_sources] if the operations in a batch are not - from the same manager *) -val check_manager_signature : - context -> - Chain_id.t -> - 'a Kind.manager contents_list -> - 'b operation -> - (unit, error trace) result Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index f69f34ec0868cd62b6eab68435a6a27d78202789..bb7fc7e85ca87e733572cacd90050bb594610b18 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -2681,17 +2681,3 @@ let block_metadata_encoding = flag. This should be replaced by a required field once the feature flag will be activated. *) (varopt "dal_slot_availability" Dal.Endorsement.encoding))) - -type 'kind prechecked_contents = { - contents : 'kind contents; - balance_updates : Receipt.balance_updates; -} - -type _ prechecked_contents_list = - | PrecheckedSingle : - 'kind prechecked_contents - -> 'kind prechecked_contents_list - | PrecheckedCons : - 'kind Kind.manager prechecked_contents - * 'rest Kind.manager prechecked_contents_list - -> ('kind * 'rest) Kind.manager prechecked_contents_list diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index 77f6256352bd09da25be4be5f997640762ed57e4..c3d168803da107c4471a0674ed8424c44fc4d84f 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -323,17 +323,3 @@ type block_metadata = { } val block_metadata_encoding : block_metadata Data_encoding.encoding - -type 'kind prechecked_contents = { - contents : 'kind contents; - balance_updates : Receipt.balance_updates; -} - -type _ prechecked_contents_list = - | PrecheckedSingle : - 'kind prechecked_contents - -> 'kind prechecked_contents_list - | PrecheckedCons : - 'kind Kind.manager prechecked_contents - * 'rest Kind.manager prechecked_contents_list - -> ('kind * 'rest) Kind.manager prechecked_contents_list diff --git a/src/proto_alpha/lib_protocol/contract_storage.ml b/src/proto_alpha/lib_protocol/contract_storage.ml index 3317ffc12b9a30bb1f22c4261e4edad335c12d20..b685d00aa15653b604b00d4ef66f750f08577acb 100644 --- a/src/proto_alpha/lib_protocol/contract_storage.ml +++ b/src/proto_alpha/lib_protocol/contract_storage.ml @@ -542,6 +542,15 @@ let get_balance_carbonated c contract = >>?= fun c -> get_balance c contract >>=? fun balance -> return (c, balance) +let check_allocated_and_get_balance c pkh = + let open Lwt_result_syntax in + let* balance_opt = + Storage.Contract.Spendable_balance.find c (Contract_repr.Implicit pkh) + in + match balance_opt with + | None -> Error_monad.fail (Empty_implicit_contract pkh) + | Some balance -> return balance + let update_script_storage c contract storage lazy_storage_diff = let storage = Script_repr.lazy_expr storage in update_script_lazy_storage c lazy_storage_diff @@ -553,27 +562,38 @@ let update_script_storage c contract storage lazy_storage_diff = in Storage.Contract.Used_storage_space.update c contract new_size +let spend_from_balance contract balance amount = + record_trace + (Balance_too_low (contract, balance, amount)) + Tez_repr.(balance -? amount) + +let check_emptiable c contract = + let open Lwt_result_syntax in + match contract with + | Contract_repr.Originated _ -> return_unit + | Implicit pkh -> ( + let* delegate = Contract_delegate_storage.find c contract in + match delegate with + | Some pkh' -> + if Signature.Public_key_hash.equal pkh pkh' then return_unit + else + (* Delegated implicit accounts cannot be emptied *) + Lwt.return (error (Empty_implicit_delegated_contract pkh)) + | None -> return_unit) + let spend_only_call_from_token c contract amount = - Storage.Contract.Spendable_balance.find c contract >>=? fun balance -> + let open Lwt_result_syntax in + let* balance = Storage.Contract.Spendable_balance.find c contract in let balance = Option.value balance ~default:Tez_repr.zero in - match Tez_repr.(balance -? amount) with - | Error _ -> fail (Balance_too_low (contract, balance, amount)) - | Ok new_balance -> ( - Storage.Contract.Spendable_balance.update c contract new_balance - >>=? fun c -> - Stake_storage.remove_contract_stake c contract amount >>=? fun c -> - if Tez_repr.(new_balance > Tez_repr.zero) then return c - else - match contract with - | Originated _ -> return c - | Implicit pkh -> ( - Contract_delegate_storage.find c contract >>=? function - | Some pkh' -> - if Signature.Public_key_hash.equal pkh pkh' then return c - else - (* Delegated implicit accounts cannot be emptied *) - fail (Empty_implicit_delegated_contract pkh) - | None -> return c)) + let*? new_balance = spend_from_balance contract balance amount in + let* c = Storage.Contract.Spendable_balance.update c contract new_balance in + let* c = Stake_storage.remove_contract_stake c contract amount in + let+ () = + when_ + Tez_repr.(new_balance <= Tez_repr.zero) + (fun () -> check_emptiable c contract) + in + c (* [Tez_repr.(amount <> zero)] is a precondition of this function. It ensures that no entry associating a null balance to an implicit contract exists in the map @@ -681,32 +701,55 @@ let has_frozen_bonds ctxt contract = let fold_on_bond_ids ctxt contract = Storage.Contract.fold_bond_ids (ctxt, contract) +(** Indicate whether the given implicit contract should avoid deletion + when it is emptied. *) +let should_keep_empty_implicit_contract ctxt contract = + let open Lwt_result_syntax in + let* has_frozen_bonds = has_frozen_bonds ctxt contract in + if has_frozen_bonds then return_true + else + (* full balance of contract is zero. *) + Contract_delegate_storage.find ctxt contract >>=? function + | Some _ -> + (* Here, we know that the contract delegates to itself. + Indeed, it does not delegate to a different one, because + the balance of such contracts cannot be zero (see + {!spend_only_call_from_token}), hence the stake of such + contracts cannot be zero either. *) + return_true + | None -> + (* Delete empty implicit contract. *) + return_false + let ensure_deallocated_if_empty ctxt contract = + let open Lwt_result_syntax in match contract with | Contract_repr.Originated _ -> return ctxt (* Never delete originated contracts *) | Implicit _ -> ( - Storage.Contract.Spendable_balance.find ctxt contract - >>=? fun balance_opt -> + let* balance_opt = + Storage.Contract.Spendable_balance.find ctxt contract + in match balance_opt with | None -> (* Nothing to do, contract is not allocated. *) return ctxt - | Some balance -> ( + | Some balance -> if Tez_repr.(balance <> zero) then return ctxt else - has_frozen_bonds ctxt contract >>=? fun has_frozen_bonds -> - if has_frozen_bonds then return ctxt - else - (* full balance of contract is zero. *) - Contract_delegate_storage.find ctxt contract >>=? function - | Some _ -> - (* Here, we know that the contract delegates to itself. - Indeed, it does not delegate to a different one, because - the balance of such contracts cannot be zero (see - [spend_only_call_from_token]), hence the stake of such - contracts cannot be zero either. *) - return ctxt - | None -> - (* Delete empty implicit contract. *) - delete ctxt contract)) + let* keep_contract = + should_keep_empty_implicit_contract ctxt contract + in + if keep_contract then return ctxt else delete ctxt contract) + +let simulate_spending ctxt ~balance ~amount source = + let open Lwt_result_syntax in + let contract = Contract_repr.Implicit source in + let*? new_balance = spend_from_balance contract balance amount in + let* still_allocated = + if Tez_repr.(new_balance > zero) then return_true + else + let* () = check_emptiable ctxt contract in + should_keep_empty_implicit_contract ctxt contract + in + return (new_balance, still_allocated) diff --git a/src/proto_alpha/lib_protocol/contract_storage.mli b/src/proto_alpha/lib_protocol/contract_storage.mli index 829cbf715474a8822ff61e3175c371b6d749f0c0..37c7232a9bee12287ac01ca965fd215ed1b8b1cb 100644 --- a/src/proto_alpha/lib_protocol/contract_storage.mli +++ b/src/proto_alpha/lib_protocol/contract_storage.mli @@ -87,6 +87,17 @@ val get_balance_carbonated : Contract_repr.t -> (Raw_context.t * Tez_repr.t) tzresult Lwt.t +(** Return the balance of spendable tez owned by the Implicit contract + of the given [public_key_hash]. + + @return [Error Empty_implicit_contract] if the contract is not + allocated in {!Storage.Contract.Spendable_balance}. + + This function is a fusion of {!must_be_allocated} and + {!get_balance} for Implicit contracts exclusively. *) +val check_allocated_and_get_balance : + Raw_context.t -> Signature.public_key_hash -> Tez_repr.t tzresult Lwt.t + val get_counter : Raw_context.t -> Signature.Public_key_hash.t -> Z.t tzresult Lwt.t @@ -275,3 +286,32 @@ val fold_on_bond_ids : full balance is zero, and it does not delegate. *) val ensure_deallocated_if_empty : Raw_context.t -> Contract_repr.t -> Raw_context.t tzresult Lwt.t + +(** [simulate_spending ctxt ~balance ~amount source] removes [amount] + from [balance] as if it were the balance of the implicit contract + associated with [source]. It returns the resulting [new_balance], + and a boolean [still_allocated] that indicates whether this + contract would still exist. + + [still_allocated] is always [true] when [new_balance] is + positive. When [new_balance] is zero, it depends on the contract's + delegated status and frozen bonds (cf {!spend_only_call_from_token} + and {!ensure_deallocated_if_empty}). + + Note that this function does not retrieve the actual balance of + the contract, nor does it update or delete it. Indeed, its purpose + is to simulate the spending of fees when validating operations, + without actually spending them. + + @return [Error Balance_too_low] if [balance] is smaller than + [amount]. + + @return [Error Empty_implicit_delegated_contract] if [new_balance] + would be zero and the contract has a delegate that is not the + contract's own manager. *) +val simulate_spending : + Raw_context.t -> + balance:Tez_repr.t -> + amount:Tez_repr.t -> + Signature.public_key_hash -> + (Tez_repr.t * bool) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index ecad17786a316e8544cee4ebe177697f34494629..d59b58f04d031589509965603ff79ff5b236f0ff 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -223,6 +223,7 @@ Dal_apply Baking Amendment + Validate_operation Apply Services_registration Constants_services @@ -448,6 +449,7 @@ dal_apply.ml dal_apply.mli baking.ml baking.mli amendment.ml amendment.mli + validate_operation.ml validate_operation.mli apply.ml apply.mli services_registration.ml services_registration.mli constants_services.ml constants_services.mli @@ -659,6 +661,7 @@ dal_apply.ml dal_apply.mli baking.ml baking.mli amendment.ml amendment.mli + validate_operation.ml validate_operation.mli apply.ml apply.mli services_registration.ml services_registration.mli constants_services.ml constants_services.mli @@ -879,6 +882,7 @@ dal_apply.ml dal_apply.mli baking.ml baking.mli amendment.ml amendment.mli + validate_operation.ml validate_operation.mli apply.ml apply.mli services_registration.ml services_registration.mli constants_services.ml constants_services.mli @@ -1103,6 +1107,7 @@ dal_apply.ml dal_apply.mli baking.ml baking.mli amendment.ml amendment.mli + validate_operation.ml validate_operation.mli apply.ml apply.mli services_registration.ml services_registration.mli constants_services.ml constants_services.mli diff --git a/src/proto_alpha/lib_protocol/gas_limit_repr.ml b/src/proto_alpha/lib_protocol/gas_limit_repr.ml index 48a52e7224b219b4bb4002871753cfc128dab31b..b865772010ef14e211619f0988620e77c65652ef 100644 --- a/src/proto_alpha/lib_protocol/gas_limit_repr.ml +++ b/src/proto_alpha/lib_protocol/gas_limit_repr.ml @@ -213,3 +213,22 @@ let ( *@ ) x y = S.mul x y let alloc_mbytes_cost n = alloc_cost (S.mul_safe_of_int_exn 12) +@ alloc_bytes_cost n + +type error += Gas_limit_too_high (* `Permanent *) + +let () = + let open Data_encoding in + register_error_kind + `Permanent + ~id:"gas_limit_too_high" + ~title:"Gas limit out of protocol hard bounds" + ~description:"A transaction tried to exceed the hard limit on gas" + empty + (function Gas_limit_too_high -> Some () | _ -> None) + (fun () -> Gas_limit_too_high) + +let check_gas_limit ~(hard_gas_limit_per_operation : Arith.integral) + ~(gas_limit : Arith.integral) = + error_unless + Arith.(gas_limit <= hard_gas_limit_per_operation && gas_limit >= zero) + Gas_limit_too_high diff --git a/src/proto_alpha/lib_protocol/gas_limit_repr.mli b/src/proto_alpha/lib_protocol/gas_limit_repr.mli index 23e3fffe8f36130f6e8d36765d9284847b9896df..834836551f282383c13e9e6d69d2c876e377bfb9 100644 --- a/src/proto_alpha/lib_protocol/gas_limit_repr.mli +++ b/src/proto_alpha/lib_protocol/gas_limit_repr.mli @@ -114,3 +114,15 @@ val ( *@ ) : _ Saturation_repr.t -> cost -> cost (** Add two costs together. *) val ( +@ ) : cost -> cost -> cost + +(** Ill-formed [gas_limit]: see {!check_gas_limit}. *) +type error += Gas_limit_too_high (* `Permanent *) + +(** Check that [gas_limit] is well-formed, i.e. it is at most the + given [hard_gas_limit_per_operation], and it is nonnegative. + + @return [Error Gas_limit_too_high] otherwise. *) +val check_gas_limit : + hard_gas_limit_per_operation:Arith.integral -> + gas_limit:'a Arith.t -> + unit tzresult diff --git a/src/proto_alpha/lib_protocol/main.ml b/src/proto_alpha/lib_protocol/main.ml index 022ebeb62a766567f09a136f444309da9796365d..5652db49d3d0e67776ed196abcaf179d645f857f 100644 --- a/src/proto_alpha/lib_protocol/main.ml +++ b/src/proto_alpha/lib_protocol/main.ml @@ -135,6 +135,8 @@ type validation_state = { liquidity_baking_toggle_ema : Alpha_context.Liquidity_baking.Toggle_EMA.t; implicit_operations_results : Apply_results.packed_successful_manager_operation_result list; + validate_operation_info : Validate_operation.validate_operation_info; + validate_operation_state : Validate_operation.validate_operation_state; } let begin_partial_application ~chain_id ~ancestor_context:ctxt @@ -177,6 +179,9 @@ let begin_partial_application ~chain_id ~ancestor_context:ctxt block_producer; } in + let validate_operation_info, validate_operation_state = + Validate_operation.init_info_and_state ctxt Block chain_id + in return { mode; @@ -189,6 +194,8 @@ let begin_partial_application ~chain_id ~ancestor_context:ctxt Apply_results.pack_migration_operation_results migration_operation_results @ liquidity_baking_operations_results; + validate_operation_info; + validate_operation_state; } (* During applications the valid consensus operations are: @@ -233,6 +240,9 @@ let begin_application ~chain_id ~predecessor_context:ctxt ~predecessor_timestamp block_producer; } in + let validate_operation_info, validate_operation_state = + Validate_operation.init_info_and_state ctxt Block chain_id + in return { mode; @@ -245,6 +255,8 @@ let begin_application ~chain_id ~predecessor_context:ctxt ~predecessor_timestamp Apply_results.pack_migration_operation_results migration_operation_results @ liquidity_baking_operations_results; + validate_operation_info; + validate_operation_state; } let begin_construction ~chain_id ~predecessor_context:ctxt @@ -276,11 +288,16 @@ let begin_construction ~chain_id ~predecessor_context:ctxt predecessor_round; } in + let validate_operation_info, validate_operation_state = + Validate_operation.init_info_and_state ctxt Mempool chain_id + in return ( mode, ctxt, liquidity_baking_operations_results, - liquidity_baking_toggle_ema ) + liquidity_baking_toggle_ema, + validate_operation_info, + validate_operation_state ) | Some proto_header -> Alpha_context.Fitness.round_from_raw predecessor_fitness >>?= fun predecessor_round -> @@ -323,15 +340,22 @@ let begin_construction ~chain_id ~predecessor_context:ctxt predecessor_level; } in + let validate_operation_info, validate_operation_state = + Validate_operation.init_info_and_state ctxt Block chain_id + in return ( mode, ctxt, liquidity_baking_operations_results, - liquidity_baking_toggle_ema )) + liquidity_baking_toggle_ema, + validate_operation_info, + validate_operation_state )) >|=? fun ( mode, ctxt, liquidity_baking_operations_results, - liquidity_baking_toggle_ema ) -> + liquidity_baking_toggle_ema, + validate_operation_info, + validate_operation_state ) -> { mode; chain_id; @@ -342,23 +366,34 @@ let begin_construction ~chain_id ~predecessor_context:ctxt implicit_operations_results = Apply_results.pack_migration_operation_results migration_operation_results @ liquidity_baking_operations_results; + validate_operation_info; + validate_operation_state; } let apply_operation_with_mode mode ctxt chain_id data op_count operation ~payload_producer = let {shell; protocol_data = Operation_data protocol_data} = operation in let operation : _ Alpha_context.operation = {shell; protocol_data} in + let oph = Alpha_context.Operation.hash operation in + Validate_operation.validate_operation + data.validate_operation_info + data.validate_operation_state + oph + operation + >>=? fun (validate_operation_state, op_validated_stamp) -> Apply.apply_operation ctxt chain_id mode Optimized ~payload_producer - (Alpha_context.Operation.hash operation) + op_validated_stamp + oph operation >|=? fun (ctxt, result) -> let op_count = op_count + 1 in - ({data with ctxt; op_count}, Operation_metadata result) + ( {data with ctxt; op_count; validate_operation_state}, + Operation_metadata result ) let apply_operation ({mode; chain_id; ctxt; op_count; _} as data) (operation : Alpha_context.packed_operation) = @@ -803,18 +838,12 @@ let value_of_key ~chain_id:_ ~predecessor_context:ctxt ~predecessor_timestamp Alpha_context.prepare ctxt ~level ~predecessor_timestamp ~timestamp >>=? fun (ctxt, _, _) -> return (Apply.value_of_key ctxt) -let check_manager_signature {chain_id; ctxt; _} op raw_op = - Apply.check_manager_signature ctxt chain_id op raw_op - -let precheck_manager {ctxt; _} op = - (* We do not account for the gas limit of the batch in the block - since this function does not return a context, but we check that - this limit is within bounds (and fail otherwise with a - permanenent error). *) - Apply.precheck_manager_contents_list ctxt op ~mempool_mode:true - >|=? fun (_ : - Alpha_context.t - * 'kind Alpha_context.Kind.manager - Apply_results.prechecked_contents_list) -> () +let precheck_manager {validate_operation_info; validate_operation_state; _} + contents_list should_check_signature = + Validate_operation.TMP_for_plugin.precheck_manager + validate_operation_info + validate_operation_state + contents_list + should_check_signature (* Vanity nonce: TBD *) diff --git a/src/proto_alpha/lib_protocol/main.mli b/src/proto_alpha/lib_protocol/main.mli index a6e20a489ec40d0612844ce7698175cbad37b9d2..bcaedc6771a25218773a41c2c04ccae5f7814391 100644 --- a/src/proto_alpha/lib_protocol/main.mli +++ b/src/proto_alpha/lib_protocol/main.mli @@ -107,6 +107,8 @@ type validation_state = { liquidity_baking_toggle_ema : Alpha_context.Liquidity_baking.Toggle_EMA.t; implicit_operations_results : Apply_results.packed_successful_manager_operation_result list; + validate_operation_info : Validate_operation.validate_operation_info; + validate_operation_state : Validate_operation.validate_operation_state; } type operation_data = Alpha_context.packed_protocol_data @@ -116,37 +118,13 @@ type operation = Alpha_context.packed_operation = { protocol_data : operation_data; } -(** [check_manager_signature validation_state op raw_operation] - The function starts by retrieving the public key hash [pkh] of the manager - operation. In case the operation is batched, the function also checks that - the sources are all the same. - Once the [pkh] is retrieved, the function looks for its associated public - key. For that, the manager operation is inspected to check if it contains - a public key revelation. If not, the public key is searched in the context. - - @return [Error Invalid_signature] if the signature check fails - @return [Error Unrevealed_manager_key] if the manager has not yet been - revealed - @return [Error Missing_manager_contract] if the key is not found in the - context - @return [Error Inconsistent_sources] if the operations in a batch are not - from the same manager *) -val check_manager_signature : - validation_state -> - 'b Alpha_context.Kind.manager Alpha_context.contents_list -> - 'a Alpha_context.operation -> - unit tzresult Lwt.t - -(** [precheck_manager validation_state op] returns [()] if the manager operation - [op] is solveable, returns an error otherwise. An operation is solveable if - it is well-formed and can pay the fees to be included in a block with either - a success or a failure status. - This function uses [Apply.precheck_manager_contents_list] but discard the - context and balance update *) +(** See {!Validate_operation.precheck_manager}. *) val precheck_manager : validation_state -> 'a Alpha_context.Kind.manager Alpha_context.contents_list -> - unit tzresult Lwt.t + 'a Alpha_context.Kind.manager + Validate_operation.TMP_for_plugin.should_check_signature -> + Validate_operation.stamp tzresult Lwt.t include Updater.PROTOCOL diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index 0d9589ffa4b6190d2fbf6e73aa5a87f13e056733..fac00958b95c54ad3b73c7ae0c4c6c8522ae71fd 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -496,19 +496,6 @@ let get_origination_nonce ctxt = let unset_origination_nonce ctxt = update_origination_nonce ctxt None -type error += Gas_limit_too_high (* `Permanent *) - -let () = - let open Data_encoding in - register_error_kind - `Permanent - ~id:"gas_limit_too_high" - ~title:"Gas limit out of protocol hard bounds" - ~description:"A transaction tried to exceed the hard limit on gas" - empty - (function Gas_limit_too_high -> Some () | _ -> None) - (fun () -> Gas_limit_too_high) - let gas_level ctxt = let open Gas_limit_repr in if unlimited_operation_gas ctxt then Unaccounted @@ -516,19 +503,14 @@ let gas_level ctxt = let block_gas_level = remaining_block_gas -let check_gas_limit_is_valid ctxt (remaining : 'a Gas_limit_repr.Arith.t) = - if - Gas_limit_repr.Arith.( - remaining > (constants ctxt).hard_gas_limit_per_operation - || remaining < zero) - then error Gas_limit_too_high - else Result.return_unit - -let consume_gas_limit_in_block ctxt (limit : 'a Gas_limit_repr.Arith.t) = +let consume_gas_limit_in_block ctxt gas_limit = let open Gas_limit_repr in - check_gas_limit_is_valid ctxt limit >>? fun () -> + check_gas_limit + ~hard_gas_limit_per_operation:(constants ctxt).hard_gas_limit_per_operation + ~gas_limit + >>? fun () -> let block_gas = block_gas_level ctxt in - let limit = Arith.fp limit in + let limit = Arith.fp gas_limit in if Arith.(limit > block_gas) then error Block_quota_exceeded else let level = Arith.sub (block_gas_level ctxt) limit in diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index 3b060e86f584d886a2f0f538f1b8dc21a4a08dc4..5d78ec250a2d297ccbd2a5479e996ad90404f503 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -129,10 +129,17 @@ val spend_collected_fees_only_call_from_token : t -> Tez_repr.t -> t tzresult producer's account at finalize_application *) val get_collected_fees : t -> Tez_repr.t -type error += Gas_limit_too_high (* `Permanent *) +(** [consume_gas_limit_in_block ctxt gas_limit] checks that + [gas_limit] is well-formed (i.e. it does not exceed the hard gas + limit per operation as defined in [ctxt], and it is positive), then + consumes [gas_limit] in the current block gas level of [ctxt]. -val check_gas_limit_is_valid : t -> 'a Gas_limit_repr.Arith.t -> unit tzresult + @return [Error Gas_limit_repr.Gas_limit_too_high] if [gas_limit] + is greater than the allowed limit for operation gas level or + negative. + @return [Error Block_quota_exceeded] if not enough gas remains in + the block. *) val consume_gas_limit_in_block : t -> 'a Gas_limit_repr.Arith.t -> t tzresult val set_gas_limit : t -> 'a Gas_limit_repr.Arith.t -> t diff --git a/src/proto_alpha/lib_protocol/test/helpers/transfers.ml b/src/proto_alpha/lib_protocol/test/helpers/transfers.ml index 66dbee89ca42362bbbd8ad3283c1dd14a238c20c..7d92c8c3ad305b5415497edfcbcb53fcb6b6bb2e 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/transfers.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/transfers.ml @@ -62,6 +62,8 @@ let n_transactions n b ?fee source dest amount = List.fold_left_es (fun b _ -> transfer_and_check_balances ~loc:__LOC__ b ?fee source dest amount - >|=? fun (b, _) -> b) + >>=? fun (i, _) -> + Incremental.finalize_block i >>=? fun b -> + Incremental.begin_construction b) b (1 -- n) diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml index 7e2edd398781727dfc7e299a33d5ba1034c1fb9f..880961fcc6cdfc4ec1a5ab890de1895a855c8428 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml @@ -128,32 +128,33 @@ let bootstrap_delegate_cannot_be_removed ~fee () = (** Contracts not registered as delegate can change their delegation. *) let delegate_can_be_changed_from_unregistered_contract ~fee () = - Context.init2 () >>=? fun (b, (bootstrap0, bootstrap1)) -> + Context.init2 ~consensus_threshold:0 () + >>=? fun (b, (bootstrap0, bootstrap1)) -> let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let unregistered = Contract.Implicit unregistered_pkh in - Incremental.begin_construction b >>=? fun i -> - Context.Contract.manager (I i) bootstrap0 >>=? fun manager0 -> - Context.Contract.manager (I i) bootstrap1 >>=? fun manager1 -> + Context.Contract.manager (B b) bootstrap0 >>=? fun manager0 -> + Context.Contract.manager (B b) bootstrap1 >>=? fun manager1 -> let credit = of_int 10 in - Op.transaction ~fee:Tez.zero (I i) bootstrap0 unregistered credit + Op.transaction ~fee:Tez.zero (B b) bootstrap0 unregistered credit >>=? fun credit_contract -> - Context.Contract.balance (I i) bootstrap0 >>=? fun balance -> - Incremental.add_operation i credit_contract >>=? fun i -> + Context.Contract.balance (B b) bootstrap0 >>=? fun balance -> + Block.bake b ~operation:credit_contract >>=? fun b -> (* delegate to bootstrap0 *) Op.delegation ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) unregistered (Some manager0.pkh) >>=? fun set_delegate -> - Incremental.add_operation i set_delegate >>=? fun i -> - Context.Contract.delegate (I i) unregistered >>=? fun delegate -> + Block.bake b ~operation:set_delegate >>=? fun b -> + Context.Contract.delegate (B b) unregistered >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate manager0.pkh >>=? fun () -> (* change delegation to bootstrap1 *) - Op.delegation ~force_reveal:true ~fee (I i) unregistered (Some manager1.pkh) + Op.delegation ~force_reveal:true ~fee (B b) unregistered (Some manager1.pkh) >>=? fun change_delegate -> + Incremental.begin_construction b >>=? fun i -> if fee > balance then expect_too_low_balance_error i change_delegate else Incremental.add_operation i change_delegate >>=? fun i -> @@ -166,36 +167,36 @@ let delegate_can_be_changed_from_unregistered_contract ~fee () = (** Contracts not registered as delegate can delete their delegation. *) let delegate_can_be_removed_from_unregistered_contract ~fee () = - Context.init1 () >>=? fun (b, bootstrap) -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let unregistered = Contract.Implicit unregistered_pkh in - Incremental.begin_construction b >>=? fun i -> - Context.Contract.manager (I i) bootstrap >>=? fun manager -> + Context.Contract.manager (B b) bootstrap >>=? fun manager -> let credit = of_int 10 in Op.transaction ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) bootstrap unregistered credit >>=? fun credit_contract -> - Context.Contract.balance (I i) bootstrap >>=? fun balance -> - Incremental.add_operation i credit_contract >>=? fun i -> + Context.Contract.balance (B b) bootstrap >>=? fun balance -> + Block.bake b ~operation:credit_contract >>=? fun b -> (* delegate to bootstrap *) Op.delegation ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) unregistered (Some manager.pkh) >>=? fun set_delegate -> - Incremental.add_operation i set_delegate >>=? fun i -> - Context.Contract.delegate (I i) unregistered >>=? fun delegate -> + Block.bake b ~operation:set_delegate >>=? fun b -> + Context.Contract.delegate (B b) unregistered >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate manager.pkh >>=? fun () -> (* remove delegation *) - Op.delegation ~fee (I i) unregistered None >>=? fun delete_delegate -> + Op.delegation ~fee (B b) unregistered None >>=? fun delete_delegate -> + Incremental.begin_construction b >>=? fun i -> if fee > balance then expect_too_low_balance_error i delete_delegate else Incremental.add_operation i delete_delegate >>=? fun i -> @@ -511,8 +512,7 @@ let test_unregistered_delegate_key_init_origination ~fee () = [Balance_too_low] is raised. Otherwise, fees are still debited. The implicit contract has no delegate. *) let test_unregistered_delegate_key_init_delegation ~fee () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let impl_contract = Contract.Implicit unregistered_pkh in @@ -523,21 +523,22 @@ let test_unregistered_delegate_key_init_delegation ~fee () = Op.transaction ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) bootstrap impl_contract credit >>=? fun credit_contract -> - Incremental.add_operation i credit_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract credit >>=? fun _ -> + Block.bake b ~operation:credit_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract credit >>=? fun _ -> (* try to delegate *) Op.delegation ~force_reveal:true ~fee - (I i) + (B b) impl_contract (Some unregistered_delegate_pkh) >>=? fun delegate_op -> + Incremental.begin_construction b >>=? fun i -> if fee > credit then expect_too_low_balance_error i delegate_op else (* fee has been debited; no delegate *) @@ -559,8 +560,7 @@ let test_unregistered_delegate_key_init_delegation ~fee () = raised. Otherwise, fees are not debited and the implicit contract delegate remains unchanged. *) let test_unregistered_delegate_key_switch_delegation ~fee () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let bootstrap_pkh = Context.Contract.pkh bootstrap in let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in @@ -572,27 +572,28 @@ let test_unregistered_delegate_key_switch_delegation ~fee () = Op.transaction ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) bootstrap impl_contract credit >>=? fun init_credit -> - Incremental.add_operation i init_credit >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract credit >>=? fun _ -> + Block.bake b ~operation:init_credit >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract credit >>=? fun _ -> (* set and check the initial delegate *) Op.delegation ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) impl_contract (Some bootstrap_pkh) >>=? fun delegate_op -> - Incremental.add_operation i delegate_op >>=? fun i -> - Context.Contract.delegate (I i) bootstrap >>=? fun delegate_pkh -> + Block.bake b ~operation:delegate_op >>=? fun b -> + Context.Contract.delegate (B b) bootstrap >>=? fun delegate_pkh -> Assert.equal_pkh ~loc:__LOC__ bootstrap_pkh delegate_pkh >>=? fun () -> (* try to delegate *) - Op.delegation ~fee (I i) impl_contract (Some unregistered_delegate_pkh) + Op.delegation ~fee (B b) impl_contract (Some unregistered_delegate_pkh) >>=? fun delegate_op -> + Incremental.begin_construction b >>=? fun i -> if fee > credit then expect_too_low_balance_error i delegate_op else (* fee has been debited; no delegate *) @@ -610,25 +611,25 @@ let test_unregistered_delegate_key_switch_delegation ~fee () = (** Same as [unregistered_delegate_key_init_origination] and credits [amount], no self-delegation. *) let test_unregistered_delegate_key_init_origination_credit ~fee ~amount () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let impl_contract = Contract.Implicit unregistered_pkh in (* credit + check balance *) - Op.transaction ~fee:Tez.zero (I i) bootstrap impl_contract amount + Op.transaction ~fee:Tez.zero (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake b ~operation:create_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* origination with delegate argument *) - Context.Contract.balance (I i) bootstrap >>=? fun balance -> + Context.Contract.balance (B b) bootstrap >>=? fun balance -> Op.contract_origination ~fee ~delegate:unregistered_pkh - (I i) + (B b) bootstrap ~script:Op.dummy_script >>=? fun (op, orig_contract) -> + Incremental.begin_construction b >>=? fun i -> if fee > balance then expect_too_low_balance_error i op else (* origination not done, fee taken *) @@ -647,8 +648,7 @@ let test_unregistered_delegate_key_init_origination_credit ~fee ~amount () = (** Same as [unregistered_delegate_key_init_delegation] and credits the amount [amount] of the implicit contract. *) let test_unregistered_delegate_key_init_delegation_credit ~fee ~amount () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let impl_contract = Contract.Implicit unregistered_pkh in @@ -658,28 +658,29 @@ let test_unregistered_delegate_key_init_delegation_credit ~fee ~amount () = Op.transaction ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* initial credit for the delegated contract *) let credit = of_int 10 in credit +? amount >>?= fun balance -> - Op.transaction ~fee:Tez.zero (I i) bootstrap impl_contract credit + Op.transaction ~fee:Tez.zero (B b) bootstrap impl_contract credit >>=? fun init_credit -> - Incremental.add_operation i init_credit >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract balance >>=? fun _ -> + Block.bake ~operation:init_credit b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract balance >>=? fun _ -> (* try to delegate *) Op.delegation ~force_reveal:true ~fee - (I i) + (B b) impl_contract (Some unregistered_delegate_pkh) >>=? fun delegate_op -> + Incremental.begin_construction b >>=? fun i -> if fee > credit then expect_too_low_balance_error i delegate_op else (* fee has been taken, no delegate for contract *) @@ -698,8 +699,7 @@ let test_unregistered_delegate_key_init_delegation_credit ~fee ~amount () = (** Same as in [unregistered_delegate_key_switch_delegation] and credits the amount [amount] to the implicit contract. *) let test_unregistered_delegate_key_switch_delegation_credit ~fee ~amount () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let bootstrap_pkh = Context.Contract.pkh bootstrap in let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in @@ -710,34 +710,35 @@ let test_unregistered_delegate_key_switch_delegation_credit ~fee ~amount () = Op.transaction ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* initial credit for the delegated contract *) let credit = of_int 10 in credit +? amount >>?= fun balance -> - Op.transaction ~fee:Tez.zero (I i) bootstrap impl_contract credit + Op.transaction ~fee:Tez.zero (B b) bootstrap impl_contract credit >>=? fun init_credit -> - Incremental.add_operation i init_credit >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract balance >>=? fun _ -> + Block.bake ~operation:init_credit b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract balance >>=? fun _ -> (* set and check the initial delegate *) Op.delegation ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) impl_contract (Some bootstrap_pkh) >>=? fun delegate_op -> - Incremental.add_operation i delegate_op >>=? fun i -> - Context.Contract.delegate (I i) bootstrap >>=? fun delegate_pkh -> + Block.bake ~operation:delegate_op b >>=? fun b -> + Context.Contract.delegate (B b) bootstrap >>=? fun delegate_pkh -> Assert.equal_pkh ~loc:__LOC__ bootstrap_pkh delegate_pkh >>=? fun () -> (* switch delegate through delegation *) - Op.delegation ~fee (I i) impl_contract (Some unregistered_delegate_pkh) + Op.delegation ~fee (B b) impl_contract (Some unregistered_delegate_pkh) >>=? fun delegate_op -> + Incremental.begin_construction b >>=? fun i -> if fee > credit then expect_too_low_balance_error i delegate_op else (* fee has been taken, delegate for contract has not changed *) @@ -756,30 +757,30 @@ let test_unregistered_delegate_key_switch_delegation_credit ~fee ~amount () = no self-delegation. *) let test_unregistered_delegate_key_init_origination_credit_debit ~fee ~amount () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let impl_contract = Contract.Implicit unregistered_pkh in (* credit + check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract amount + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake b ~operation:create_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* debit + check balance *) - Op.transaction ~force_reveal:true (I i) impl_contract bootstrap amount + Op.transaction ~force_reveal:true (B b) impl_contract bootstrap amount >>=? fun debit_contract -> - Incremental.add_operation i debit_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.zero >>=? fun _ -> + Block.bake b ~operation:debit_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.zero >>=? fun _ -> (* origination with delegate argument *) - Context.Contract.balance (I i) bootstrap >>=? fun balance -> + Context.Contract.balance (B b) bootstrap >>=? fun balance -> Op.contract_origination ~fee ~delegate:unregistered_pkh - (I i) + (B b) bootstrap ~script:Op.dummy_script >>=? fun (op, orig_contract) -> + Incremental.begin_construction b >>=? fun i -> if fee > balance then expect_too_low_balance_error i op else (* fee taken, origination not processed *) @@ -799,8 +800,7 @@ let test_unregistered_delegate_key_init_origination_credit_debit ~fee ~amount () then debits the amount [amount] to the implicit contract. *) let test_unregistered_delegate_key_init_delegation_credit_debit ~amount ~fee () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let impl_contract = Contract.Implicit unregistered_pkh in @@ -810,38 +810,39 @@ let test_unregistered_delegate_key_init_delegation_credit_debit ~amount ~fee () Op.transaction ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake b ~operation:create_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* debit + check balance *) Op.transaction ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) impl_contract bootstrap amount >>=? fun debit_contract -> - Incremental.add_operation i debit_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.zero >>=? fun _ -> + Block.bake b ~operation:debit_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.zero >>=? fun _ -> (* initial credit for the delegated contract *) let credit = of_int 10 in - Op.transaction ~fee:Tez.zero (I i) bootstrap impl_contract credit + Op.transaction ~fee:Tez.zero (B b) bootstrap impl_contract credit >>=? fun credit_contract -> - Incremental.add_operation i credit_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract credit >>=? fun _ -> + Block.bake b ~operation:credit_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract credit >>=? fun _ -> (* try to delegate *) Op.delegation ~force_reveal:true ~fee - (I i) + (B b) impl_contract (Some unregistered_delegate_pkh) >>=? fun delegate_op -> + Incremental.begin_construction b >>=? fun i -> if fee > credit then expect_too_low_balance_error i delegate_op else (* fee has been taken, no delegate for contract *) @@ -861,8 +862,7 @@ let test_unregistered_delegate_key_init_delegation_credit_debit ~amount ~fee () credits then debits the amount [amount] to the implicit contract. *) let test_unregistered_delegate_key_switch_delegation_credit_debit ~fee ~amount () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let bootstrap_pkh = Context.Contract.pkh bootstrap in let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in @@ -873,38 +873,39 @@ let test_unregistered_delegate_key_switch_delegation_credit_debit ~fee ~amount Op.transaction ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake b ~operation:create_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* debit + check balance *) - Op.transaction ~force_reveal:true (I i) impl_contract bootstrap amount + Op.transaction ~force_reveal:true (B b) impl_contract bootstrap amount >>=? fun debit_contract -> - Incremental.add_operation i debit_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.zero >>=? fun _ -> + Block.bake b ~operation:debit_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.zero >>=? fun _ -> (* delegation - initial credit for the delegated contract *) let credit = of_int 10 in - Op.transaction ~fee:Tez.zero (I i) bootstrap impl_contract credit + Op.transaction ~fee:Tez.zero (B b) bootstrap impl_contract credit >>=? fun credit_contract -> - Incremental.add_operation i credit_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract credit >>=? fun _ -> + Block.bake b ~operation:credit_contract >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract credit >>=? fun _ -> (* set and check the initial delegate *) Op.delegation ~force_reveal:true ~fee:Tez.zero - (I i) + (B b) impl_contract (Some bootstrap_pkh) >>=? fun delegate_op -> - Incremental.add_operation i delegate_op >>=? fun i -> - Context.Contract.delegate (I i) bootstrap >>=? fun delegate_pkh -> + Block.bake b ~operation:delegate_op >>=? fun b -> + Context.Contract.delegate (B b) bootstrap >>=? fun delegate_pkh -> Assert.equal_pkh ~loc:__LOC__ bootstrap_pkh delegate_pkh >>=? fun () -> (* switch delegate through delegation *) - Op.delegation (I i) ~fee impl_contract (Some unregistered_delegate_pkh) + Op.delegation (B b) ~fee impl_contract (Some unregistered_delegate_pkh) >>=? fun delegate_op -> + Incremental.begin_construction b >>=? fun i -> if fee > credit then expect_too_low_balance_error i delegate_op else (* fee has been taken, delegate for contract has not changed *) @@ -941,24 +942,24 @@ let test_failed_self_delegation_no_transaction () = is emptied). Self-delegation fails. *) let test_failed_self_delegation_emptied_implicit_contract amount () = (* create an implicit contract *) - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let account = Account.new_account () in let unregistered_pkh = Account.(account.pkh) in let impl_contract = Contract.Implicit unregistered_pkh in (* credit implicit contract and check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract amount + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* empty implicit contract and check balance *) - Op.transaction ~force_reveal:true (I i) impl_contract bootstrap amount + Op.transaction ~force_reveal:true (B b) impl_contract bootstrap amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.zero >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.zero >>=? fun _ -> (* self delegation fails *) - Op.delegation (I i) impl_contract (Some unregistered_pkh) + Op.delegation (B b) impl_contract (Some unregistered_pkh) >>=? fun self_delegation -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i self_delegation >>= fun err -> Assert.proto_error_with_info ~loc:__LOC__ err "Empty implicit contract" @@ -966,28 +967,28 @@ let test_failed_self_delegation_emptied_implicit_contract amount () = tz, then it is delegated. The operation of debit of [amount] tz should fail as the contract is already delegated. *) let test_emptying_delegated_implicit_contract_fails amount () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> - Context.Contract.manager (I i) bootstrap >>=? fun bootstrap_manager -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> + Context.Contract.manager (B b) bootstrap >>=? fun bootstrap_manager -> let account = Account.new_account () in let unregistered_pkh = Account.(account.pkh) in let impl_contract = Contract.Implicit unregistered_pkh in (* credit unregistered implicit contract and check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract amount + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* delegate the contract to the bootstrap *) Op.delegation ~force_reveal:true - (I i) + (B b) impl_contract (Some bootstrap_manager.pkh) >>=? fun delegation -> - Incremental.add_operation i delegation >>=? fun i -> + Block.bake ~operation:delegation b >>=? fun b -> (* empty implicit contract and expect error since the contract is delegated *) - Op.transaction (I i) impl_contract bootstrap amount + Op.transaction (B b) impl_contract bootstrap amount >>=? fun create_contract -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i create_contract >>= fun err -> Assert.proto_error_with_info ~loc:__LOC__ @@ -1005,41 +1006,40 @@ let test_emptying_delegated_implicit_contract_fails amount () = self-delegated. *) let test_valid_delegate_registration_init_delegation_credit amount () = (* create an implicit contract *) - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let delegate_account = Account.new_account () in let delegate_pkh = Account.(delegate_account.pkh) in let impl_contract = Contract.Implicit delegate_pkh in (* credit > 0ꜩ + check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract amount + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* self delegation + verification *) - Op.delegation ~force_reveal:true (I i) impl_contract (Some delegate_pkh) + Op.delegation ~force_reveal:true (B b) impl_contract (Some delegate_pkh) >>=? fun self_delegation -> - Incremental.add_operation i self_delegation >>=? fun i -> - Context.Contract.delegate (I i) impl_contract >>=? fun delegate -> + Block.bake ~operation:self_delegation b >>=? fun b -> + Context.Contract.delegate (B b) impl_contract >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate delegate_pkh >>=? fun _ -> (* create an implicit contract with no delegate *) let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let delegator = Contract.Implicit unregistered_pkh in - Op.transaction ~fee:Tez.zero (I i) bootstrap delegator Tez.one + Op.transaction ~fee:Tez.zero (B b) bootstrap delegator Tez.one >>=? fun credit_contract -> - Incremental.add_operation i credit_contract >>=? fun i -> + Block.bake ~operation:credit_contract b >>=? fun b -> (* check no delegate for delegator contract *) - Context.Contract.delegate (I i) delegator >>= fun err -> + Context.Contract.delegate (B b) delegator >>= fun err -> Assert.error ~loc:__LOC__ err (function | RPC_context.Not_found _ -> true | _ -> false) >>=? fun _ -> (* delegation to the newly registered key *) - Op.delegation ~force_reveal:true (I i) delegator (Some delegate_account.pkh) + Op.delegation ~force_reveal:true (B b) delegator (Some delegate_account.pkh) >>=? fun delegation -> - Incremental.add_operation i delegation >>=? fun i -> + Block.bake ~operation:delegation b >>=? fun b -> (* check delegation *) - Context.Contract.delegate (I i) delegator >>=? fun delegator_delegate -> + Context.Contract.delegate (B b) delegator >>=? fun delegator_delegate -> Assert.equal_pkh ~loc:__LOC__ delegator_delegate delegate_pkh (** Create an implicit contract, credits with [amount] @@ -1048,92 +1048,90 @@ let test_valid_delegate_registration_init_delegation_credit amount () = contract. *) let test_valid_delegate_registration_switch_delegation_credit amount () = (* create an implicit contract *) - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let delegate_account = Account.new_account () in let delegate_pkh = Account.(delegate_account.pkh) in let impl_contract = Contract.Implicit delegate_pkh in (* credit > 0ꜩ + check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract amount + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* self delegation + verification *) - Op.delegation ~force_reveal:true (I i) impl_contract (Some delegate_pkh) + Op.delegation ~force_reveal:true (B b) impl_contract (Some delegate_pkh) >>=? fun self_delegation -> - Incremental.add_operation i self_delegation >>=? fun i -> - Context.Contract.delegate (I i) impl_contract >>=? fun delegate -> + Block.bake ~operation:self_delegation b >>=? fun b -> + Context.Contract.delegate (B b) impl_contract >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate delegate_pkh >>=? fun _ -> (* create an implicit contract with bootstrap's account as delegate *) let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let delegator = Contract.Implicit unregistered_pkh in - Op.transaction ~fee:Tez.zero (I i) bootstrap delegator Tez.one + Op.transaction ~fee:Tez.zero (B b) bootstrap delegator Tez.one >>=? fun credit_contract -> - Incremental.add_operation i credit_contract >>=? fun i -> - Context.Contract.manager (I i) bootstrap >>=? fun bootstrap_manager -> - Op.delegation ~force_reveal:true (I i) delegator (Some bootstrap_manager.pkh) + Block.bake ~operation:credit_contract b >>=? fun b -> + Context.Contract.manager (B b) bootstrap >>=? fun bootstrap_manager -> + Op.delegation ~force_reveal:true (B b) delegator (Some bootstrap_manager.pkh) >>=? fun delegation -> - Incremental.add_operation i delegation >>=? fun i -> + Block.bake ~operation:delegation b >>=? fun b -> (* test delegate of new contract is bootstrap *) - Context.Contract.delegate (I i) delegator >>=? fun delegator_delegate -> + Context.Contract.delegate (B b) delegator >>=? fun delegator_delegate -> Assert.equal_pkh ~loc:__LOC__ delegator_delegate bootstrap_manager.pkh >>=? fun _ -> (* delegation with newly registered key *) - Op.delegation (I i) delegator (Some delegate_account.pkh) + Op.delegation (B b) delegator (Some delegate_account.pkh) >>=? fun delegation -> - Incremental.add_operation i delegation >>=? fun i -> - Context.Contract.delegate (I i) delegator >>=? fun delegator_delegate -> + Block.bake ~operation:delegation b >>=? fun b -> + Context.Contract.delegate (B b) delegator >>=? fun delegator_delegate -> Assert.equal_pkh ~loc:__LOC__ delegator_delegate delegate_pkh (** Create an implicit contract. *) let test_valid_delegate_registration_init_delegation_credit_debit amount () = (* create an implicit contract *) - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let delegate_account = Account.new_account () in let delegate_pkh = Account.(delegate_account.pkh) in let impl_contract = Contract.Implicit delegate_pkh in (* credit > 0ꜩ+ check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract amount + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* self delegation + verification *) - Op.delegation ~force_reveal:true (I i) impl_contract (Some delegate_pkh) + Op.delegation ~force_reveal:true (B b) impl_contract (Some delegate_pkh) >>=? fun self_delegation -> - Incremental.add_operation i self_delegation >>=? fun i -> - Context.Contract.delegate (I i) impl_contract >>=? fun delegate -> + Block.bake ~operation:self_delegation b >>=? fun b -> + Context.Contract.delegate (B b) impl_contract >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate_pkh delegate >>=? fun _ -> (* empty implicit contracts are usually deleted but they are kept if they were registered as delegates. we empty the contract in order to verify this. *) - Op.transaction (I i) impl_contract bootstrap amount >>=? fun empty_contract -> - Incremental.add_operation i empty_contract >>=? fun i -> + Op.transaction (B b) impl_contract bootstrap amount >>=? fun empty_contract -> + Block.bake ~operation:empty_contract b >>=? fun b -> (* impl_contract is empty *) - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.zero >>=? fun _ -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.zero >>=? fun _ -> (* verify self-delegation after contract is emptied *) - Context.Contract.delegate (I i) impl_contract >>=? fun delegate -> + Context.Contract.delegate (B b) impl_contract >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate_pkh delegate >>=? fun _ -> (* create an implicit contract with no delegate *) let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let delegator = Contract.Implicit unregistered_pkh in - Op.transaction ~fee:Tez.zero (I i) bootstrap delegator Tez.one + Op.transaction ~fee:Tez.zero (B b) bootstrap delegator Tez.one >>=? fun credit_contract -> - Incremental.add_operation i credit_contract >>=? fun i -> + Block.bake ~operation:credit_contract b >>=? fun b -> (* check no delegate for delegator contract *) - Context.Contract.delegate (I i) delegator >>= fun err -> + Context.Contract.delegate (B b) delegator >>= fun err -> Assert.error ~loc:__LOC__ err (function | RPC_context.Not_found _ -> true | _ -> false) >>=? fun _ -> (* delegation to the newly registered key *) - Op.delegation ~force_reveal:true (I i) delegator (Some delegate_account.pkh) + Op.delegation ~force_reveal:true (B b) delegator (Some delegate_account.pkh) >>=? fun delegation -> - Incremental.add_operation i delegation >>=? fun i -> + Block.bake ~operation:delegation b >>=? fun b -> (* check delegation *) - Context.Contract.delegate (I i) delegator >>=? fun delegator_delegate -> + Context.Contract.delegate (B b) delegator >>=? fun delegator_delegate -> Assert.equal_pkh ~loc:__LOC__ delegator_delegate delegate_pkh (** A created implicit contract is credited with [amount] tz, then is @@ -1143,49 +1141,48 @@ let test_valid_delegate_registration_init_delegation_credit_debit amount () = be re-delegated to the latter contract. *) let test_valid_delegate_registration_switch_delegation_credit_debit amount () = (* create an implicit contract *) - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let delegate_account = Account.new_account () in let delegate_pkh = Account.(delegate_account.pkh) in let impl_contract = Contract.Implicit delegate_pkh in (* credit > 0ꜩ + check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract amount + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract amount >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract amount >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract amount >>=? fun _ -> (* self delegation + verification *) - Op.delegation ~force_reveal:true (I i) impl_contract (Some delegate_pkh) + Op.delegation ~force_reveal:true (B b) impl_contract (Some delegate_pkh) >>=? fun self_delegation -> - Incremental.add_operation i self_delegation >>=? fun i -> - Context.Contract.delegate (I i) impl_contract >>=? fun delegate -> + Block.bake ~operation:self_delegation b >>=? fun b -> + Context.Contract.delegate (B b) impl_contract >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate_pkh delegate >>=? fun _ -> (* empty implicit contracts are usually deleted but they are kept if they were registered as delegates. we empty the contract in order to verify this. *) - Op.transaction (I i) impl_contract bootstrap amount >>=? fun empty_contract -> - Incremental.add_operation i empty_contract >>=? fun i -> + Op.transaction (B b) impl_contract bootstrap amount >>=? fun empty_contract -> + Block.bake ~operation:empty_contract b >>=? fun b -> (* impl_contract is empty *) - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.zero >>=? fun _ -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.zero >>=? fun _ -> (* create an implicit contract with bootstrap's account as delegate *) let unregistered_account = Account.new_account () in let unregistered_pkh = Account.(unregistered_account.pkh) in let delegator = Contract.Implicit unregistered_pkh in - Op.transaction ~fee:Tez.zero (I i) bootstrap delegator Tez.one + Op.transaction ~fee:Tez.zero (B b) bootstrap delegator Tez.one >>=? fun credit_contract -> - Incremental.add_operation i credit_contract >>=? fun i -> - Context.Contract.manager (I i) bootstrap >>=? fun bootstrap_manager -> - Op.delegation ~force_reveal:true (I i) delegator (Some bootstrap_manager.pkh) + Block.bake ~operation:credit_contract b >>=? fun b -> + Context.Contract.manager (B b) bootstrap >>=? fun bootstrap_manager -> + Op.delegation ~force_reveal:true (B b) delegator (Some bootstrap_manager.pkh) >>=? fun delegation -> - Incremental.add_operation i delegation >>=? fun i -> + Block.bake ~operation:delegation b >>=? fun b -> (* test delegate of new contract is bootstrap *) - Context.Contract.delegate (I i) delegator >>=? fun delegator_delegate -> + Context.Contract.delegate (B b) delegator >>=? fun delegator_delegate -> Assert.equal_pkh ~loc:__LOC__ delegator_delegate bootstrap_manager.pkh >>=? fun _ -> (* delegation with newly registered key *) - Op.delegation ~force_reveal:true (I i) delegator (Some delegate_account.pkh) + Op.delegation ~force_reveal:true (B b) delegator (Some delegate_account.pkh) >>=? fun delegation -> - Incremental.add_operation i delegation >>=? fun i -> - Context.Contract.delegate (I i) delegator >>=? fun delegator_delegate -> + Block.bake ~operation:delegation b >>=? fun b -> + Context.Contract.delegate (B b) delegator >>=? fun delegator_delegate -> Assert.equal_pkh ~loc:__LOC__ delegator_delegate delegate_pkh (* Part C. @@ -1194,94 +1191,94 @@ let test_valid_delegate_registration_switch_delegation_credit_debit amount () = (** Second self-delegation should fail with implicit contract with some credit. *) let test_double_registration () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let account = Account.new_account () in let pkh = Account.(account.pkh) in let impl_contract = Contract.Implicit pkh in (* credit 1μꜩ+ check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract Tez.one_mutez + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract Tez.one_mutez >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.one_mutez >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.one_mutez >>=? fun _ -> (* self-delegation *) - Op.delegation ~force_reveal:true (I i) impl_contract (Some pkh) + Op.delegation ~force_reveal:true (B b) impl_contract (Some pkh) >>=? fun self_delegation -> - Incremental.add_operation i self_delegation >>=? fun i -> + Block.bake ~operation:self_delegation b >>=? fun b -> (* second self-delegation *) - Op.delegation (I i) impl_contract (Some pkh) >>=? fun second_registration -> + Op.delegation (B b) impl_contract (Some pkh) >>=? fun second_registration -> + Incremental.begin_construction b >>=? fun i -> expect_delegate_already_active_error i second_registration (** Second self-delegation should fail with implicit contract emptied after first self-delegation. *) let test_double_registration_when_empty () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let account = Account.new_account () in let pkh = Account.(account.pkh) in let impl_contract = Contract.Implicit pkh in (* credit 1μꜩ+ check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract Tez.one_mutez + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract Tez.one_mutez >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.one_mutez >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.one_mutez >>=? fun _ -> (* self delegation *) - Op.delegation ~force_reveal:true (I i) impl_contract (Some pkh) + Op.delegation ~force_reveal:true (B b) impl_contract (Some pkh) >>=? fun self_delegation -> - Incremental.add_operation i self_delegation >>=? fun i -> + Block.bake ~operation:self_delegation b >>=? fun b -> (* empty the delegate account *) - Op.transaction (I i) impl_contract bootstrap Tez.one_mutez + Op.transaction (B b) impl_contract bootstrap Tez.one_mutez >>=? fun empty_contract -> - Incremental.add_operation i empty_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.zero >>=? fun _ -> + Block.bake ~operation:empty_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.zero >>=? fun _ -> (* second self-delegation *) - Op.delegation (I i) impl_contract (Some pkh) >>=? fun second_registration -> + Op.delegation (B b) impl_contract (Some pkh) >>=? fun second_registration -> + Incremental.begin_construction b >>=? fun i -> expect_delegate_already_active_error i second_registration (** Second self-delegation should fail with implicit contract emptied then credited back after first self-delegation. *) let test_double_registration_when_recredited () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let account = Account.new_account () in let pkh = Account.(account.pkh) in let impl_contract = Contract.Implicit pkh in (* credit 1μꜩ+ check balance *) - Op.transaction ~force_reveal:true (I i) bootstrap impl_contract Tez.one_mutez + Op.transaction ~force_reveal:true (B b) bootstrap impl_contract Tez.one_mutez >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.one_mutez >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.one_mutez >>=? fun _ -> (* self delegation *) - Op.delegation ~force_reveal:true (I i) impl_contract (Some pkh) + Op.delegation ~force_reveal:true (B b) impl_contract (Some pkh) >>=? fun self_delegation -> - Incremental.add_operation i self_delegation >>=? fun i -> + Block.bake ~operation:self_delegation b >>=? fun b -> (* empty the delegate account *) - Op.transaction ~force_reveal:true (I i) impl_contract bootstrap Tez.one_mutez + Op.transaction ~force_reveal:true (B b) impl_contract bootstrap Tez.one_mutez >>=? fun empty_contract -> - Incremental.add_operation i empty_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.zero >>=? fun _ -> + Block.bake ~operation:empty_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.zero >>=? fun _ -> (* credit 1μꜩ+ check balance *) - Op.transaction (I i) bootstrap impl_contract Tez.one_mutez + Op.transaction (B b) bootstrap impl_contract Tez.one_mutez >>=? fun create_contract -> - Incremental.add_operation i create_contract >>=? fun i -> - Assert.balance_is ~loc:__LOC__ (I i) impl_contract Tez.one_mutez >>=? fun _ -> + Block.bake ~operation:create_contract b >>=? fun b -> + Assert.balance_is ~loc:__LOC__ (B b) impl_contract Tez.one_mutez >>=? fun _ -> (* second self-delegation *) - Op.delegation (I i) impl_contract (Some pkh) >>=? fun second_registration -> + Op.delegation (B b) impl_contract (Some pkh) >>=? fun second_registration -> + Incremental.begin_construction b >>=? fun i -> expect_delegate_already_active_error i second_registration (** Self-delegation on unrevealed contract. *) let test_unregistered_and_unrevealed_self_delegate_key_init_delegation ~fee () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let {Account.pkh; _} = Account.new_account () in let {Account.pkh = delegate_pkh; _} = Account.new_account () in let contract = Alpha_context.Contract.Implicit pkh in - Op.transaction ~force_reveal:true (I i) bootstrap contract (of_int 10) - >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.delegation ~force_reveal:true ~fee (I i) contract (Some delegate_pkh) + Op.transaction ~force_reveal:true (B b) bootstrap contract (of_int 10) + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.delegation ~fee ~force_reveal:true (B b) contract (Some delegate_pkh) >>=? fun op -> - Context.Contract.balance (I i) contract >>=? fun balance -> + Context.Contract.balance (B b) contract >>=? fun balance -> + Incremental.begin_construction b >>=? fun i -> if fee > balance then expect_too_low_balance_error i op else (* origination did not proceed; fee has been debited *) @@ -1294,17 +1291,17 @@ let test_unregistered_and_unrevealed_self_delegate_key_init_delegation ~fee () = (** Self-delegation on revealed but not registered contract. *) let test_unregistered_and_revealed_self_delegate_key_init_delegation ~fee () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let {Account.pkh; pk; _} = Account.new_account () in let {Account.pkh = delegate_pkh; _} = Account.new_account () in let contract = Alpha_context.Contract.Implicit pkh in - Op.transaction (I i) bootstrap contract (of_int 10) >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.revelation (I i) pk >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.delegation ~fee (I i) contract (Some delegate_pkh) >>=? fun op -> - Context.Contract.balance (I i) contract >>=? fun balance -> + Op.transaction (B b) bootstrap contract (of_int 10) >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.revelation (B b) pk >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.delegation ~fee (B b) contract (Some delegate_pkh) >>=? fun op -> + Context.Contract.balance (B b) contract >>=? fun balance -> + Incremental.begin_construction b >>=? fun i -> if fee > balance then expect_too_low_balance_error i op else (* origination did not proceed; fee has been debited *) @@ -1317,21 +1314,21 @@ let test_unregistered_and_revealed_self_delegate_key_init_delegation ~fee () = (** Self-delegation emptying a fresh contract. *) let test_self_delegation_emptying_contract () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let {Account.pkh; pk; _} = Account.new_account () in let {Account.pkh = delegate_pkh; _} = Account.new_account () in let contract = Alpha_context.Contract.Implicit pkh in let amount = of_int 10 in - Op.transaction (I i) bootstrap contract amount >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.revelation ~fee:Tez.zero (I i) pk >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.delegation ~fee:amount (I i) contract (Some delegate_pkh) >>=? fun op -> - (Context.Contract.is_manager_key_revealed (I i) contract >>=? function + Op.transaction (B b) bootstrap contract amount >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.revelation ~fee:Tez.zero (B b) pk >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.delegation ~fee:amount (B b) contract (Some delegate_pkh) >>=? fun op -> + (Context.Contract.is_manager_key_revealed (B b) contract >>=? function | false -> failwith "contract should exist" | true -> return_unit) >>=? fun () -> + Incremental.begin_construction b >>=? fun i -> (* The delegation operation should be applied and the fees debited but it is expected to fail in the apply-part. *) Incremental.add_operation ~expect_apply_failure:(fun _ -> return_unit) i op @@ -1342,27 +1339,28 @@ let test_self_delegation_emptying_contract () = (** Self-delegation on revealed and registered contract. *) let test_registered_self_delegate_key_init_delegation () = - Context.init1 () >>=? fun (b, bootstrap) -> - Incremental.begin_construction b >>=? fun i -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap) -> let {Account.pkh; _} = Account.new_account () in let {Account.pkh = delegate_pkh; pk = delegate_pk; _} = Account.new_account () in let contract = Alpha_context.Contract.Implicit pkh in let delegate_contract = Alpha_context.Contract.Implicit delegate_pkh in - Op.transaction ~force_reveal:true (I i) bootstrap contract (of_int 10) - >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.transaction (I i) bootstrap delegate_contract (of_int 1) >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.revelation (I i) delegate_pk >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.delegation (I i) delegate_contract (Some delegate_pkh) >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.delegation ~force_reveal:true (I i) contract (Some delegate_pkh) - >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Context.Contract.delegate (I i) contract >>=? fun delegate -> + Op.transaction ~force_reveal:true (B b) bootstrap contract (of_int 10) + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.transaction (B b) bootstrap delegate_contract (of_int 1) + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.revelation (B b) delegate_pk >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.delegation (B b) delegate_contract (Some delegate_pkh) + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.delegation ~force_reveal:true (B b) contract (Some delegate_pkh) + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Context.Contract.delegate (B b) contract >>=? fun delegate -> Assert.equal_pkh ~loc:__LOC__ delegate delegate_pkh >>=? fun () -> return_unit let tests_delegate_registration = diff --git a/src/proto_alpha/lib_protocol/test/integration/gas/test_gas_levels.ml b/src/proto_alpha/lib_protocol/test/integration/gas/test_gas_levels.ml index b9248c38be8ac0f3c0db228217fcf4411c27a00a..c33c298706d9172eb2ea113f9bf42353877c0bf7 100644 --- a/src/proto_alpha/lib_protocol/test/integration/gas/test_gas_levels.ml +++ b/src/proto_alpha/lib_protocol/test/integration/gas/test_gas_levels.ml @@ -254,17 +254,20 @@ let originate_contract block source script = prepare_origination block source script >>=? fun (operation, dst) -> Block.bake ~operation block >>=? fun block -> return (block, dst) -let init_block to_originate = - Context.init1 ~consensus_threshold:0 () >>=? fun (block, src) -> - (*** originate contracts ***) - let rec full_originate block originated = function - | [] -> return (block, List.rev originated) - | h :: t -> - originate_contract block src h >>=? fun (block, ct) -> - full_originate block (ct :: originated) t - in - full_originate block [] to_originate >>=? fun (block, originated) -> - return (block, src, originated) +let init_block n to_originate = + Context.init_n n ~consensus_threshold:0 () >>=? fun (block, src_list) -> + match src_list with + | [] -> assert false + | src :: _ -> + (*** originate contracts ***) + let rec full_originate block originated = function + | [] -> return (block, List.rev originated) + | h :: t -> + originate_contract block src h >>=? fun (block, ct) -> + full_originate block (ct :: originated) t + in + full_originate block [] to_originate >>=? fun (block, originated) -> + return (block, src_list, originated) let nil_contract = "parameter unit;\n\ @@ -292,24 +295,24 @@ let loop_contract = \ UNIT; NIL operation; PAIR\n\ \ }\n" -let block_with_one_origination contract = - init_block [contract] >>=? fun (block, src, originated) -> - match originated with [dst] -> return (block, src, dst) | _ -> assert false +let block_with_one_origination n contract = + init_block n [contract] >>=? fun (block, srcs, originated) -> + match originated with [dst] -> return (block, srcs, dst) | _ -> assert false -let full_block () = - init_block [nil_contract; fail_contract; loop_contract] - >>=? fun (block, src, originated) -> +let full_block n () = + init_block n [nil_contract; fail_contract; loop_contract] + >>=? fun (block, src_list, originated) -> let dst_nil, dst_fail, dst_loop = match originated with [c1; c2; c3] -> (c1, c2, c3) | _ -> assert false in - return (block, src, dst_nil, dst_fail, dst_loop) + return (block, src_list, dst_nil, dst_fail, dst_loop) (** Combine a list of operations into an operation list. Also returns the sum of their gas limits.*) -let combine_operations_with_gas ?counter block src list_dst = - let rec make_op_list full_gas op_list = function - | [] -> return (full_gas, List.rev op_list) - | (dst, gas_limit) :: t -> +let combine_operations_with_gas block list_dst = + let rec make_op_list src full_gas op_list = function + | [] -> return (src, full_gas, List.rev op_list) + | (src, dst, gas_limit) :: t -> Op.transaction ~gas_limit:(Custom_gas gas_limit) (B block) @@ -318,32 +321,29 @@ let combine_operations_with_gas ?counter block src list_dst = Alpha_context.Tez.zero >>=? fun op -> make_op_list + (Some src) (Alpha_context.Gas.Arith.add full_gas gas_limit) (op :: op_list) t in - make_op_list Alpha_context.Gas.Arith.zero [] list_dst - >>=? fun (full_gas, op_list) -> - Op.combine_operations ?counter ~source:src (B block) op_list - >>=? fun operation -> return (operation, full_gas) + make_op_list None Alpha_context.Gas.Arith.zero [] list_dst + >>=? fun (src, full_gas, op_list) -> + match src with + | None -> assert false + | Some source -> + Op.batch_operations ~recompute_counters:true ~source (B block) op_list + >>=? fun operation -> return (operation, full_gas) (** Applies [combine_operations_with_gas] to lists in a list, then bake a block with this list of operations. Also returns the sum of all gas limits *) -let bake_operations_with_gas ?counter block src list_list_dst = - let counter = Option.value ~default:Z.zero counter in - let rec make_list full_gas op_list counter = function +let bake_operations_with_gas block list_list_dst = + let rec make_list full_gas op_list = function | [] -> return (full_gas, List.rev op_list) | list_dst :: t -> - let n = Z.of_int (List.length list_dst) in - combine_operations_with_gas ~counter block src list_dst - >>=? fun (op, gas) -> - make_list - (Alpha_context.Gas.Arith.add full_gas gas) - (op :: op_list) - (Z.add counter n) - t + combine_operations_with_gas block list_dst >>=? fun (op, gas) -> + make_list (Alpha_context.Gas.Arith.add full_gas gas) (op :: op_list) t in - make_list Alpha_context.Gas.Arith.zero [] counter list_list_dst + make_list Alpha_context.Gas.Arith.zero [] list_list_dst >>=? fun (gas_limit_total, operations) -> bake_with_gas ~operations block >>=? fun (block, consumed_gas) -> return (block, consumed_gas, gas_limit_total) @@ -354,9 +354,18 @@ let basic_gas_sampler () = + Random.int 900) let generic_test_block_one_origination contract gas_sampler structure = - block_with_one_origination contract >>=? fun (block, src, dst) -> - let lld = List.map (List.map (fun _ -> (dst, gas_sampler ()))) structure in - bake_operations_with_gas ~counter:Z.one block src lld + let sources_number = List.length structure in + block_with_one_origination sources_number contract + >>=? fun (block, src_list, dst) -> + let lld = + List.mapi + (fun i t -> + match List.nth src_list i with + | None -> assert false + | Some src -> (List.map (fun _ -> (src, dst, gas_sampler ()))) t) + structure + in + bake_operations_with_gas block lld >>=? fun (_block, consumed_gas, gas_limit_total) -> check_consumed_gas consumed_gas gas_limit_total @@ -377,33 +386,43 @@ let make_batch_test_block_one_origination name contract gas_sampler = (** Tests the consumption of all gas in a block, should pass *) let test_consume_exactly_all_block_gas () = - block_with_one_origination nil_contract >>=? fun (block, src, dst) -> + let number_of_ops = 5 in + block_with_one_origination number_of_ops nil_contract + >>=? fun (block, src_list, dst) -> (* assumptions: hard gas limit per operation = 1040000 hard gas limit per block = 5200000 *) let lld = List.map - (fun _ -> [(dst, Alpha_context.Gas.Arith.integral_of_int_exn 1040000)]) - [1; 1; 1; 1; 1] + (fun src -> + [(src, dst, Alpha_context.Gas.Arith.integral_of_int_exn 1040000)]) + src_list in - bake_operations_with_gas ~counter:Z.one block src lld >>=? fun _ -> return () + bake_operations_with_gas block lld >>=? fun _ -> return () (** Tests the consumption of more than the block gas level with many single operations, should fail *) let test_malformed_block_max_limit_reached () = - block_with_one_origination nil_contract >>=? fun (block, src, dst) -> + let number_of_ops = 6 in + block_with_one_origination number_of_ops nil_contract + >>=? fun (block, src_list, dst) -> (* assumptions: hard gas limit per operation = 1040000 hard gas limit per block = 5200000 *) let lld = - [(dst, Alpha_context.Gas.Arith.integral_of_int_exn 1)] - :: List.map - (fun _ -> [(dst, Alpha_context.Gas.Arith.integral_of_int_exn 1040000)]) - [1; 1; 1; 1; 1] + List.mapi + (fun i src -> + [ + ( src, + dst, + Alpha_context.Gas.Arith.integral_of_int_exn + (if i = number_of_ops - 1 then 1 else 1040000) ); + ]) + src_list in - bake_operations_with_gas ~counter:Z.one block src lld >>= function + bake_operations_with_gas block lld >>= function | Error _ -> return_unit | Ok _ -> fail @@ -414,20 +433,25 @@ let test_malformed_block_max_limit_reached () = (** Tests the consumption of more than the block gas level with one big operation list, should fail *) let test_malformed_block_max_limit_reached' () = - block_with_one_origination nil_contract >>=? fun (block, src, dst) -> + let number_of_ops = 6 in + block_with_one_origination number_of_ops nil_contract + >>=? fun (block, src_list, dst) -> (* assumptions: hard gas limit per operation = 1040000 hard gas limit per block = 5200000 *) let lld = - [ - (dst, Alpha_context.Gas.Arith.integral_of_int_exn 1) - :: List.map - (fun _ -> (dst, Alpha_context.Gas.Arith.integral_of_int_exn 1040000)) - [1; 1; 1; 1; 1]; - ] + List.mapi + (fun i src -> + [ + ( src, + dst, + Alpha_context.Gas.Arith.integral_of_int_exn + (if i = number_of_ops - 1 then 1 else 1040000) ); + ]) + src_list in - bake_operations_with_gas ~counter:Z.one block src lld >>= function + bake_operations_with_gas block lld >>= function | Error _ -> return_unit | Ok _ -> fail @@ -436,10 +460,17 @@ let test_malformed_block_max_limit_reached' () = gas limit per block") let test_block_mixed_operations () = - full_block () >>=? fun (block, src, dst_nil, dst_fail, dst_loop) -> + let number_of_ops = 4 in + full_block number_of_ops () + >>=? fun (block, src_list, dst_nil, dst_fail, dst_loop) -> let l = [[dst_nil]; [dst_nil; dst_fail; dst_nil]; [dst_loop]; [dst_nil]] in - let lld = List.map (List.map (fun x -> (x, basic_gas_sampler ()))) l in - bake_operations_with_gas ~counter:(Z.of_int 3) block src lld + List.map2 + ~when_different_lengths:[] + (fun src l -> (List.map (fun x -> (src, x, basic_gas_sampler ()))) l) + src_list + l + >>?= fun lld -> + bake_operations_with_gas block lld >>=? fun (_block, consumed_gas, gas_limit_total) -> check_consumed_gas consumed_gas gas_limit_total diff --git a/src/proto_alpha/lib_protocol/test/integration/michelson/test_annotations.ml b/src/proto_alpha/lib_protocol/test/integration/michelson/test_annotations.ml index a8cc970c615e81abb5ebe481e773419285bb3c82..27183a1be94d58558a835c3023adc89f27bf4cc2 100644 --- a/src/proto_alpha/lib_protocol/test/integration/michelson/test_annotations.ml +++ b/src/proto_alpha/lib_protocol/test/integration/michelson/test_annotations.ml @@ -121,6 +121,8 @@ let test_internal_origination () = ~parameters:lazy_none Tez.zero >>=? fun operation -> + Incremental.finalize_block inc >>=? fun b -> + Incremental.begin_construction b >>=? fun inc -> Incremental.add_operation inc operation >>=? fun inc -> get_address_from_storage inc factory >>=? fun addr -> assert_stored_script_equal inc addr contract_with_annotations diff --git a/src/proto_alpha/lib_protocol/test/integration/michelson/test_global_constants_storage.ml b/src/proto_alpha/lib_protocol/test/integration/michelson/test_global_constants_storage.ml index 1ad510b6c707f4bd9c89754e6c9cba0e9206f56e..d08c70285a5149453e77164fc48d1182c2ecb098 100644 --- a/src/proto_alpha/lib_protocol/test/integration/michelson/test_global_constants_storage.ml +++ b/src/proto_alpha/lib_protocol/test/integration/michelson/test_global_constants_storage.ml @@ -101,22 +101,22 @@ let test_registration_of_bad_expr_fails () = (* You cannot register the same expression twice. *) let test_no_double_register () = - Context.init1 () >>=? fun (b, alice) -> - Incremental.begin_construction b >>=? fun b -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, alice) -> let expr = Expr.from_string "Pair 1 2" in Op.register_global_constant - (I b) + (B b) ~source:alice ~value:(Script_repr.lazy_expr expr) - >>=? fun op -> - Incremental.add_operation b op >>=? fun b -> + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> (* Register the same expression again *) Op.register_global_constant - (I b) + (B b) ~source:alice ~value:(Script_repr.lazy_expr expr) >>=? fun op -> - Incremental.add_operation b op + Incremental.begin_construction b >>=? fun i -> + Incremental.add_operation i op >>= assert_proto_error_id __LOC__ "Expression_already_registered" let tests = diff --git a/src/proto_alpha/lib_protocol/test/integration/michelson/test_sapling.ml b/src/proto_alpha/lib_protocol/test/integration/michelson/test_sapling.ml index 8607ad55f501614416fda999ba67b1c9375ae882..df7bf0f1908f31e6da0f5e10de6d33598155b913 100644 --- a/src/proto_alpha/lib_protocol/test/integration/michelson/test_sapling.ml +++ b/src/proto_alpha/lib_protocol/test/integration/michelson/test_sapling.ml @@ -918,9 +918,8 @@ module Interpreter_tests = struct (Contract.Originated dst) amount_tez ~parameters:parameters_1 - >>=? fun operation -> + >>=? fun operation1 -> Incremental.begin_construction block_start >>=? fun incr -> - Incremental.add_operation incr operation >>=? fun incr -> (* We need to manually get the counter here *) let ctx = Incremental.alpha_ctxt incr in let pkh = Context.Contract.pkh src in @@ -934,6 +933,12 @@ module Interpreter_tests = struct (Contract.Originated dst) Tez.zero ~parameters:parameters_2 + >>=? fun operation2 -> + Op.batch_operations + ~recompute_counters:true + ~source:src + (I incr) + [operation1; operation2] >>=? fun operation -> Incremental.add_operation incr operation >>=? fun incr -> Incremental.finalize_block incr >>=? fun block_2 -> diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_combined_operations.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_combined_operations.ml index 71baae6762925c9837172e59b99180efd6b0470d..976ede73da8373a26ffa6dbcbe83866569ef5331 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_combined_operations.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_combined_operations.ml @@ -263,43 +263,43 @@ let test_failing_operation_in_the_middle_with_fees () = return_unit let test_wrong_signature_in_the_middle () = - Context.init2 () >>=? fun (blk, (c1, c2)) -> + Context.init2 ~consensus_threshold:0 () >>=? fun (blk, (c1, c2)) -> Op.transaction ~gas_limit ~fee:Tez.one (B blk) c1 c2 Tez.one >>=? fun op1 -> Op.transaction ~gas_limit ~fee:Tez.one (B blk) c2 c1 Tez.one >>=? fun op2 -> - Incremental.begin_construction blk >>=? fun inc -> (* Make legit transfers, performing reveals *) - Incremental.add_operation inc op1 >>=? fun inc -> - Incremental.add_operation inc op2 >>=? fun inc -> + Block.bake ~operations:[op1; op2] blk >>=? fun b -> (* Make c2 reach counter 5 *) - Op.transaction ~gas_limit ~fee:Tez.one (I inc) c2 c1 Tez.one >>=? fun op -> - Incremental.add_operation inc op >>=? fun inc -> - Op.transaction ~gas_limit ~fee:Tez.one (I inc) c2 c1 Tez.one >>=? fun op -> - Incremental.add_operation inc op >>=? fun inc -> - Op.transaction ~gas_limit ~fee:Tez.one (I inc) c2 c1 Tez.one >>=? fun op -> - Incremental.add_operation inc op >>=? fun inc -> + Op.transaction ~gas_limit ~fee:Tez.one (B b) c2 c1 Tez.one + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.transaction ~gas_limit ~fee:Tez.one (B b) c2 c1 Tez.one + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.transaction ~gas_limit ~fee:Tez.one (B b) c2 c1 Tez.one + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> (* Cook transactions for actual test *) - Op.transaction ~gas_limit ~fee:Tez.one (I inc) c1 c2 Tez.one >>=? fun op1 -> - Op.transaction ~gas_limit ~fee:Tez.one (I inc) c1 c2 Tez.one >>=? fun op2 -> - Op.transaction ~gas_limit ~fee:Tez.one (I inc) c1 c2 Tez.one >>=? fun op3 -> - Op.transaction ~gas_limit ~fee:Tez.one (I inc) c2 c1 Tez.one + Op.transaction ~gas_limit ~fee:Tez.one (B b) c1 c2 Tez.one >>=? fun op1 -> + Op.transaction ~gas_limit ~fee:Tez.one (B b) c1 c2 Tez.one >>=? fun op2 -> + Op.transaction ~gas_limit ~fee:Tez.one (B b) c1 c2 Tez.one >>=? fun op3 -> + Op.transaction ~gas_limit ~fee:Tez.one (B b) c2 c1 Tez.one >>=? fun spurious_operation -> let operations = [op1; op2; op3] in - Op.combine_operations ~spurious_operation ~source:c1 (I inc) operations + + Op.combine_operations ~spurious_operation ~source:c1 (B b) operations >>=? fun operation -> let expect_failure = function - | Environment.Ecoproto_error err :: _ -> + | Environment.Ecoproto_error + (Validate_operation.Manager.Inconsistent_sources as err) + :: _ -> Assert.test_error_encodings err ; - let error_info = - Error_monad.find_info_of_error (Environment.wrap_tzerror err) - in - if error_info.title = "Inconsistent sources in operation pack" then - return_unit - else failwith "unexpected error" + return_unit | _ -> failwith "Packed operation has invalid source in the middle : operation \ expected to fail." in + Incremental.begin_construction b >>=? fun inc -> Incremental.add_operation ~expect_failure inc operation >>=? fun _inc -> return_unit @@ -307,7 +307,9 @@ let expect_inconsistent_counters list = if List.exists (function - | Environment.Ecoproto_error Apply.Inconsistent_counters -> true + | Environment.Ecoproto_error + Validate_operation.Manager.Inconsistent_counters -> + true | _ -> false) list then return_unit @@ -322,17 +324,15 @@ let test_inconsistent_counters () = Context.init2 () >>=? fun (blk, (c1, c2)) -> Op.transaction ~gas_limit ~fee:Tez.one (B blk) c1 c2 Tez.one >>=? fun op1 -> Op.transaction ~gas_limit ~fee:Tez.one (B blk) c2 c1 Tez.one >>=? fun op2 -> - Incremental.begin_construction blk >>=? fun inc -> (* Make legit transfers, performing reveals *) - Incremental.add_operation inc op1 >>=? fun inc -> - Incremental.add_operation inc op2 >>=? fun inc -> + Block.bake ~operations:[op1; op2] blk >>=? fun b -> (* Now, counter c1 = counter c2 = 1, Op.transaction builds with counter + 1 *) - Op.transaction ~gas_limit ~fee:Tez.one (B blk) c1 c2 ~counter:Z.one Tez.one + Op.transaction ~gas_limit ~fee:Tez.one (B b) c1 c2 ~counter:Z.one Tez.one >>=? fun op1 -> Op.transaction ~gas_limit ~fee:Tez.one - (B blk) + (B b) c1 c2 ~counter:(Z.of_int 2) @@ -341,7 +341,7 @@ let test_inconsistent_counters () = Op.transaction ~gas_limit ~fee:Tez.one - (B blk) + (B b) c1 c2 ~counter:(Z.of_int 2) @@ -350,7 +350,7 @@ let test_inconsistent_counters () = Op.transaction ~gas_limit ~fee:Tez.one - (B blk) + (B b) c1 c2 ~counter:(Z.of_int 3) @@ -359,21 +359,22 @@ let test_inconsistent_counters () = Op.transaction ~gas_limit ~fee:Tez.one - (B blk) + (B b) c1 c2 ~counter:(Z.of_int 4) Tez.one >>=? fun op4 -> (* Canari: Check counters are ok *) - Op.batch_operations ~source:c1 (I inc) [op1; op2; op3; op4] >>=? fun op -> + Op.batch_operations ~source:c1 (B b) [op1; op2; op3; op4] >>=? fun op -> + Incremental.begin_construction b >>=? fun inc -> Incremental.add_operation inc op >>=? fun _ -> (* Gap in counter in the following op *) - Op.batch_operations ~source:c1 (I inc) [op1; op2; op4] >>=? fun op -> + Op.batch_operations ~source:c1 (B b) [op1; op2; op4] >>=? fun op -> Incremental.add_operation ~expect_failure:expect_inconsistent_counters inc op >>=? fun _ -> (* Same counter used twice in the following op *) - Op.batch_operations ~source:c1 (I inc) [op1; op2; op2'] >>=? fun op -> + Op.batch_operations ~source:c1 (B b) [op1; op2; op2'] >>=? fun op -> Incremental.add_operation ~expect_failure:expect_inconsistent_counters inc op >>=? fun _ -> return_unit diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_origination.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_origination.ml index 2385c1aff1bf95c782e4e41e18b1e005e066e6ae..2452c049409c8361e0b88087366284fde0132cbd 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_origination.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_origination.ml @@ -150,24 +150,26 @@ let test_pay_fee () = (** Create an originate contract where the contract does not have enough tez to pay for the fee. *) let test_not_tez_in_contract_to_pay_fee () = - Context.init2 () >>=? fun (b, (contract_1, contract_2)) -> - Incremental.begin_construction b >>=? fun inc -> + Context.init2 ~consensus_threshold:0 () + >>=? fun (b, (contract_1, contract_2)) -> (* transfer everything but one tez from 1 to 2 and check balance of 1 *) - Context.Contract.balance (I inc) contract_1 >>=? fun balance -> + Context.Contract.balance (B b) contract_1 >>=? fun balance -> balance -? Tez.one >>?= fun amount -> - Op.transaction (I inc) contract_1 contract_2 amount >>=? fun operation -> - Incremental.add_operation inc operation >>=? fun inc -> - Assert.balance_was_debited ~loc:__LOC__ (I inc) contract_1 balance amount + Op.transaction (B b) contract_1 contract_2 amount >>=? fun operation -> + let pkh1 = Context.Contract.pkh contract_1 in + Block.bake ~policy:(Excluding [pkh1]) ~operation b >>=? fun b -> + Assert.balance_was_debited ~loc:__LOC__ (B b) contract_1 balance amount >>=? fun _ -> (* use this source contract to create an originate contract where it requires to pay a fee and add an amount of credit into this new contract *) Op.contract_origination - (I inc) + (B b) ~fee:ten_tez ~credit:Tez.one contract_1 ~script:Op.dummy_script >>=? fun (op, _) -> + Incremental.begin_construction b >>=? fun inc -> Incremental.add_operation inc op >>= fun inc -> Assert.proto_error_with_info ~loc:__LOC__ inc "Balance too low" @@ -195,21 +197,13 @@ let test_multiple_originations () = (** Cannot originate two contracts with the same context's counter. *) let test_counter () = - Context.init1 () >>=? fun (b, contract) -> - Incremental.begin_construction b >>=? fun inc -> - Op.contract_origination - (I inc) - ~credit:Tez.one - contract - ~script:Op.dummy_script + Context.init1 ~consensus_threshold:0 () >>=? fun (b, contract) -> + Op.contract_origination (B b) ~credit:Tez.one contract ~script:Op.dummy_script >>=? fun (op1, _) -> - Op.contract_origination - (I inc) - ~credit:Tez.one - contract - ~script:Op.dummy_script + Op.contract_origination (B b) ~credit:Tez.one contract ~script:Op.dummy_script >>=? fun (op2, _) -> - Incremental.add_operation inc op1 >>=? fun inc -> + Block.bake ~operation:op1 b >>=? fun b -> + Incremental.begin_construction b >>=? fun inc -> Incremental.add_operation inc op2 >>= fun res -> Assert.proto_error_with_info ~loc:__LOC__ diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_reveal.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_reveal.ml index b2baee4da48e60ba3fb60b1da8bc881e5a25ba7d..a6f16e28c19ad46e0e26348f13a28a86002a55ef 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_reveal.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_reveal.ml @@ -322,9 +322,16 @@ let test_backtracked_reveal_in_batch () = [op_reveal; op_transfer] >>=? fun batched_operation -> let expect_apply_failure = function - | [Environment.Ecoproto_error (Contract_storage.Balance_too_low _)] -> + | [ + Environment.Ecoproto_error (Contract_storage.Balance_too_low _); + Environment.Ecoproto_error (Tez_repr.Subtraction_underflow _); + ] -> return_unit - | _ -> assert false + | err -> + failwith + "Error trace:@, %a does not match the expected one" + Error_monad.pp_print_trace + err in Incremental.add_operation ~expect_apply_failure inc batched_operation >>=? fun inc -> @@ -436,7 +443,8 @@ let test_no_reveal_when_gas_exhausted () = {! Protocol.Raw_context.Operation_quota_exceeded} error *) let expect_failure = function | [ - Environment.Ecoproto_error Apply.Insufficient_gas_for_manager; + Environment.Ecoproto_error + Validate_operation.Manager.Insufficient_gas_for_manager; Environment.Ecoproto_error Raw_context.Operation_quota_exceeded; ] -> return_unit @@ -499,7 +507,10 @@ let test_reveal_incorrect_position_in_batch () = [op_transfer; op_reveal] >>=? fun batched_operation -> let expect_failure = function - | [Environment.Ecoproto_error Apply.Incorrect_reveal_position] -> + | [ + Environment.Ecoproto_error + Validate_operation.Manager.Incorrect_reveal_position; + ] -> return_unit | _ -> assert false in @@ -535,7 +546,10 @@ let test_duplicate_valid_reveals () = [op_rev1; op_rev2] >>=? fun batched_operation -> let expect_failure = function - | [Environment.Ecoproto_error Apply.Incorrect_reveal_position] -> + | [ + Environment.Ecoproto_error + Validate_operation.Manager.Incorrect_reveal_position; + ] -> return_unit | _ -> assert false in @@ -575,7 +589,10 @@ let test_valid_reveal_after_gas_exhausted_one () = [bad_reveal; good_reveal] >>=? fun batched_operation -> let expect_failure = function - | [Environment.Ecoproto_error Apply.Incorrect_reveal_position] -> + | [ + Environment.Ecoproto_error + Validate_operation.Manager.Incorrect_reveal_position; + ] -> return_unit | _ -> assert false in @@ -619,7 +636,10 @@ let test_valid_reveal_after_insolvent_one () = [bad_reveal; good_reveal; transfer] >>=? fun batched_operation -> let expect_failure = function - | [Environment.Ecoproto_error Apply.Incorrect_reveal_position] -> + | [ + Environment.Ecoproto_error + Validate_operation.Manager.Incorrect_reveal_position; + ] -> return_unit | _ -> assert false in @@ -659,7 +679,10 @@ let test_valid_reveal_after_emptying_balance () = [bad_reveal; good_reveal] >>=? fun batched_operation -> let expect_failure = function - | [Environment.Ecoproto_error Apply.Incorrect_reveal_position] -> + | [ + Environment.Ecoproto_error + Validate_operation.Manager.Incorrect_reveal_position; + ] -> return_unit | _ -> assert false in diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_sc_rollup.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_sc_rollup.ml index 5de4f470101114965ecf6ea833aca71944057d71..2d4e228a9ce56358fd6962ce05bf11a9ca80d663 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_sc_rollup.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_sc_rollup.ml @@ -92,7 +92,9 @@ let test_disable_feature_flag () = Op.sc_rollup_origination (I i) contract kind "" parameters_ty in let expect_apply_failure = function - | Environment.Ecoproto_error (Apply.Sc_rollup_feature_disabled as e) :: _ -> + | Environment.Ecoproto_error + (Validate_operation.Manager.Sc_rollup_feature_disabled as e) + :: _ -> Assert.test_error_encodings e ; return_unit | _ -> failwith "It should have failed with [Sc_rollup_feature_disabled]" diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_transfer.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_transfer.ml index d34b68459dd45a429f0fc1eb81bb3e763ec550b9..e9096b5045c95fae4c6cd67519298b6598ee3bbd 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_transfer.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_transfer.ml @@ -55,12 +55,13 @@ open Transfers This function returns a pair: - a block that added the valid transaction - an valid transaction *) -let transfer_to_itself_and_check_balances ~loc b ?(fee = Tez.zero) contract - amount = - Context.Contract.balance (I b) contract >>=? fun bal -> - Op.transaction (I b) ~fee contract contract amount >>=? fun op -> - Incremental.add_operation b op >>=? fun b -> - Assert.balance_was_debited ~loc (I b) contract bal fee >|=? fun () -> (b, op) +let transfer_to_itself_and_check_balances ~loc ?policy b ?(fee = Tez.zero) + contract amount = + Context.Contract.balance (B b) contract >>=? fun bal -> + Op.transaction (B b) ~fee contract contract amount >>=? fun operation -> + Block.bake ?policy ~operation b >>=? fun b -> + Assert.balance_was_debited ~loc (B b) contract bal fee >|=? fun () -> + (b, operation) let ten_tez = of_int 10 @@ -69,8 +70,8 @@ let ten_tez = of_int 10 (*********************************************************************) (** Compute a fraction of 2/[n] of the balance of [contract] *) -let two_over_n_of_balance incr contract n = - Context.Contract.balance (I incr) contract >>=? fun balance -> +let two_over_n_of_balance ctxt contract n = + Context.Contract.balance ctxt contract >>=? fun balance -> Lwt.return (balance /? n >>? fun res -> res *? 2L) (********************) @@ -102,13 +103,9 @@ let test_block_with_a_single_transfer_with_fee () = (** Single transfer without fee. *) let test_transfer_zero_tez () = let expect_apply_failure = function - | Environment.Ecoproto_error err :: _ -> + | Environment.Ecoproto_error (Apply.Empty_transaction _ as err) :: _ -> Assert.test_error_encodings err ; - let error_info = - Error_monad.find_info_of_error (Environment.wrap_tzerror err) - in - if error_info.title = "Empty transaction" then return_unit - else failwith "unexpected error" + return_unit | _ -> failwith "Empty transaction should fail" in single_transfer ~expect_apply_failure Tez.zero @@ -121,21 +118,25 @@ let test_transfer_zero_implicit () = let src = Contract.Implicit account.Account.pkh in Op.transaction (I i) src dest Tez.zero >>=? fun op -> Incremental.add_operation i op >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Empty implicit contract" + Assert.proto_error ~loc:__LOC__ res (function + | Contract_storage.Empty_implicit_contract _ as err -> + Assert.test_error_encodings err ; + true + | _ -> false) (** Transfer to originated contract. *) let test_transfer_to_originate_with_fee () = - Context.init1 () >>=? fun (b, contract) -> - Incremental.begin_construction b >>=? fun b -> - two_over_n_of_balance b contract 10L >>=? fun fee -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, contract) -> + two_over_n_of_balance (B b) contract 10L >>=? fun fee -> (* originated contract, paying a fee to originated this contract *) - Op.contract_origination (I b) ~fee:ten_tez contract ~script:Op.dummy_script + Op.contract_origination (B b) ~fee:ten_tez contract ~script:Op.dummy_script >>=? fun (operation, new_contract) -> - Incremental.add_operation b operation >>=? fun b -> - two_over_n_of_balance b contract 3L >>=? fun amount -> - transfer_and_check_balances ~loc:__LOC__ b ~fee contract new_contract amount - >>=? fun (b, _) -> - Incremental.finalize_block b >>=? fun _ -> return_unit + Block.bake ~operation b >>=? fun b -> + two_over_n_of_balance (B b) contract 3L >>=? fun amount -> + Incremental.begin_construction b >>=? fun i -> + transfer_and_check_balances ~loc:__LOC__ i ~fee contract new_contract amount + >>=? fun (i, _) -> + Incremental.finalize_block i >>=? fun _ -> return_unit (** Transfer from balance. *) let test_transfer_amount_of_contract_balance () = @@ -154,44 +155,54 @@ let test_transfer_amount_of_contract_balance () = (** Transfer to oneself. *) let test_transfers_to_self () = - Context.init1 () >>=? fun (b, contract) -> - Incremental.begin_construction b >>=? fun b -> - two_over_n_of_balance b contract 3L >>=? fun amount -> - transfer_to_itself_and_check_balances ~loc:__LOC__ b contract amount - >>=? fun (b, _) -> - two_over_n_of_balance b contract 5L >>=? fun fee -> - transfer_to_itself_and_check_balances ~loc:__LOC__ b ~fee contract ten_tez + Context.init2 ~consensus_threshold:0 () >>=? fun (b, (contract, _)) -> + two_over_n_of_balance (B b) contract 3L >>=? fun amount -> + let pkh1 = Context.Contract.pkh contract in + transfer_to_itself_and_check_balances + ~loc:__LOC__ + ~policy:(Block.Excluding [pkh1]) + b + contract + amount >>=? fun (b, _) -> - Incremental.finalize_block b >>=? fun _ -> return_unit + two_over_n_of_balance (B b) contract 5L >>=? fun fee -> + transfer_to_itself_and_check_balances + ~loc:__LOC__ + b + ~policy:(Block.Excluding [pkh1]) + ~fee + contract + ten_tez + >>=? fun _ -> return_unit (** Forgot to add the valid transaction into the block. *) let test_missing_transaction () = - Context.init2 () >>=? fun (b, (contract_1, contract_2)) -> + Context.init2 ~consensus_threshold:0 () + >>=? fun (b, (contract_1, contract_2)) -> (* given that contract_1 no longer has a sufficient balance to bake, make sure it cannot be chosen as baker *) let pkh1 = Context.Contract.pkh contract_1 in Incremental.begin_construction b ~policy:(Block.Excluding [pkh1]) - >>=? fun b -> - two_over_n_of_balance b contract_1 6L >>=? fun amount -> + >>=? fun i -> + two_over_n_of_balance (B b) contract_1 6L >>=? fun amount -> (* Do the transfer 3 times from source contract to destination contract *) - n_transactions 3 b contract_1 contract_2 amount >>=? fun b -> + n_transactions 3 i contract_1 contract_2 amount >>=? fun i -> (* do the fourth transfer from source contract to destination contract *) - Op.transaction (I b) contract_1 contract_2 amount >>=? fun _ -> - Incremental.finalize_block b >>=? fun _ -> return_unit + Op.transaction (I i) contract_1 contract_2 amount >>=? fun _ -> + Incremental.finalize_block i >>=? fun _ -> return_unit (** Transfer zero tez to an implicit contract, with fee equals balance of src. *) let test_transfer_zero_implicit_with_bal_src_as_fee () = - Context.init1 () >>=? fun (b, dest) -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, dest) -> let account = Account.new_account () in let src_pkh = account.Account.pkh in - Incremental.begin_construction b >>=? fun i -> let src = Contract.Implicit src_pkh in - Op.transaction ~force_reveal:true (I i) dest src (Tez.of_mutez_exn 100L) - >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Context.Contract.balance (I i) src >>=? fun bal_src -> + Op.transaction ~force_reveal:true (B b) dest src (Tez.of_mutez_exn 100L) + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Context.Contract.balance (B b) src >>=? fun bal_src -> Assert.equal_tez ~loc:__LOC__ bal_src (Tez.of_mutez_exn 100L) >>=? fun () -> - Op.transaction ~force_reveal:true (I i) ~fee:bal_src src dest Tez.zero + Op.transaction ~force_reveal:true (B b) ~fee:bal_src src dest Tez.zero >>=? fun op -> (* Transferring zero tez should result in an application failure as the implicit contract has been depleted. *) @@ -203,6 +214,7 @@ let test_transfer_zero_implicit_with_bal_src_as_fee () = return_unit | _ -> assert false in + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation ~expect_apply_failure i op >>=? fun inc -> Context.Contract.balance (I inc) src >>=? fun balance -> (* We assert that the failing operation was included and that the @@ -215,38 +227,42 @@ let test_transfer_zero_implicit_with_bal_src_as_fee () = (** Transfer zero tez to an originated contract, with fee equals balance of src. *) let test_transfer_zero_to_originated_with_bal_src_as_fee () = - Context.init1 () >>=? fun (b, dest) -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, dest) -> let account = Account.new_account () in - Incremental.begin_construction b >>=? fun i -> let src = Contract.Implicit account.Account.pkh in - Op.transaction (I i) dest src (Tez.of_mutez_exn 100L) >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.contract_origination (I i) dest ~script:Op.dummy_script - >>=? fun (op, new_contract) -> - Incremental.add_operation i op >>=? fun i -> - Context.Contract.balance (I i) src >>=? fun bal_src -> - Op.revelation (I i) ~fee:Tez.zero account.pk >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.transaction (I i) ~fee:bal_src src new_contract Tez.zero >>=? fun op -> + Op.transaction (B b) dest src (Tez.of_mutez_exn 100L) >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.contract_origination (B b) dest ~script:Op.dummy_script + >>=? fun (operation, new_contract) -> + Block.bake ~operation b >>=? fun b -> + Context.Contract.balance (B b) src >>=? fun bal_src -> + Op.revelation (B b) ~fee:Tez.zero account.pk >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.transaction (B b) ~fee:bal_src src new_contract Tez.zero + >>=? fun operation -> Assert.equal_tez ~loc:__LOC__ bal_src (Tez.of_mutez_exn 100L) >>=? fun () -> - Incremental.add_operation i op >>=? fun i -> - Incremental.finalize_block i >>=? fun _ -> return_unit + Block.bake ~operation b >>=? fun _ -> return_unit (** Transfer one tez to an implicit contract, with fee equals balance of src. *) let test_transfer_one_to_implicit_with_bal_src_as_fee () = - Context.init1 () >>=? fun (b, dest) -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, dest) -> let account = Account.new_account () in - Incremental.begin_construction b >>=? fun i -> let src = Contract.Implicit account.Account.pkh in - Op.transaction (I i) dest src (Tez.of_mutez_exn 100L) >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Context.Contract.balance (I i) src >>=? fun bal_src -> + Op.transaction (B b) dest src (Tez.of_mutez_exn 100L) >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Context.Contract.balance (B b) src >>=? fun bal_src -> Assert.equal_tez ~loc:__LOC__ bal_src (Tez.of_mutez_exn 100L) >>=? fun () -> - Op.revelation (I i) ~fee:Tez.zero account.pk >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.transaction (I i) ~fee:bal_src src dest Tez.one >>=? fun op -> - Incremental.add_operation i op >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Balance too low" + Op.revelation (B b) ~fee:Tez.zero account.pk >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Op.transaction (B b) ~fee:bal_src src dest Tez.one >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> + Incremental.add_operation i op ~expect_apply_failure:(function + | Environment.Ecoproto_error (Contract_storage.Balance_too_low _ as err) + :: _ -> + Assert.test_error_encodings err ; + return_unit + | t -> failwith "Unexpected error: %a" Error_monad.pp_print_trace t) + >>=? fun _ -> return_unit (********************) (* The following tests are for different kind of contracts: @@ -259,32 +275,34 @@ let test_transfer_one_to_implicit_with_bal_src_as_fee () = (** Implicit to Implicit. *) let test_transfer_from_implicit_to_implicit_contract () = - Context.init1 () >>=? fun (b, bootstrap_contract) -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap_contract) -> let account_a = Account.new_account () in let account_b = Account.new_account () in - Incremental.begin_construction b >>=? fun b -> let src = Contract.Implicit account_a.Account.pkh in - two_over_n_of_balance b bootstrap_contract 3L >>=? fun amount1 -> - two_over_n_of_balance b bootstrap_contract 10L >>=? fun fee1 -> + two_over_n_of_balance (B b) bootstrap_contract 3L >>=? fun amount1 -> + two_over_n_of_balance (B b) bootstrap_contract 10L >>=? fun fee1 -> + Incremental.begin_construction b >>=? fun i -> transfer_and_check_balances ~with_burn:true ~loc:__LOC__ ~fee:fee1 - b + i bootstrap_contract src amount1 - >>=? fun (b, _) -> + >>=? fun (i, _) -> + Incremental.finalize_block i >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> (* Create an implicit contract as a destination contract. *) let dest = Contract.Implicit account_b.pkh in - two_over_n_of_balance b bootstrap_contract 4L >>=? fun amount2 -> - two_over_n_of_balance b bootstrap_contract 10L >>=? fun fee2 -> + two_over_n_of_balance (I i) bootstrap_contract 4L >>=? fun amount2 -> + two_over_n_of_balance (I i) bootstrap_contract 10L >>=? fun fee2 -> (* Transfer from implicit contract to another implicit contract. *) transfer_and_check_balances ~with_burn:true ~loc:__LOC__ ~fee:fee2 - b + i src dest amount2 @@ -293,34 +311,36 @@ let test_transfer_from_implicit_to_implicit_contract () = (** Implicit to originated. *) let test_transfer_from_implicit_to_originated_contract () = - Context.init1 () >>=? fun (b, bootstrap_contract) -> + Context.init1 ~consensus_threshold:0 () >>=? fun (b, bootstrap_contract) -> let contract = bootstrap_contract in let account = Account.new_account () in let src = Contract.Implicit account.Account.pkh in - Incremental.begin_construction b >>=? fun b -> - two_over_n_of_balance b bootstrap_contract 3L >>=? fun amount1 -> + two_over_n_of_balance (B b) bootstrap_contract 3L >>=? fun amount1 -> + Incremental.begin_construction b >>=? fun i -> (* transfer the money to implicit contract *) transfer_and_check_balances ~with_burn:true ~loc:__LOC__ - b + i bootstrap_contract src amount1 - >>=? fun (b, _) -> + >>=? fun (i, _) -> + Incremental.finalize_block i >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> (* originated contract *) Op.contract_origination ~force_reveal:true - (I b) + (I i) contract ~script:Op.dummy_script >>=? fun (operation, new_contract) -> - Incremental.add_operation b operation >>=? fun b -> - two_over_n_of_balance b bootstrap_contract 4L >>=? fun amount2 -> + Incremental.add_operation i operation >>=? fun i -> + two_over_n_of_balance (I i) bootstrap_contract 4L >>=? fun amount2 -> (* transfer from implicit contract to originated contract *) - transfer_and_check_balances ~loc:__LOC__ b src new_contract amount2 - >>=? fun (b, _) -> - Incremental.finalize_block b >>=? fun _ -> return_unit + transfer_and_check_balances ~loc:__LOC__ i src new_contract amount2 + >>=? fun (i, _) -> + Incremental.finalize_block i >>=? fun _ -> return_unit (********************) (* Slow tests case *) @@ -328,7 +348,8 @@ let test_transfer_from_implicit_to_originated_contract () = (********************) let multiple_transfer n ?fee amount = - Context.init2 () >>=? fun (b, (contract_1, contract_2)) -> + Context.init2 ~consensus_threshold:0 () + >>=? fun (b, (contract_1, contract_2)) -> Incremental.begin_construction b >>=? fun b -> n_transactions n b ?fee contract_1 contract_2 amount >>=? fun b -> Incremental.finalize_block b >>=? fun _ -> return_unit @@ -349,7 +370,7 @@ let test_block_with_multiple_transfers_pay_fee () = 2- Apply multiple transfers without fees; 3- Apply multiple transfers with fees. *) let test_block_with_multiple_transfers_with_without_fee () = - Context.init_n 8 () >>=? fun (b, contracts) -> + Context.init_n ~consensus_threshold:0 8 () >>=? fun (b, contracts) -> let contracts = Array.of_list contracts in Incremental.begin_construction b >>=? fun b -> let hundred = of_int 100 in @@ -395,33 +416,35 @@ let test_build_a_chain () = let test_empty_implicit () = Context.init1 () >>=? fun (b, dest) -> let account = Account.new_account () in - Incremental.begin_construction b >>=? fun incr -> let src = Contract.Implicit account.Account.pkh in - two_over_n_of_balance incr dest 3L >>=? fun amount -> + two_over_n_of_balance (B b) dest 3L >>=? fun amount -> (* Transfer zero tez from an implicit contract. *) - Op.transaction (I incr) src dest amount >>=? fun op -> + Op.transaction (B b) src dest amount >>=? fun op -> + Incremental.begin_construction b >>=? fun incr -> Incremental.add_operation incr op >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Empty implicit contract" + Assert.proto_error ~loc:__LOC__ res (function + | Contract_storage.Empty_implicit_contract _ as err -> + Assert.test_error_encodings err ; + true + | _ -> false) (** Balance is too low to transfer. *) let test_balance_too_low fee () = - Context.init2 () >>=? fun (b, (contract_1, contract_2)) -> - Incremental.begin_construction b >>=? fun i -> - Context.Contract.balance (I i) contract_1 >>=? fun balance1 -> - Context.Contract.balance (I i) contract_2 >>=? fun balance2 -> + Context.init2 ~consensus_threshold:0 () + >>=? fun (b, (contract_1, contract_2)) -> + Context.Contract.balance (B b) contract_1 >>=? fun balance1 -> + Context.Contract.balance (B b) contract_2 >>=? fun balance2 -> (* transfer the amount of tez that is bigger than the balance in the source contract *) - Op.transaction ~fee (I i) contract_1 contract_2 max_tez >>=? fun op -> + Op.transaction ~fee (B b) contract_1 contract_2 max_tez >>=? fun op -> let expect_apply_failure = function - | Environment.Ecoproto_error err :: _ -> + | Environment.Ecoproto_error (Contract_storage.Balance_too_low _ as err) + :: _ -> Assert.test_error_encodings err ; - let error_info = - Error_monad.find_info_of_error (Environment.wrap_tzerror err) - in - if String.equal error_info.title "Balance too low" then return_unit - else failwith "unexpected error: %s" error_info.title - | _ -> failwith "balance too low should fail" + return_unit + | t -> failwith "Unexpected error: %a" Error_monad.pp_print_trace t in (* the fee is higher than the balance then raise an error "Balance_too_low" *) + Incremental.begin_construction b >>=? fun i -> if fee > balance1 then Incremental.add_operation ~expect_apply_failure i op >>= fun _res -> return_unit @@ -441,7 +464,8 @@ let test_balance_too_low fee () = 3- Add another transfer that send tez from a zero balance contract; 4- Catch the expected error: Balance_too_low. *) let test_balance_too_low_two_transfers fee () = - Context.init3 () >>=? fun (b, (contract_1, contract_2, contract_3)) -> + Context.init3 ~consensus_threshold:0 () + >>=? fun (b, (contract_1, contract_2, contract_3)) -> Incremental.begin_construction b >>=? fun i -> Context.Contract.balance (I i) contract_1 >>=? fun balance -> balance /? 3L >>?= fun res -> @@ -453,20 +477,21 @@ let test_balance_too_low_two_transfers fee () = contract_2 two_third_of_balance >>=? fun (i, _) -> - Context.Contract.balance (I i) contract_1 >>=? fun balance1 -> - Context.Contract.balance (I i) contract_3 >>=? fun balance3 -> - Op.transaction ~fee (I i) contract_1 contract_3 two_third_of_balance + Incremental.finalize_block i >>=? fun b -> + Context.Contract.balance (B b) contract_1 >>=? fun balance1 -> + Context.Contract.balance (B b) contract_3 >>=? fun balance3 -> + Op.transaction ~fee (B b) contract_1 contract_3 two_third_of_balance >>=? fun operation -> let expect_apply_failure = function - | Environment.Ecoproto_error err :: _ -> + | Environment.Ecoproto_error (Contract_storage.Balance_too_low _ as err) + :: _ -> Assert.test_error_encodings err ; - let error_info = - Error_monad.find_info_of_error (Environment.wrap_tzerror err) - in - if error_info.title = "Balance too low" then return_unit - else failwith "unexpected error" - | _ -> failwith "balance too low should fail" + return_unit + | t -> + failwith "Unexpected error: %a" Error_monad.pp_print_trace t + >>=? fun _ -> return_unit in + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation ~expect_apply_failure i operation >>=? fun i -> (* contract_1 loses the fees *) Assert.balance_was_debited ~loc:__LOC__ (I i) contract_1 balance1 fee @@ -476,30 +501,35 @@ let test_balance_too_low_two_transfers fee () = (** The counter is already used for the previous operation. *) let invalid_counter () = - Context.init2 () >>=? fun (b, (contract_1, contract_2)) -> - Incremental.begin_construction b >>=? fun b -> - Op.transaction (I b) contract_1 contract_2 Tez.one >>=? fun op1 -> - Op.transaction (I b) contract_1 contract_2 Tez.one >>=? fun op2 -> - Incremental.add_operation b op1 >>=? fun b -> - Incremental.add_operation b op2 >>= fun b -> - Assert.proto_error_with_info - ~loc:__LOC__ - b - "Invalid counter (already used) in a manager operation" + Context.init2 ~consensus_threshold:0 () + >>=? fun (b, (contract_1, contract_2)) -> + Op.transaction (B b) contract_1 contract_2 Tez.one >>=? fun op1 -> + Op.transaction (B b) contract_1 contract_2 Tez.one >>=? fun op2 -> + Block.bake ~operation:op1 b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + Incremental.add_operation i op2 >>= fun b -> + Assert.proto_error ~loc:__LOC__ b (function + | Contract_storage.Counter_in_the_past _ as err -> + Assert.test_error_encodings err ; + true + | _ -> false) (** Same as before but through a different way to perform this error. *) let test_add_the_same_operation_twice () = Context.init2 () >>=? fun (b, (contract_1, contract_2)) -> - Incremental.begin_construction b >>=? fun b -> - transfer_and_check_balances ~loc:__LOC__ b contract_1 contract_2 ten_tez - >>=? fun (b, op_transfer) -> - Op.transaction (I b) contract_1 contract_2 ten_tez >>=? fun _ -> - Incremental.add_operation b op_transfer >>= fun b -> - Assert.proto_error_with_info - ~loc:__LOC__ - b - "Invalid counter (already used) in a manager operation" + Incremental.begin_construction b >>=? fun i -> + transfer_and_check_balances ~loc:__LOC__ i contract_1 contract_2 ten_tez + >>=? fun (i, op_transfer) -> + Incremental.finalize_block i >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + Op.transaction (I i) contract_1 contract_2 ten_tez >>=? fun _ -> + Incremental.add_operation i op_transfer >>= fun b -> + Assert.proto_error ~loc:__LOC__ b (function + | Contract_storage.Counter_in_the_past _ as err -> + Assert.test_error_encodings err ; + true + | _ -> false) (** The counter is in the future *) let invalid_counter_in_the_future () = @@ -509,10 +539,11 @@ let invalid_counter_in_the_future () = let counter = Z.add cpt (Z.of_int 10) in Op.transaction (I b) contract_1 contract_2 Tez.one ~counter >>=? fun op -> Incremental.add_operation b op >>= fun b -> - Assert.proto_error_with_info - ~loc:__LOC__ - b - "Invalid counter (not yet reached) in a manager operation" + Assert.proto_error ~loc:__LOC__ b (function + | Contract_storage.Counter_in_the_future _ as err -> + Assert.test_error_encodings err ; + true + | _ -> false) (** Check ownership. *) let test_ownership_sender () = @@ -552,14 +583,20 @@ let test_random_transfer () = let source_pkh = Context.Contract.pkh source in (* given that source may not have a sufficient balance for the transfer + to bake, make sure it cannot be chosen as baker *) - Incremental.begin_construction b ~policy:(Block.Excluding [source_pkh]) - >>=? fun b -> - Context.Contract.balance (I b) source >>=? fun amount -> - (if source = dest then - transfer_to_itself_and_check_balances ~loc:__LOC__ b source amount - else transfer_and_check_balances ~loc:__LOC__ b source dest amount) - >>=? fun (b, _) -> - Incremental.finalize_block b >>=? fun _ -> return_unit + Context.Contract.balance (B b) source >>=? fun amount -> + if source = dest then + transfer_to_itself_and_check_balances + ~loc:__LOC__ + ~policy:(Block.Excluding [source_pkh]) + b + source + amount + >>=? fun _ -> return_unit + else + Incremental.begin_construction ~policy:(Block.Excluding [source_pkh]) b + >>=? fun i -> + transfer_and_check_balances ~loc:__LOC__ i source dest amount >>=? fun _ -> + return_unit (** Transfer random transactions. *) let test_random_multi_transactions () = diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml index 6f9a91110df61c1cf87fa9a0019f96bbb5a44b60..d8006e0118d40f9d787f140883242f169c262298 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml @@ -38,6 +38,12 @@ open Alpha_context open Test_tez open Error_monad_operators +(* Similar to [Block.bake] but ensure that the operation [op] is applied + in the block *) +let add_operation b op = + Incremental.begin_construction b >>=? fun i -> + Incremental.add_operation i op >>=? fun i -> Incremental.finalize_block i + (** [check_tx_rollup_exists ctxt tx_rollup] returns [()] iff [tx_rollup] is a valid address for a transaction rollup. Otherwise, it fails. *) let check_tx_rollup_exists ctxt tx_rollup = @@ -79,7 +85,8 @@ let test_disable_feature_flag () = Incremental.begin_construction b >>=? fun i -> Op.tx_rollup_origination (I i) contract >>=? fun (op, _tx_rollup) -> Incremental.add_operation - ~expect_failure:(check_proto_error Apply.Tx_rollup_feature_disabled) + ~expect_failure: + (check_proto_error Validate_operation.Manager.Tx_rollup_feature_disabled) i op >>=? fun _i -> return_unit @@ -101,7 +108,8 @@ let test_sunset () = Incremental.begin_construction b >>=? fun i -> Op.tx_rollup_origination (I i) contract >>=? fun (op, _tx_rollup) -> Incremental.add_operation - ~expect_failure:(check_proto_error Apply.Tx_rollup_feature_disabled) + ~expect_failure: + (check_proto_error Validate_operation.Manager.Tx_rollup_feature_disabled) i op >>=? fun _i -> return_unit @@ -451,6 +459,10 @@ let check_bond ctxt tx_rollup contract count = Alcotest.(check int "Pending bonded commitment count correct" count pending) ; return () +let check_bond_from_block b tx_rollup contract count = + Incremental.begin_construction b >>=? fun i -> + check_bond (Incremental.alpha_ctxt i) tx_rollup contract count + let rec bake_until i top = let level = Incremental.level i in if level >= top then return i @@ -868,7 +880,8 @@ let test_add_two_batches () = tx_rollup contents2 >>=? fun op2 -> - Block.bake ~operations:[op1; op2] b >>=? fun b -> + Op.batch_operations ~source:contract (B b) [op1; op2] >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> (* There were a first inbox with one message, and we are looking for its successor. *) Context.Tx_rollup.inbox (B b) tx_rollup Tx_rollup_level.(succ root) @@ -928,26 +941,25 @@ let fill_inbox b tx_rollup contract contents k = constant.parametric.tx_rollup.hard_size_limit_per_inbox in Context.Contract.counter (B b) contract >>=? fun counter -> - Incremental.begin_construction b >>=? fun i -> - let rec fill_inbox i inbox_size counter = + let rec fill_inbox inbox_size counter operations = (* We set an arbitrary gas limit to be able to reach the size limit of an operation. *) Op.tx_rollup_submit_batch ~gas_limit:(Custom_gas (Gas.Arith.integral_of_int_exn 20_000)) ~counter - (I i) + (B b) contract tx_rollup contents - >>=? fun op -> + >>=? fun operation -> let new_inbox_size = inbox_size + message_size in if new_inbox_size < tx_rollup_inbox_limit then - Incremental.add_operation i op >>=? fun i -> - fill_inbox i new_inbox_size (Z.succ counter) - else k i inbox_size op + fill_inbox new_inbox_size (Z.succ counter) (operation :: operations) + else + Incremental.begin_construction b >>=? fun i -> + k i inbox_size (operation, operations) in - - fill_inbox i 0 counter + fill_inbox 0 counter [] (** Try to add enough large batches to reach the size limit of an inbox. *) let test_inbox_size_too_big () = @@ -958,10 +970,17 @@ let test_inbox_size_too_big () = in let contents = String.make tx_rollup_batch_limit 'd' in originate b contract >>=? fun (b, tx_rollup) -> - fill_inbox b tx_rollup contract contents (fun i _ op -> + fill_inbox b tx_rollup contract contents (fun i _ (op, ops) -> + Op.batch_operations + ~recompute_counters:true + ~source:contract + (I i) + (ops @ [op]) + >>=? fun op -> Incremental.add_operation i op + ~check_size:false ~expect_apply_failure: (check_proto_error_f (function | Tx_rollup_errors.Inbox_size_would_exceed_limit _ -> true @@ -983,38 +1002,52 @@ let test_inbox_count_too_big () = b (Context.Contract.pkh contract) >>=? fun (deposit_contract, b) -> - Incremental.begin_construction b >>=? fun i -> - let rec fill_inbox i counter n = + let rec fill_inbox b counter n batch = (* By default, the [gas_limit] is the maximum gas that can be consumed by an operation. We set a lower (arbitrary) limit to be able to reach the size limit of an operation. *) Op.tx_rollup_submit_batch ~gas_limit:(Custom_gas (Gas.Arith.integral_of_int_exn 3_500)) ~counter - (I i) + (B b) contract tx_rollup contents >>=? fun op -> - if n > 0 then - Incremental.add_operation i op >>=? fun i -> - fill_inbox i (Z.succ counter) (n - 1) - else return (i, counter) + (match batch with + | None -> return op + | Some batch -> + Op.batch_operations + ~recompute_counters:true + ~source:contract + (B b) + [batch; op]) + >>=? fun op -> + if n > 0 then fill_inbox b (Z.succ counter) (n - 1) (Some op) + else return (op, counter) in Context.Contract.counter (B b) contract >>=? fun counter -> - fill_inbox i counter message_count >>=? fun (i, counter) -> + fill_inbox b counter message_count None >>=? fun (batch, counter) -> Op.tx_rollup_submit_batch ~gas_limit:(Custom_gas (Gas.Arith.integral_of_int_exn 2_500)) ~counter - (I i) + (B b) contract tx_rollup contents + >>=? fun op1 -> + Op.batch_operations + ~recompute_counters:true + ~source:contract + (B b) + [batch; op1] >>=? fun op -> (* Submitting a new batch to a full inbox fails *) + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op + ~check_size:false ~expect_apply_failure: (check_proto_error_f @@ function | Tx_rollup_errors.Inbox_count_would_exceed_limit rollup -> @@ -1031,11 +1064,18 @@ let test_inbox_count_too_big () = deposit_contract Tez.zero ~parameters + >>=? fun op2 -> + Op.batch_operations + ~recompute_counters:true + ~source:contract + (B b) + [batch; op2] >>=? fun op -> (* Submitting a new deposit to a full inbox fails *) Incremental.add_operation i op + ~check_size:false ~expect_apply_failure: (check_proto_error_f @@ function | Tx_rollup_errors.Inbox_count_would_exceed_limit rollup -> @@ -1471,7 +1511,10 @@ let test_finalization () = (* Repeating fill inbox and finalize block to increase EMA until EMA is enough to provoke a change of fees. *) let rec increase_ema n b tx_rollup f = - f b tx_rollup >>=? fun (inbox_size, i) -> + f b tx_rollup >>=? fun (inbox_size, i, (_operation, ops)) -> + Op.batch_operations ~recompute_counters:true ~source:filler (I i) ops + >>=? fun op -> + Incremental.add_operation ~check_size:false i op >>=? fun i -> Incremental.finalize_block i >>=? fun b -> Context.Tx_rollup.state (B b) tx_rollup >>=? fun state -> let inbox_ema = @@ -1482,7 +1525,8 @@ let test_finalization () = else increase_ema (n + 1) b tx_rollup f in ( increase_ema 1 b tx_rollup @@ fun b tx_rollup -> - fill_inbox b tx_rollup filler contents (fun i size _ -> return (size, i)) ) + fill_inbox b tx_rollup filler contents (fun i size ops -> + return (size, i, ops)) ) >>=? fun (b, n, inbox_size) -> let rec update_burn_per_byte_n_time n state = if n > 0 then @@ -1548,9 +1592,8 @@ let test_commitment_duplication () = let contents = "batch" in Op.tx_rollup_submit_batch (B b) contract1 tx_rollup contents >>=? fun operation -> - Block.bake ~operation b >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> - make_incomplete_commitment_for_batch (I i) Tx_rollup_level.root tx_rollup [] + add_operation b operation >>=? fun b -> + make_incomplete_commitment_for_batch (B b) Tx_rollup_level.root tx_rollup [] >>=? fun (commitment, _) -> (* Successfully fail to submit a different commitment from contract2 *) let batches2 : Tx_rollup_message_result_hash.t list = @@ -1566,8 +1609,9 @@ let test_commitment_duplication () = let commitment_with_wrong_count : Tx_rollup_commitment.Full.t = {commitment with messages = batches2} in - Op.tx_rollup_commit (I i) contract2 tx_rollup commitment_with_wrong_count + Op.tx_rollup_commit (B b) contract2 tx_rollup commitment_with_wrong_count >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -1576,14 +1620,15 @@ let test_commitment_duplication () = (* Submit the correct one *) Context.get_level (I i) >>?= fun level -> let submitted_level = Raw_level.succ level in - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Context.get_constants (I i) >>=? fun constants -> + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun operation -> + add_operation b operation >>=? fun b -> + Context.get_constants (B b) >>=? fun constants -> let cost = constants.parametric.tx_rollup.commitment_bond in - Assert.balance_was_debited ~loc:__LOC__ (I i) contract1 balance cost + Assert.balance_was_debited ~loc:__LOC__ (B b) contract1 balance cost >>=? fun () -> (* Successfully fail to submit a duplicate commitment *) - Op.tx_rollup_commit (I i) contract2 tx_rollup commitment >>=? fun op -> + Op.tx_rollup_commit (B b) contract2 tx_rollup commitment >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> (Incremental.add_operation i op >>= function | Ok _ -> failwith "an error was expected" | Error e -> @@ -1810,7 +1855,7 @@ let test_storage_burn_for_commitment_and_bond () = (* test freed storage space after remove commitment *) Op.tx_rollup_remove_commitment (B b) contract tx_rollup >>=? fun operation -> - Block.bake b ~operation >>=? fun b -> + add_operation b operation >>=? fun b -> occupied_storage_size (B b) tx_rollup >>=? fun freed_space_after_remove_commitment -> let commitment_remove_delta = -135 in @@ -1830,7 +1875,7 @@ let test_storage_burn_for_commitment_and_bond () = commitment_remove_delta) ; (* test freed storage space after return bond *) Op.tx_rollup_return_bond (B b) contract tx_rollup >>=? fun operation -> - Block.bake b ~operation >>=? fun b -> + add_operation b operation >>=? fun b -> occupied_storage_size (B b) tx_rollup >>=? fun freed_space_after_return_bond -> let bond_remove_delta = -4 in @@ -1848,29 +1893,29 @@ let test_commitment_predecessor () = originate b contract1 >>=? fun (b, tx_rollup) -> (* Transactions in blocks 2, 3, 6 *) make_transactions_in tx_rollup contract1 [2; 3; 6] b >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> (* Check error: Commitment for nonexistent block *) let bogus_hash = Tx_rollup_commitment_hash.of_bytes_exn (Bytes.of_string "tcu1deadbeefdeadbeefdeadbeefdead") in - make_incomplete_commitment_for_batch (I i) Tx_rollup_level.root tx_rollup [] + make_incomplete_commitment_for_batch (B b) Tx_rollup_level.root tx_rollup [] >>=? fun (commitment, _) -> let commitment_for_invalid_inbox = {commitment with level = tx_level 10l} in - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment_for_invalid_inbox + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment_for_invalid_inbox >>=? fun op -> let error = Tx_rollup_errors.Commitment_too_early {provided = tx_level 10l; expected = tx_level 0l} in + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op ~expect_apply_failure:(check_proto_error error) >>=? fun _ -> (* Now we submit a real commitment *) - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Commitment without predecessor for block with predecessor*) make_incomplete_commitment_for_batch - (I i) + (B b) Tx_rollup_level.(succ root) tx_rollup [] @@ -1879,11 +1924,12 @@ let test_commitment_predecessor () = {commitment with predecessor = None} in Op.tx_rollup_commit - (I i) + (B b) contract1 tx_rollup commitment_with_missing_predecessor >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -1892,7 +1938,7 @@ let test_commitment_predecessor () = | Tx_rollup_errors.Wrong_predecessor_hash {provided = None; expected} -> expected = commitment.predecessor | _ -> false) - >>=? fun i -> + >>=? fun _i -> (* Commitment refers to a predecessor which does not exist *) let commitment_with_wrong_pred = {commitment with predecessor = Some bogus_hash} @@ -1958,10 +2004,10 @@ let test_bond_finalization () = (* Transactions in block 2, 3, 4 *) make_transactions_in tx_rollup contract1 [2; 3; 4] b >>=? fun b -> (* Let’s try to remove the bond *) - Incremental.begin_construction b >>=? fun i -> - Context.get_constants (I i) >>=? fun constants -> + Context.get_constants (B b) >>=? fun constants -> let bond = constants.parametric.tx_rollup.commitment_bond in - Op.tx_rollup_return_bond (I i) contract1 tx_rollup >>=? fun op -> + Op.tx_rollup_return_bond (B b) contract1 tx_rollup >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -1970,11 +2016,14 @@ let test_bond_finalization () = | Tx_rollup_errors.Bond_does_not_exist a_pkh1 -> a_pkh1 = pkh1 | _ -> false) >>=? fun i -> - make_incomplete_commitment_for_batch (I i) Tx_rollup_level.root tx_rollup [] + Incremental.finalize_block i >>=? fun b -> + make_incomplete_commitment_for_batch (B b) Tx_rollup_level.root tx_rollup [] >>=? fun (commitment_a, _) -> - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment_a >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.tx_rollup_return_bond (I i) contract1 tx_rollup >>=? fun op -> + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment_a + >>=? fun operation -> + add_operation b operation >>=? fun b -> + Op.tx_rollup_return_bond (B b) contract1 tx_rollup >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -1987,22 +2036,16 @@ let test_bond_finalization () = Assert.balance_was_debited ~loc:__LOC__ (B b) contract1 balance bond >>=? fun () -> (* Finalize the commitment of level 0. *) - Incremental.begin_construction b >>=? fun i -> - Op.tx_rollup_finalize (I i) contract1 tx_rollup >>=? fun operation -> - Incremental.add_operation i operation >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> + Op.tx_rollup_finalize (B b) contract1 tx_rollup >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Bake enough block, and remove the commitment of level 0. *) Block.bake b ~operations:[] >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> - Op.tx_rollup_remove_commitment (I i) contract1 tx_rollup >>=? fun operation -> - Incremental.add_operation i operation >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> + Op.tx_rollup_remove_commitment (B b) contract1 tx_rollup >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Try to return the bond *) Context.Contract.balance (B b) contract1 >>=? fun balance -> - Incremental.begin_construction b >>=? fun i -> - Op.tx_rollup_return_bond (I i) contract1 tx_rollup >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> + Op.tx_rollup_return_bond (B b) contract1 tx_rollup >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Check the balance*) Assert.balance_was_credited ~loc:__LOC__ (B b) contract1 balance bond @@ -2022,9 +2065,9 @@ let test_finalization_edge_cases () = >>=? fun _i -> let message = "bogus" in Op.tx_rollup_submit_batch (B b) contract1 tx_rollup message >>=? fun op -> - Block.bake ~operation:op b >>=? fun b -> + add_operation b op >>=? fun b -> Op.tx_rollup_submit_batch (B b) contract1 tx_rollup message >>=? fun op -> - Block.bake ~operation:op b >>=? fun b -> + add_operation b op >>=? fun b -> Op.tx_rollup_finalize (B b) contract1 tx_rollup >>=? fun op -> (* With an inbox, but no commitment *) Incremental.begin_construction b >>=? fun i -> @@ -2050,7 +2093,7 @@ let test_finalization_edge_cases () = >>=? fun _i -> Incremental.finalize_block i >>=? fun b -> (* Now our finalization is valid *) - Block.bake ~operation:op b >>=? fun _block -> return_unit + add_operation b op >>=? fun _block -> return_unit (** [test_too_many_commitments] tests that you can't submit new commitments if there are too many finalized commitments. *) @@ -2059,27 +2102,30 @@ let test_too_many_commitments () = originate b contract1 >>=? fun (b, tx_rollup) -> (* Transactions in block 2, 3, 4, 5 *) make_transactions_in tx_rollup contract1 [2; 3; 4; 5] b >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> - let rec make_commitments i level n = - if n = 0 then return (i, level) + let rec make_commitments b level n = + if n = 0 then return (b, level) else - make_incomplete_commitment_for_batch (I i) level tx_rollup [] + make_incomplete_commitment_for_batch (B b) level tx_rollup [] >>=? fun (commitment, _) -> - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - make_commitments i (Tx_rollup_level.succ level) (n - 1) + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment + >>=? fun operation -> + add_operation b operation >>=? fun b -> + make_commitments b (Tx_rollup_level.succ level) (n - 1) in - make_commitments i Tx_rollup_level.root 3 >>=? fun (i, level) -> + make_commitments b Tx_rollup_level.root 3 >>=? fun (b, level) -> (* Make sure all commitments can be finalized. *) + Incremental.begin_construction b >>=? fun i -> bake_until i 10l >>=? fun i -> - Op.tx_rollup_finalize (I i) contract1 tx_rollup >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.tx_rollup_finalize (I i) contract1 tx_rollup >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> + Incremental.finalize_block i >>=? fun b -> + Op.tx_rollup_finalize (B b) contract1 tx_rollup >>=? fun operation -> + add_operation b operation >>=? fun b -> + Op.tx_rollup_finalize (B b) contract1 tx_rollup >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Fail to add a new commitment. *) - make_incomplete_commitment_for_batch (I i) level tx_rollup [] + make_incomplete_commitment_for_batch (B b) level tx_rollup [] >>=? fun (commitment, _) -> - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -2088,13 +2134,14 @@ let test_too_many_commitments () = >>=? fun i -> (* Wait out the withdrawal period. *) bake_until i 12l >>=? fun i -> + Incremental.finalize_block i >>=? fun b -> (* Remove one finalized commitment. *) - Op.tx_rollup_remove_commitment (I i) contract1 tx_rollup >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> + Op.tx_rollup_remove_commitment (B b) contract1 tx_rollup >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Now we can add a new commitment. *) - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - ignore i ; + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment >>=? fun operation -> + add_operation b operation >>=? fun b -> + ignore b ; return () @@ -2191,24 +2238,24 @@ module Rejection = struct let message = "bogus" in Op.tx_rollup_submit_batch (B b) contract1 tx_rollup message >>=? fun operation -> - Block.bake ~operation b >>=? fun b -> - Incremental.begin_construction b >|=? fun i -> + add_operation b operation >|=? fun b -> let level = Tx_rollup_level.root in - (i, contract1, tx_rollup, level, message) + (b, contract1, tx_rollup, level, message) let init_with_valid_commitment () = init_with_bogus_batch () - >>=? fun (i, contract1, tx_rollup, level, message) -> - make_incomplete_commitment_for_batch (I i) level tx_rollup [] + >>=? fun (b, contract1, tx_rollup, level, message) -> + make_incomplete_commitment_for_batch (B b) level tx_rollup [] >>=? fun (commitment, _batches_result) -> - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >|=? fun i -> - (i, contract1, tx_rollup, level, message, commitment) + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment + >>=? fun operation -> + add_operation b operation >|=? fun b -> + (b, contract1, tx_rollup, level, message, commitment) let init_with_invalid_commitment () = init_with_bogus_batch () - >>=? fun (i, contract1, tx_rollup, level, message) -> - make_incomplete_commitment_for_batch (I i) level tx_rollup [] + >>=? fun (b, contract1, tx_rollup, level, message) -> + make_incomplete_commitment_for_batch (B b) level tx_rollup [] >>=? fun (commitment, _batches_result) -> let commitment = { @@ -2225,7 +2272,8 @@ module Rejection = struct ]; } in - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op >|=? fun i -> (i, contract1, tx_rollup, level, message, commitment) @@ -2488,17 +2536,15 @@ module Rejection = struct let message_path = single_message_path message_hash in Op.tx_rollup_submit_batch (B b) account tx_rollup batch_bytes >>=? fun operation -> - Block.bake ~operation b >>=? fun b -> + add_operation b operation >>=? fun b -> (* Make an invalid commitment for the submitted transfer *) let level = Tx_rollup_level.(succ root) in - Incremental.begin_construction b >>=? fun i -> - make_incomplete_commitment_for_batch (I i) level tx_rollup [] + make_incomplete_commitment_for_batch (B b) level tx_rollup [] >>=? fun (commitment, _) -> - Op.tx_rollup_commit (I i) account tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> + Op.tx_rollup_commit (B b) account tx_rollup commitment >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Now we produce a valid proof rejecting the commitment *) - l2_parameters (I i) >>=? fun l2_parameters -> + l2_parameters (B b) >>=? fun l2_parameters -> make_proof store l2_parameters message >>= fun proof -> let message_position = 0 in let message_result_hash, message_result_path = @@ -2518,8 +2564,8 @@ module Rejection = struct ~proof ~previous_message_result:(message_result l2_context_hash []) ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path - >>=? fun op -> - Incremental.add_operation i op >>=? fun _ -> return_unit + >>=? fun operation -> + add_operation b operation >>=? fun _ -> return_unit (** It is really similar to {!test_valid_proof_on_invalid_commitment} but it tries to reject a valid commitment, thus, fails. *) @@ -2529,8 +2575,7 @@ module Rejection = struct >>=? fun (b, account, _, tx_rollup, store, ticket_hash) -> (* init_with_deposit creates a commitment -- we'll just check the bond here so that this test is easier to read. *) - Incremental.begin_construction b >>=? fun i -> - check_bond (Incremental.alpha_ctxt i) tx_rollup account 1 >>=? fun () -> + check_bond_from_block b tx_rollup account 1 >>=? fun () -> hash_tree_from_store store >>= fun l2_context_hash -> (* Create a transfer from [pk] to a new address *) let _, _, addr2 = gen_l2_account () in @@ -2543,15 +2588,13 @@ module Rejection = struct let message_path = single_message_path message_hash in Op.tx_rollup_submit_batch (B b) account tx_rollup batch_bytes >>=? fun operation -> - Block.bake ~operation b >>=? fun b -> + add_operation b operation >>=? fun b -> (* Make an invalid commitment for the submitted transfer *) let level = Tx_rollup_level.(succ root) in - Incremental.begin_construction b >>=? fun i -> - make_valid_commitment_for_messages (I i) ~level ~tx_rollup ~store [message] + make_valid_commitment_for_messages (B b) ~level ~tx_rollup ~store [message] >>=? fun commitment -> - Op.tx_rollup_commit (I i) account tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> + Op.tx_rollup_commit (B b) account tx_rollup commitment >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Now we produce a valid proof rejecting the commitment *) l2_parameters (B b) >>=? fun l2_parameters -> make_proof store l2_parameters message >>= fun proof -> @@ -2573,6 +2616,7 @@ module Rejection = struct ~previous_message_result:(message_result l2_context_hash []) ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -2602,26 +2646,25 @@ module Rejection = struct Context.Contract.balance (B b) contract1 >>=? fun balance -> Context.Contract.balance (B b) contract2 >>=? fun balance2 -> (* [check_frozen] checks that contract1 has [expect] frozen tez. *) - let check_frozen ~loc i expect = + let check_frozen ~loc b expect = + Incremental.begin_construction b >>=? fun i -> Contract.get_frozen_bonds (Incremental.alpha_ctxt i) contract1 >>=?? fun frozen -> Assert.equal_tez ~loc expect frozen in - Incremental.begin_construction b >>=? fun i -> (* Nothing frozen to start *) - check_frozen ~loc:__LOC__ i Tez.zero >>=? fun () -> + check_frozen ~loc:__LOC__ b Tez.zero >>=? fun () -> (* No-op batch for second inbox *) Op.tx_rollup_submit_batch (B b) contract1 tx_rollup "fake" >>=? fun operation -> - Block.bake ~operation b >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> + add_operation b operation >>=? fun b -> l2_parameters (B b) >>=? fun l2_parameters -> let message, _ = Tx_rollup_message.make_batch "fake" in let message_hash = Tx_rollup_message_hash.hash_uncarbonated message in let message_path = single_message_path message_hash in hash_tree_from_store store >>= fun l2_context_hash -> - let make_invalid_commitment i level h = + let make_invalid_commitment b level h = (* Make some invalid commitments for the submitted messages *) - make_incomplete_commitment_for_batch (I i) level tx_rollup [] + make_incomplete_commitment_for_batch (B b) level tx_rollup [] >>=? fun (commitment, _) -> (* Make this commitment bogus *) let message_result = @@ -2635,22 +2678,21 @@ module Rejection = struct Tx_rollup_message_result_hash.hash_uncarbonated message_result in let commitment = {commitment with messages = [message_result_hash]} in - Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >|=? fun i -> (i, commitment) + Op.tx_rollup_commit (B b) contract1 tx_rollup commitment + >>=? fun operation -> + add_operation b operation >|=? fun b -> (b, commitment) in let level0 = tx_level 0l in let level1 = tx_level 1l in - make_invalid_commitment i level0 l2_context_hash - >>=? fun (i, commitment0) -> - make_invalid_commitment i level1 Context_hash.zero - >>=? fun (i, commitment1) -> - Context.get_constants (I i) >>=? fun constants -> + make_invalid_commitment b level0 l2_context_hash + >>=? fun (b, commitment0) -> + make_invalid_commitment b level1 Context_hash.zero + >>=? fun (b, commitment1) -> + Context.get_constants (B b) >>=? fun constants -> let bond_cost = constants.parametric.tx_rollup.commitment_bond in - Assert.balance_was_debited ~loc:__LOC__ (I i) contract1 balance bond_cost + Assert.balance_was_debited ~loc:__LOC__ (B b) contract1 balance bond_cost >>=? fun () -> - check_frozen ~loc:__LOC__ i bond_cost >>=? fun () -> - Incremental.finalize_block i >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> + check_frozen ~loc:__LOC__ b bond_cost >>=? fun () -> (* Now we produce a valid proof rejecting the second commitment *) make_proof store l2_parameters message >>= fun proof -> let message_position = 0 in @@ -2658,7 +2700,7 @@ module Rejection = struct message_result_hash_and_path commitment1 ~message_position in Op.tx_rollup_reject - (I i) + (B b) contract2 tx_rollup level1 @@ -2670,19 +2712,19 @@ module Rejection = struct ~proof ~previous_message_result:(message_result l2_context_hash []) ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path - >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - check_bond (Incremental.alpha_ctxt i) tx_rollup contract1 0 >>=? fun () -> - Assert.balance_was_debited ~loc:__LOC__ (I i) contract1 balance bond_cost + >>=? fun operation -> + add_operation b operation >>=? fun b -> + check_bond_from_block b tx_rollup contract1 0 >>=? fun () -> + Assert.balance_was_debited ~loc:__LOC__ (B b) contract1 balance bond_cost >>=? fun () -> (* Now we need to check that the tez is really gone -- not just frozen *) - check_frozen ~loc:__LOC__ i Tez.zero >>=? fun () -> + check_frozen ~loc:__LOC__ b Tez.zero >>=? fun () -> let reward = assert_ok Tez.(bond_cost /? 2L) in - Assert.balance_was_credited ~loc:__LOC__ (I i) contract2 balance2 reward + Assert.balance_was_credited ~loc:__LOC__ (B b) contract2 balance2 reward >>=? fun () -> (* Now, we can still reject the root commitment, but we won't get a reward *) - Context.Contract.balance (I i) contract1 >>=? fun balance -> - Context.Contract.balance (I i) contract2 >>=? fun balance2 -> + Context.Contract.balance (B b) contract1 >>=? fun balance -> + Context.Contract.balance (B b) contract2 >>=? fun balance2 -> make_proof store l2_parameters deposit_message >>= fun proof -> let message_hash = Tx_rollup_message_hash.hash_uncarbonated deposit_message @@ -2693,7 +2735,7 @@ module Rejection = struct message_result_hash_and_path commitment0 ~message_position in Op.tx_rollup_reject - (I i) + (B b) contract2 tx_rollup level0 @@ -2705,14 +2747,14 @@ module Rejection = struct ~proof ~previous_message_result:(message_result l2_context_hash []) ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path - >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - check_bond (Incremental.alpha_ctxt i) tx_rollup contract1 0 >>=? fun () -> - Assert.balance_was_debited ~loc:__LOC__ (I i) contract1 balance Tez.zero + >>=? fun operation -> + add_operation b operation >>=? fun b -> + check_bond_from_block b tx_rollup contract1 0 >>=? fun () -> + Assert.balance_was_debited ~loc:__LOC__ (B b) contract1 balance Tez.zero >>=? fun () -> (* Now we need to check that the tez still really gone -- not just frozen *) - check_frozen ~loc:__LOC__ i Tez.zero >>=? fun () -> - Assert.balance_was_credited ~loc:__LOC__ (I i) contract2 balance2 Tez.zero + check_frozen ~loc:__LOC__ b Tez.zero >>=? fun () -> + Assert.balance_was_credited ~loc:__LOC__ (B b) contract2 balance2 Tez.zero (** Test the proof production (used in this test file) and the proof verification handles a hard failure. [make_bad_message] makes a @@ -2728,15 +2770,13 @@ module Rejection = struct let message_path = single_message_path message_hash in Op.tx_rollup_submit_batch (B b) account tx_rollup batch_bytes >>=? fun operation -> - Block.bake ~operation b >>=? fun b -> + add_operation b operation >>=? fun b -> (* Make an invalid commitment for the submitted transfer *) let level = Tx_rollup_level.(succ root) in - Incremental.begin_construction b >>=? fun i -> - make_incomplete_commitment_for_batch (I i) level tx_rollup [] + make_incomplete_commitment_for_batch (B b) level tx_rollup [] >>=? fun (commitment, _) -> - Op.tx_rollup_commit (I i) account tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> + Op.tx_rollup_commit (B b) account tx_rollup commitment >>=? fun operation -> + add_operation b operation >>=? fun b -> (* Now we produce a valid proof rejecting the commitment *) l2_parameters (B b) >>=? fun l2_parameters -> make_proof store l2_parameters message >>= fun proof -> @@ -2758,6 +2798,7 @@ module Rejection = struct ~previous_message_result:(message_result l2_context_hash []) ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op >>=? fun i -> check_bond (Incremental.alpha_ctxt i) tx_rollup account 0 >>=? fun () -> return_unit @@ -2812,18 +2853,18 @@ module Rejection = struct (** Test that an empty proof is not able to reject a valid commitment. *) let test_empty_proof_on_invalid_message () = init_with_valid_commitment () - >>=? fun (i, contract, tx_rollup, level, message, commitment) -> + >>=? fun (b, contract, tx_rollup, level, message, commitment) -> let msg, _ = Tx_rollup_message.make_batch message in let message_hash = Tx_rollup_message_hash.hash_uncarbonated msg in let message_path = single_message_path message_hash in - l2_parameters (I i) >>=? fun l2_parameters -> + l2_parameters (B b) >>=? fun l2_parameters -> valid_empty_proof l2_parameters >>= fun proof -> let message_position = 0 in let message_result_hash, message_result_path = message_result_hash_and_path commitment ~message_position in Op.tx_rollup_reject - (I i) + (B b) contract tx_rollup level @@ -2835,13 +2876,13 @@ module Rejection = struct ~proof ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path - >>=? fun op -> - Incremental.add_operation i op >>=? fun _ -> return_unit + >>=? fun operation -> + add_operation b operation >>=? fun _ -> return_unit (** Test that an empty proof is not able to reject a valid commitment. *) let test_invalid_proof_on_invalid_commitment () = init_with_valid_commitment () - >>=? fun (i, contract, tx_rollup, level, message, commitment) -> + >>=? fun (b, contract, tx_rollup, level, message, commitment) -> let msg, _ = Tx_rollup_message.make_batch message in let message_hash = Tx_rollup_message_hash.hash_uncarbonated msg in let message_path = single_message_path message_hash in @@ -2850,7 +2891,7 @@ module Rejection = struct message_result_hash_and_path commitment ~message_position in Op.tx_rollup_reject - (I i) + (B b) contract tx_rollup level @@ -2863,6 +2904,7 @@ module Rejection = struct ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -2874,7 +2916,7 @@ module Rejection = struct the previous state. *) let test_invalid_agreed () = init_with_valid_commitment () - >>=? fun (i, contract, tx_rollup, level, message, commitment) -> + >>=? fun (b, contract, tx_rollup, level, message, commitment) -> let msg, _ = Tx_rollup_message.make_batch message in (* This intentionally does not match *) let previous_message_result : Tx_rollup_message_result.t = @@ -2891,7 +2933,7 @@ module Rejection = struct message_result_hash_and_path commitment ~message_position in Op.tx_rollup_reject - (I i) + (B b) contract tx_rollup level @@ -2904,6 +2946,7 @@ module Rejection = struct ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -2929,7 +2972,7 @@ module Rejection = struct let message = "bogus" in Op.tx_rollup_submit_batch (B b) contract tx_rollup message >>=? fun operation -> - Block.bake ~operation b >>=? fun b -> + add_operation b operation >>=? fun b -> Incremental.begin_construction b >>=? fun i -> let level = Tx_rollup_level.root in let message, _size = Tx_rollup_message.make_batch message in @@ -2964,31 +3007,31 @@ module Rejection = struct already final *) let test_commitment_is_final () = init_with_valid_commitment () - >>=? fun (i, contract, tx_rollup, level, message, commitment) -> + >>=? fun (b, contract, tx_rollup, level, message, commitment) -> (* Create a new commitment so that once we have finalized the first one, we still have a range of valid final commitments *) - Op.tx_rollup_submit_batch (I i) contract tx_rollup message >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> + Op.tx_rollup_submit_batch (B b) contract tx_rollup message + >>=? fun operation -> + add_operation b operation >>=? fun b -> let level2 = Tx_rollup_level.succ level in - make_incomplete_commitment_for_batch (I i) level2 tx_rollup [] + make_incomplete_commitment_for_batch (B b) level2 tx_rollup [] >>=? fun (commitment2, _) -> - Op.tx_rollup_commit (I i) contract tx_rollup commitment2 >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Op.tx_rollup_finalize (I i) contract tx_rollup >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> + Op.tx_rollup_commit (B b) contract tx_rollup commitment2 + >>=? fun operation -> + add_operation b operation >>=? fun b -> + Op.tx_rollup_finalize (B b) contract tx_rollup >>=? fun operation -> + add_operation b operation >>=? fun b -> let message, _size = Tx_rollup_message.make_batch message in let message_hash = Tx_rollup_message_hash.hash_uncarbonated message in let message_path = single_message_path message_hash in - l2_parameters (I i) >>=? fun l2_parameters -> + l2_parameters (B b) >>=? fun l2_parameters -> valid_empty_proof l2_parameters >>= fun proof -> let message_position = 0 in let message_result_hash, message_result_path = message_result_hash_and_path commitment ~message_position in Op.tx_rollup_reject - (I i) + (B b) contract tx_rollup level @@ -3001,6 +3044,7 @@ module Rejection = struct ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -3014,7 +3058,7 @@ module Rejection = struct match the one stored in the inbox *) let test_wrong_message_hash () = init_with_valid_commitment () - >>=? fun (i, contract1, tx_rollup, level, prev_message, commitment) -> + >>=? fun (b, contract1, tx_rollup, level, prev_message, commitment) -> let prev_message, _size = Tx_rollup_message.make_batch prev_message in let prev_message_hash = Tx_rollup_message_hash.hash_uncarbonated prev_message @@ -3025,14 +3069,14 @@ module Rejection = struct let message, _size = Tx_rollup_message.make_batch "wrong message" in let message_hash = Tx_rollup_message_hash.hash_uncarbonated message in let message_path = single_message_path message_hash in - l2_parameters (I i) >>=? fun l2_parameters -> + l2_parameters (B b) >>=? fun l2_parameters -> valid_empty_proof l2_parameters >>= fun proof -> let message_position = 0 in let message_result_hash, message_result_path = message_result_hash_and_path commitment ~message_position in Op.tx_rollup_reject - (I i) + (B b) contract1 tx_rollup level @@ -3045,6 +3089,7 @@ module Rejection = struct ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -3057,14 +3102,14 @@ module Rejection = struct exist in the inbox. *) let test_wrong_message_position () = init_with_valid_commitment () - >>=? fun (i, contract1, tx_rollup, level, message, _commitment) -> + >>=? fun (b, contract1, tx_rollup, level, message, _commitment) -> let message, _size = Tx_rollup_message.make_batch message in let message_hash = Tx_rollup_message_hash.hash_uncarbonated message in let message_path = single_message_path message_hash in - l2_parameters (I i) >>=? fun l2_parameters -> + l2_parameters (B b) >>=? fun l2_parameters -> valid_empty_proof l2_parameters >>= fun proof -> Op.tx_rollup_reject - (I i) + (B b) contract1 tx_rollup level @@ -3077,6 +3122,7 @@ module Rejection = struct ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op @@ -3096,19 +3142,16 @@ module Rejection = struct make_deposit b tx_rollup account addr >>=? fun (b, (deposit, _), _) -> let message_hash = Tx_rollup_message_hash.hash_uncarbonated deposit in let message_path = single_message_path message_hash in - Incremental.begin_construction b >>=? fun i -> - make_incomplete_commitment_for_batch (I i) Tx_rollup_level.root tx_rollup [] + make_incomplete_commitment_for_batch (B b) Tx_rollup_level.root tx_rollup [] >>=? fun (commitment, _) -> - Op.tx_rollup_commit (I i) account tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> + Op.tx_rollup_commit (B b) account tx_rollup commitment >>=? fun operation -> + add_operation b operation >>=? fun b -> let message_position = 0 in let message_result_hash, message_result_path = message_result_hash_and_path commitment ~message_position in Op.tx_rollup_reject - (I i) + (B b) account tx_rollup Tx_rollup_level.root @@ -3121,17 +3164,18 @@ module Rejection = struct ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op ~expect_apply_failure: (check_proto_error Tx_rollup_errors.Proof_failed_to_reject) - >>=? fun i -> + >>=? fun _i -> (* Check with a reasonable proof *) - l2_parameters (I i) >>=? fun l2_parameters -> + l2_parameters (B b) >>=? fun l2_parameters -> make_proof store l2_parameters deposit >>= fun proof -> Op.tx_rollup_reject - (I i) + (B b) account tx_rollup Tx_rollup_level.root @@ -3143,8 +3187,8 @@ module Rejection = struct ~proof ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path - >>=? fun op -> - Incremental.add_operation i op >>=? fun _ -> return_unit + >>=? fun operation -> + add_operation b operation >>=? fun _ -> return_unit let add_store_to_ctxt ctxt store = let open L2_Context.Syntax in @@ -3314,15 +3358,14 @@ module Rejection = struct (* 2. Submit and commit the batch. *) Op.tx_rollup_submit_batch (B b) account tx_rollup batch_bytes >>=? fun operation -> - Block.bake ~operation b >>=? fun b -> + add_operation b operation >>=? fun b -> let level = Tx_rollup_level.(succ root) in let withdrawals = Stdlib.List.init n_withdraw (fun _ -> Tx_rollup_withdraw.{claimer = destination; ticket_hash; amount = qty}) in - Incremental.begin_construction b >>=? fun i -> make_incomplete_commitment_for_batch - (I i) + (B b) level tx_rollup [(0, withdrawals)] @@ -3345,11 +3388,10 @@ module Rejection = struct } in let commitment = {commitment with messages = [result_hash]} in - Op.tx_rollup_commit (I i) account tx_rollup commitment >>=? fun op -> - Incremental.add_operation i op >>=? fun i -> - Incremental.finalize_block i >>=? fun b -> + Op.tx_rollup_commit (B b) account tx_rollup commitment >>=? fun operation -> + add_operation b operation >>=? fun b -> (* 4. Now we create a proof that used the correct layer2 apply with - the correct parameters. *) + the correct parameters. *) l2_parameters (B b) >>=? fun l2_parameters -> make_proof store l2_parameters message >>= fun proof -> let previous_message_result : Tx_rollup_message_result.t = @@ -3376,6 +3418,7 @@ module Rejection = struct ~previous_message_result ~previous_message_result_path:Tx_rollup_commitment.Merkle.dummy_path >>=? fun op -> + Incremental.begin_construction b >>=? fun i -> Incremental.add_operation i op ?expect_apply_failure >>=? fun _ -> return_unit diff --git a/src/proto_alpha/lib_protocol/test/integration/precheck/test_batched_manager_operation_precheck.ml b/src/proto_alpha/lib_protocol/test/integration/precheck/test_batched_manager_operation_precheck.ml index ed2a54779f4a62dc446d6975daa041f15de50e36..28bcc3c6fb299a816383446b6a6b54d11a765a5b 100644 --- a/src/proto_alpha/lib_protocol/test/integration/precheck/test_batched_manager_operation_precheck.ml +++ b/src/proto_alpha/lib_protocol/test/integration/precheck/test_batched_manager_operation_precheck.ml @@ -43,7 +43,10 @@ open Manager_operation_helpers let batch_reveal_in_the_middle_diagnostic (infos : infos) op = let expect_failure errs = match errs with - | [Environment.Ecoproto_error Apply.Incorrect_reveal_position] -> + | [ + Environment.Ecoproto_error + Validate_operation.Manager.Incorrect_reveal_position; + ] -> return_unit | err -> failwith @@ -88,7 +91,10 @@ let generate_batches_reveal_in_the_middle () = let batch_two_reveals_diagnostic (infos : infos) op = let expected_failure errs = match errs with - | [Environment.Ecoproto_error Apply.Incorrect_reveal_position] -> + | [ + Environment.Ecoproto_error + Validate_operation.Manager.Incorrect_reveal_position; + ] -> return_unit | err -> failwith @@ -131,7 +137,10 @@ let generate_tests_batches_two_reveals () = let batch_two_sources_diagnostic (infos : infos) op = let expect_failure errs = match errs with - | [Environment.Ecoproto_error Apply.Inconsistent_sources] -> return_unit + | [ + Environment.Ecoproto_error Validate_operation.Manager.Inconsistent_sources; + ] -> + return_unit | err -> failwith "Error trace:@, %a does not match the expected one" @@ -233,7 +242,10 @@ let test_batch_inconsistent_counters kind1 kind2 () = in let expect_failure errs = match errs with - | [Environment.Ecoproto_error Apply.Inconsistent_counters] -> return_unit + | [ + Environment.Ecoproto_error Validate_operation.Manager.Inconsistent_counters; + ] -> + return_unit | err -> failwith "Error trace:@, %a does not match the expected one" diff --git a/src/proto_alpha/lib_protocol/test/integration/precheck/test_manager_operation_precheck.ml b/src/proto_alpha/lib_protocol/test/integration/precheck/test_manager_operation_precheck.ml index e9e2ca11ed121dcdfc7f9542451aba253e2ccf93..cdaf8a886d2af12d009530aae05071faced88b53 100644 --- a/src/proto_alpha/lib_protocol/test/integration/precheck/test_manager_operation_precheck.ml +++ b/src/proto_alpha/lib_protocol/test/integration/precheck/test_manager_operation_precheck.ml @@ -119,7 +119,8 @@ let low_gas_limit_diagnostic (infos : infos) op = let expect_failure errs = match errs with | [ - Environment.Ecoproto_error Apply.Gas_quota_exceeded_init_deserialize; + Environment.Ecoproto_error + Validate_operation.Manager.Gas_quota_exceeded_init_deserialize; Environment.Ecoproto_error Raw_context.Operation_quota_exceeded; ] -> return_unit @@ -346,7 +347,10 @@ let generate_unrevealed_key () = let high_fee_diagnostic (infos : infos) op = let expect_failure errs = match errs with - | [Environment.Ecoproto_error (Contract_storage.Balance_too_low _)] -> + | [ + Environment.Ecoproto_error (Contract_storage.Balance_too_low _); + Environment.Ecoproto_error (Tez_repr.Subtraction_underflow _); + ] -> return_unit | err -> failwith diff --git a/src/proto_alpha/lib_protocol/tez_repr.mli b/src/proto_alpha/lib_protocol/tez_repr.mli index 50b4c79e7f0f674d7d50147492bf691be68f565b..d602044d3f2e573b38aa8e7ace05382f0654f7ff 100644 --- a/src/proto_alpha/lib_protocol/tez_repr.mli +++ b/src/proto_alpha/lib_protocol/tez_repr.mli @@ -38,6 +38,8 @@ type repr parameters of [Script_typed_ir.ty]. *) type t = Tez_tag of repr [@@ocaml.unboxed] +type error += Subtraction_underflow of t * t (* `Temporary *) + type tez = t val zero : t diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml new file mode 100644 index 0000000000000000000000000000000000000000..42c5e4fd911742fa34f3c781ee42e4d0e1ee5ee2 --- /dev/null +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -0,0 +1,737 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs *) +(* *) +(* 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 Alpha_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. *) +type manager_info = { + hard_storage_limit_per_operation : Z.t; + hard_gas_limit_per_operation : Gas.Arith.integral; +} + +let init_manager_info ctxt = + { + hard_storage_limit_per_operation = + Constants.hard_storage_limit_per_operation ctxt; + hard_gas_limit_per_operation = Constants.hard_gas_limit_per_operation ctxt; + } + +(** State used and modified when validating manager operations. *) +type manager_state = { + managers_seen : Operation_hash.t Signature.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; +} + +let init_manager_state ctxt = + { + managers_seen = Signature.Public_key_hash.Map.empty; + remaining_block_gas = Gas.Arith.fp (Constants.hard_gas_limit_per_block ctxt); + } + +(* If you add a new mode, please make sure that it has a way to bound + the size of the map {!recfield:managers_seen}. *) +type mode = Block | Mempool + +type validate_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; +} + +type validate_operation_state = {manager_state : manager_state} + +let init_validate_operation_info ctxt mode chain_id = + { + ctxt; + mode; + chain_id; + current_level = Level.current ctxt; + manager_info = init_manager_info ctxt; + } + +let init_validate_operation_state ctxt = + {manager_state = init_manager_state ctxt} + +let init_info_and_state ctxt mode chain_id = + let vi = init_validate_operation_info ctxt mode chain_id in + let vs = init_validate_operation_state ctxt in + (vi, vs) + +(* See mli file. *) +type stamp = Operation_validated_stamp + +module Manager = struct + type error += + | Manager_restriction of Signature.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_disabled + + let () = + 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:(fun ppf (d, hash) -> + Format.fprintf + ppf + "Manager %a already has the operation %a in the current block." + Signature.Public_key_hash.pp + d + Operation_hash.pp + hash) + 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)) ; + let inconsistent_sources_description = + "The operation batch includes operations from different sources." + in + register_error_kind + `Permanent + ~id:"validate_operation.inconsistent_sources" + ~title:"Inconsistent sources in operation batch" + ~description:inconsistent_sources_description + ~pp:(fun ppf () -> + Format.fprintf ppf "%s" inconsistent_sources_description) + Data_encoding.empty + (function Inconsistent_sources -> Some () | _ -> None) + (fun () -> Inconsistent_sources) ; + let inconsistent_counters_description = + "Inconsistent counters in operation. Counters of an operation must be \ + successive." + in + register_error_kind + `Permanent + ~id:"validate_operation.inconsistent_counters" + ~title:"Inconsistent counters in operation" + ~description:inconsistent_counters_description + ~pp:(fun ppf () -> + Format.fprintf ppf "%s" inconsistent_counters_description) + Data_encoding.empty + (function Inconsistent_counters -> Some () | _ -> None) + (fun () -> Inconsistent_counters) ; + let incorrect_reveal_description = + "Incorrect reveal operation position in batch: only allowed in first \ + position." + in + register_error_kind + `Permanent + ~id:"validate_operation.incorrect_reveal_position" + ~title:"Incorrect reveal position" + ~description:incorrect_reveal_description + ~pp:(fun ppf () -> Format.fprintf ppf "%s" incorrect_reveal_description) + Data_encoding.empty + (function Incorrect_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_cost + Michelson_v1_gas.Cost_of.manager_operation) + Data_encoding.empty + (function Insufficient_gas_for_manager -> Some () | _ -> None) + (fun () -> Insufficient_gas_for_manager) ; + let gas_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." + in + register_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:(fun ppf () -> Format.fprintf ppf "%s" gas_deserialize_description) + Data_encoding.empty + (function Gas_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:(fun ppf () -> + Format.fprintf + ppf + "Cannot apply a tx rollup operation as it is disabled. This feature \ + will be enabled in a future proposal") + Data_encoding.unit + (function Tx_rollup_feature_disabled -> Some () | _ -> None) + (fun () -> Tx_rollup_feature_disabled) ; + let scoru_disabled_description = + "Smart contract rollups will be enabled in a future proposal." + in + register_error_kind + `Permanent + ~id:"validate_operation.sc_rollup_disabled" + ~title:"Smart contract rollups are disabled" + ~description:scoru_disabled_description + ~pp:(fun ppf () -> Format.fprintf ppf "%s" scoru_disabled_description) + Data_encoding.unit + (function Sc_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. *) + type batch_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. *) + let check_sanity_and_find_public_key vi vs + (contents_list : _ Kind.manager contents_list) = + let open Result_syntax in + let check_source_and_counter ~expected_source ~source ~previous_counter + ~counter = + let* () = + error_unless + (Signature.Public_key_hash.equal expected_source source) + Inconsistent_sources + in + error_unless + Compare.Z.(Z.succ previous_counter = counter) + Inconsistent_counters + in + let rec check_batch_tail_sanity : + type kind. + public_key_hash -> + counter -> + kind Kind.manager contents_list -> + unit tzresult = + fun expected_source previous_counter -> function + | Single (Manager_operation {operation = Reveal _key; _}) -> + error Incorrect_reveal_position + | Cons (Manager_operation {operation = Reveal _key; _}, _res) -> + error Incorrect_reveal_position + | Single (Manager_operation {source; counter; _}) -> + check_source_and_counter + ~expected_source + ~source + ~previous_counter + ~counter + | Cons (Manager_operation {source; counter; _}, rest) -> + let open Result_syntax in + let* () = + check_source_and_counter + ~expected_source + ~source + ~previous_counter + ~counter + in + check_batch_tail_sanity source counter rest + in + let check_batch : + type kind. + kind Kind.manager contents_list -> + (public_key_hash * public_key option * counter) tzresult = + fun contents_list -> + match contents_list with + | Single (Manager_operation {source; operation = Reveal key; counter; _}) + -> + ok (source, Some key, counter) + | Single (Manager_operation {source; counter; _}) -> + ok (source, None, counter) + | Cons + (Manager_operation {source; operation = Reveal key; counter; _}, rest) + -> + check_batch_tail_sanity source counter rest >>? fun () -> + ok (source, Some key, counter) + | Cons (Manager_operation {source; counter; _}, rest) -> + check_batch_tail_sanity source counter rest >>? fun () -> + ok (source, None, counter) + in + let open Lwt_result_syntax in + let*? source, revealed_key, first_counter = check_batch contents_list in + let* balance = Contract.check_allocated_and_get_balance vi.ctxt source in + let* () = Contract.check_counter_increment vi.ctxt source first_counter in + let* pk = + match revealed_key with + | Some pk -> return pk + | None -> Contract.get_manager_key vi.ctxt source + in + let initial_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; + } + in + return (initial_batch_state, pk) + + let check_gas_limit_and_consume_from_block_gas vi ~remaining_block_gas + ~gas_limit = + (match vi.mode with + | Block -> fun res -> 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_trace Gas.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) + + let check_storage_limit vi storage_limit = + error_unless + Compare.Z.( + storage_limit <= vi.manager_info.hard_storage_limit_per_operation + && storage_limit >= Z.zero) + Fees.Storage_limit_too_high + + let assert_tx_rollup_feature_enabled vi = + let open Result_syntax in + let* sunset = + Raw_level.of_int32 (Constants.tx_rollup_sunset_level vi.ctxt) + in + error_unless + (Constants.tx_rollup_enable vi.ctxt + && Raw_level.(vi.current_level.level < sunset)) + Tx_rollup_feature_disabled + + let assert_sc_rollup_feature_enabled vi = + error_unless (Constants.sc_rollup_enable vi.ctxt) Sc_rollup_feature_disabled + + let consume_decoding_gas ctxt lexpr = + record_trace Gas_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_gas ctxt lexpr + + let validate_tx_rollup_submit_batch vi remaining_gas content = + let open Result_syntax in + let* () = assert_tx_rollup_feature_enabled vi in + let size_limit = Constants.tx_rollup_hard_size_limit_per_message vi.ctxt in + let _message, message_size = Tx_rollup_message.make_batch content in + let* cost = Tx_rollup_gas.hash_cost message_size in + let* remaining_gas = Gas.consume_from remaining_gas cost in + let* () = + error_unless + Compare.Int.(message_size <= size_limit) + Tx_rollup_errors.Message_size_exceeds_limit + in + return remaining_gas + + let validate_tx_rollup_dispatch_tickets vi remaining_gas operation = + let open Result_syntax in + let* () = assert_tx_rollup_feature_enabled vi in + let (Tx_rollup_dispatch_tickets {tickets_info; message_result_path; _}) = + operation + in + let Constants.Parametric. + {max_messages_per_inbox; max_withdrawals_per_batch; _} = + Constants.tx_rollup vi.ctxt + in + let* () = + Tx_rollup_errors.check_path_depth + `Commitment + (Tx_rollup_commitment.Merkle.path_depth message_result_path) + ~count_limit:max_messages_per_inbox + in + let* () = + error_when + Compare.List_length_with.(tickets_info = 0) + Tx_rollup_errors.No_withdrawals_to_dispatch + in + let* () = + error_when + Compare.List_length_with.(tickets_info > max_withdrawals_per_batch) + Tx_rollup_errors.Too_many_withdrawals + in + record_trace + Gas_quota_exceeded_init_deserialize + (List.fold_left_e + (fun remaining_gas Tx_rollup_reveal.{contents; ty; _} -> + let* remaining_gas = + Script.consume_decoding_gas remaining_gas contents + in + Script.consume_decoding_gas remaining_gas ty) + remaining_gas + tickets_info) + + let validate_tx_rollup_rejection vi operation = + let open Result_syntax in + let* () = assert_tx_rollup_feature_enabled vi in + let (Tx_rollup_rejection + {message_path; message_result_path; previous_message_result_path; _}) + = + operation + in + let Constants.Parametric.{max_messages_per_inbox; _} = + Constants.tx_rollup vi.ctxt + in + let* () = + Tx_rollup_errors.check_path_depth + `Inbox + (Tx_rollup_inbox.Merkle.path_depth message_path) + ~count_limit:max_messages_per_inbox + in + let* () = + Tx_rollup_errors.check_path_depth + `Commitment + (Tx_rollup_commitment.Merkle.path_depth message_result_path) + ~count_limit:max_messages_per_inbox + in + Tx_rollup_errors.check_path_depth + `Commitment + (Tx_rollup_commitment.Merkle.path_depth previous_message_result_path) + ~count_limit:max_messages_per_inbox + + let validate_contents (type kind) vi batch_state + (contents : kind Kind.manager contents) = + let open Lwt_result_syntax in + let (Manager_operation + {source; fee; counter = _; operation; gas_limit; storage_limit}) = + contents + in + let*? remaining_block_gas = + check_gas_limit_and_consume_from_block_gas + vi + ~remaining_block_gas:batch_state.remaining_block_gas + ~gas_limit + in + let*? remaining_gas = + record_trace + Insufficient_gas_for_manager + (Gas.consume_from + (Gas.Arith.fp gas_limit) + Michelson_v1_gas.Cost_of.manager_operation) + in + let*? () = check_storage_limit vi storage_limit in + let*? () = + (* {!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_unless + batch_state.is_allocated + (Contract_storage.Empty_implicit_contract source) + in + let*? (_remaining_gas : Gas.Arith.fp) = + let open Result_syntax in + match operation with + | Reveal pk -> + let* () = Contract.check_public_key pk source in + return remaining_gas + | Transaction {parameters; _} -> + consume_decoding_gas remaining_gas parameters + | Origination {script; _} -> + let* remaining_gas = consume_decoding_gas remaining_gas script.code in + consume_decoding_gas remaining_gas script.storage + | Register_global_constant {value} -> + consume_decoding_gas remaining_gas value + | Delegation _ | Set_deposits_limit _ -> return remaining_gas + | Tx_rollup_origination -> + let* () = assert_tx_rollup_feature_enabled vi in + return remaining_gas + | Tx_rollup_submit_batch {content; _} -> + validate_tx_rollup_submit_batch vi remaining_gas content + | Tx_rollup_commit _ | Tx_rollup_return_bond _ + | Tx_rollup_finalize_commitment _ | Tx_rollup_remove_commitment _ -> + let* () = assert_tx_rollup_feature_enabled vi in + return remaining_gas + | Transfer_ticket {contents; ty; _} -> + let* () = assert_tx_rollup_feature_enabled vi in + let* remaining_gas = consume_decoding_gas remaining_gas contents in + consume_decoding_gas remaining_gas ty + | Tx_rollup_dispatch_tickets _ -> + validate_tx_rollup_dispatch_tickets vi remaining_gas operation + | Tx_rollup_rejection _ -> + let* () = validate_tx_rollup_rejection vi operation in + return remaining_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_enabled vi in + return remaining_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_enabled vi in + return remaining_gas + | Dal_publish_slot_header {slot} -> + let* () = Dal_apply.validate_publish_slot_header vi.ctxt slot in + return remaining_gas + in + let* balance, is_allocated = + Contract.simulate_spending + vi.ctxt + ~balance:batch_state.balance + ~amount:fee + source + in + return {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]. *) + let rec validate_contents_list : + type kind. + validate_operation_info -> + batch_state -> + kind Kind.manager contents_list -> + batch_state tzresult Lwt.t = + fun vi batch_state contents_list -> + let open Lwt_result_syntax in + match contents_list with + | Single contents -> validate_contents vi batch_state contents + | Cons (contents, tail) -> + let* batch_state = validate_contents vi batch_state contents in + validate_contents_list vi batch_state tail + + (** 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. *) + let maybe_update_remaining_block_gas vi vs batch_state = + match vi.mode with + | Block -> batch_state.remaining_block_gas + | Mempool -> vs.manager_state.remaining_block_gas + + let validate_manager_operation vi vs source oph (type kind) + (operation : kind Kind.manager operation) = + let open Lwt_result_syntax in + let*? () = + (* 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}. *) + match + Signature.Public_key_hash.Map.find source vs.manager_state.managers_seen + with + | None -> Result.return_unit + | Some conflicting_oph -> + error (Manager_restriction (source, conflicting_oph)) + in + let contents_list = operation.protocol_data.contents in + let* batch_state, source_pk = + check_sanity_and_find_public_key vi vs contents_list + in + let* batch_state = validate_contents_list vi batch_state contents_list in + let*? () = Operation.check_signature source_pk vi.chain_id operation in + let managers_seen = + Signature.Public_key_hash.Map.add + source + oph + vs.manager_state.managers_seen + in + let remaining_block_gas = + maybe_update_remaining_block_gas vi vs batch_state + in + return {manager_state = {managers_seen; remaining_block_gas}} +end + +let validate_operation (vi : validate_operation_info) + (vs : validate_operation_state) oph (type kind) (operation : kind operation) + = + let open Lwt_result_syntax in + let* vs = + match operation.protocol_data.contents with + | Single (Manager_operation {source; _}) -> + Manager.validate_manager_operation vi vs source oph operation + | Cons (Manager_operation {source; _}, _) -> + Manager.validate_manager_operation vi vs source oph operation + | Single (Preendorsement _) + | Single (Endorsement _) + | Single (Dal_slot_availability _) + | Single (Seed_nonce_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). *) + return vs + in + return (vs, Operation_validated_stamp) + +module TMP_for_plugin = struct + type 'a should_check_signature = + | Check_signature of 'a operation + | Skip_signature_check + + let precheck_manager vi vs contents_list should_check_signature = + let open Lwt_result_syntax in + let open Manager in + let* batch_state, source_pk = + check_sanity_and_find_public_key vi vs contents_list + in + let* _batch_state = validate_contents_list vi batch_state contents_list in + let*? () = + match should_check_signature with + | Check_signature operation -> + Operation.check_signature source_pk vi.chain_id operation + | Skip_signature_check -> ok () + in + return Operation_validated_stamp + + let precheck_manager__do_nothing_on_non_manager_op ctxt chain_id (type kind) + (contents_list : kind contents_list) should_check_signature = + let handle_manager (type a) (contents_list : a Kind.manager contents_list) = + let vi, vs = init_info_and_state ctxt Mempool chain_id in + precheck_manager vi vs contents_list should_check_signature + in + match contents_list with + | Single (Manager_operation _) -> handle_manager contents_list + | Cons (Manager_operation _, _) -> handle_manager contents_list + | Single (Preendorsement _) + | Single (Endorsement _) + | Single (Dal_slot_availability _) + | Single (Seed_nonce_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) *) + return Operation_validated_stamp +end diff --git a/src/proto_alpha/lib_protocol/validate_operation.mli b/src/proto_alpha/lib_protocol/validate_operation.mli new file mode 100644 index 0000000000000000000000000000000000000000..d0d60956d6df8d0ff60753e9aa4ccff8acb87210 --- /dev/null +++ b/src/proto_alpha/lib_protocol/validate_operation.mli @@ -0,0 +1,211 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** The purpose of this module is to provide the {!validate_operation} + function, that decides quickly whether an operation may safely be + included in a block. See the function's description for further + information. + + Most elements in this module are either used or wrapped in the + {!Main} module. *) + +(** Static information needed in {!validate_operation}. + + It lives in memory, not in the storage. *) +type validate_operation_info + +(** State used and modified by {!validate_operation}. + + It lives in memory, not in the storage. *) +type validate_operation_state + +(** Circumstances of the call to {!validate_operation}: + + - [Block]: called during the validation or application of a block + (received from a peer of freshly constructed). Corresponds to + [Application], [Partial_application], and [Full_construction] modes + of {!Main.validation_mode}. + + - [Mempool]: called by the mempool (either directly or through the + plugin). Corresponds to [Partial_construction] of + {!Main.validation_mode}. *) +type mode = Block | Mempool + +(** Initialize the {!validate_operation_info} and + {!validate_operation_state} that are needed in + {!validate_operation}. *) +val init_info_and_state : + Alpha_context.t -> + mode -> + Chain_id.t -> + validate_operation_info * validate_operation_state + +(** A receipt to guarantee that an operation is always validated + before it is applied. + + Indeed, some functions in {!Apply} require a value of this type, + which may only be created by calling {!validate_operation} (or a + function in {!TMP_for_plugin}). *) +type stamp + +(** Errors that may arise while validating a manager operation. *) +module Manager : sig + type error += + | Manager_restriction of Signature.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_disabled +end + +(** Check the validity of the given operation; return an updated + {!validate_operation_state}, and a {!stamp} attesting that the + operation has been validated. + + An operation is valid if it may be included in a block without + causing the block's application to fail. The purpose of this + function is to decide validity quickly, that is, without trying to + actually apply the operation (ie. compute modifications to the + context: see {!Apply.apply_operation}) and see whether it causes an + error. + + An operation's validity may be checked in different situations: + when we receive a block from a peer or we are constructing a fresh + block, we validate each operation in the block right before trying + to apply it; when a mempool receives an operation, it validates it + to decide whether the operation should be propagated (note that for + now, this only holds for manager operations, since + [validate_operation] is not impleted yet for other operations: see + below). See {!type:mode}. + + The [validate_operation_info] contains every information we need + about the status of the chain to validate an operation, notably the + context (of type {!Alpha_context.t}) at the end of the previous + block. This context is never updated by the validation of + operations, since validation is separate from application. Yet + sometimes, the presence of some previous operations in a block or a + mempool may render the current operation invalid. E.g. the + one-operation-per-manager-per-block restriction (1M) states that a + block is invalid if it contains two separate operations from the + same manager; therefore the validation of an operation will return + [Error Manager_restriction] if another operation by the same + manager has already been validated in the same block or mempool. In + order to track this kind of operation incompatibilities, we use a + [validate_operation_state] with minimal information that gets + updated during validation. + + For a manager operation, validity is solvability, ie. it must be + well-formed, and we need to be able to take its fees. Indeed, this + is sufficient for the safe inclusion of the operation in a block: + even if there is an error during the subsequent application of the + manager operation, this will cause the operation to have no further + effects, but won't impact the success of the block's + application. The solvability of a manager operation notably + includes it being correctly signed: indeed, we can't take anything + from a manager without having checked their signature. + + TODO: https://gitlab.com/tezos/tezos/-/issues/2603 + + This function currently does nothing for non-manager operations + (instead, the validity of a non-manager operation is decided by + calling {!Apply.apply_operation} to check whether it returns an + error). We should specify and implement the validation of every + kind of operation. *) +val validate_operation : + validate_operation_info -> + validate_operation_state -> + Operation_hash.t -> + 'kind Alpha_context.operation -> + (validate_operation_state * stamp) tzresult Lwt.t + +(** Functions for the plugin. + + These functions are temporary. + + TODO: https://gitlab.com/tezos/tezos/-/issues/3245 + Update the plugin to call directly {!validate_operation} then + remove these functions. *) +module TMP_for_plugin : sig + (** Indicate whether the signature should be checked in + {!precheck_manager}; if so, provide the raw operation. + + We could have used an [option], but this makes calls to + {!precheck_manager} more readable. *) + type 'a should_check_signature = + | Check_signature of 'a Alpha_context.operation + | Skip_signature_check + + (** Similar to {!validate_operation}, but do not check the + one-operation-per-manager-per-block restriction (1M). + + Indeed, 1M is already handled by the plugin. This function is + purposefully close to the former + [Apply.precheck_manager_contents_list], so that few changes are + needed in the plugin. + + The signature is only checked if the [should_check_signature] + argument is [Check_signature _]. + + The {!validate_operation_state} does not need to be updated + because: + + + 1M is not handled here anyway. + + + In mempool mode, the block gas limit is not tracked. + + This function is called by {!Main.precheck_manager}, which is + called in [lib_plugin/mempool.ml]. *) + val precheck_manager : + validate_operation_info -> + validate_operation_state -> + 'a Alpha_context.Kind.manager Alpha_context.contents_list -> + 'a Alpha_context.Kind.manager should_check_signature -> + stamp tzresult Lwt.t + + (** Same as {!precheck_manager}, except that: + + - This function does not require [validate_operation_info] and + [validate_operation_state] arguments. Instead, they are + constructed internally from the given context and chain_id. + + - This function accepts any kind of operation as its + [contents_list] argument rather than just manager + operations. However, on non-manager operations, this function + does not check anything. + + This function is called in [lib_plugin/RPC.ml], where we do not + have access to a {!Main.validation_state} containing + [validate_operation_info] and [_state], and where we need a + {!stamp} even for non-manager operations. *) + val precheck_manager__do_nothing_on_non_manager_op : + Alpha_context.t -> + Chain_id.t -> + 'kind Alpha_context.contents_list -> + 'kind should_check_signature -> + stamp tzresult Lwt.t +end diff --git a/tezt/lib_tezos/RPC.ml b/tezt/lib_tezos/RPC.ml index 8a48d654c43b14461620c1b0040e86ca4e675d12..203eeb17bce83de25ff80d7b3364a6142213f283 100644 --- a/tezt/lib_tezos/RPC.ml +++ b/tezt/lib_tezos/RPC.ml @@ -191,6 +191,15 @@ let post_private_injection_operation ?(async = false) data = ~data Fun.id +let post_run_operation ?(chain = "main") ?(block = "head") ?(async = false) data + = + make + POST + ["chains"; chain; "blocks"; block; "helpers"; "scripts"; "run_operation"] + ~query_string:(if async then [("async", "")] else []) + ~data + Fun.id + let get_chain_chain_id ?(chain = "main") () = make GET ["chains"; chain; "chain_id"] JSON.as_string diff --git a/tezt/lib_tezos/RPC.mli b/tezt/lib_tezos/RPC.mli index 52b79b30734b29a28cf61047507ff37e26c5df71..e403c78f87f43a0832f1b57a8378aee1fd68c701 100644 --- a/tezt/lib_tezos/RPC.mli +++ b/tezt/lib_tezos/RPC.mli @@ -222,6 +222,13 @@ val post_injection_operation : ?async:bool -> JSON.u -> JSON.t t (** RPC: [POST /private/injection/operation] *) val post_private_injection_operation : ?async:bool -> JSON.u -> JSON.t t +(** RPC: [POST /chains/[chain]/blocks/[block]/helpers/scripts/run_operation] + + [chain] defaults to ["main"]. + [block] defaults to ["head"]. *) +val post_run_operation : + ?chain:string -> ?block:string -> ?async:bool -> JSON.u -> JSON.t t + (** RPC: [GET /chains/[chain]/chain_id] Returns the chain ID. *) diff --git a/tezt/lib_tezos/operation_core.mli b/tezt/lib_tezos/operation_core.mli index 6773ba06468bc8bc76c075198806e911bb2b316b..ea952ed145e0aa1de7155ce0dbb5f29cd6a23262 100644 --- a/tezt/lib_tezos/operation_core.mli +++ b/tezt/lib_tezos/operation_core.mli @@ -228,6 +228,9 @@ module Manager : sig provided, the same one as {!val:make} is used. *) val get_next_counter : ?source:Account.key -> Client.t -> int Lwt.t + (** [json t] gives the json representation of a manager operation. *) + val json : Client.t -> t -> JSON.u Lwt.t + (** [operation ?branch t client] constructs an operation from a manager operation. [branch] can be used to set manually the branch. [client] can be used to get some meta information such as diff --git a/tezt/tests/main.ml b/tezt/tests/main.ml index c4da175c14af8ab3c22fa5539ddb7cfbab722b65..d1dde59d9a8678e5043c78e01bb8603a9a5af7c3 100644 --- a/tezt/tests/main.ml +++ b/tezt/tests/main.ml @@ -128,5 +128,7 @@ let () = Client_run_view.register ~protocols:[Alpha; Jakarta] ; Multinode_snapshot.register ~protocols:[Alpha] ; Config.register () ; + (* Relies on a feature only available since K. *) + Op_validation.register ~protocols ; (* Test.run () should be the last statement, don't register afterwards! *) Test.run () diff --git a/tezt/tests/manager_operations.ml b/tezt/tests/manager_operations.ml index 1f7488b479f99b4c6f30c35254d91fc1ff2586c3..2e1d1281c1eef7cff6cfc7ff4c640e743db2c081 100644 --- a/tezt/tests/manager_operations.ml +++ b/tezt/tests/manager_operations.ml @@ -1600,10 +1600,11 @@ module Simple_transfers = struct in unit - let test_simple_transfers_successive_wrong_counters = + let test_simple_transfers_successive_wrong_counters ~supports decide_error = Protocol.register_test ~__FILE__ ~title:"Test succesive injections with same manager" + ~supports ~tags:["transaction"; "transfer"; "counters"] @@ fun protocol -> let* nodes = Helpers.init ~protocol () in @@ -1655,13 +1656,7 @@ module Simple_transfers = struct nodes.main.client in let* _ = - Memchecks.with_branch_delayed_checks - ~__LOC__ - nodes - ~classification_after_flush:`Absent - ~should_include:true - (* applied after flush *) - @@ fun () -> + decide_error nodes @@ fun () -> Operation.inject_transfer ~protocol ~source:Constant.bootstrap2 @@ -1672,6 +1667,22 @@ module Simple_transfers = struct in unit + let test_simple_transfers_successive_wrong_counters protocols = + test_simple_transfers_successive_wrong_counters + ~supports:(Protocol.Until_protocol 13) + (Memchecks.with_branch_delayed_checks + ~__LOC__ + ~classification_after_flush:`Absent + ~should_include:true (* applied after flush *)) + protocols ; + test_simple_transfers_successive_wrong_counters + ~supports:(Protocol.From_protocol 14) + (Memchecks.with_branch_delayed_checks + ~__LOC__ (* ~classification_after_flush:`Branch_delayed *) + ~classification_after_flush:`Applied + ~should_include:false (* applied after flush *)) + protocols + let test_simple_transfers_successive_wrong_counters_no_op_pre = Protocol.register_test ~__FILE__ diff --git a/tezt/tests/op_validation.ml b/tezt/tests/op_validation.ml new file mode 100644 index 0000000000000000000000000000000000000000..e53125b6246a7ceb068855f887189f5c4ce357f4 --- /dev/null +++ b/tezt/tests/op_validation.ml @@ -0,0 +1,116 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(* Testing + ------- + Component: Validation components + Invocation: dune exec tezt/tests/main.exe -- --file "op_validation.ml" + Subject: Checks the validation of operations +*) + +let check_run_operation_illformed_batch ~supports check_answer = + Protocol.register_test + ~__FILE__ + ~supports + ~title:"Run_operation ill-formed batch" + ~tags:["rpc"; "run_operation"; "batch"] + @@ fun protocol -> + Log.info "Initialize a node and a client." ; + let* node, client = + Client.init_with_protocol + ~nodes_args:[Synchronisation_threshold 0] + ~protocol + `Client + () + in + + Log.info + "Do a transfer from %s and bake to increment its counter." + Constant.bootstrap2.alias ; + let* _ = + Client.transfer + ~amount:Tez.one + ~giver:Constant.bootstrap2.alias + ~receiver:Constant.bootstrap3.alias + client + in + let* _ = Client.bake_for_and_wait ~protocol ~node client in + + Log.info "Create a first operation." ; + let source1 = Constant.bootstrap1 in + let dest = Constant.bootstrap3 in + let op1 = Operation.Manager.(make ~source:source1 @@ transfer ~dest ()) in + let* op1_json = Operation.Manager.json client op1 in + + Log.info + "Create a second operation with a different source and an incremented \ + counter." ; + let source2 = Constant.bootstrap2 in + let* counter = Operation.get_next_counter ~source:source2 client in + let op2 = + Operation.Manager.(make ~source:source2 ~counter @@ transfer ~dest ()) + in + let* op2_json = Operation.Manager.json client op2 in + + Log.info "Craft a batch in JSON that contains both operations." ; + let* branch = Operation.get_injection_branch client in + let signature = Tezos_crypto.Signature.zero in + let* chain_id = RPC.Client.call client @@ RPC.get_chain_chain_id () in + let batch = + Format.asprintf + {|{ "operation": + {"branch": "%s", + "contents": [%s,%s], + "signature": "%a" }, + "chain_id": %s }|} + branch + (Ezjsonm.value_to_string op1_json) + (Ezjsonm.value_to_string op2_json) + Tezos_crypto.Signature.pp + signature + (JSON.encode_u (`String chain_id)) + in + + Log.info "Call the [run_operation] RPC with this JSON batch." ; + let*? p = + RPC.Client.spawn client + @@ RPC.post_run_operation (Ezjsonm.from_string batch) + in + check_answer p + +(** This test checks that the [run_operation] RPC used to allow + batches of manager operations containing different sources in + protocol versions before 14, but rejects them from 14 on. *) +let check_run_operation_illformed_batch ~protocols = + check_run_operation_illformed_batch + ~supports:(Protocol.Until_protocol 13) + (Process.check ~expect_failure:false) + protocols ; + check_run_operation_illformed_batch + ~supports:(Protocol.From_protocol 14) + (Process.check ~expect_failure:true) + protocols + +let register ~protocols = check_run_operation_illformed_batch ~protocols diff --git a/tezt/tests/prevalidator.ml b/tezt/tests/prevalidator.ml index e168edb4795cccb3ed2b07989988720379a1028d..9ce01d280352355d770b40b4b8f01fc0b7c115e4 100644 --- a/tezt/tests/prevalidator.ml +++ b/tezt/tests/prevalidator.ml @@ -2242,7 +2242,8 @@ let forge_run_and_inject_n_batched_operation n ~branch ~fee ~gas_limit ~source signature (JSON.encode_u (`String chain_id)) in - RPC.post_run_operation ~data:(Ezjsonm.from_string op_runnable) client + RPC.Client.call client + @@ RPC.post_run_operation (Ezjsonm.from_string op_runnable) in let (`Hex signature) = Tezos_crypto.Signature.to_hex signature in let signed_op = op_str_hex ^ signature in