diff --git a/src/proto_014_PtKathma/lib_protocol/test/helpers/op.mli b/src/proto_014_PtKathma/lib_protocol/test/helpers/op.mli index d4e2d7bf3fe656590fc32676f9f3fcceb6bc7723..92eba04681d60e977cd272c8a2c56d5de629b5ab 100644 --- a/src/proto_014_PtKathma/lib_protocol/test/helpers/op.mli +++ b/src/proto_014_PtKathma/lib_protocol/test/helpers/op.mli @@ -29,6 +29,13 @@ open Alpha_context (* TODO: https://gitlab.com/tezos/tezos/-/issues/3181 Improve documentation of the operation helpers *) +val sign : + ?watermark:Signature.watermark -> + Signature.secret_key -> + Context.t -> + packed_contents_list -> + packed_operation + val endorsement : ?delegate:public_key_hash * Slot.t list -> ?slot:Slot.t -> diff --git a/src/proto_014_PtKathma/lib_protocol/test/integration/operations/test_origination.ml b/src/proto_014_PtKathma/lib_protocol/test/integration/operations/test_origination.ml index 2452c049409c8361e0b88087366284fde0132cbd..fefaa6375c1148e124ad39e44b6a63d14b292e7f 100644 --- a/src/proto_014_PtKathma/lib_protocol/test/integration/operations/test_origination.ml +++ b/src/proto_014_PtKathma/lib_protocol/test/integration/operations/test_origination.ml @@ -210,6 +210,108 @@ let test_counter () = res "Invalid counter (already used) in a manager operation" +let test_unparsable_script () = + let open Lwt_result_syntax in + let* b, contract = Context.init1 ~consensus_threshold:0 () in + let open Alpha_context in + (* Craft an ill-typed origination's contract. *) + let pkh = + match contract with Implicit pkh -> pkh | Originated _ -> assert false + in + let dummy_expr = + Script.lazy_expr + Environment.Micheline.(strip_locations (Int ((), Z.of_int 123))) + in + let script = Script.{code = dummy_expr; storage = dummy_expr} in + let origination = Origination {delegate = None; script; credit = Tez.one} in + let gas_limit = + Gas.Arith.integral_of_int_exn + (49_000 + + Michelson_v1_gas.Internal_for_tests.int_cost_of_manager_operation) + in + let op = + Contents_list + (Single + (Manager_operation + { + source = pkh; + fee = Tez.one; + counter = Z.of_int 1; + operation = origination; + gas_limit; + storage_limit = Z.zero; + })) + in + let encoded_op = + Data_encoding.Binary.to_bytes_exn Operation.contents_list_encoding op + |> Bytes.to_string + in + let* account = Account.find pkh in + let ill_typed_op = + Data_encoding.Binary.of_string_exn + Operation.contents_list_encoding + encoded_op + |> Op.sign account.sk (B b) + in + (* Ensure that the application fails with [Ill_typed_contract]. *) + let* i = Incremental.begin_construction b in + let* _i = + Incremental.add_operation + ~expect_apply_failure:(function + | Environment.Ecoproto_error (Script_tc_errors.Ill_typed_contract _) + :: _ -> + return_unit + | trace -> + failwith + "Expected error trace [Ill_typed_contract], but got:@\n%a" + pp_print_trace + trace) + i + ill_typed_op + in + (* Craft an unparsable lazy expr. *) + let encoded_dummy_expr = + let b = + Data_encoding.Binary.to_bytes_exn Script.lazy_expr_encoding dummy_expr + in + assert (Hex.to_bytes_exn (`Hex "0000000300bb01") = b) ; + Bytes.to_string b + in + let unparsable_dummy_expr = + Hex.to_bytes_exn (`Hex "00000003ffffff") |> Bytes.to_string + in + let unparsable_operation = + let encoded_bad_op = + Re.( + replace_string + ~all:true + (compile (str encoded_dummy_expr)) + encoded_op + ~by:unparsable_dummy_expr) + in + Data_encoding.Binary.of_string_exn + Operation.contents_list_encoding + encoded_bad_op + |> Op.sign account.sk (B b) + in + (* Ensure that the operation is valid but the application fails with + [Lazy_script_decode]. *) + let* _i = + Incremental.add_operation + ~expect_apply_failure:(function + (* Lazy_script_decode is not exposed so we only make sure that + the application indeed fails. *) + | [Environment.Ecoproto_error _] -> return_unit + | trace -> + failwith + "Expected error trace [Lazy_script_decode], but got:@\n%a" + pp_print_trace + trace) + i + unparsable_operation + in + return_unit + (******************************************************) let tests = @@ -224,4 +326,5 @@ let tests = test_not_tez_in_contract_to_pay_fee; Tztest.tztest "multiple originations" `Quick test_multiple_originations; Tztest.tztest "counter" `Quick test_counter; + Tztest.tztest "unparsable script" `Quick test_unparsable_script; ] diff --git a/src/proto_015_PtLimaPt/lib_protocol/test/helpers/op.mli b/src/proto_015_PtLimaPt/lib_protocol/test/helpers/op.mli index 563fe5b97a246284773dabd5ef656977b0231c1a..8b4b79bbf999874f09bd60081852cefdcefd94c0 100644 --- a/src/proto_015_PtLimaPt/lib_protocol/test/helpers/op.mli +++ b/src/proto_015_PtLimaPt/lib_protocol/test/helpers/op.mli @@ -44,6 +44,13 @@ open Alpha_context val pack_operation : Context.t -> signature option -> 'a contents_list -> packed_operation +val sign : + ?watermark:Signature.watermark -> + Signature.secret_key -> + Context.t -> + packed_contents_list -> + packed_operation + val endorsement : ?delegate:public_key_hash * Slot.t list -> ?slot:Slot.t -> diff --git a/src/proto_015_PtLimaPt/lib_protocol/test/integration/operations/test_origination.ml b/src/proto_015_PtLimaPt/lib_protocol/test/integration/operations/test_origination.ml index 2452c049409c8361e0b88087366284fde0132cbd..fefaa6375c1148e124ad39e44b6a63d14b292e7f 100644 --- a/src/proto_015_PtLimaPt/lib_protocol/test/integration/operations/test_origination.ml +++ b/src/proto_015_PtLimaPt/lib_protocol/test/integration/operations/test_origination.ml @@ -210,6 +210,108 @@ let test_counter () = res "Invalid counter (already used) in a manager operation" +let test_unparsable_script () = + let open Lwt_result_syntax in + let* b, contract = Context.init1 ~consensus_threshold:0 () in + let open Alpha_context in + (* Craft an ill-typed origination's contract. *) + let pkh = + match contract with Implicit pkh -> pkh | Originated _ -> assert false + in + let dummy_expr = + Script.lazy_expr + Environment.Micheline.(strip_locations (Int ((), Z.of_int 123))) + in + let script = Script.{code = dummy_expr; storage = dummy_expr} in + let origination = Origination {delegate = None; script; credit = Tez.one} in + let gas_limit = + Gas.Arith.integral_of_int_exn + (49_000 + + Michelson_v1_gas.Internal_for_tests.int_cost_of_manager_operation) + in + let op = + Contents_list + (Single + (Manager_operation + { + source = pkh; + fee = Tez.one; + counter = Z.of_int 1; + operation = origination; + gas_limit; + storage_limit = Z.zero; + })) + in + let encoded_op = + Data_encoding.Binary.to_bytes_exn Operation.contents_list_encoding op + |> Bytes.to_string + in + let* account = Account.find pkh in + let ill_typed_op = + Data_encoding.Binary.of_string_exn + Operation.contents_list_encoding + encoded_op + |> Op.sign account.sk (B b) + in + (* Ensure that the application fails with [Ill_typed_contract]. *) + let* i = Incremental.begin_construction b in + let* _i = + Incremental.add_operation + ~expect_apply_failure:(function + | Environment.Ecoproto_error (Script_tc_errors.Ill_typed_contract _) + :: _ -> + return_unit + | trace -> + failwith + "Expected error trace [Ill_typed_contract], but got:@\n%a" + pp_print_trace + trace) + i + ill_typed_op + in + (* Craft an unparsable lazy expr. *) + let encoded_dummy_expr = + let b = + Data_encoding.Binary.to_bytes_exn Script.lazy_expr_encoding dummy_expr + in + assert (Hex.to_bytes_exn (`Hex "0000000300bb01") = b) ; + Bytes.to_string b + in + let unparsable_dummy_expr = + Hex.to_bytes_exn (`Hex "00000003ffffff") |> Bytes.to_string + in + let unparsable_operation = + let encoded_bad_op = + Re.( + replace_string + ~all:true + (compile (str encoded_dummy_expr)) + encoded_op + ~by:unparsable_dummy_expr) + in + Data_encoding.Binary.of_string_exn + Operation.contents_list_encoding + encoded_bad_op + |> Op.sign account.sk (B b) + in + (* Ensure that the operation is valid but the application fails with + [Lazy_script_decode]. *) + let* _i = + Incremental.add_operation + ~expect_apply_failure:(function + (* Lazy_script_decode is not exposed so we only make sure that + the application indeed fails. *) + | [Environment.Ecoproto_error _] -> return_unit + | trace -> + failwith + "Expected error trace [Lazy_script_decode], but got:@\n%a" + pp_print_trace + trace) + i + unparsable_operation + in + return_unit + (******************************************************) let tests = @@ -224,4 +326,5 @@ let tests = test_not_tez_in_contract_to_pay_fee; Tztest.tztest "multiple originations" `Quick test_multiple_originations; Tztest.tztest "counter" `Quick test_counter; + Tztest.tztest "unparsable script" `Quick test_unparsable_script; ] diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 2573191328e3cd36bc1180ab177cf25da37ef5ec..9a3e7fafaf5b5d537e6fbf4a5a5337ac2ef8910e 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -514,6 +514,8 @@ module Entrypoint : module type of Entrypoint_repr (** This module re-exports definitions from {!Script_repr} and {!Michelson_v1_primitives}. *) module Script : sig + type error += Lazy_script_decode + type prim = Michelson_v1_primitives.prim = | K_parameter | K_storage diff --git a/src/proto_alpha/lib_protocol/script_repr.ml b/src/proto_alpha/lib_protocol/script_repr.ml index ad8631e90abdd38d35d6c9a0f4d31f0411a6f9b3..7c5aca4c7893fa5fc7ac098650c304d82c12e08c 100644 --- a/src/proto_alpha/lib_protocol/script_repr.ml +++ b/src/proto_alpha/lib_protocol/script_repr.ml @@ -52,6 +52,11 @@ let () = ~title:"Invalid binary format" ~description: "Could not deserialize some piece of data from its binary representation" + ~pp:(fun fmt () -> + Format.fprintf + fmt + "Could not deserialize some piece of data from its binary \ + representation") Data_encoding.empty (function Lazy_script_decode -> Some () | _ -> None) (fun () -> Lazy_script_decode) diff --git a/src/proto_alpha/lib_protocol/test/helpers/op.mli b/src/proto_alpha/lib_protocol/test/helpers/op.mli index 1dea72fd81c166050b15a9b056c69c7867bd7903..aa869b298bae1c9b9c06a5387cc46b71aad338c3 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/op.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/op.mli @@ -44,6 +44,13 @@ open Alpha_context val pack_operation : Context.t -> signature option -> 'a contents_list -> packed_operation +val sign : + ?watermark:Signature.watermark -> + Signature.secret_key -> + Context.t -> + packed_contents_list -> + packed_operation + val endorsement : ?delegate:public_key_hash * Slot.t list -> ?slot:Slot.t -> 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 2452c049409c8361e0b88087366284fde0132cbd..e7c455a04999b7b9664901dc9a5b1b0fe5e84362 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 @@ -210,6 +210,106 @@ let test_counter () = res "Invalid counter (already used) in a manager operation" +let test_unparsable_script () = + let open Lwt_result_syntax in + let* b, contract = Context.init1 ~consensus_threshold:0 () in + let open Alpha_context in + (* Craft an ill-typed origination's contract. *) + let pkh = + match contract with Implicit pkh -> pkh | Originated _ -> assert false + in + let dummy_expr = + Script.lazy_expr + Environment.Micheline.(strip_locations (Int ((), Z.of_int 123))) + in + let script = Script.{code = dummy_expr; storage = dummy_expr} in + let origination = Origination {delegate = None; script; credit = Tez.one} in + let gas_limit = + Gas.Arith.integral_of_int_exn + (49_000 + + Michelson_v1_gas.Internal_for_tests.int_cost_of_manager_operation) + in + let op = + Contents_list + (Single + (Manager_operation + { + source = pkh; + fee = Tez.one; + counter = Z.of_int 1; + operation = origination; + gas_limit; + storage_limit = Z.zero; + })) + in + let encoded_op = + Data_encoding.Binary.to_bytes_exn Operation.contents_list_encoding op + |> Bytes.to_string + in + let* account = Account.find pkh in + let ill_typed_op = + Data_encoding.Binary.of_string_exn + Operation.contents_list_encoding + encoded_op + |> Op.sign account.sk (B b) + in + (* Ensure that the application fails with [Ill_typed_contract]. *) + let* i = Incremental.begin_construction b in + let* _i = + Incremental.add_operation + ~expect_apply_failure:(function + | Environment.Ecoproto_error (Script_tc_errors.Ill_typed_contract _) + :: _ -> + return_unit + | trace -> + failwith + "Expected error trace [Ill_typed_contract], but got:@\n%a" + pp_print_trace + trace) + i + ill_typed_op + in + (* Craft an unparsable lazy expr. *) + let encoded_dummy_expr = + let b = + Data_encoding.Binary.to_bytes_exn Script.lazy_expr_encoding dummy_expr + in + assert (Hex.to_bytes_exn (`Hex "0000000300bb01") = b) ; + Bytes.to_string b + in + let unparsable_dummy_expr = + Hex.to_bytes_exn (`Hex "00000003ffffff") |> Bytes.to_string + in + let unparsable_operation = + let encoded_bad_op = + Re.( + replace_string + ~all:true + (compile (str encoded_dummy_expr)) + encoded_op + ~by:unparsable_dummy_expr) + in + Data_encoding.Binary.of_string_exn + Operation.contents_list_encoding + encoded_bad_op + |> Op.sign account.sk (B b) + in + (* Ensure that the operation is valid but the application fails with + [Lazy_script_decode]. *) + let* _i = + Incremental.add_operation + ~expect_apply_failure:(function + | [Environment.Ecoproto_error Script.Lazy_script_decode] -> return_unit + | trace -> + failwith + "Expected error trace [Lazy_script_decode], but got:@\n%a" + pp_print_trace + trace) + i + unparsable_operation + in + return_unit + (******************************************************) let tests = @@ -224,4 +324,5 @@ let tests = test_not_tez_in_contract_to_pay_fee; Tztest.tztest "multiple originations" `Quick test_multiple_originations; Tztest.tztest "counter" `Quick test_counter; + Tztest.tztest "unparsable script" `Quick test_unparsable_script; ]