1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com> *)(* Copyright (c) 2023 TriliTech <contact@trili.tech> *)(* *)(* 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. *)(* *)(*****************************************************************************)openRepl_helpersopenTezos_scoru_wasmopenCustom_section(* Possible step kinds. *)typeeval_step=|Tick(** Tick per tick *)|Result(** Up to Eval (Result (SK_Result _ | SK_Trap _)) *)|Kernel_run(** Up to the end of the current `kernel_run` *)|Inbox(** Until input requested *)typeprofile_options={collapse:bool;with_time:bool;no_reboot:bool}(* Possible commands for the REPL. *)typecommands=|Timeofcommands|Show_inbox|Show_outboxofint32|Show_status|Show_durable_storage|Show_subkeysofstring|Show_keyofstring*printable_value_kind|Show_memoryofint32*int*printable_value_kind|Dump_function_symbols|Stepofeval_step|Load_inputs|Reveal_preimageofstringoption|Reveal_metadata|Profileofprofile_options|Unknownofstring|Help|Stopletparse_eval_step=function|"tick"->SomeTick|"result"->SomeResult|"kernel_run"->SomeKernel_run|"inbox"->SomeInbox|_->Noneletparse_memory_commands=function|["at";address;"for";length;"bytes"]->(match(Int32.of_string_optaddress,int_of_string_optlength)with|Someaddress,Somelength->Some(Show_memory(address,length,`Hex))|_,_->None)|["at";address;"for";length;"bytes";"as";"string"]->(match(Int32.of_string_optaddress,int_of_string_optlength)with|Someaddress,Somelength->Some(Show_memory(address,length,`String))|_,_->None)|["at";address;"as";kind]->(match(Int32.of_string_optaddress,integer_value_kind_of_stringkind)with|Someaddress,Somekind->Some(Show_memory(address,value_kind_lengthkind,kind))|_,_->None)|_->Noneletparse_profile_options=letset_optionprofile_options=function|"--collapsed"->Option.map(funopts->{optswithcollapse=true})profile_options|"--without-time"->Option.map(funopts->{optswithwith_time=false})profile_options|"--no-reboot"->Option.map(funopts->{optswithno_reboot=true})profile_options|_->NoneinList.fold_leftset_option(Some{collapse=false;with_time=true;no_reboot=false})(* Documentation for commands type *)typecommand_description={parse:stringlist->commandsoption;documentation:string;}(* Parsing and documentation of each possible command *)letreccommands_docs=[{parse=(function|"time"::rst->try_parserst|>Option.map(funx->Timex)|_->None);documentation="time <command>: Executes <command> and returns the time required for \
its execution.";};{parse=(function["show";"inbox"]->SomeShow_inbox|_->None);documentation="show inbox: Prints the current input buffer and the number of \
messages it contains.";};{parse=(function|["show";"outbox";"at";"level";level]->Int32.of_string_optlevel|>Option.map(funl->Show_outboxl)|_->None);documentation="show outbox at level <level>: Prints the outbox messages for <level>.";};{parse=(function["show";"status"]->SomeShow_status|_->None);documentation="show status: Shows the state of the PVM.";};{parse=(function|["show";"durable";"storage"]->SomeShow_durable_storage|_->None);documentation="show durable storage: Prints the durable storage from the root of the \
tree.";};{parse=(function|["show";"subkeys";path]|["ls";path]->Some(Show_subkeyspath)|["show";"subkeys"]|["ls"]->Some(Show_subkeys"/")|_->None);documentation="show subkeys [<path>] | ls [<path>]: Prints the direct subkeys under \
<path> (default '/')";};{parse=(function|["show";"key";key]->Some(Show_key(key,`Hex))|["show";"key";key;"as";kind]->printable_value_kind_of_stringkind|>Option.map(funkind->Show_key(key,kind))|_->None);documentation="show key <key> [as <kind>]: Looks for given <key> in durable storage \
and prints its value in <kind> format. Prints errors if <key> is \
invalid or not existing.";};{parse=(function|["dump";"function";"symbols"]->SomeDump_function_symbols|_->None);documentation="dump function symbols: Pretty-prints the parsed functions custom \
section. The result will be empty if the kernel is in `wast` format \
or has been stripped of its custom sections.";};{parse=(function|["step";step]->parse_eval_stepstep|>Option.map(funs->Steps)|_->None);documentation="step <eval_step>: Depending on <eval_step>:\n\
\ - tick: Run for a tick.\n\
\ - result: Run up to the end of `kernel_run` but before \
rebooting of waiting for reboot, before the memory is flushed.\n\
\ - kernel_run: Run up to the end of the current `kernel_run` \
with padding up to the maximum ticks per reboot for the current \
version of the PVM.\n\
\ - inbox: Run until the next inbox level. Loads the next \
inbox if the interpreter is waiting for inputs.";};{parse=(function["load";"inputs"]->SomeLoad_inputs|_->None);documentation="load inputs: If the kernel is waiting for inputs, go the the next \
level and load the next inbox.";};{parse=(function|["reveal";"preimage"]->Some(Reveal_preimageNone)|["reveal";"preimage";hex_encoded_preimage]->Some(Reveal_preimage(Somehex_encoded_preimage))|_->None);documentation="reveal preimage [<hex_encoded_preimage>]: Checks the current state is \
waiting for a preimage, parses <hex_encoded_preimage> as an \
hexadecimal representation of the data or uses the builtin if none is \
given, and does a reveal step.";};{parse=(function["reveal";"metadata"]->SomeReveal_metadata|_->None);documentation="reveal metadata: While in a reveal step, print the current metadata \
from PVM.";};{parse=(function["stop"]->SomeStop|_->None);documentation="stop: Stops the execution of the current session.";};{parse=(function|"profile"::options->parse_profile_optionsoptions|>Option.map(funopts->Profileopts)|_->None);documentation="profile <option>: Profiles the execution of a full inbox and outputs \
a flamegraph compatible stack fill in the temporary directory of the \
system. \n\
Options:\n\
\ - `--collapsed`: reduces the size of the resulting file by merging \
the identical stacks, at the cost of not being able to track the call \
stack on a time basis.\n\
\ - `--without-time`: does not profile the time (can have an impact on \
performance if the kernel does too many function calls).\n\
\ - `--no-reboot`: profile a single `kernel_run`, not a full inbox.";};{parse=(function|"show"::"memory"::rest->parse_memory_commandsrest|_->None);documentation="show memory at <address> for <length> bytes [as string] | show memory \
at <address> as <kind>: Loads the <length> bytes at <address> in the \
memory, and prints it in its hexadecimal (or string) representation.\n\
The command is only available if the memory hasn't been flushed and \
during the evaluation of the kernel, i.e. after `step result` or \
`step tick`.";};{parse=(function["help"]|["man"]->SomeHelp|_->None);documentation="help: Provide documentation about the available commands.";};]andtry_parsecommand=letrecgo=function|[]->None|command_docs::commands_docs->(matchcommand_docs.parsecommandwith|None->gocommands_docs|command->command)ingocommands_docsletparse_commandss=letcommands=try_parse(String.split_no_empty' '(String.trims))inmatchcommandswithNone->Unknowns|Somecmd->cmdletbuild_metadataconfig=Tezos_protocol_alpha.Protocol.Alpha_context.Sc_rollup.Metadata.({address=config.Config.destination;origination_level=Tezos_protocol_alpha.Protocol.Alpha_context.Raw_level.of_int32_exn0l;}|>Data_encoding.Binary.to_string_exnencoding)letread_data_from_filepath=letch=open_inpathinlets=really_input_stringch(in_channel_lengthch)inclose_inch;sletread_data_from_stdinretries=letopenLwt_syntaxinletrecinput_dataretries:stringLwt.t=ifretries<=0thenStdlib.failwith"Too many tries, aborting."elselet*()=Lwt_fmt.printf"> "inlet*input=Option.catch_s(fun()->Lwt_io.read_lineLwt_io.stdin)inmatchOption.bindinput(funbytes->Hex.to_string(`Hexbytes))with|Somedata->Lwt.returndata|None->let*()=Lwt_fmt.printf"Error: the data is not a valid hexadecimal value.\n%!"ininput_data(predretries)ininput_dataretriesletread_data~kind~directory~filenameretries=Lwt.catch(fun()->letpath=Filename.concatdirectoryfilenameinlets=read_data_from_filepathinLwt.returns)(fun_->letopenLwt_syntaxinlet*()=Lwt_fmt.printf"%s %s not found in %s, or there was a reading error. Please provide \
it manually.\n\
%!"kindfilenamedirectoryinread_data_from_stdinretries)letreveal_preimage_builtinconfigretrieshash=lethex=Tezos_protocol_alpha.Protocol.Sc_rollup_reveal_hash.to_hexhashinread_data~kind:"Preimage"~directory:config.Config.preimage_directory~filename:hexretriesletrequest_dal_pageconfigretriespublished_levelslot_indexpage_index=letopenTezos_protocol_alpha.Protocolinletfilename=Format.asprintf"%a-%a-%a"Alpha_context.Raw_level.pppublished_levelAlpha_context.Dal.Slot_index.ppslot_indexDal_slot_repr.Page.Index.pppage_indexinread_data~kind:"DAL page"~directory:config.Config.dal_pages_directory~filenameretriesletrevealsconfigrequest=letopenTezos_protocol_alpha.Protocol.Alpha_context.Sc_rollupinletnum_retries=3inmatchWasm_2_0_0PVM.decode_revealrequestwith|Reveal_raw_datahash->reveal_preimage_builtinconfignum_retrieshash|Reveal_metadata->Lwt.return(build_metadataconfig)|Reveal_dal_parameters->(* TODO: https://gitlab.com/tezos/tezos/-/issues/6547
Support reveal_dal_parameters in the WASM debugger. *)Stdlib.failwith"The kernel tried to request DAL parameters, but this is not yet \
supported by the debugger."|Request_dal_page{slot_id={published_level;index};page_index}->request_dal_pageconfignum_retriespublished_levelindexpage_indexletwrite_debugconfig=ifconfig.Config.kernel_debugthenTezos_scoru_wasm.Builtins.Printer(funmsg->Lwt_fmt.printf"%s%!"msg)elseTezos_scoru_wasm.Builtins.NoopmoduleMake(Wasm_utils:Wasm_utils_intf.S)=structmoduleProf=Profiling.Make(Wasm_utils)openWasm_utils(* [compute_step tree] is a wrapper around [Wasm_pvm.compute_step] that also
returns the number of ticks elapsed (whi is always 1). *)letcompute_stepconfigtree=letopenLwt_syntaxintrap_exn(fun()->let+tree=Wasm.compute_step_with_debug~write_debug:(write_debugconfig)treein(tree,1L))(** [eval_to_result tree] tries to evaluates the PVM until the next `SK_Result`
or `SK_Trap`, and stops in case of reveal tick or input tick. It has the
property that the memory hasn't been flushed yet and can be inspected. *)leteval_to_resultconfigtree=trap_exn(fun()->eval_to_result~write_debug:(write_debugconfig)~reveal_builtins:(revealsconfig)tree)(* [eval_kernel_run tree] evals up to the end of the current `kernel_run` (or
starts a new one if already at snapshot point). *)leteval_kernel_runconfigtree=letopenLwt_syntaxintrap_exn(fun()->let*info_before=Wasm.get_infotreeinlet*tree,_=Wasm_fast.compute_step_many~reveal_builtins:(revealsconfig)~write_debug:(write_debugconfig)~stop_at_snapshot:true~max_steps:Int64.max_inttreeinlet+info_after=Wasm.get_infotreein(tree,Z.to_int64@@Z.subinfo_after.current_tickinfo_before.current_tick))(* Wrapper around {Wasm_utils.eval_until_input_requested}. *)leteval_until_input_requestedconfigtree=letopenLwt_syntaxintrap_exn(fun()->let*info_before=Wasm.get_infotreeinlet*tree=eval_until_input_requested~fast_exec:true~reveal_builtins:(Some(revealsconfig))~write_debug:(write_debugconfig)~max_steps:Int64.max_inttreeinlet+info_after=Wasm.get_infotreein(tree,Z.to_int64@@Z.subinfo_after.current_tickinfo_before.current_tick))letproduce_flamegraph~collapse~max_depthkernel_runs=letfilename=Time.System.(Format.asprintf"wasm-debugger-profiling-%a.out"pp_hum(now()))inletpath=Filename.(concat(get_temp_dir_name())filename)inletfile=open_outpathinletpp_kernel_runppf=function|Somerun->Profiling.pp_flamegraph~collapse~max_depthProfiling.pp_callppfrun|None->()inletpp_kernel_runsppfruns=Format.pp_print_list~pp_sep:(fun__->())pp_kernel_runppfrunsinFormat.fprintf(Format.formatter_of_out_channelfile)"%a"pp_kernel_runskernel_runs;close_outfile;Format.printf"Profiling result can be found in %s\n%!"pathletprofiling_results=function|Somerun->letpvm_steps=Profiling.aggregate_toplevel_time_and_ticksruninletfull_ticks,full_time=Profiling.full_ticks_and_timepvm_stepsinFormat.printf"----------------------\n\
Detailed results for a `kernel_run`:\n\
%a\n\n\
Full execution: %a ticks%a\n\
%!"(Format.pp_print_list~pp_sep:(funppf()->Format.fprintfppf"\n")Profiling.pp_ticks_and_time)pvm_stepsZ.pp_printfull_ticksProfiling.pp_time_optfull_time|None->Format.printf"----------------------\n\
The resulting call graph is inconsistent for this specific \
`kernel_run`, please open an issue.\n\
%!"leteval_and_profile~collapse~with_time~no_rebootconfigfunction_symbolstree=letopenLwt_syntaxintrap_exn(fun()->Format.printf"Starting the profiling until new messages are expected. Please note \
that it will take some time and does not reflect a real computation \
time.\n\
%!";let+tree,ticks,graph=Prof.eval_and_profile~write_debug:(write_debugconfig)~reveal_builtins:(revealsconfig)~with_time~no_rebootfunction_symbolstreeinproduce_flamegraph~collapse~max_depth:100graph;List.iterprofiling_resultsgraph;Format.printf"----------------------\nFull execution with padding: %a ticks\n%!"Z.pp_printticks;tree)letset_raw_message_input_steplevelcounterencoded_messagetree=Wasm.set_input_step(input_infolevelcounter)encoded_messagetree(* [check_input_request tree] checks the PVM is waiting for inputs, and returns
an hint to go to the next input request state otherwise. *)letcheck_input_requesttree=letopenLwt_syntaxinlet*info=Wasm.get_infotreeinmatchinfo.Wasm_pvm_state.input_requestwith|Input_required->return_ok_unit|No_input_required->return_error"The PVM is not expecting inputs yet. You can `step inbox` to \
evaluate the current inbox."|Reveal_required_->(* TODO: https://gitlab.com/tezos/tezos/-/issues/4208 *)return_error"The PVM is expecting a reveal. This command is not implemented yet."(* [load_inputs_gen inboxes level tree] reads the next inbox from [inboxes], set the
messages at [level] in the [tree], and returns the remaining inbox, the next
level and the tree. *)letload_inputs_geninboxesleveltree=letopenLwt_result_syntaxinmatchSeq.unconsinboxeswith|Some(inputs,inboxes)->let*tree=trap_exn(fun()->set_full_input_step_genset_raw_message_input_stepinputsleveltree)inlet*!()=Lwt_fmt.printf"Loaded %d inputs at level %ld\n%!"(List.lengthinputs)levelinreturn(tree,inboxes,Int32.succlevel)|None->let*!()=Lwt_fmt.printf"No more inputs at level %ld\n%!"levelinreturn(tree,inboxes,level)letload_inputsinboxesleveltree=letopenLwt_result_syntaxinlet*!status=check_input_requesttreeinmatchstatuswith|Ok()->load_inputs_geninboxesleveltree|Errormsg->Format.printf"%s\n%!"msg;return(tree,inboxes,level)(* Eval dispatcher. *)letevallevelinboxesconfigsteptree=letopenLwt_result_syntaxinletreturn'?(inboxes=inboxes)f=let*tree,count=finreturn(tree,count,inboxes,level)inmatchstepwith|Tick->return'(compute_stepconfigtree)|Result->return'(eval_to_resultconfigtree)|Kernel_run->return'(eval_kernel_runconfigtree)|Inbox->(let*!status=check_input_requesttreeinmatchstatuswith|Ok()->let*tree,inboxes,level=load_inputsinboxesleveltreeinlet*tree,ticks=eval_until_input_requestedconfigtreeinreturn(tree,ticks,inboxes,level)|Error_->return'(eval_until_input_requestedconfigtree))letprofile~collapse~with_time~no_rebootlevelinboxesconfigfunction_symbolstree=letopenLwt_result_syntaxinlet*!pvm_state=Wasm_utils.Tree_encoding_runner.decodeWasm_pvm.pvm_state_encodingtreeinlet*!status=check_input_requesttreeinletis_profilable=matchpvm_state.tick_statewith|Collect|Snapshot|DecodeTezos_webassembly_interpreter.Decode.{module_kont=MKStart;_}->true|_->falseinmatchstatuswith|Ok()whenis_profilable->let*tree,inboxes,level=load_inputsinboxesleveltreeinlet*tree=eval_and_profile~collapse~with_time~no_rebootconfigfunction_symbolstreeinreturn(tree,inboxes,level)|Error_whenis_profilable->let*tree=eval_and_profile~collapse~with_time~no_rebootconfigfunction_symbolstreeinreturn(tree,inboxes,level)|_->let*!()=Lwt_fmt.printf"Profiling can only be done from a snapshotable state or when \
waiting for input. You can use `step kernel_run` to go to the \
next snapshotable state.\n\
%!"inreturn(tree,inboxes,level)letpp_input_requestppf=function|Wasm_pvm_state.No_input_required->Format.fprintfppf"Evaluating"|Input_required->Format.fprintfppf"Waiting for input"|Reveal_requiredreq->letopenTezos_protocol_alpha.Protocol.Alpha_context.Sc_rollupinletreq=Wasm_2_0_0PVM.decode_revealreqinFormat.fprintfppf"Waiting for reveal: %a"pp_revealreq(* [show_status tree] show the state of the PVM. *)letshow_statustree=letopenLwt_syntaxinlet*state=Wasm.Internal_for_tests.get_tick_statetreeinlet*info=Wasm.get_infotreeinLwt_fmt.printf"%s\n%!"(Format.asprintf"Status: %a\nInternal_status: %a"pp_input_requestinfo.Wasm_pvm_state.input_requestpp_statestate)(* [step level inboxes config kind tree] evals according to the step kind and
prints the number of ticks elapsed and the new status. *)letsteplevelinboxesconfigkindtree=letopenLwt_result_syntaxinlet*tree,ticks,inboxes,level=evallevelinboxesconfigkindtreeinlet*!()=Lwt_fmt.printf"Evaluation took %Ld ticks so far\n"ticksinlet*!()=show_statustreeinreturn_some(tree,inboxes,level)(* [show_inbox tree] prints the current input buffer and the number of messages
it contains. *)letshow_inboxtree=letopenLwt_syntaxinlet*input_buffer=Wasm.Internal_for_tests.get_input_buffertreeinlet*messages=Tezos_lazy_containers.Lazy_vector.(Mutable.ZVector.snapshotinput_buffer|>ZVector.to_list)inletmessages_sorted=List.sortMessages.compare_input_buffer_messagesmessagesinletpp_messageppfTezos_webassembly_interpreter.Input_buffer.{raw_level;message_counter;payload}=Format.fprintfppf{|{ raw_level: %ld;
counter: %s
payload: %a }|}raw_level(Z.to_stringmessage_counter)Messages.pp_inputpayloadinletpp_messages()=Format.asprintf"%a"(Format.pp_print_list~pp_sep:(funppf()->Format.fprintfppf"\n")pp_message)messages_sortedinletsize=Tezos_lazy_containers.Lazy_vector.Mutable.ZVector.num_elementsinput_bufferinLwt_fmt.printf"Inbox has %s messages:\n%s\n%!"(Z.to_stringsize)(pp_messages())(* [show_outbox_gen tree level] prints the outbox messages for a given level. *)letshow_outbox_gentreelevel=letopenLwt_syntaxinlet*output_buffer=Wasm.Internal_for_tests.get_output_buffertreeinlet*level_vector=Tezos_webassembly_interpreter.Output_buffer.get_outboxoutput_bufferlevelinlet*messages=Tezos_lazy_containers.Lazy_vector.(level_vector|>ZVector.to_list)inletpp_messages()=Format.asprintf"%a"(Format.pp_print_list~pp_sep:(funppf()->Format.fprintfppf"\n")Messages.pp_output)messagesinletsize=Tezos_lazy_containers.Lazy_vector.ZVector.num_elementslevel_vectorinLwt_fmt.printf"Outbox has %s messages:\n%s\n%!"(Z.to_stringsize)(pp_messages())(* [show_outbox tree level] prints the outbox messages for a given level. *)letshow_outboxtreelevel=Lwt.catch(fun()->show_outbox_gentreelevel)(fun_->Lwt_fmt.printf"No outbox found at level %ld\n%!"level)(* [find_key_in_durable] retrieves the given [key] from the durable storage in
the tree. Returns `None` if the key does not exists. *)letfind_key_in_durabletreekey=letopenLwt_syntaxinlet*durable=Wasm_utils.wrap_as_durable_storagetreeinletdurable=Tezos_scoru_wasm.Durable.of_storage_exndurableinTezos_scoru_wasm.Durable.find_valuedurablekey(* [print_durable ~depth ~show_values ~path tree] prints the keys in the durable
storage from the given path and their values in their hexadecimal
representation. By default, it prints from the root of the durable
storage. *)letprint_durable?(depth=10)?(show_values=true)?(path=[])tree=letopenLwt_syntaxinletdurable_path="durable"::pathinlet*path_exists=Wasm_utils.Ctx.Tree.mem_treetreedurable_pathinifpath_existsthenWasm_utils.Ctx.Tree.fold~depth:(`Ledepth)tree("durable"::path)~order:`Sorted~init:()~f:(funkeytree()->letfull_key=String.concat"/"keyin(* If we need to show the values, we show every keys, even the root and
'@'. *)ifshow_valuesthenlet+value=Wasm_utils.Ctx.Tree.findtree[]inletvalue=Option.value~default:(Bytes.create0)valueinFormat.printf"/%s\n %a\n%!"full_keyHex.pp(Hex.of_bytesvalue)elseifkey<>[]&&key<>["@"]thenreturn(Format.printf"/%s\n%!"full_key)elsereturn_unit)elseLwt.return@@Format.printf"The path /%s is not available in the durable storage\n%!"(String.concat"/"path)(* [show_durable] prints the durable storage from the tree. *)letshow_durabletree=print_durable~depth:10tree(* [show_subkeys tree path] prints the direct subkeys under the given path. *)letshow_subkeystreepath=letinvalid_path()=Lwt.return@@Format.printf"Invalid path, it must start with '/'\n%!"inmatchString.index_optpath'/'with|Someiwheni<>0->invalid_path()|None->invalid_path()|Some_->print_durable~depth:1~path:(String.split_no_empty'/'path)~show_values:falsetreeletshow_valuekindvalue=letprintable=Repl_helpers.print_wasm_encoded_valuekindvalueinmatchprintablewith|Oks->s|Errorerr->Format.asprintf"Error: %s. Defaulting to hexadecimal value\n%a"errHex.pp(Hex.of_stringvalue)(* [show_key_gen tree key] looks for the given [key] in the durable storage and
print its value in hexadecimal format. *)letshow_key_gentreekeykind=letopenLwt_syntaxinlet*value=find_key_in_durabletreekeyinmatchvaluewith|None->Format.printf"Key not found\n%!";return_unit|Somev->let+str_value=Tezos_lazy_containers.Chunked_byte_vector.to_stringvinFormat.printf"%s\n%!"@@show_valuekindstr_value(* [show_key tree key] looks for the given [key] in the durable storage and
print its value in hexadecimal format. Prints errors in case the key is
invalid or not existing. *)letshow_keytreekeykind=Lwt.catch(fun()->letkey=Tezos_scoru_wasm.Durable.key_of_string_exnkeyinshow_key_gentreekeykind)(function|Tezos_scoru_wasm.Durable.Invalid_key_->Lwt_fmt.printf"Invalid key\n%!"|Tezos_scoru_wasm.Durable.Value_not_found->Lwt_fmt.printf"No value found for key\n%!"|Tezos_scoru_wasm.Durable.Tree_not_found->Lwt_fmt.printf"No tree found for key\n%!"|exn->Lwt_fmt.printf"Unknown exception: %s\n%!"(Printexc.to_stringexn))exceptionCannot_inspect_memoryofstring(* [load_memory tree] finds the memory module 0 from the tree, only and only if
the PVM is in an Init or Eval state. *)letload_memorytree=letopenLwt_syntaxinlet*state=Wasm.Internal_for_tests.get_tick_statetreeinlet*module_inst=matchstatewith|Eval_|Init_->Wasm.Internal_for_tests.get_module_instance_exntree|_->raise(Cannot_inspect_memory(Format.asprintf"%a"pp_statestate))inLwt.catch(fun()->Tezos_lazy_containers.Lazy_vector.Int32Vector.get0lmodule_inst.memories)(fun_->raiseTezos_webassembly_interpreter.Eval.Missing_memory_0_export)(* [show_memory tree address length] loads the [length] bytes at address
[address] in the memory, and prints it in its hexadecimal representation. *)letshow_memorytreeaddresslengthkind=letopenLwt_syntaxinLwt.catch(fun()->let*memory=load_memorytreeinlet*value=Tezos_webassembly_interpreter.Memory.load_bytesmemoryaddresslengthinLwt_fmt.printf"%s\n%!"@@show_valuekindvalue)(function|Cannot_inspect_memorystate->Lwt_fmt.printf"Error: Cannot inspect memory during internal state %s\n%!"state|exn->Lwt_fmt.printf"Error: %s\n%!"(Printexc.to_stringexn))letdump_function_symbolsfunction_symbols=letfunctions=Format.asprintf"%a"Custom_section.pp_function_subsectionfunction_symbolsinLwt_fmt.printf"Functions:\n%s\n"functions(* [reveal_preimage config hex tree] checks the current state is waiting for a
preimage, parses [hex] as an hexadecimal representation of the data or use
the builtin if none is given, and does a reveal step. *)letreveal_preimageconfigbytestree=letopenLwt_syntaxinlet*info=Wasm.get_infotreeinmatchinfo.Tezos_scoru_wasm.Wasm_pvm_state.input_requestwith|Reveal_requiredreq->(match(Tezos_protocol_alpha.Protocol.Alpha_context.Sc_rollup.Wasm_2_0_0PVM.decode_revealreq,Option.bindbytes(funbytes->Hex.to_bytes(`Hexbytes)))with|Reveal_raw_data_,Somepreimage->Wasm.reveal_steppreimagetree|Reveal_raw_datahash,None->Lwt.catch(fun()->let*preimage=reveal_preimage_builtinconfig1hashinWasm.reveal_step(String.to_bytespreimage)tree)(fun_->returntree)|_->let+()=Lwt_fmt.printf"Error: not the expected reveal step\n%!"intree)|_->let+()=Lwt_fmt.printf"Error: not a reveal step\n%!"intreeletreveal_metadataconfigtree=letopenLwt_syntaxinlet*info=Wasm.get_infotreeinmatchinfo.Tezos_scoru_wasm.Wasm_pvm_state.input_requestwith|Tezos_scoru_wasm.Wasm_pvm_state.(Reveal_requiredreq)->(matchTezos_protocol_alpha.Protocol.Alpha_context.Sc_rollup.Wasm_2_0_0PVM.decode_revealreqwith|Reveal_metadata->letdata=build_metadataconfiginWasm.reveal_step(Bytes.of_stringdata)tree|_->let+()=Lwt_fmt.printf"Error: Not the expected reveal step\n%!"intree)|_->let+()=Lwt_fmt.printf"Error: Not in a reveal step\n%!"intreeletget_function_symbolstree=letopenLwt_result_syntaxinlet*!durable=Wasm_utils.wrap_as_durable_storagetreeinletdurable=Tezos_scoru_wasm.Durable.of_storage_exndurableinlet*!module_=Tezos_scoru_wasm.(Durable.find_value_exndurableConstants.kernel_key)inlet*!module_string=Tezos_lazy_containers.Chunked_byte_vector.to_stringmodule_inlet*function_symbols=Repl_helpers.trap_exn(fun()->parse_custom_sections"kernel"module_string)inreturnfunction_symbols(* [handle_command command tree inboxes level] dispatches the commands to their
actual implementation. *)lethandle_commandcconfigtreeinboxeslevel=letopenLwt_result_syntaxinletcommand=parse_commandscinletreturn?(tree=tree)?(inboxes=inboxes)()=return_some(tree,inboxes,level)inletrecgo=function|Timecmd->lett=Time.System.now()inlet*res=gocmdinlett'=Time.System.now()inlet*!()=Lwt_fmt.printf"took %s\n%!"(Format.asprintf"%a"Ptime.Span.pp(Ptime.difft't))inLwt_result_syntax.returnres|Load_inputs->let+ctx=load_inputsinboxesleveltreeinSomectx|Show_status->let*!()=show_statustreeinreturn()|Stepkind->steplevelinboxesconfigkindtree|Show_inbox->let*!()=show_inboxtreeinreturn()|Show_outboxlevel->let*!()=show_outboxtreelevelinreturn()|Show_durable_storage->let*!()=show_durabletreeinreturn()|Show_subkeyspath->let*!()=show_subkeystreepathinreturn()|Show_key(key,kind)->let*!()=show_keytreekeykindinreturn()|Show_memory(address,length,kind)->let*!()=show_memorytreeaddresslengthkindinreturn()|Dump_function_symbols->let*function_symbols=get_function_symbolstreeinlet*!()=dump_function_symbolsfunction_symbolsinreturn()|Reveal_preimagebytes->let*!tree=reveal_preimageconfigbytestreeinreturn~tree()|Reveal_metadata->let*!tree=reveal_metadataconfigtreeinreturn~tree()|Profile{collapse;with_time;no_reboot}->let*function_symbols=get_function_symbolstreeinlet*tree,inboxes,level=profile~collapse~with_time~no_rebootlevelinboxesconfigfunction_symbolstreeinreturn_some(tree,inboxes,level)|Unknowns->let*!()=Lwt_fmt.eprintf"Unknown command `%s`\n%!"sinreturn()|Help->let*!()=List.iter_s(funcommand_docs->Lwt_fmt.printf" %s\n"command_docs.documentation)commands_docsinreturn()|Stop->return_noneingocommandend