123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2022 Nomadic Labs, <contact@nomadic-labs.com> *)(* Copyright (c) 2022 Marigold, <contact@marigold.dev> *)(* Copyright (c) 2022 Oxhead Alpha <info@oxhead-alpha.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. *)(* *)(*****************************************************************************)openProtocol.Alpha_contexttypemode=Observer|Accuser|Batcher|Maintenance|Operator|Customtype'apurposed={operator:'a;submit_batch:'a;finalize_commitment:'a;remove_commitment:'a;rejection:'a;dispatch_withdrawals:'a;}typesigners=Tezos_crypto.Signature.V0.public_key_hashoptionpurposedtypecost_caps={fee_cap:Protocol.Alpha_context.Tez.t;burn_cap:Protocol.Alpha_context.Tez.t;}typecaps=cost_capspurposedtypet={data_dir:string;rollup_id:Protocol.Alpha_context.Tx_rollup.t;origination_level:int32option;rpc_addr:P2p_point.Id.t;cors_origins:stringlist;cors_headers:stringlist;reconnection_delay:float;mode:mode;signers:signers;allow_deposit:bool;l2_blocks_cache_size:int;caps:caps;batch_burn_limit:Protocol.Alpha_context.Tez.toption;}letdefault_data_dirrollup_id=letopenLwt_syntaxinlethome=Sys.getenv"HOME"inletdir=".tezos-tx-rollup-node-"^Protocol.Alpha_context.Tx_rollup.to_b58checkrollup_idinletdir=Filename.concathomedirinlet+()=Lwt_utils_unix.create_dirdirindirletdefault_rpc_addr=(Ipaddr.V6.localhost,9999)letdefault_reconnection_delay=2.0letdefault_l2_blocks_cache_size=64letmodes=[Observer;Accuser;Batcher;Maintenance;Operator;Custom]letstring_of_mode=function|Observer->"observer"|Accuser->"accuser"|Batcher->"batcher"|Maintenance->"maintenance"|Operator->"operator"|Custom->"custom"letmode_of_string=function|"observer"->OkObserver|"accuser"->OkAccuser|"batcher"->OkBatcher|"maintenance"->OkMaintenance|"operator"->OkOperator|"custom"->OkCustom|_->Error[Exn(Failure"Invalid mode")]letmode_encoding=Data_encoding.string_enum[("observer",Observer);("accuser",Accuser);("batcher",Batcher);("maintenance",Maintenance);("operator",Operator);("custom",Custom);]lettezt=Tez.of_mutez_exnInt64.(mul(of_intt)1_000_000L)letdefault_cost_caps={fee_cap=Tez.one;burn_cap=tez2}letdefault_commitment_caps={fee_cap=tez2;burn_cap=tez3}letdefault_submit_batch_caps={fee_cap=tez5;burn_cap=tez5}letdefault_finalize_commitment_caps=default_cost_capsletdefault_remove_commitment_caps=default_cost_capsletdefault_rejection_caps={fee_cap=tez10;burn_cap=tez10}letdefault_dispatch_withdrawals_caps={fee_cap=tez2;burn_cap=tez3}letdefault_caps={operator=default_commitment_caps;submit_batch=default_submit_batch_caps;finalize_commitment=default_finalize_commitment_caps;remove_commitment=default_remove_commitment_caps;rejection=default_rejection_caps;dispatch_withdrawals=default_dispatch_withdrawals_caps;}letsigners_encoding=letopenData_encodinginconv(fun{operator;submit_batch;finalize_commitment;remove_commitment;rejection;dispatch_withdrawals;}->(operator,submit_batch,finalize_commitment,remove_commitment,rejection,dispatch_withdrawals))(fun(operator,submit_batch,finalize_commitment,remove_commitment,rejection,dispatch_withdrawals)->{operator;submit_batch;finalize_commitment;remove_commitment;rejection;dispatch_withdrawals;})@@obj6(opt~description:"The operator of the rollup (public key hash) if any""operator"Tezos_crypto.Signature.V0.Public_key_hash.encoding)(opt"submit_batch"Tezos_crypto.Signature.V0.Public_key_hash.encoding~description:"The public key hash of the signer for batch submission")(opt"finalize_commitment"Tezos_crypto.Signature.V0.Public_key_hash.encoding~description:"The public key hash of the signer for finalization of commitments")(opt"remove_commitment"Tezos_crypto.Signature.V0.Public_key_hash.encoding~description:"The public key hash of the signer for removals of commitments")(opt"rejection"Tezos_crypto.Signature.V0.Public_key_hash.encoding~description:"The public key hash of the signer for rejections")(opt"dispatch_withdrawals"Tezos_crypto.Signature.V0.Public_key_hash.encoding~description:"The public key hash of the signer for the dispatch of withdrawals")letcost_caps_encoding=letopenData_encodinginconv(fun{fee_cap;burn_cap}->(fee_cap,burn_cap))(fun(fee_cap,burn_cap)->{fee_cap;burn_cap})@@obj2(req"fee_cap"Tez.encoding)(req"burn_cap"Tez.encoding)letcaps_encoding=letopenData_encodinginconv(fun{operator;submit_batch;finalize_commitment;remove_commitment;rejection;dispatch_withdrawals;}->(operator,submit_batch,finalize_commitment,remove_commitment,rejection,dispatch_withdrawals))(fun(operator,submit_batch,finalize_commitment,remove_commitment,rejection,dispatch_withdrawals)->{operator;submit_batch;finalize_commitment;remove_commitment;rejection;dispatch_withdrawals;})@@obj6(dft~description:"The caps for the cost of commitment operations""commitment"cost_caps_encodingdefault_commitment_caps)(dft~description:"The caps for the cost of submit batch operations""submit_batch"cost_caps_encodingdefault_submit_batch_caps)(dft~description:"The caps for the cost of finalize commitment operations""finalized_commitment"cost_caps_encodingdefault_finalize_commitment_caps)(dft~description:"The caps for the cost of remove commitment operations""remove_commitment"cost_caps_encodingdefault_remove_commitment_caps)(dft~description:"The caps for the cost of rejection operations""rejection"cost_caps_encodingdefault_rejection_caps)(dft~description:"The caps for the cost of dispatch withdrawals operations""dispatch_withdrawals"cost_caps_encodingdefault_dispatch_withdrawals_caps)letencodingdata_dir=letopenData_encodinginconv(fun{data_dir=_;rollup_id;origination_level;rpc_addr;cors_origins;cors_headers;reconnection_delay;mode;signers;allow_deposit;l2_blocks_cache_size;caps;batch_burn_limit;}->(((),rollup_id,origination_level,rpc_addr,reconnection_delay,mode,signers,allow_deposit,l2_blocks_cache_size,caps),(batch_burn_limit,cors_origins,cors_headers)))(fun(((),rollup_id,origination_level,rpc_addr,reconnection_delay,mode,signers,allow_deposit,l2_blocks_cache_size,caps),(batch_burn_limit,cors_origins,cors_headers))->{data_dir;rollup_id;origination_level;rpc_addr;cors_origins;cors_headers;reconnection_delay;mode;signers;allow_deposit;l2_blocks_cache_size;caps;batch_burn_limit;})@@merge_objs(obj10(dft~description:"Location where the rollup node data (store, context, etc.) is \
stored""data_dir"(constantdata_dir)())(req~description:"Rollup id of the rollup to target""rollup_id"Protocol.Alpha_context.Tx_rollup.encoding)(opt~description:"Level of the block where the rollup was originated""origination_level"int32)(dft~description:"RPC address on which the rollup node listens""rpc_addr"P2p_point.Id.encodingdefault_rpc_addr)(dft~description:"The reconnection (to the tezos node) delay in seconds""reconnection_delay"floatdefault_reconnection_delay)(req~description:"The mode for this rollup node""mode"mode_encoding)(req~description:"The signers for the various tx rollup operations""signers"signers_encoding)(dft~description:"Allow the operator to make a first deposit for commitments""allow_deposit"boolfalse)(dft~description:"The size of the L2 block cache in number of blocks""l2_blocks_cache_size"int31default_l2_blocks_cache_size)(dft~description:"The cost caps for the injection of operations""caps"caps_encodingdefault_caps))(obj3(opt~description:"The burn limit in for a batch (to be paid for the submission \
of messages in the protocol inbox)""batch_burn_limit"Tez.encoding)(dft~description:"CORS origin allowed by the RPC server via \
Access-Control-Allow-Origin""cors-origins"(liststring)[])(dft~description:"Headers reported by Access-Control-Allow-Headers reported \
during CORS""cors-headers"(liststring)[]))letget_configuration_filenamedata_dir=letfilename="config.json"inFilename.concatdata_dirfilenamemoduleSigKindSet=structincludeSet.Make(structtypet=[`operator|`submit_batch|`finalize_commitment|`remove_commitment|`rejection|`dispatch_withdrawals]letcompare=Stdlib.compareend)letelt_to_string=function|`operator->"operator"|`submit_batch->"submit_batch"|`finalize_commitment->"finalize_commitment"|`remove_commitment->"remove_commitment"|`rejection->"rejection"|`dispatch_withdrawals->"dispatch_withdrawals"letto_string_lists=elementss|>List.mapelt_to_stringendletcheck_modeconfig=letwith_signerssigners=letconfig_signers=letaddsignernameacc=Option.fold~none:acc~some:(fun_->SigKindSet.addnameacc)signerinSigKindSet.empty|>addconfig.signers.operator`operator|>addconfig.signers.submit_batch`submit_batch|>addconfig.signers.finalize_commitment`finalize_commitment|>addconfig.signers.remove_commitment`remove_commitment|>addconfig.signers.rejection`rejection|>addconfig.signers.dispatch_withdrawals`dispatch_withdrawalsinletsigners=SigKindSet.of_listsignersinifSigKindSet.equalconfig_signerssignersthenOkconfigelseletmissing_signers=SigKindSet.(diffsignersconfig_signers|>to_string_list)inifmissing_signers<>[]thenletmode=string_of_modeconfig.modeinError[Error.Tx_rollup_missing_mode_signers{mode;missing_signers}]elseletextra_signers=SigKindSet.(diffconfig_signerssigners)inletremovekindsigners=matchkindwith|`operator->{signerswithoperator=None}|`submit_batch->{signerswithsubmit_batch=None}|`finalize_commitment->{signerswithfinalize_commitment=None}|`remove_commitment->{signerswithremove_commitment=None}|`rejection->{signerswithrejection=None}|`dispatch_withdrawals->{signerswithdispatch_withdrawals=None}inletsigners=SigKindSet.foldremoveextra_signersconfig.signersinOk{configwithsigners}inmatchconfig.modewith|Observer->with_signers[]|Accuser->with_signers[`rejection]|Batcher->with_signers[`submit_batch]|Maintenance->with_signers[`operator;`finalize_commitment;`remove_commitment;`rejection;`dispatch_withdrawals;]|Operator->with_signers[`operator;`submit_batch;`finalize_commitment;`remove_commitment;`rejection;`dispatch_withdrawals;]|Custom->Okconfigletsave~forceconfiguration=letopenLwt_result_syntaxinletjson=Data_encoding.Json.construct(encodingconfiguration.data_dir)configurationinlet*!()=Lwt_utils_unix.create_dirconfiguration.data_dirinletfile=get_configuration_filenameconfiguration.data_dirinlet*!exists=Lwt_unix.file_existsfileinlet*()=fail_when(exists&¬force)(Error.Tx_rollup_configuration_file_already_existsfile)inlet*!v=Lwt_utils_unix.with_atomic_open_outfile(funchan->letcontent=Data_encoding.Json.to_stringjsoninLwt_utils_unix.write_stringchancontent)inlet*()=Lwt.return(Result.map_error(fun_->[Error.Tx_rollup_unable_to_write_configuration_filefile])v)inreturnfileletload~data_dir=letopenLwt_result_syntaxinletfile=get_configuration_filenamedata_dirinlet*!exists=Lwt_unix.file_existsfileinlet*()=fail_unlessexists(Error.Tx_rollup_configuration_file_does_not_existsfile)inlet*json=Lwt_utils_unix.Json.read_filefileinletconfig=Data_encoding.Json.destruct(encodingdata_dir)jsoninreturnconfig