diff --git a/CHANGES.rst b/CHANGES.rst index afde8bf966d242b2f85bf5e1f40515c25023f718..59dd972be1afeed41b16d48f3c7c267ea22f8053 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -150,6 +150,12 @@ Node metric, and renamed the ``octez_mempool_pending_prechecked`` one to ``octez_mempool_pending_validated``. (MR :gl:`!9137`) +- Added version ``1`` to RPC ``POST ../helpers/preapply/operations``. 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:`!8891`) + Client ------ - Adding client commands to generate, open and verify a time-lock. diff --git a/src/lib_mockup/local_services.ml b/src/lib_mockup/local_services.ml index 285368c2237da4e781ba18201d7d4018267d6fcc..85da3719d45db770b6e11506d7d2a929405578be 100644 --- a/src/lib_mockup/local_services.ml +++ b/src/lib_mockup/local_services.ml @@ -562,7 +562,7 @@ module Make (E : MENV) = struct Directory.empty (* /chains//blocks//helpers/preapply/operations *) E.Block_services.S.Helpers.Preapply.operations - (fun ((_, chain), _block) () op_list -> + (fun ((_, chain), _block) params op_list -> with_chain ~caller_name:"preapply operations" chain (fun () -> let*! outcome = let* proto_state = partial_construction ~cache:`Lazy () in @@ -586,7 +586,7 @@ module Make (E : MENV) = struct return (List.rev acc) in match outcome with - | Ok result -> Tezos_rpc.Answer.return result + | Ok result -> Tezos_rpc.Answer.return (params#version, result) | Error errs -> Tezos_rpc.Answer.fail errs))) let hash_op (shell, proto) = diff --git a/src/lib_shell/block_directory.ml b/src/lib_shell/block_directory.ml index 54643fcbd835d7e699de3c82ae83783a306233c2..f4b44cfa068a63e00f9ff52b95b9e080ab75a404 100644 --- a/src/lib_shell/block_directory.ml +++ b/src/lib_shell/block_directory.ml @@ -686,7 +686,9 @@ let build_raw_rpc_directory (module Proto : Block_services.PROTO) ~timestamp ~protocol_data operations) ; - register0 S.Helpers.Preapply.operations (fun (chain_store, block) () ops -> + register0 + S.Helpers.Preapply.operations + (fun (chain_store, block) params ops -> let* ctxt = Store.Block.context chain_store block in let chain_id = Store.Chain.chain_id chain_store in let mode = @@ -737,7 +739,7 @@ let build_raw_rpc_directory (module Proto : Block_services.PROTO) (validation_state, application_state, []) hashed_ops in - return (List.rev acc)) ; + return (params#version, List.rev acc)) ; register1 S.Helpers.complete (fun (chain_store, block) prefix () () -> let* ctxt = Store.Block.context chain_store block in let*! l1 = Tezos_crypto.Base58.complete prefix in diff --git a/src/lib_shell_services/block_services.ml b/src/lib_shell_services/block_services.ml index 4299c6a8b5e72506ff66728f928464b74b87d372..5c20ce55e3a0b6d2e52199c35cabe2cdb56b97e9 100644 --- a/src/lib_shell_services/block_services.ml +++ b/src/lib_shell_services/block_services.ml @@ -881,6 +881,23 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct module Preapply = struct let path = Tezos_rpc.Path.(path / "preapply") + let preapply_operation_encoding = + union + [ + case + ~title:"operation_data_encoding" + (Tag 0) + next_operation_encoding + Option.some + Fun.id; + case + ~title:"operation_data_encoding_with_legacy_attestation_name" + Json_only + next_operation_encoding_with_legacy_attestation_name + Option.some + Fun.id; + ] + let block_result_encoding = obj2 (req "shell_header" Block_header.shell_header_encoding) @@ -908,10 +925,7 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct (dynamic_size Next_proto.block_header_data_encoding)))) (req "operations" - (list - (dynamic_size - (list - next_operation_encoding_with_legacy_attestation_name))))) + (list (dynamic_size (list preapply_operation_encoding))))) let block_query = let open Tezos_rpc.Query in @@ -935,19 +949,58 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct ~output:block_result_encoding Tezos_rpc.Path.(path / "block") + let default_preapply_operations_version = Version_0 + + let preapply_supported_versions = [Version_0; Version_1] + + let operations_query = + let open Tezos_rpc.Query in + query (fun version -> + object + method version = version + end) + |+ field + "version" + (version_arg preapply_supported_versions) + default_preapply_operations_version + (fun t -> t#version) + |> seal + + let preapplied_operations_encoding = + union + [ + case + ~title:"preapplied_operations_encoding" + (Tag 1) + (list + (dynamic_size Next_proto.operation_data_and_receipt_encoding)) + (function + | Version_1, preapply_operations -> Some preapply_operations + | (Version_0 | Version_2), _ -> None) + (fun preapply_operations -> (Version_1, preapply_operations)); + case + ~title: + "preapplied_operations_encoding_with_legacy_attestation_name" + (Tag 0) + (list + (dynamic_size + Next_proto + .operation_data_and_receipt_encoding_with_legacy_attestation_name)) + (function + | Version_0, preapply_operations -> Some preapply_operations + | (Version_1 | Version_2), _ -> None) + (fun preapply_operations -> (Version_0, preapply_operations)); + ] + let operations = Tezos_rpc.Service.post_service ~description: "Simulate the application of the operations with the context of \ the given block and return the result of each operation \ application." - ~query:Tezos_rpc.Query.empty - ~input:(list next_operation_encoding_with_legacy_attestation_name) - ~output: - (list - (dynamic_size - Next_proto - .operation_data_and_receipt_encoding_with_legacy_attestation_name)) + ~query:operations_query + ~input:(list preapply_operation_encoding) + ~output:preapplied_operations_encoding Tezos_rpc.Path.(path / "operations") end @@ -1312,7 +1365,7 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct union [ case - ~title:"new_encoding_pending_operations_with_attestation" + ~title:"pending_operations_encoding" (Tag 2) version_2_encoding (function @@ -1320,7 +1373,7 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct | (Version_0 | Version_1), _ -> None) (fun pending_operations -> (Version_2, pending_operations)); case - ~title:"new_encoding_pending_operations" + ~title:"pending_operations_encoding_with_legacy_attestation_name" (Tag 1) version_1_encoding (function @@ -1468,7 +1521,7 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct union [ case - ~title:"monitor_operations_with_attestation" + ~title:"monitor_operations_encoding" (Tag 1) (list (monitor_operations_encoding ~use_legacy_name:false)) (function @@ -1483,7 +1536,7 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct None) (fun monitor_operations -> (Version_1, monitor_operations)); case - ~title:"old_encoding_monitor_operations" + ~title:"monitor_operations_encoding_with_legacy_attestation_name" (Tag 0) (list (monitor_operations_encoding ~use_legacy_name:true)) (function @@ -1765,10 +1818,21 @@ module Make (Proto : PROTO) (Next_proto : PROTO) = struct end) {protocol_data; operations} - let operations ctxt = - let f = make_call0 S.operations ctxt in - fun ?(chain = `Main) ?(block = `Head 0) operations -> - f chain block () operations + let operations ctxt ?(chain = `Main) ?(block = `Head 0) + ?(version = S.default_preapply_operations_version) operations = + let open Lwt_result_syntax in + let* (Version_0 | Version_1 | Version_2), preapply_operations = + make_call0 + S.operations + ctxt + chain + block + (object + method version = version + end) + operations + in + return preapply_operations end let complete ctxt = diff --git a/src/lib_shell_services/block_services.mli b/src/lib_shell_services/block_services.mli index be26ee2122cf7e9537dd171c629dd1ccb17516c3..2eccbe4f2fb548de4a99be99f41f78e6035a61d6 100644 --- a/src/lib_shell_services/block_services.mli +++ b/src/lib_shell_services/block_services.mli @@ -385,6 +385,7 @@ module Make (Proto : PROTO) (Next_proto : PROTO) : sig #simple -> ?chain:chain -> ?block:block -> + ?version:version -> Next_proto.operation list -> (Next_proto.operation_data * Next_proto.operation_receipt) list tzresult Lwt.t @@ -701,9 +702,11 @@ module Make (Proto : PROTO) (Next_proto : PROTO) : sig ( [`POST], prefix, prefix, - unit, + < version : version >, Next_proto.operation list, - (Next_proto.operation_data * Next_proto.operation_receipt) list ) + version + * (Next_proto.operation_data * Next_proto.operation_receipt) list + ) Tezos_rpc.Service.t end diff --git a/tezt/lib_tezos/RPC.ml b/tezt/lib_tezos/RPC.ml index 723e7e1ba2e1f637a66a42760f8a05e2c9bb7c6c..80b493c663568d92223167bd24ec3207a11aa762 100644 --- a/tezt/lib_tezos/RPC.ml +++ b/tezt/lib_tezos/RPC.ml @@ -513,6 +513,16 @@ let post_chain_block_helpers_preapply_block ?(chain = "main") ?(block = "head") ["chains"; chain; "blocks"; block; "helpers"; "preapply"; "block"] Fun.id +let post_chain_block_helpers_preapply_operations ?(chain = "main") + ?(block = "head") ?version ~data () = + let query_string = Query_arg.opt "version" Fun.id version in + make + ~query_string + ~data + POST + ["chains"; chain; "blocks"; block; "helpers"; "preapply"; "operations"] + Fun.id + let post_chain_block_helpers_forge_operations ?(chain = "main") ?(block = "head") ~data () = make diff --git a/tezt/lib_tezos/RPC.mli b/tezt/lib_tezos/RPC.mli index e89a693052dc9cb206b2ebd2b4915ca58000f60a..51743e7eabbf6d276126b170278b372116cfc761 100644 --- a/tezt/lib_tezos/RPC.mli +++ b/tezt/lib_tezos/RPC.mli @@ -524,6 +524,19 @@ val post_chain_mempool_filter : ?chain:string -> data:data -> unit -> JSON.t t val post_chain_block_helpers_preapply_block : ?chain:string -> ?block:string -> data:data -> unit -> JSON.t t +(** RPC: [POST /chains//blocks//helpers/preapply/operations] + + [chain] defaults to ["main"]. + [block] defaults to ["head"]. +*) +val post_chain_block_helpers_preapply_operations : + ?chain:string -> + ?block:string -> + ?version:string -> + data:data -> + unit -> + JSON.t t + (** RPC: [POST /chains//blocks//helpers/forge/operations] [chain] defaults to ["main"]. diff --git a/tezt/lib_tezos/operation_core.ml b/tezt/lib_tezos/operation_core.ml index cf476faa68df20dce40b60ca087af119c90656d1..125ffa24659f30867311d6c0c5669570df216ab2 100644 --- a/tezt/lib_tezos/operation_core.ml +++ b/tezt/lib_tezos/operation_core.ml @@ -209,6 +209,16 @@ let make_run_operation_input ?chain_id t client = ("chain_id", `String chain_id); ]) +let make_preapply_operation_input ~protocol ~signature t = + let protocol = Protocol.hash protocol in + `O + [ + ("protocol", `String protocol); + ("branch", `String t.branch); + ("contents", t.contents); + ("signature", `String (Tezos_crypto.Signature.to_b58check signature)); + ] + module Consensus = struct type t = | Consensus of { diff --git a/tezt/lib_tezos/operation_core.mli b/tezt/lib_tezos/operation_core.mli index cc95ef5efd25fbd1e8b134468118f4eda2514100..6e5505798ea411a406157e6ff83c544b972974c1 100644 --- a/tezt/lib_tezos/operation_core.mli +++ b/tezt/lib_tezos/operation_core.mli @@ -183,6 +183,15 @@ val inject_operations : [chain_id] when it is not provided. *) val make_run_operation_input : ?chain_id:string -> t -> Client.t -> JSON.u Lwt.t +(** Craft a json representing the full operation, in a format that is + compatible with the [preapply/operations] RPC + ({!RPC.post_chain_block_helpers_preapply_operations}). + + This json contains many more fields than the one produced by the + {!json} function above. *) +val make_preapply_operation_input : + protocol:Protocol.t -> signature:Tezos_crypto.Signature.t -> t -> JSON.u + module Consensus : sig (** A representation of a consensus operation. *) type t diff --git a/tezt/tests/rpc_versioning_attestation.ml b/tezt/tests/rpc_versioning_attestation.ml index 7161aa6f00eb0cf82c31f1eee23f37af0be1310b..19ee65878779e3bcc218f4aa051987f74cc1fae5 100644 --- a/tezt/tests/rpc_versioning_attestation.ml +++ b/tezt/tests/rpc_versioning_attestation.ml @@ -75,21 +75,46 @@ 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 ~use_legacy_name ~check ~rpc ~get_name ~data client = + let* t = RPC.Client.call client @@ rpc ~version data in + return (check t (get_name use_legacy_name)) -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 check_unknown_version ~version ~rpc ~data client = 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 check_rpc_versions ?(old = "0") ?(new_ = "1") ?(unknown = "2") ~check ~rpc + ~get_name ~data client = + Log.info + "Call the rpc with the old version and check that the operations returned \ + contain endorsement kinds" ; + let* () = + check_version + ~version:old + ~use_legacy_name:true + ~check + ~rpc + ~get_name + ~data + client + in + Log.info + "Call the rpc with the new version and check that the operations returned \ + contain attestation kinds" ; + let* () = + check_version + ~version:new_ + ~use_legacy_name:false + ~check + ~rpc + ~get_name + ~data + client + in + Log.info "Call the rpc with an unknown version and check that the call fails" ; + check_unknown_version ~version:unknown ~rpc ~data client + let create_consensus_op ?slot ?level ?round ?block_payload_hash ~use_legacy_name ~signer ~kind client = let consensus_name = @@ -377,24 +402,12 @@ module Parse = struct in let* raw = create_raw_op ~protocol consensus_op client in - 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 ~version:`Old in - let* () = check_version ~version:`New in - check_unknown_version ~rpc ~data:raw client + check_rpc_versions + ~check:check_parsed_kind + ~rpc + ~get_name:(Operation.Consensus.kind_to_string kind) + ~data:raw + client let test_parse_consensus = register_test @@ -423,24 +436,12 @@ module Parse = struct in let* raw = create_raw_double_consensus_evidence () 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_parsed_kind - ~rpc - ~name - ~data:raw - client - in - - let* () = check_version ~version:`Old in - let* () = check_version ~version:`New in - check_unknown_version ~rpc ~data:raw client + check_rpc_versions + ~check:check_parsed_kind + ~rpc + ~get_name:(Operation.Anonymous.kind_to_string double_evidence_kind) + ~data:raw + client let test_parse_double_consensus_evidence = register_test @@ -470,6 +471,8 @@ module Parse = struct end module Mempool = struct + let rpc ~version () = RPC.get_chain_mempool_pending_operations ~version () + let test_pending_operations_consensus kind protocol = let* _node, client = Client.init_with_protocol ~protocol `Client () in let signer = Constant.bootstrap1 in @@ -481,20 +484,19 @@ module Mempool = struct Operation.inject ~force:true ~request:`Inject consensus_op client in - let check_mempool ~use_legacy_name = - let* mempool_json = - RPC.Client.call client - @@ RPC.get_chain_mempool_pending_operations - ~version:(if use_legacy_name then "1" else "2") - () - in - return - (check_kind - JSON.(mempool_json |-> "refused" |> as_list |> List.hd) - (Operation.Consensus.kind_to_string kind use_legacy_name)) + let check json = + check_kind JSON.(json |-> "refused" |> as_list |> List.hd) in - let* () = check_mempool ~use_legacy_name:true in - check_mempool ~use_legacy_name:false + let get_name = Operation.Consensus.kind_to_string kind in + check_rpc_versions + ~old:"1" + ~new_:"2" + ~unknown:"3" + ~check + ~rpc + ~get_name + ~data:() + client let test_pending_consensus = register_test @@ -525,22 +527,19 @@ module Mempool = struct Operation.inject ~force:true ~request:`Inject consensus_op client in - let check_mempool ~use_legacy_name = - let* mempool_json = - RPC.Client.call client - @@ RPC.get_chain_mempool_pending_operations - ~version:(if use_legacy_name then "1" else "2") - () - in - return - (check_kind - JSON.(mempool_json |-> "applied" |> as_list |> List.hd) - (Operation.Anonymous.kind_to_string - double_evidence_kind - use_legacy_name)) + let check json = + check_kind JSON.(json |-> "applied" |> as_list |> List.hd) in - let* () = check_mempool ~use_legacy_name:true in - check_mempool ~use_legacy_name:false + let get_name = Operation.Anonymous.kind_to_string double_evidence_kind in + check_rpc_versions + ~old:"1" + ~new_:"2" + ~unknown:"3" + ~check + ~rpc + ~get_name + ~data:() + client let test_pending_double_consensus_evidence = register_test @@ -818,17 +817,12 @@ module Run_Simulate = struct let* op_json = Operation.make_run_operation_input consensus_op client in 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 + check_rpc_versions + ~check:check_kind + ~rpc + ~get_name:(Operation.Anonymous.kind_to_string double_evidence_kind) + ~data:op_json + client in let* () = call_and_check_versions ~use_legacy_name_in_input:true in call_and_check_versions ~use_legacy_name_in_input:false @@ -884,8 +878,111 @@ module Run_Simulate = struct test_simulate_operation_double_preconsensus_evidence protocols end +module Preapply = struct + let rpc ~version data = + RPC.post_chain_block_helpers_preapply_operations + ~data:(Data (`A [data])) + ~version + () + + let test_consensus kind protocol = + let* node, client = Client.init_with_protocol ~protocol `Client () in + let* () = Client.bake_for_and_wait ~node client in + let signer = Constant.bootstrap1 in + let* level, slots, block_payload_hash = + get_consensus_info signer.public_key_hash client + in + + let preapply_op ~use_legacy_name = + let* consensus_op = + create_consensus_op + ~level + ~slot:(List.hd slots) + ~block_payload_hash + ~use_legacy_name + ~signer + ~kind + client + in + let* signature = Operation.sign consensus_op client in + let consensus_json = + Operation.make_preapply_operation_input + ~protocol + ~signature + consensus_op + in + let get_name = Operation.Consensus.kind_to_string kind in + let check json = check_kind JSON.(json |> as_list |> List.hd) in + check_rpc_versions ~check ~rpc ~get_name ~data:consensus_json client + in + let* () = preapply_op ~use_legacy_name:true in + preapply_op ~use_legacy_name:false + + let test_preapply_consensus = + register_test + ~title:"Preapply operation with consensus operations" + ~additionnal_tags:["preapply"; "operations"; "consensus"] + @@ fun protocol -> test_consensus Operation.Attestation protocol + + let test_preapply_preconsensus = + register_test + ~title:"Preapply operation with preconsensus operations" + ~additionnal_tags:["preapply"; "operations"; "pre"; "consensus"] + @@ fun protocol -> test_consensus Operation.Preattestation protocol + + let test_double_consensus_evidence double_evidence_kind protocol = + let* node, client = Client.init_with_protocol ~protocol `Client () in + let* () = Client.bake_for_and_wait ~node client in + + let preapply_op ~use_legacy_name = + let* double_consensus_evidence_op = + create_double_consensus_evidence + ~use_legacy_name + ~double_evidence_kind + client + in + let* signature = Operation.sign double_consensus_evidence_op client in + let consensus_json = + Operation.make_preapply_operation_input + ~protocol + ~signature + double_consensus_evidence_op + in + let get_name = Operation.Anonymous.kind_to_string double_evidence_kind in + let check json = check_kind JSON.(json |> as_list |> List.hd) in + check_rpc_versions ~check ~rpc ~get_name ~data:consensus_json client + in + let* () = preapply_op ~use_legacy_name:true in + preapply_op ~use_legacy_name:false + + let test_preapply_double_consensus_evidence = + register_test + ~title:"Preapply operation with double consensus evidence operations" + ~additionnal_tags:["preapply"; "double"; "operations"; "consensus"] + @@ fun protocol -> + test_double_consensus_evidence + Operation.Anonymous.Double_attestation_evidence + protocol + + let test_preapply_double_preconsensus_evidence = + register_test + ~title:"Preapply operation with duoble preconsensus evidence operations" + ~additionnal_tags:["preapply"; "double"; "operations"; "pre"; "consensus"] + @@ fun protocol -> + test_double_consensus_evidence + Operation.Anonymous.Double_preattestation_evidence + protocol + + let register ~protocols = + test_preapply_consensus protocols ; + test_preapply_preconsensus protocols ; + test_preapply_double_consensus_evidence protocols ; + test_preapply_double_preconsensus_evidence protocols +end + let register ~protocols = Forge.register ~protocols ; Parse.register ~protocols ; Mempool.register ~protocols ; - Run_Simulate.register ~protocols + Run_Simulate.register ~protocols ; + Preapply.register ~protocols