From 32adbe11219555ea064a4b593d6073ccab97aae9 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Thu, 1 Jun 2023 16:51:47 +0200 Subject: [PATCH 1/4] proto_alpha/plugin: add versioning to run and simulate operations RPCs --- src/proto_alpha/lib_plugin/RPC.ml | 191 +++++++++++++++++++----------- 1 file changed, 125 insertions(+), 66 deletions(-) diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index 0995f5a7a719..b81c999f68d2 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -30,6 +30,25 @@ open Environment open Alpha_context open Environment.Error_monad +type version = Version_0 | Version_1 + +let string_of_version = function Version_0 -> "0" | Version_1 -> "1" + +let version_of_string = function + | "0" -> Ok Version_0 + | "1" -> Ok Version_1 + | _ -> Error "Cannot parse version (supported versions \"0\" and \"1\")" + +let default_operations_version = Version_0 + +let version_arg = + let open RPC_arg in + make + ~name:"version" + ~destruct:version_of_string + ~construct:string_of_version + () + (** The assumed number of blocks between operation-creation time and the actual time when the operation is included in a block. *) let default_operation_inclusion_latency = 3 @@ -409,6 +428,16 @@ module Scripts = struct ~query:RPC_query.empty RPC_path.(path / "normalize_type") + let run_operation_query = + let open RPC_query in + query (fun version -> + object + method version = version + end) + |+ field "version" version_arg default_operations_version (fun t -> + t#version) + |> seal + let operations_encodings = union [ @@ -426,6 +455,26 @@ module Scripts = struct Fun.id; ] + let run_operation_output_encoding = + union + [ + case + ~title:"new_encoding_run_operation_output" + (Tag 1) + Apply_results.operation_data_and_metadata_encoding + (function + | Version_1, operation -> Some operation | Version_0, _ -> None) + (fun operation -> (Version_1, operation)); + case + ~title:"old_encoding_run_operation_output" + (Tag 0) + Apply_results + .operation_data_and_metadata_encoding_with_legacy_attestation_name + (function + | Version_0, operation -> Some operation | Version_1, _ -> None) + (fun operation -> (Version_0, operation)); + ] + let run_operation = RPC_service.post_service ~description: @@ -433,22 +482,24 @@ module Scripts = struct signature checks. Return the operation application result, \ including the consumed gas. This RPC does not support consensus \ operations." - ~query:RPC_query.empty + ~query:run_operation_query ~input: (obj2 (req "operation" operations_encodings) (req "chain_id" Chain_id.encoding)) - ~output: - Apply_results - .operation_data_and_metadata_encoding_with_legacy_attestation_name + ~output:run_operation_output_encoding RPC_path.(path / "run_operation") let simulate_query = let open RPC_query in - query (fun successor_level -> + query (fun version successor_level -> object + method version = version + method successor_level = successor_level end) + |+ field "version" version_arg default_operations_version (fun t -> + t#version) |+ flag ~descr: "If true, the simulation is done on the successor level of the \ @@ -474,9 +525,7 @@ module Scripts = struct (req "operation" operations_encodings) (req "chain_id" Chain_id.encoding) (dft "latency" int16 default_operation_inclusion_latency)) - ~output: - Apply_results - .operation_data_and_metadata_encoding_with_legacy_attestation_name + ~output:run_operation_output_encoding RPC_path.(path / "simulate_operation") let entrypoint_type = @@ -953,23 +1002,26 @@ module Scripts = struct Return the unchanged operation protocol data, and the operation receipt ie. metadata containing balance updates, consumed gas, application success or failure, etc. *) - let run_operation_service rpc_ctxt () (packed_operation, chain_id) = + let run_operation_service rpc_ctxt params (packed_operation, chain_id) = + let open Lwt_result_syntax in let {Services_registration.context; block_header; _} = rpc_ctxt in - (match packed_operation.protocol_data with - | Operation_data {contents = Single (Preendorsement _); _} - | Operation_data {contents = Single (Endorsement _); _} - | Operation_data {contents = Single (Dal_attestation _); _} -> - error Run_operation_does_not_support_consensus_operations - | _ -> ok ()) - >>?= fun () -> + let*? () = + match packed_operation.protocol_data with + | Operation_data {contents = Single (Preendorsement _); _} + | Operation_data {contents = Single (Endorsement _); _} + | Operation_data {contents = Single (Dal_attestation _); _} -> + error Run_operation_does_not_support_consensus_operations + | _ -> ok () + in let oph = Operation.hash_packed packed_operation in let validity_state = Validate.begin_no_predecessor_info context chain_id in - Validate.validate_operation - ~check_signature:false - validity_state - oph - packed_operation - >>=? fun _validate_operation_state -> + let* _validate_operation_state = + Validate.validate_operation + ~check_signature:false + validity_state + oph + packed_operation + in let application_mode = Apply.Partial_construction {predecessor_fitness = block_header.fitness} in @@ -988,9 +1040,10 @@ module Scripts = struct implicit_operations_results = []; } in - Apply.apply_operation application_state oph packed_operation - >|=? fun (_ctxt, op_metadata) -> - (packed_operation.protocol_data, op_metadata) + let* _ctxt, op_metadata = + Apply.apply_operation application_state oph packed_operation + in + return (params#version, (packed_operation.protocol_data, op_metadata)) (* @@ -1007,16 +1060,22 @@ module Scripts = struct time of the operation. *) - let simulate_operation_service rpc_ctxt - (_simulate_query : < successor_level : bool >) + let simulate_operation_service rpc_ctxt params (blocks_before_activation, op, chain_id, time_in_blocks) = + let open Lwt_result_syntax in let {Services_registration.context; _} = rpc_ctxt in - Cache.Admin.future_cache_expectation - context - ~time_in_blocks - ?blocks_before_activation - >>=? fun context -> - run_operation_service {rpc_ctxt with context} () (op, chain_id) + let* context = + Cache.Admin.future_cache_expectation + context + ~time_in_blocks + ?blocks_before_activation + in + run_operation_service + {rpc_ctxt with context} + (object + method version = params#version + end) + (op, chain_id) let default_from_context ctxt get = function | None -> get ctxt @@ -1770,19 +1829,38 @@ module Scripts = struct let normalize_type ~ty ctxt block = RPC_context.make_call0 S.normalize_type ctxt block () ty - let run_operation ~op ~chain_id ctxt block = - RPC_context.make_call0 S.run_operation ctxt block () (op, chain_id) + let run_operation ~op ~chain_id ?(version = default_operations_version) ctxt + block = + let open Lwt_result_syntax in + let* (Version_0 | Version_1), run_operation = + RPC_context.make_call0 + S.run_operation + ctxt + block + (object + method version = version + end) + (op, chain_id) + in + return run_operation - let simulate_operation ~op ~chain_id ~latency ?(successor_level = false) + let simulate_operation ~op ~chain_id ~latency + ?(version = default_operations_version) ?(successor_level = false) ?blocks_before_activation ctxt block = - RPC_context.make_call0 - S.simulate_operation - ctxt - block - (object - method successor_level = successor_level - end) - (blocks_before_activation, op, chain_id, latency) + let open Lwt_result_syntax in + let* (Version_0 | Version_1), simulate_operation = + RPC_context.make_call0 + S.simulate_operation + ctxt + block + (object + method version = version + + method successor_level = successor_level + end) + (blocks_before_activation, op, chain_id, latency) + in + return simulate_operation let entrypoint_type ~script ~entrypoint ctxt block = RPC_context.make_call0 S.entrypoint_type ctxt block () (script, entrypoint) @@ -2876,32 +2954,13 @@ module Parse = struct let path = RPC_path.(path / "parse") - type version = Version_0 | Version_1 - - let string_of_version = function Version_0 -> "0" | Version_1 -> "1" - - let version_of_string = function - | "0" -> Ok Version_0 - | "1" -> Ok Version_1 - | _ -> Error "Cannot parse version (supported versions \"0\" and \"1\")" - - let default_parse_operations_version = Version_0 - - let version_arg = - let open RPC_arg in - make - ~name:"version" - ~destruct:version_of_string - ~construct:string_of_version - () - let operations_query = let open RPC_query in query (fun version -> object method version = version end) - |+ field "version" version_arg default_parse_operations_version (fun t -> + |+ field "version" version_arg default_operations_version (fun t -> t#version) |> seal @@ -2993,8 +3052,8 @@ module Parse = struct Registration.register0_noctxt ~chunked:false S.block (fun () raw_block -> return @@ parse_protocol_data raw_block.protocol_data) - let operations ctxt block ?(version = S.default_parse_operations_version) - ?check operations = + let operations ctxt block ?(version = default_operations_version) ?check + operations = let open Lwt_result_syntax in let*! v = RPC_context.make_call0 -- GitLab From 5cbc88a5514b04442118a9604134dadfba13dadd Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Thu, 1 Jun 2023 16:57:24 +0200 Subject: [PATCH 2/4] tezt/lib_tezos: add version arg to run/simulate_operation RPC helper --- tezt/lib_tezos/RPC.ml | 11 ++++++++--- tezt/lib_tezos/RPC.mli | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tezt/lib_tezos/RPC.ml b/tezt/lib_tezos/RPC.ml index 3e8d88a4cd13..53b5bd79f0b5 100644 --- a/tezt/lib_tezos/RPC.ml +++ b/tezt/lib_tezos/RPC.ml @@ -263,11 +263,14 @@ let post_private_injection_operation ?(async = false) data = Fun.id let post_chain_block_helpers_scripts_run_operation ?(chain = "main") - ?(block = "head") ?(async = false) data = + ?(block = "head") ?version ?(async = false) data = + let query_string = + Query_arg.opt "version" Fun.id version @ Query_arg.switch "async" async + in make POST ["chains"; chain; "blocks"; block; "helpers"; "scripts"; "run_operation"] - ~query_string:(Query_arg.switch "async" async) + ~query_string ~data Fun.id @@ -520,8 +523,10 @@ let post_chain_block_helpers_parse_operations ?(chain = "main") Fun.id let post_chain_block_helpers_scripts_simulate_operation ?(chain = "main") - ?(block = "head") ~data () = + ?(block = "head") ?version ~data () = + let query_string = Query_arg.opt "version" Fun.id version in make + ~query_string ~data POST [ diff --git a/tezt/lib_tezos/RPC.mli b/tezt/lib_tezos/RPC.mli index c0fd1fd7b038..28d536f84fbf 100644 --- a/tezt/lib_tezos/RPC.mli +++ b/tezt/lib_tezos/RPC.mli @@ -267,7 +267,12 @@ val post_private_injection_operation : ?async:bool -> data -> JSON.t t [chain] defaults to ["main"]. [block] defaults to ["head"]. *) val post_chain_block_helpers_scripts_run_operation : - ?chain:string -> ?block:string -> ?async:bool -> data -> JSON.t t + ?chain:string -> + ?block:string -> + ?version:string -> + ?async:bool -> + data -> + JSON.t t (** RPC: [GET /chains//chain_id] @@ -539,7 +544,12 @@ val post_chain_block_helpers_parse_operations : [block] defaults to ["head"]. *) val post_chain_block_helpers_scripts_simulate_operation : - ?chain:string -> ?block:string -> data:data -> unit -> JSON.t t + ?chain:string -> + ?block:string -> + ?version:string -> + data:data -> + unit -> + JSON.t t (** RPC: [POST /chains//blocks//helpers/scripts/event_address] -- GitLab From 7346eaae72a20512a19460519d665508a3c8a1c3 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Fri, 2 Jun 2023 09:20:39 +0200 Subject: [PATCH 3/4] tezt/tests: add run/simulate_operation versioning tests --- tezt/tests/rpc_versioning_attestation.ml | 107 +++++++++++++++-------- 1 file changed, 69 insertions(+), 38 deletions(-) diff --git a/tezt/tests/rpc_versioning_attestation.ml b/tezt/tests/rpc_versioning_attestation.ml index bee1811802b1..621c4794ed85 100644 --- a/tezt/tests/rpc_versioning_attestation.ml +++ b/tezt/tests/rpc_versioning_attestation.ml @@ -75,6 +75,21 @@ let check_kind json kind = if not (String.equal json_kind kind) then Test.fail ~__LOC__ "Operation should have %s kind, got: %s" kind json_kind +let use_legacy_name_from_version = function `Old -> true | `New -> false + +let check_version ~version ~check ~rpc ~name ~data client = + let* t = + RPC.Client.call client + @@ rpc ~version:(match version with `Old -> "0" | `New -> "1") data + in + return (check t name) + +let check_unknown_version ~rpc ~data client = + let version = "2" in + let*? p = RPC.Client.spawn client @@ rpc ~version data in + let msg = rex "Failed to parse argument 'version'" in + Process.check_error ~msg p + let create_consensus_op ?slot ?level ?round ?block_payload_hash ~use_legacy_name ~signer ~kind client = let consensus_name = @@ -334,12 +349,12 @@ module Forge = struct end module Parse = struct - let parse_operations_rpc ~version raw = + let rpc ~version raw = RPC.post_chain_block_helpers_parse_operations ~version (`A [JSON.unannotate raw]) - let check_parsed_kind kind json = + let check_parsed_kind json kind = check_kind JSON.(json |> as_list |> List.hd) kind let create_raw_op ~protocol op client = @@ -353,21 +368,6 @@ module Parse = struct ~name:(Protocol.encoding_prefix protocol ^ "." ^ "operation.raw") h1 - let check_version ~use_legacy_name name raw client = - let* t = - RPC.Client.call client - @@ parse_operations_rpc - ~version:(if use_legacy_name then "0" else "1") - raw - in - return (check_parsed_kind name t) - - let check_unknown_version raw client = - let version = "2" in - let*? p = RPC.Client.spawn client @@ parse_operations_rpc ~version raw in - let msg = rex (sf "Failed to parse argument 'version'") in - Process.check_error ~msg p - let test_parse kind protocol = let* _node, client = Client.init_with_protocol ~protocol `Client () in let signer = Constant.bootstrap1 in @@ -377,14 +377,24 @@ module Parse = struct in let* raw = create_raw_op ~protocol consensus_op client in - let check_version ~use_legacy_name = - let name = Operation.Consensus.kind_to_string kind use_legacy_name in - check_version ~use_legacy_name name raw client + let check_version ~version = + let name = + Operation.Consensus.kind_to_string + kind + (use_legacy_name_from_version version) + in + check_version + ~version + ~check:check_parsed_kind + ~rpc + ~name + ~data:raw + client in - let* () = check_version ~use_legacy_name:true in - let* () = check_version ~use_legacy_name:false in - check_unknown_version raw client + let* () = check_version ~version:`Old in + let* () = check_version ~version:`New in + check_unknown_version ~rpc ~data:raw client let test_parse_consensus = register_test @@ -413,16 +423,24 @@ module Parse = struct in let* raw = create_raw_double_consensus_evidence () in - let check_version ~use_legacy_name = + let check_version ~version = let name = - Operation.Anonymous.kind_to_string double_evidence_kind use_legacy_name + Operation.Anonymous.kind_to_string + double_evidence_kind + (use_legacy_name_from_version version) in - check_version ~use_legacy_name name raw client + check_version + ~version + ~check:check_parsed_kind + ~rpc + ~name + ~data:raw + client in - let* () = check_version ~use_legacy_name:true in - let* () = check_version ~use_legacy_name:false in - check_unknown_version raw client + let* () = check_version ~version:`Old in + let* () = check_version ~version:`New in + check_unknown_version ~rpc ~data:raw client let test_parse_double_consensus_evidence = register_test @@ -562,11 +580,13 @@ end module Run_Simulate = struct type rpc = Run | Simulate - let get_rpc rpc op = + let get_rpc rpc ?version op = match rpc with - | Run -> RPC.post_chain_block_helpers_scripts_run_operation (Data op) + | Run -> + RPC.post_chain_block_helpers_scripts_run_operation ?version (Data op) | Simulate -> RPC.post_chain_block_helpers_scripts_simulate_operation + ?version ~data:(Data op) () @@ -637,26 +657,37 @@ module Run_Simulate = struct let* node, client = Client.init_with_protocol ~protocol `Client () in let* () = Client.bake_for_and_wait ~node client in - let call_and_check_error ~use_legacy_name = + let call_and_check_versions ~use_legacy_name_in_input = Log.info "Create a %s operation and call %s " (Operation.Anonymous.kind_to_string double_evidence_kind - use_legacy_name) + use_legacy_name_in_input) (get_rpc_name rpc) ; let* consensus_op = create_double_consensus_evidence - ~use_legacy_name + ~use_legacy_name:use_legacy_name_in_input ~double_evidence_kind client in let* op_json = Operation.make_run_operation_input consensus_op client in - let* _ = RPC.Client.call client @@ get_rpc rpc op_json in - unit + + let rpc ~version data = get_rpc rpc ~version data in + let check_version ~version = + let name = + Operation.Anonymous.kind_to_string + double_evidence_kind + (use_legacy_name_from_version version) + in + check_version ~version ~check:check_kind ~name ~rpc ~data:op_json client + in + let* () = check_version ~version:`Old in + let* () = check_version ~version:`New in + check_unknown_version ~rpc ~data:op_json client in - let* () = call_and_check_error ~use_legacy_name:true in - call_and_check_error ~use_legacy_name:false + let* () = call_and_check_versions ~use_legacy_name_in_input:true in + call_and_check_versions ~use_legacy_name_in_input:false let test_run_operation_double_consensus_evidence = register_test -- GitLab From fcb4371b297fea999381255fd94a4ad072e7b515 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Fri, 2 Jun 2023 09:24:49 +0200 Subject: [PATCH 4/4] changes: add entry for new version parameter of run_operation and simulate_operations RPCs --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5a585a295ac2..7bf0d249bfb9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -120,6 +120,13 @@ Node - Improved performances of RPC responses on request for older blocks by caching the archived metadata accesses. (MR :gl:`!8976`) +- Added version ``1`` to RPCs ``POST ../helpers/scripts/run_operation`` and + ``POST ../helpers/scripts/simulate_operation``. It can be used by calling the + RPC with the parameter ``?version=1`` (default version is still ``0``). + Version ``1`` allows the RPC to output ``attestation``, ``preattestation``, + ``double_attestation_evidence`` and ``double_preattestation_evidence`` kinds + in the JSON result. (MR :gl:`!8949`) + Client ------ - Adding client commands to generate, open and verify a time-lock. -- GitLab