123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com> *)(* *)(* 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. *)(* *)(*****************************************************************************)moduleParameters=structtypepersistent_state={runner:Runner.toption;base_dir:string;uri:Uri.t;keys:Account.keylist;mutablepending_ready:unitoptionLwt.ulist;}typesession_state={mutableready:bool}letsigner_path="./octez-signer"letbase_default_name="signer"letdefault_uri()=Uri.make~scheme:"http"~host:"localhost"~port:(Port.fresh())()letdefault_colors=Log.Color.[|BG.yellow++FG.blue;BG.yellow++FG.gray;BG.yellow++FG.blue|]endopenParametersincludeDaemon.Make(Parameters)leturisigner=signer.persistent_state.urilettrigger_readysignervalue=letpending=signer.persistent_state.pending_readyinsigner.persistent_state.pending_ready<-[];List.iter(funpending->Lwt.wakeup_laterpendingvalue)pendingletset_readysigner=(matchsigner.statuswith|Not_running->()|Runningstatus->status.session_state.ready<-true);trigger_readysigner(Some())lethandle_readinesssigner(event:event)=ifevent.name="signer_listening.v0"thenset_readysignerletbase_dir_argclient=["--base-dir";client.base_dir]letspawn_command?(env=String_map.empty)?hookssignercommand=letenv=(* Set disclaimer to "Y" if unspecified, otherwise use given value *)String_map.update"TEZOS_CLIENT_UNSAFE_DISABLE_DISCLAIMER"(funo->Option.value~default:"Y"o|>Option.some)envinProcess.spawn~name:signer.name~color:signer.color~env?hookssigner.path@@base_dir_argsigner.persistent_state@commandletpassfile=ref""letspawn_import_secret_keysigner(key:Account.key)=letsk_uri=matchkey.secret_keywith|Unencryptedsk->"unencrypted:"^sk|Encrypted_->Test.fail"[spawn_import_secret_key] expected an unencrypted key"inspawn_commandsigner["import";"secret";"key";key.alias;sk_uri]letimport_secret_keysigner(key:Account.key)=spawn_import_secret_keysignerkey|>Process.checkletcreate?name?color?event_pipe?base_dir?uri?runner?(keys=[Constant.bootstrap1])()=letname=matchnamewithNone->fresh_name()|Somename->nameinletbase_dir=matchbase_dir withNone->Temp.dirname|Somedir->dirinleturi=matchuriwithNone->Parameters.default_uri()|Someuri->uriinletsigner =create~path:signer_path?name:(Somename)?color?event_pipe?runner{runner;base_dir;uri;keys;pending_ready=[]}inon_eventsigner(handle_readinesssigner);let*()=Lwt_list.iter_s(import_secret_keysigner)keysinreturnsignerletrunsigner=(matchsigner.statuswith|Not_running->()|Running_->Test.fail"signer %s is already running"signer.name);letrunner=signer.persistent_state.runnerinlethost=Option.value~default:"localhost"(Uri.hostsigner.persistent_state.uri)inletport_args=matchUri.portsigner.persistent_state.uriwith|None->[]|Someport->["--port";Int.to_stringport]inletarguments=["--base-dir";signer.persistent_state.base_dir;"launch";"http";"signer";"--address";host;]@port_argsinletarguments=if!passfile=""thenargumentselse["--password-filename";!passfile]@argumentsinleton_terminate_=(* Cancel all [Ready] event listeners. *)trigger_readysignerNone;unitinrunsigner{ready=false}arguments~on_terminate?runnerletcheck_event?wheresignernamepromise=let*result=promiseinmatchresultwith|None->raise(Terminated_before_event{daemon=signer.name;event=name;where})|Somex->returnxletwait_for_readysigner=matchsigner.statuswith|Running{session_state={ready=true;_};_}->unit|Not_running|Running{session_state={ready=false;_};_}->letpromise,resolver=Lwt.task()insigner.persistent_state.pending_ready<-resolver::signer.persistent_state.pending_ready;check_eventsigner"Signer started."promiseletinit?name?color?event_pipe?base_dir?uri?runner?keys()=let*signer=create?name?color?event_pipe?base_dir?uri?runner?keys()inlet*()=runsignerinlet*()=wait_for_readysignerinreturnsignerletrestartsigner=let*()=terminatesignerinlet*()=runsignerinwait_for_readysigner