123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279(*****************************************************************************)(* *)(* 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. *)(* *)(*****************************************************************************)(* The L2 context for the rollup node has the form of a binary Merkle tree. We
reuse the implementation provided by [Tezos_context] as it is stable. Proofs
produced by the node will be verifiable by the protocol. *)moduleRaw=Tezos_context.Context_binaryincludeRawmoduleKeys=structletl2_context=["l2_context"]lettickets=["tickets"]letaddress_indexes=["addresses"]endmoduleIrmin_storage:Protocol.Tx_rollup_l2_storage_sig.STORAGEwithtypet=contextandtype'am='atzresultLwt.t=structtypet=contexttype'am='atzresultLwt.tletpathk=Keys.l2_context@[Bytes.to_stringk]letget(ctxt:context)key:bytesoptionm=letopenLwt_result_syntaxinlet*!res=Raw.findctxt(pathkey)inreturnresletsetctxtkeyvalue=letopenLwt_result_syntaxinlet*!ctxt=Raw.addctxt(pathkey)valueinreturnctxtletremovectxtkey=letopenLwt_result_syntaxinlet*!ctxt=Raw.removectxt(pathkey)inreturnctxtmoduleSyntax=structincludeLwt_result_syntaxletcatchmkh=letopenLwt_syntaxinlet*res=minmatchreswith|Okx->kx|Error(Environment.Ecoproto_errore::_)->he|Errorerr->(* TODO/TORU: replace error either in STORAGE or here *)(* Should not happen *)let*()=Debug_events.(emitshould_not_happen)__LOC__infailerrletfaile=lete=Environment.wrap_tzerroreinLwt.return(Error[e])letlist_fold_left_m=List.fold_left_esendendincludeProtocol.Tx_rollup_l2_context.Make(Irmin_storage)letcontext_hash_to_l2hash=Context_hash.to_byteshash|>Protocol.Tx_rollup_l2_context_hash.of_bytes_exnletl2_to_context_hashhash=Protocol.Tx_rollup_l2_context_hash.to_byteshash|>Context_hash.of_bytes_exnletexistsindexhash=Raw.existsindex(l2_to_context_hashhash)(* The context hashes are not dependant on the time, we use EPOCH (i.e. 0) to
commit (and hash). *)lethash?(message="")context=Raw.hash~time:Time.Protocol.epoch~messagecontext|>context_hash_to_l2letcommit?(message="")context=letopenLwt_syntaxinifRaw.is_emptycontextthen(* We cannot commit empty contexts with Irmin 3 *)return(hash~messagecontext)elselet+hash=Raw.commit~time:Time.Protocol.epoch~messagecontextincontext_hash_to_l2hashletcheckout_optindexcontext_hash=letopenLwt_syntaxinlet+context=Raw.checkoutindex(l2_to_context_hashcontext_hash)inmatchcontextwith|Somecontext->Somecontext|None->letempty=Raw.emptyindexinlethash_empty=hashemptyinifProtocol.Tx_rollup_l2_context_hash.(context_hash=hash_empty)thenSomeemptyelseNoneletcheckout_exnindexhash=letopenLwt_syntaxinlet+context=checkout_optindexhashinmatchcontextwithNone->raiseNot_found|Somecontext->contextletcheckoutindexhash=letopenLwt_syntaxinlet+context=checkout_optindexhashinOption.to_result~none:[Error.Tx_rollup_cannot_checkout_contexthash]context(** {2 Prover context} *)exceptionErrorofEnvironment.Error_monad.errormoduleProver_storage:Protocol.Tx_rollup_l2_storage_sig.STORAGEwithtypet=treeandtype'am='aLwt.t=structtypet=treetype'am='aLwt.tmoduleSyntax=structincludeLwt.Syntaxletreturn=Lwt.returnletfaile=Lwt.fail(Errore)letcatch(m:'am)kh=Lwt.catch(fun()->m>>=k)(functionErrore->he|e->Lwt.faile)letlist_fold_left_m=Lwt_list.fold_left_sendletpathk=[Bytes.to_stringk]letgetstorekey=Raw.Tree.findstore(pathkey)letsetstorekeyvalue=Raw.Tree.addstore(pathkey)valueletremovestorekey=Raw.Tree.removestore(pathkey)endmoduleProver_context=Protocol.Tx_rollup_l2_context.Make(Prover_storage)type'aproduce_proof_result={tree:tree;result:'a}letget_treectxt=letopenLwt_result_syntaxinlet*!tree_opt=Raw.find_treectxtKeys.l2_contextinmatchtree_optwith|Sometree->returntree|None->fail[Error.Tx_rollup_tree_not_found]letproduce_proofctxtf=letopenLwt_result_syntaxinletindex=Raw.indexctxtinlet*tree=get_treectxtinlet*kinded_key=matchRaw.Tree.kinded_keytreewith|Somekinded_key->returnkinded_key|None->fail[Error.Tx_rollup_tree_kinded_key_not_found]inlet*!proof,result=Raw.produce_stream_proofindexkinded_key(funtree->let*!res=ftreeinLwt.return(res.tree,res))inreturn(proof,result)lethash_tree=Raw.Tree.hashlettree_hash_of_contextctxt=letopenLwt_result_syntaxinlet+tree=get_treectxtinhash_treetreeletadd_treectxttree=letopenLwt_syntaxinlet*ctxt=Raw.add_treectxtKeys.l2_contexttreein(* Irmin requires that we commit the context before generating the proof. *)let*ctxt_hash=commitctxtinreturn(ctxt,ctxt_hash)(** The initial context must be constructed using the internal empty tree.
This tree however, *needs* to be non-empty. Otherwise, its hash will
be inconsistent.
See {!Protocol.Tx_rollup_commitment_repr.empty_l2_context_hash} for more
context.
*)letinit_contextindex=letopenProver_context.Syntaxinletctxt=Raw.emptyindexinlettree=Raw.Tree.emptyctxtinlet*tree=Prover_context.Address_index.init_countertreeinlet*tree=Prover_context.Ticket_index.init_countertreeinlettree_hash=hash_treetreeinassert(Context_hash.(tree_hash=Protocol.Tx_rollup_message_result_repr.empty_l2_context_hash));let*ctxt,_=add_treectxttreeinreturnctxt(** {2 Sub-context for tickets } *)moduleTicket_indexable=Protocol.Indexable.Make(Protocol.Alpha_context.Ticket_hash)letregister_ticketctxt(ticket_index:Protocol.Tx_rollup_l2_context_sig.ticket_index)ticket=letindex_int32=Protocol.Indexable.to_int32ticket_indexinletkey=Keys.tickets@[Int32.to_stringindex_int32]inletvalue=Data_encoding.Binary.to_bytes_exnTicket.encodingticketinRaw.addctxtkeyvalueletget_ticketctxt(ticket_index:Protocol.Tx_rollup_l2_context_sig.ticket_index)=letopenLwt_syntaxinletindex_int32=Protocol.Indexable.to_int32ticket_indexinletkey=Keys.tickets@[Int32.to_stringindex_int32]inlet*value=Raw.findctxtkeyinmatchvaluewith|None->return_none|Somevalue->return_some(Data_encoding.Binary.of_bytes_exnTicket.encodingvalue)(** {2 Sub-context for address indexes } *)letregister_addressctxt(index:Protocol.Tx_rollup_l2_context_sig.address_index)(address:Protocol.Tx_rollup_l2_address.t)=letindex_int32=Protocol.Indexable.to_int32indexinletkey=Keys.address_indexes@[Int32.to_stringindex_int32]inletvalue=Data_encoding.Binary.to_bytes_exnProtocol.Tx_rollup_l2_address.encodingaddressinRaw.addctxtkeyvalueletget_addressctxt(index:Protocol.Tx_rollup_l2_context_sig.address_index)=letopenLwt_syntaxinletindex_int32=Protocol.Indexable.to_int32indexinletkey=Keys.address_indexes@[Int32.to_stringindex_int32]inlet*value=Raw.findctxtkeyinmatchvaluewith|None->return_none|Somevalue->return_some(Data_encoding.Binary.of_bytes_exnProtocol.Tx_rollup_l2_address.encodingvalue)