From a97048c3781713b89cbc11067dbf3f673d8e8659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Tue, 29 Aug 2023 14:14:33 +0200 Subject: [PATCH 1/8] Plugin/Michelson: improve description of {run,trace}_code RPCs Makes the descriptions more precise by mentionning that a full Michelson script is required. --- src/proto_alpha/lib_plugin/RPC.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index bb312ff9d105..f650f62a896c 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -377,7 +377,7 @@ module Scripts = struct let run_code = RPC_service.post_service - ~description:"Run a piece of code in the current context" + ~description:"Run a Michelson script in the current context" ~query:RPC_query.empty ~input:run_code_input_encoding ~output:run_code_output_encoding @@ -386,7 +386,7 @@ module Scripts = struct let trace_code = RPC_service.post_service ~description: - "Run a piece of code in the current context, keeping a trace" + "Run a Michelson script in the current context, keeping a trace" ~query:RPC_query.empty ~input:trace_code_input_encoding ~output:trace_code_output_encoding -- GitLab From 12602b5a8d21de028347ef3d3f5cf1684b715c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Fri, 18 Feb 2022 16:00:18 +0100 Subject: [PATCH 2/8] Plugin/Michelson: add a RPC exporting the interpreter step function This commit adds a new plugin RPC permitting to call the Michelson interpreter on a single Michelson instruction (or a sequence of Michelson instructions) and a stack. --- src/proto_alpha/lib_plugin/RPC.ml | 163 +++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 3 deletions(-) diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index f650f62a896c..6804daf297b5 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -324,6 +324,30 @@ module Scripts = struct (req "type" Script.expr_encoding) (req "val" Script.expr_encoding)) + let run_instr_input_encoding = + merge_objs + (obj10 + (req "input" stack_encoding) + (req "code" Script.expr_encoding) + (req "chain_id" Chain_id.encoding) + (opt "gas" Gas.Arith.z_integral_encoding) + (opt "now" Script_timestamp.encoding) + (opt "level" Script_int.n_encoding) + (opt "sender" Contract.encoding) + (opt "source" Contract.implicit_encoding) + (opt "self" Contract.originated_encoding) + (opt "parameter" Script.expr_encoding)) + (obj6 + (req "amount" Tez.encoding) + (opt "balance" Tez.encoding) + (opt "other_contracts" other_contracts_encoding) + (opt "big_maps" extra_big_maps_encoding) + (opt "unparsing_mode" unparsing_mode_encoding) + (dft "legacy" bool false)) + + let run_instr_output_encoding = + obj2 (req "output" stack_encoding) (req "gas" Gas.encoding) + let run_tzip4_view_encoding = let open Data_encoding in merge_objs @@ -411,6 +435,14 @@ module Scripts = struct ~query:RPC_query.empty RPC_path.(path / "run_script_view") + let run_instr = + RPC_service.post_service + ~description:"Run a single Michelson instruction" + ~query:RPC_query.empty + ~input:run_instr_input_encoding + ~output:run_instr_output_encoding + RPC_path.(path / "run_instruction") + let typecheck_code = RPC_service.post_service ~description:"Typecheck a piece of code in the current context" @@ -1325,7 +1357,7 @@ module Scripts = struct let sender = Destination.Contract sender in (ctxt, {sender; payer; self; amount; balance; chain_id; now; level}) in - let configure_gas_and_step_constants ctxt script ~gas_opt ~balance ~amount + let configure_gas_and_step_constants ctxt ~script ~gas_opt ~balance ~amount ~chain_id ~sender_opt ~payer_opt ~self_opt ~now_opt ~level_opt = let gas = match gas_opt with @@ -1423,7 +1455,7 @@ module Scripts = struct let* ctxt, step_constants = configure_gas_and_step_constants ctxt - {storage; code} + ~script:{storage; code} ~gas_opt ~balance ~amount @@ -1490,7 +1522,7 @@ module Scripts = struct let* ctxt, step_constants = configure_gas_and_step_constants ctxt - {storage; code} + ~script:{storage; code} ~gas_opt ~balance ~amount @@ -1882,6 +1914,115 @@ module Scripts = struct in let normalized = Unparse_types.unparse_ty ~loc:() typ in return @@ Micheline.strip_locations normalized) ; + Registration.register0 + ~chunked:true + S.run_instr + (fun + ctxt + () + ( ( input, + code, + chain_id, + gas_opt, + now_opt, + level_opt, + sender_opt, + source_opt, + self_opt, + parameter_opt ), + ( amount, + balance, + other_contracts, + extra_big_maps, + unparsing_mode, + legacy ) ) + -> + let unparsing_mode = Option.value ~default:Readable unparsing_mode in + let other_contracts = Option.value ~default:[] other_contracts in + let* ctxt = originate_dummy_contracts ctxt other_contracts in + let extra_big_maps = Option.value ~default:[] extra_big_maps in + let* ctxt = initialize_big_maps ctxt extra_big_maps in + let parameter = + Option.value + ~default: + (Micheline.strip_locations + (Prim (0, Michelson_v1_primitives.T_unit, [], []))) + parameter_opt + in + let*? Ex_parameter_ty_and_entrypoints {arg_type; entrypoints}, ctxt = + Script_ir_translator.parse_parameter_ty_and_entrypoints + ctxt + ~legacy + (Micheline.root parameter) + in + let* ctxt, step_constants = + configure_gas_and_step_constants + ctxt + ~script:(View_helpers.make_tzip4_viewer_script parameter) + ~gas_opt + ~balance + ~amount + ~chain_id + ~sender_opt + ~payer_opt:source_opt + ~self_opt + ~now_opt + ~level_opt + in + let input_nodes = + List.map (fun (a, b) -> (Micheline.root a, Micheline.root b)) input + in + let* Normalize_stack.Ex_stack (st_ty, x, st), ctxt = + Normalize_stack.parse_stack ctxt ~legacy input_nodes + in + let* j, ctxt = + Script_ir_translator.parse_instr + ~elab_conf:(Script_ir_translator_config.make ~legacy ()) + (Script_tc_context.toplevel + ~storage_type:Script_typed_ir.unit_t + ~param_type:arg_type + ~entrypoints) + ctxt + (Micheline.root code) + st_ty + in + match j with + | Failed {descr} -> ( + let impossible_stack_ty = + Script_typed_ir.(Item_t (never_t, Bot_t)) + in + let descr = descr impossible_stack_ty in + let descr = Script_ir_translator.close_descr descr in + let* absurd = + Script_interpreter.Internals.step_descr + None + ctxt + step_constants + descr + x + st + in + match absurd with _ -> .) + | Typed descr -> + let descr = Script_ir_translator.close_descr descr in + let* y, output_st, _ctxt = + Script_interpreter.Internals.step_descr + None + ctxt + step_constants + descr + x + st + in + let+ output, ctxt = + Normalize_stack.unparse_stack + ctxt + unparsing_mode + descr.kaft + y + output_st + in + (output, Gas.level ctxt)) ; (* TODO: https://gitlab.com/tezos/tezos/-/issues/3364 Should [run_operation] be registered at successor level? *) @@ -2003,6 +2144,22 @@ module Scripts = struct now ), (level, other_contracts, extra_big_maps) ) + let run_instr ~gas ~legacy ~input ~code ~chain_id ~now ~level ~unparsing_mode + ~source ~sender ~self ~parameter ~amount ~balance ~other_contracts + ~extra_big_maps ctxt block = + RPC_context.make_call0 + S.run_instr + ctxt + block + () + ( (input, code, chain_id, gas, now, level, sender, source, self, parameter), + ( amount, + balance, + other_contracts, + extra_big_maps, + unparsing_mode, + legacy ) ) + let typecheck_code ~gas ~legacy ~script ~show_types ctxt block = RPC_context.make_call0 S.typecheck_code -- GitLab From b63eda88bc63b5d3a581f18c8edd24a996923ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Wed, 8 Nov 2023 22:57:42 +0100 Subject: [PATCH 3/8] Client/Michelson: client command wrapping the run_instruction RPC --- .../lib_client/client_proto_programs.ml | 66 ++++++++++++++ .../lib_client/client_proto_programs.mli | 26 ++++++ .../client_proto_programs_commands.ml | 89 ++++++++++++++++--- 3 files changed, 171 insertions(+), 10 deletions(-) diff --git a/src/proto_alpha/lib_client/client_proto_programs.ml b/src/proto_alpha/lib_client/client_proto_programs.ml index e3762a69cafa..33105916b2af 100644 --- a/src/proto_alpha/lib_client/client_proto_programs.ml +++ b/src/proto_alpha/lib_client/client_proto_programs.ml @@ -140,6 +140,22 @@ let print_trace_result (cctxt : #Client_context.printer) ~show_source ~parsed = return_unit | Error errs -> print_errors cctxt errs ~show_source ~parsed +let print_run_instr_result (cctxt : #Client_context.printer) ~show_source + ~parsed = + let open Lwt_result_syntax in + function + | Ok (stack, gas) -> + let*! () = + cctxt#message + "@[@[Result@,%a@]@,@[Gas remaining: %a@]@]@." + Michelson_v1_printer.print_typed_stack + stack + Alpha_context.Gas.pp + gas + in + return_unit + | Error errs -> print_errors cctxt errs ~show_source ~parsed + type simulation_params = { input : Michelson_v1_parser.parsed; unparsing_mode : Script_ir_unparser.unparsing_mode; @@ -175,6 +191,16 @@ type run_params = { self : Contract_hash.t option; } +type run_instr_params = { + shared_params : simulation_params; + amount : Tez.t; + balance : Tez.t option; + stack : (Script.expr * Script.expr) list; + self : Contract_hash.t option; + parameter : Script.expr option; + legacy : bool; +} + let run_view (cctxt : #Protocol_client_context.rpc_context) ~(chain : Chain_services.chain) ~block (params : run_view_params) = let open Lwt_result_syntax in @@ -349,6 +375,46 @@ let trace (cctxt : #Protocol_client_context.rpc_context) ~other_contracts ~extra_big_maps +let run_instr (cctxt : #Protocol_client_context.rpc_context) + ~(chain : Chain_services.chain) ~block (params : run_instr_params) = + let open Lwt_result_syntax in + let* chain_id = Chain_services.chain_id cctxt ~chain () in + let {shared_params; amount; balance; stack; self; parameter; legacy} = + params + in + let { + input; + unparsing_mode; + now; + level; + sender; + payer; + gas; + other_contracts; + extra_big_maps; + } = + shared_params + in + Plugin.RPC.Scripts.run_instr + ~gas + ~legacy + ~input:stack + ~code:input.expanded + ~chain_id + ~now + ~level + ~unparsing_mode:(Some unparsing_mode) + ~source:payer + ~sender + ~self + ~parameter + ~amount + ~balance + ~other_contracts + ~extra_big_maps + cctxt + (chain, block) + let typecheck_data cctxt ~(chain : Chain_services.chain) ~block ~gas ~legacy ~(data : Michelson_v1_parser.parsed) ~(ty : Michelson_v1_parser.parsed) () = Plugin.RPC.Scripts.typecheck_data diff --git a/src/proto_alpha/lib_client/client_proto_programs.mli b/src/proto_alpha/lib_client/client_proto_programs.mli index 638d4e6a2752..eb7593fdc204 100644 --- a/src/proto_alpha/lib_client/client_proto_programs.mli +++ b/src/proto_alpha/lib_client/client_proto_programs.mli @@ -71,6 +71,17 @@ type run_params = { self : Contract_hash.t option; } +(* Parameters specific to simulations of single steps *) +type run_instr_params = { + shared_params : simulation_params; + amount : Tez.t; + balance : Tez.t option; + stack : (Script.expr * Script.expr) list; + self : Contract_hash.t option; + parameter : Script.expr option; + legacy : bool; +} + (** Calls {!Tezos_protocol_plugin_alpha.Plugin.RPC.Scripts.run_tzip4_view} *) val run_view : #Protocol_client_context.rpc_context -> @@ -115,6 +126,14 @@ val trace : tzresult Lwt.t +(** Calls {!Tezos_protocol_plugin_alpha.Plugin.RPC.Scripts.run_instr} *) +val run_instr : + #Protocol_client_context.rpc_context -> + chain:Shell_services.chain -> + block:Shell_services.block -> + run_instr_params -> + ((Script.expr * Script.expr) list * Gas.t) tzresult Lwt.t + val print_view_result : #Protocol_client_context.full -> Script_repr.expr tzresult -> @@ -141,6 +160,13 @@ val print_trace_result : tzresult -> unit tzresult Lwt.t +val print_run_instr_result : + #Protocol_client_context.full -> + show_source:bool -> + parsed:Michelson_v1_parser.parsed -> + ((Script.expr * Script.expr) list * Gas.t) tzresult -> + unit tzresult Lwt.t + (** Calls {!Tezos_protocol_plugin_alpha.Plugin.RPC.Scripts.typecheck_data} *) val typecheck_data : #Protocol_client_context.rpc_context -> diff --git a/src/proto_alpha/lib_client_commands/client_proto_programs_commands.ml b/src/proto_alpha/lib_client_commands/client_proto_programs_commands.ml index de0eba0556e6..27f6370be44b 100644 --- a/src/proto_alpha/lib_client_commands/client_proto_programs_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_proto_programs_commands.ml @@ -179,6 +179,15 @@ let commands () = ~from_text:(fun _cctx s -> Lwt_result_syntax.return s) ()) in + let stack_param () = + param + ~name:"stack" + ~desc: + "a Michelson stack in the following format: {Stack_elt ; \ + ...; Stack_elt }, where each is a Michelson \ + value of type . The topmost element of the stack is ." + micheline_parameter + in let handle_parsing_error label (cctxt : Protocol_client_context.full) (emacs_mode, no_print_source) program body = let open Lwt_result_syntax in @@ -347,6 +356,75 @@ let commands () = } in print_run_result cctxt ~show_source ~parsed:program res); + command + ~group + ~desc: + "Ask the node to run a Michelson instruction or a sequence of \ + Michelson instructions on a stack." + (args12 + amount_arg + balance_arg + source_arg + payer_arg + self_arg + run_gas_limit_arg + (unparsing_mode_arg ~default:"Readable") + now_arg + level_arg + other_contracts_arg + extra_big_maps_arg + legacy_switch) + (prefixes ["run"; "michelson"; "code"] + @@ Program.source_param + @@ prefixes ["on"; "stack"] + @@ stack_param () @@ stop) + (fun ( amount, + balance, + sender, + payer, + self, + gas, + unparsing_mode, + now, + level, + other_contracts, + extra_big_maps, + legacy ) + program + (stack, stack_source) + cctxt -> + let open Lwt_result_syntax in + let*? program = Micheline_parser.no_parsing_error program in + let*? stack = + Michelson_v1_stack.parse_stack ~source:stack_source stack + in + let*! res = + run_instr + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + { + stack; + shared_params = + { + input = program; + unparsing_mode; + now; + level; + sender; + payer; + gas; + other_contracts; + extra_big_maps; + }; + amount; + balance; + self; + parameter = None; + legacy; + } + in + print_run_instr_result cctxt ~show_source:false ~parsed:program res); command ~group ~desc:"Ask the node to compute the size of a script." @@ -776,16 +854,7 @@ let commands () = legacy_switch other_contracts_arg extra_big_maps_arg) - (prefixes ["normalize"; "stack"] - @@ param - ~name:"stack" - ~desc: - "the stack to normalize, in the following format: {Stack_elt \ - ; ...; Stack_elt }, where each \ - is a Michelson value of type . The topmost element \ - of the stack is ." - micheline_parameter - @@ stop) + (prefixes ["normalize"; "stack"] @@ stack_param () @@ stop) (fun (unparsing_mode, legacy, other_contracts, extra_big_maps) (stack, source) cctxt -> -- GitLab From bfe85e44fcf1c0752f6f9fce2755fdcb3e68219c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Tue, 7 Nov 2023 16:55:35 +0100 Subject: [PATCH 4/8] Tezt/Client: add support for the run code command --- tezt/lib_tezos/client.ml | 70 ++++++++++++++++++++++++++++++++++++--- tezt/lib_tezos/client.mli | 42 +++++++++++++++++++++++ 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/tezt/lib_tezos/client.ml b/tezt/lib_tezos/client.ml index 735c43895388..533812ff4aa8 100644 --- a/tezt/lib_tezos/client.ml +++ b/tezt/lib_tezos/client.ml @@ -49,6 +49,11 @@ type mockup_sync_mode = Asynchronous | Synchronous type normalize_mode = Readable | Optimized | Optimized_legacy +let normalize_mode_to_string = function + | Readable -> "Readable" + | Optimized -> "Optimized" + | Optimized_legacy -> "Optimized_legacy" + type t = { path : string; admin_path : string; @@ -1780,6 +1785,23 @@ let spawn_run_script_at ?hooks ?protocol_hash ?balance ?self_address ?source ~input client +let spawn_run_code ?hooks ?protocol_hash ?no_base_dir_warnings ?amount ?balance + ?source ?payer ?self_address ?gas ?mode ?level ?now ?other_contracts + ?extra_big_maps ~src ~stack client = + spawn_command ?hooks ?protocol_hash ?no_base_dir_warnings client + @@ ["run"; "michelson"; "code"; src; "on"; "stack"; stack] + @ optional_arg "amount" Tez.to_string amount + @ optional_arg "balance" Tez.to_string balance + @ optional_arg "source" Fun.id source + @ optional_arg "payer" Fun.id payer + @ optional_arg "self-address" Fun.id self_address + @ optional_arg "gas" string_of_int gas + @ optional_arg "unparsing-mode" normalize_mode_to_string mode + @ optional_arg "now" Fun.id now + @ optional_arg "level" string_of_int level + @ optional_arg "other-contracts" Fun.id other_contracts + @ optional_arg "extra-big-maps" Fun.id extra_big_maps + let stresstest_estimate_gas ?endpoint client = let* output = spawn_command ?endpoint client ["stresstest"; "estimate"; "gas"] @@ -1917,6 +1939,49 @@ let run_script_at ?hooks ?protocol_hash ?balance ?self_address ?source ?payer ~prg client +let run_code ?hooks ?protocol_hash ?no_base_dir_warnings ?amount ?balance + ?source ?payer ?self_address ?gas ?mode ?level ?now ?other_contracts + ?extra_big_maps ~src ~stack client = + let* client_output = + spawn_run_code + ?hooks + ?protocol_hash + ?no_base_dir_warnings + ?amount + ?balance + ?source + ?payer + ?self_address + ?gas + ?mode + ?level + ?now + ?other_contracts + ?extra_big_maps + ~src + ~stack + client + |> Process.check_and_read_stdout + in + (* Extract the final stack from [client_output]. + + The [client_ouput] has the following format: + Result + + Gas_remaining: units remaining *) + let client_output_lines = String.split_on_char '\n' client_output in + let stack, _tail = + span + (fun line -> not (String.starts_with ~prefix:"Gas remaining" line)) + client_output_lines + in + match stack with + | "Result" :: stack -> return (String.trim (String.concat "\n" stack)) + | _ -> + Test.fail + "Cannot extract resulting stack from client_output: %s," + client_output + let spawn_register_global_constant ?(wait = "none") ?burn_cap ~value ~src client = spawn_command @@ -2037,11 +2102,6 @@ let hash_data ?hooks ~data ~typ client = raw_sha512_hash = get "Raw Sha512 hash"; } -let normalize_mode_to_string = function - | Readable -> "Readable" - | Optimized -> "Optimized" - | Optimized_legacy -> "Optimized_legacy" - let spawn_normalize_data ?hooks ?mode ?(legacy = false) ~data ~typ client = let mode_cmd = Option.map normalize_mode_to_string mode diff --git a/tezt/lib_tezos/client.mli b/tezt/lib_tezos/client.mli index f454e872a77c..b48b64162325 100644 --- a/tezt/lib_tezos/client.mli +++ b/tezt/lib_tezos/client.mli @@ -1448,6 +1448,48 @@ val spawn_run_script_at : Protocol.t -> Process.t +(** Run [octez-client run michelson code .. on stack ..]. *) +val run_code : + ?hooks:Process.hooks -> + ?protocol_hash:string -> + ?no_base_dir_warnings:bool -> + ?amount:Tez.t -> + ?balance:Tez.t -> + ?source:string -> + ?payer:string -> + ?self_address:string -> + ?gas:int -> + ?mode:normalize_mode -> + ?level:int -> + ?now:string -> + ?other_contracts:string -> + ?extra_big_maps:string -> + src:string -> + stack:string -> + t -> + string Lwt.t + +(** Same as [run_code] but do not wait for the process to exit. *) +val spawn_run_code : + ?hooks:Process.hooks -> + ?protocol_hash:string -> + ?no_base_dir_warnings:bool -> + ?amount:Tez.t -> + ?balance:Tez.t -> + ?source:string -> + ?payer:string -> + ?self_address:string -> + ?gas:int -> + ?mode:normalize_mode -> + ?level:int -> + ?now:string -> + ?other_contracts:string -> + ?extra_big_maps:string -> + src:string -> + stack:string -> + t -> + Process.t + (** Run [octez-client register global constant value from src]. Returns the address hash of the new constant. *) val register_global_constant : -- GitLab From 25b08246b70151f0d6846f376e185a41739120dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Tue, 7 Nov 2023 16:55:15 +0100 Subject: [PATCH 5/8] Tezt/Michelson: add tests for the run_code command --- tezt/tests/main.ml | 1 + tezt/tests/run_code.ml | 264 +++++++++++++++++++++++++++++++++++++++ tezt/tests/run_script.ml | 4 +- 3 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 tezt/tests/run_code.ml diff --git a/tezt/tests/main.ml b/tezt/tests/main.ml index 07367b1b9b02..8754f2c31d33 100644 --- a/tezt/tests/main.ml +++ b/tezt/tests/main.ml @@ -184,6 +184,7 @@ let register_protocol_tests_that_use_supports_correctly () = Rpc_config_logging.register ~protocols ; Run_operation_RPC.register ~protocols ; Run_script.register ~protocols ; + Run_code.register ~protocols ; Runtime_script_failure.register ~protocols ; Sapling.register ~protocols ; Script_annotations.register ~protocols ; diff --git a/tezt/tests/run_code.ml b/tezt/tests/run_code.ml new file mode 100644 index 000000000000..64d4a3ec8fda --- /dev/null +++ b/tezt/tests/run_code.ml @@ -0,0 +1,264 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2023 Nomadic Labs *) +(* *) +(*****************************************************************************) + +(* Testing + ------- + Component: Client + Invocation: dune exec tezt/tests/main.exe -- --file run_code.ml + Subject: Check that run code command to octez-client behaves correctly +*) + +let test_balance_and_self_address = + Protocol.register_test + ~__FILE__ + ~title:"Run code with balance and self address" + ~tags:["client"; "michelson"] + ~supports:(Protocol.From_protocol 018) + @@ fun protocol -> + let* client = Client.init_mockup ~protocol () in + (* With no parameters, the default BALANCE is 4 000 000 ęś©. *) + let* stack = Client.run_code ~src:"BALANCE" ~stack:"{}" client in + assert (stack = "{ Stack_elt mutez 4000000000000 }") ; + + (* When --balance is given, BALANCE should match the expected value. *) + let* stack = + Client.run_code ~balance:(Tez.of_int 1) ~src:"BALANCE" ~stack:"{}" client + in + assert (stack = "{ Stack_elt mutez 1000000 }") ; + + let* self_address = + Client.originate_contract + ~burn_cap:(Tez.of_int 1) + ~alias:"test_contract" + ~amount:(Tez.of_int 100) + ~src:"bootstrap1" + ~prg:{|parameter unit; storage unit; code {CDR; NIL operation; PAIR}|} + client + in + + (* When --self-address is given, SELF_ADDRESS should match with it. *) + let* stack = + Client.run_code ~self_address ~src:"SELF_ADDRESS" ~stack:"{}" client + in + assert (stack = Format.sprintf "{ Stack_elt address %S }" self_address) ; + (* When --self-address is given, BALANCE should be equal to that of the + given account. *) + let* stack = + Client.run_code ~self_address ~src:"BALANCE" ~stack:"{}" client + in + assert (stack = "{ Stack_elt mutez 100000000 }") ; + + (* When both --self-address and --balance are given, the BALANCE should be + equal to the given value and SELF_ADDRESS should still match the given one. *) + let* stack = + Client.run_code + ~balance:(Tez.of_int 1) + ~self_address + ~src:"SELF_ADDRESS" + ~stack:"{}" + client + in + assert (stack = Format.sprintf "{ Stack_elt address %S }" self_address) ; + let* stack = + Client.run_code + ~balance:(Tez.of_int 1) + ~self_address + ~src:"BALANCE" + ~stack:"{}" + client + in + assert (stack = "{ Stack_elt mutez 1000000 }") ; + unit + +let test_source_and_sender = + Protocol.register_test + ~__FILE__ + ~title:"Run code with source and sender" + ~tags:["client"; "michelson"] + ~supports:(Protocol.From_protocol 018) + @@ fun protocol -> + let* client = Client.init_mockup ~protocol () in + let* bootstrap1 = Client.show_address ~alias:"bootstrap1" client in + let* bootstrap2 = Client.show_address ~alias:"bootstrap2" client in + + (* When --payer is absent, --source sets SENDER, but SOURCE is the + zero address. *) + let expected_source = "0x00000000000000000000000000000000000000000000" in + let* stack = + Client.run_code + ~source:"bootstrap1" + ~src:"SOURCE" + ~stack:"{}" + ~mode:Optimized + client + in + assert (stack = Format.sprintf "{ Stack_elt address %s }" expected_source) ; + let* stack = + Client.run_code ~source:"bootstrap1" ~src:"SENDER" ~stack:"{}" client + in + assert ( + stack = Format.sprintf "{ Stack_elt address %S }" bootstrap1.public_key_hash) ; + + (* When --source is absent, --payer sets *both* SENDER and SOURCE. *) + let* stack = + Client.run_code ~payer:"bootstrap1" ~src:"SOURCE" ~stack:"{}" client + in + assert ( + stack = Format.sprintf "{ Stack_elt address %S }" bootstrap1.public_key_hash) ; + let* stack = + Client.run_code ~payer:"bootstrap1" ~src:"SENDER" ~stack:"{}" client + in + assert ( + stack = Format.sprintf "{ Stack_elt address %S }" bootstrap1.public_key_hash) ; + + (* When both --source and --payer are given, their values may differ. *) + let* stack = + Client.run_code + ~payer:"bootstrap1" + ~source:"bootstrap2" + ~src:"SOURCE" + ~stack:"{}" + client + in + assert ( + stack = Format.sprintf "{ Stack_elt address %S }" bootstrap1.public_key_hash) ; + let* stack = + Client.run_code + ~payer:"bootstrap1" + ~source:"bootstrap2" + ~src:"SENDER" + ~stack:"{}" + client + in + assert ( + stack = Format.sprintf "{ Stack_elt address %S }" bootstrap2.public_key_hash) ; + unit + +let test_other_contracts = + Protocol.register_test + ~__FILE__ + ~title:"Run code with other_contracts" + ~tags:["client"; "michelson"] + ~supports:(Protocol.From_protocol 018) + @@ fun protocol -> + let* client = Client.init_mockup ~protocol () in + let unused_address = "KT1Q36KWPSba7dHsH5E4ZsQHehrChc51e19d" in + let* stack = + Client.run_code + ~src:"CONTRACT nat" + ~stack:(Format.sprintf "{ Stack_elt address %S }" unused_address) + ~other_contracts:(Printf.sprintf "{Contract %S nat}" unused_address) + client + in + assert ( + stack + = Format.sprintf + "{ Stack_elt (option (contract nat)) (Some %S) }" + unused_address) ; + unit + +let test_extra_big_maps = + Protocol.register_test + ~__FILE__ + ~title:"Run code with extra_big_maps" + ~tags:["client"; "michelson"] + ~supports:(Protocol.From_protocol 018) + @@ fun protocol -> + let* client = Client.init_mockup ~protocol () in + let* stack = + Client.run_code + ~src:"GET" + ~stack:{|{ Stack_elt nat 42; Stack_elt (big_map nat string) 4 }|} + ~extra_big_maps:{|{Big_map 4 nat string {Elt 42 "foobar"}}|} + client + in + assert (stack = {|{ Stack_elt (option string) (Some "foobar") }|}) ; + unit + +let test_amount = + Protocol.register_test + ~__FILE__ + ~title:"Run code with amount" + ~tags:["client"; "michelson"] + ~supports:(Protocol.From_protocol 018) + @@ fun protocol -> + let* client = Client.init_mockup ~protocol () in + let* stack = Client.run_code ~src:"AMOUNT" ~stack:"{}" client in + assert (stack = {|{ Stack_elt mutez 50000 }|}) ; + let* stack = + Client.run_code ~src:"AMOUNT" ~stack:"{}" ~amount:Tez.one client + in + assert (stack = {|{ Stack_elt mutez 1000000 }|}) ; + unit + +let test_level = + Protocol.register_test + ~__FILE__ + ~title:"Run code with level" + ~tags:["client"; "michelson"] + ~supports:(Protocol.From_protocol 018) + @@ fun protocol -> + let* client = Client.init_mockup ~protocol () in + let* stack = Client.run_code ~src:"LEVEL" ~stack:"{}" client in + assert (stack = {|{ Stack_elt nat 0 }|}) ; + let* stack = Client.run_code ~src:"LEVEL" ~stack:"{}" ~level:1000 client in + assert (stack = {|{ Stack_elt nat 1000 }|}) ; + unit + +let test_now = + Protocol.register_test + ~__FILE__ + ~title:"Run code with now" + ~tags:["client"; "michelson"] + ~supports:(Protocol.From_protocol 018) + @@ fun protocol -> + let* client = Client.init_mockup ~protocol () in + let* stack = Client.run_code ~src:"NOW" ~stack:"{}" client in + assert (stack = {|{ Stack_elt timestamp "1970-01-01T00:00:01Z" }|}) ; + let now = "2023-11-07T16:39:20Z" in + let* stack = Client.run_code ~src:"NOW" ~stack:"{}" ~now client in + assert (stack = Format.sprintf {|{ Stack_elt timestamp %S }|} now) ; + unit + +let test_long_output = + Protocol.register_test + ~__FILE__ + ~title:"Run code outputing a long stack" + ~tags:["client"; "michelson"] + ~supports:(Protocol.From_protocol 018) + @@ fun protocol -> + let* client = Client.init_mockup ~protocol () in + let* stack = + Client.run_code + ~src:"UNPAIR 10" + ~stack: + {|{Stack_elt (pair nat bool string int mutez bytes (option nat) (list string) (set bool) (map string nat)) {42; False; "foo"; -24; 1000; 0x00; None; {"foo"; "bar"}; {False; True}; {Elt "bar" 42} }}|} + client + in + assert ( + stack + = {|{ Stack_elt nat 42 ; + Stack_elt bool False ; + Stack_elt string "foo" ; + Stack_elt int -24 ; + Stack_elt mutez 1000 ; + Stack_elt bytes 0x00 ; + Stack_elt (option nat) None ; + Stack_elt (list string) { "foo" ; "bar" } ; + Stack_elt (set bool) { False ; True } ; + Stack_elt (map string nat) { Elt "bar" 42 } }|}) ; + unit + +let register ~protocols = + test_balance_and_self_address protocols ; + test_source_and_sender protocols ; + test_other_contracts protocols ; + test_extra_big_maps protocols ; + test_amount protocols ; + test_level protocols ; + test_now protocols ; + test_long_output protocols diff --git a/tezt/tests/run_script.ml b/tezt/tests/run_script.ml index 8d9c2b963082..a68baf283913 100644 --- a/tezt/tests/run_script.ml +++ b/tezt/tests/run_script.ml @@ -111,7 +111,7 @@ let test_balance_and_self_address = client in - (* When --self-address is given, SELF_ADDRESS should match the given. *) + (* When --self-address is given, SELF_ADDRESS should match with it. *) let* _storage = Client.run_script ~self_address @@ -132,7 +132,7 @@ let test_balance_and_self_address = in (* When both --self-address and --balance are given, the BALANCE should be - equal to the given value and SELF_ADDRESS should still match the given. *) + equal to the given value and SELF_ADDRESS should still match the given one. *) let* _storage = Client.run_script ~balance:(Tez.of_int 1) -- GitLab From 9f42b8fca837c7d887461c6b49cfba726b74d01f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Tue, 7 Nov 2023 16:59:15 +0100 Subject: [PATCH 6/8] Changes: mention !9935 --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b29dc58533a4..73a6a5732730 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -72,6 +72,10 @@ Client - Added options to temporarily extend the context with other contracts and extra big maps in Michelson commands. (MR :gl:`!9946`) +- Added a ``run_step`` RPC in the plugin and a ``run michelson code`` + client command allowing to run a single Michelson instruction or a + sequence of Michelson instructions on a given stack. (MR :gl:`!9935`) + Baker ----- -- GitLab From 2ddf9fd33be7fc287f32804dd3c30599ea44d325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Wed, 8 Nov 2023 23:20:15 +0100 Subject: [PATCH 7/8] Scripts: Michelson REPL --- docs/user/various.rst | 19 +++++++++++++++++++ scripts/README.md | 6 ++++-- scripts/michelson_repl.sh | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100755 scripts/michelson_repl.sh diff --git a/docs/user/various.rst b/docs/user/various.rst index 8175e23ef74b..8977ec61a9f7 100644 --- a/docs/user/various.rst +++ b/docs/user/various.rst @@ -8,6 +8,25 @@ Environment for writing Michelson contracts editing and debugging Michelson programs. `Install it `_ and follow the configuration instructions in the Michelson Emacs README `here `__. +Michelson interactive toplevel +------------------------------ + +An interactive Michelson toplevel (also known as a `REPL +`__) +built on the :doc:`mockup` mode of Octez client is available in +``scripts/michelson_repl.sh``, the typical usage is: + +:: + + $ octez-client --mode mockup --base-dir /tmp/mockup create mockup + $ rlwrap scripts/michelson_repl.sh + > UNIT + { Stack_elt unit Unit } + > UNIT + { Stack_elt unit Unit ; Stack_elt unit Unit } + > COMPARE + { Stack_elt int 0 } + .. _octez-admin-client: Admin Client diff --git a/scripts/README.md b/scripts/README.md index 7e9f28213561..2f3399a0afe1 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -22,8 +22,10 @@ In particular, this includes scripts for: * setting user-activated upgrades (`user_activated_upgrade.sh`) * generating commitments (initial accounts) for test networks (`create_genesis/create_genesis_info.py`) -This directory also includes an example docker-compose file to run a node -with a baker and an accuser (`docker/docker-compose-generic.yml`). +This directory also includes an example docker-compose file to run a +node with a baker and an accuser (`docker/docker-compose-generic.yml`) +and a minimalistic Michelson REPL built on top of `octez-client` +(`michelson_repl.sh`). ## API