123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2021 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. *)(* *)(*****************************************************************************)(**
A Merkelized inbox represents a list of available messages. This
list is decomposed into sublist of messages, one for each Tezos
level greater than the level of the Last Cemented Commitment
(LCC).
This module is designed to:
1. give a constant-time access to the number of available messages ;
2. provide a space-efficient representation for proofs of inbox
inclusions (only for inboxes obtained at the end of block
validation) ;
3. offer an efficient function to add a new batch of messages in
the inbox at the current level.
To solve (1), we simply maintain the number of available messages
in a field.
To solve (2), we use a proof tree H which is implemented by a merkelized
skip list allowing for compact inclusion proofs
(See {!skip_list_repr.ml}).
To solve (3), we maintain a separate proof tree C witnessing the
contents of messages of the current level.
The protocol maintains the number of available messages, the
hashes of the head of H, and the root hash of C.
The rollup node needs to maintain a full representation for C and a
partial representation for H back to the level of the LCC.
*)typeerror+=Invalid_level_add_messagesofRaw_level_repr.ttypeerror+=Invalid_number_of_messages_to_consumeofint64let()=letopenData_encodinginregister_error_kind`Permanent~id:"sc_rollup_inbox.invalid_level_add_messages"~title:"Internal error: Trying to add a message to an inbox from the past"~description:"An inbox can only accept messages for its current level or for the next \
levels."(obj1(req"level"Raw_level_repr.encoding))(functionInvalid_level_add_messageslevel->Somelevel|_->None)(funlevel->Invalid_level_add_messageslevel);register_error_kind`Permanent~id:"sc_rollup_inbox.consume_n_messages"~title:"Internal error: Trying to consume a negative number of messages"~description:"Sc_rollup_inbox.consume_n_messages must be called with a non negative \
integer."(obj1(req"consume_n_messages"int64))(functionInvalid_number_of_messages_to_consumen->Somen|_->None)(funn->Invalid_number_of_messages_to_consumen)(* 32 *)lethash_prefix="\003\250\174\238\208"(* scib1(55) *)moduleHash=structletprefix="scib1"letencoded_size=55moduleH=Blake2B.Make(Base58)(structletname="inbox_hash"lettitle="The hash of an inbox of a smart contract rollup"letb58check_prefix=hash_prefix(* defaults to 32 *)letsize=Noneend)includeHlet()=Base58.check_encoded_prefixb58check_encodingprefixencoded_sizeletof_context_hashcontext_hash=hash_bytes[Context_hash.to_bytescontext_hash]includePath_encoding.Make_hex(H)endmoduleSkip_list_parameters=structletbasis=2endmoduleSkip_list=Skip_list_repr.Make(Skip_list_parameters)typeproof_hash=Hash.ttypehistory_proof_hash=Hash.ttypehistory_proof=(proof_hash,history_proof_hash)Skip_list.cellletequal_history_proof=Skip_list.equalHash.equalHash.equallethistory_proof_encoding:history_proofData_encoding.t=Skip_list.encodingHash.encodingHash.encodingletpp_history_prooffmtcell=Format.fprintffmt{|
content = %a
index = %d
back_pointers = %a
|}Hash.pp(Skip_list.contentcell)(Skip_list.indexcell)(Format.pp_print_listHash.pp)(Skip_list.back_pointerscell)moduleV1=struct(*
At a given level, an inbox is composed of metadata of type [t] and
[current_messages], a [tree] representing the messages of the current level
(held by the [Raw_context.t] in the protocol).
The metadata contains :
- [rollup] : the address of the rollup ;
- [level] : the inbox level ;
- [message_counter] : the number of messages in the [level]'s inbox ;
- [nb_available_messages] :
the number of messages that have not been consumed by a commitment cementing ;
- [nb_messages_in_commitment_period] :
the number of messages during the commitment period ;
- [starting_level_of_current_commitment_period] :
the level marking the beginning of the current commitment period ;
- [current_messages_hash] : the root hash of [current_messages] ;
- [old_levels_messages] : a witness of the inbox history.
When new messages are appended to the current level inbox, the
metadata stored in the context may be related to an older level.
In that situation, an archival process is applied to the metadata.
This process saves the [current_messages_hash] in the
[old_levels_messages] and empties [current_messages]. If
there are intermediate levels between [inbox.level] and the current
level, this archival process is applied until we reach the current
level using an empty [current_messages]. See {!MakeHashingScheme.archive}
for details.
The [current_messages_hash] is either:
- the hash of 'empty bytes' when there are no current messages ;
- the root hash of the tree, where the contents of each message sit at the
key [[message_index, "payload"]], where [message_index] is the index of the
message in the list of [current_messages], if there are one or more
messages.
*)typet={rollup:Sc_rollup_repr.t;level:Raw_level_repr.t;nb_available_messages:int64;nb_messages_in_commitment_period:int64;starting_level_of_current_commitment_period:Raw_level_repr.t;message_counter:Z.t;(* Lazy to avoid hashing O(n^2) time in [add_external_messages] *)current_messages_hash:unit->Hash.t;old_levels_messages:history_proof;}letequalinbox1inbox2=(* To be robust to addition of fields in [t]. *)let{rollup;level;nb_available_messages;nb_messages_in_commitment_period;starting_level_of_current_commitment_period;message_counter;current_messages_hash;old_levels_messages;}=inbox1inSc_rollup_repr.Address.equalrollupinbox2.rollup&&Raw_level_repr.equallevelinbox2.level&&Compare.Int64.(equalnb_available_messagesinbox2.nb_available_messages)&&Compare.Int64.(equalnb_messages_in_commitment_periodinbox2.nb_messages_in_commitment_period)&&Raw_level_repr.(equalstarting_level_of_current_commitment_periodinbox2.starting_level_of_current_commitment_period)&&Z.equalmessage_counterinbox2.message_counter&&Hash.equal(current_messages_hash())(inbox2.current_messages_hash())&&equal_history_proofold_levels_messagesinbox2.old_levels_messagesletppfmt{rollup;level;nb_available_messages;nb_messages_in_commitment_period;starting_level_of_current_commitment_period;message_counter;current_messages_hash;old_levels_messages;}=Format.fprintffmt{|
rollup = %a
level = %a
current messages hash = %a
nb_available_messages = %Ld
nb_messages_in_commitment_period = %s
starting_level_of_current_commitment_period = %a
message_counter = %a
old_levels_messages = %a
|}Sc_rollup_repr.Address.pprollupRaw_level_repr.pplevelHash.pp(current_messages_hash())nb_available_messages(Int64.to_stringnb_messages_in_commitment_period)Raw_level_repr.ppstarting_level_of_current_commitment_periodZ.pp_printmessage_counterpp_history_proofold_levels_messagesletinbox_levelinbox=inbox.levelletold_levels_messages_encoding=Skip_list.encodingHash.encodingHash.encodingletencoding=Data_encoding.(conv(fun{rollup;message_counter;nb_available_messages;nb_messages_in_commitment_period;starting_level_of_current_commitment_period;level;current_messages_hash;old_levels_messages;}->(rollup,message_counter,nb_available_messages,nb_messages_in_commitment_period,starting_level_of_current_commitment_period,level,current_messages_hash(),old_levels_messages))(fun(rollup,message_counter,nb_available_messages,nb_messages_in_commitment_period,starting_level_of_current_commitment_period,level,current_messages_hash,old_levels_messages)->{rollup;message_counter;nb_available_messages;nb_messages_in_commitment_period;starting_level_of_current_commitment_period;level;current_messages_hash=(fun()->current_messages_hash);old_levels_messages;})(obj8(req"rollup"Sc_rollup_repr.encoding)(req"message_counter"n)(req"nb_available_messages"int64)(req"nb_messages_in_commitment_period"int64)(req"starting_level_of_current_commitment_period"Raw_level_repr.encoding)(req"level"Raw_level_repr.encoding)(req"current_messages_hash"Hash.encoding)(req"old_levels_messages"old_levels_messages_encoding)))letnumber_of_available_messagesinbox=Z.of_int64inbox.nb_available_messagesletnumber_of_messages_during_commitment_periodinbox=inbox.nb_messages_in_commitment_periodletstart_new_commitment_periodinboxlevel={inboxwithstarting_level_of_current_commitment_period=level;nb_messages_in_commitment_period=0L;}letstarting_level_of_current_commitment_periodinbox=inbox.starting_level_of_current_commitment_periodletno_messages_hash=Hash.hash_bytes[Bytes.empty]letemptyrolluplevel={rollup;level;message_counter=Z.zero;nb_available_messages=0L;nb_messages_in_commitment_period=0L;starting_level_of_current_commitment_period=level;current_messages_hash=(fun()->no_messages_hash);old_levels_messages=Skip_list.genesisno_messages_hash;}letconsume_n_messagesn({nb_available_messages;_}asinbox):toptiontzresult=letn=Int64.of_int32ninifCompare.Int64.(n<0L)thenerror(Invalid_number_of_messages_to_consumen)elseifCompare.Int64.(n>nb_available_messages)thenokNoneelseletnb_available_messages=Int64.(subnb_available_messagesn)inok(Some{inboxwithnb_available_messages})endtypeversioned=V1ofV1.tletversioned_encoding=letopenData_encodinginunion[case~title:"V1"(Tag0)V1.encoding(functionV1inbox->Someinbox)(funinbox->V1inbox);]includeV1letof_versioned=functionV1inbox->inbox[@@inline]letto_versionedinbox=V1inbox[@@inline]letkey_of_message=Data_encoding.Binary.to_string_exnData_encoding.zmoduletypeMerkelizedOperations=sigtypetreetypemessages=treetypemessage=treetypehistoryvalhistory_encoding:historyData_encoding.tvalpp_history:Format.formatter->history->unitvalhistory_at_genesis:bound:int64->historyvaladd_external_messages:history->t->Raw_level_repr.t->stringlist->messages->(messages*history*t)tzresultLwt.tvaladd_messages_no_history:t->Raw_level_repr.t->Sc_rollup_inbox_message_repr.serializedlist->messages->(messages*t)tzresultLwt.tvalget_message:messages->Z.t->messageoptionLwt.tvalget_message_payload:messages->Z.t->stringoptionLwt.ttypeinclusion_proofvalinclusion_proof_encoding:inclusion_proofData_encoding.tvalpp_inclusion_proof:Format.formatter->inclusion_proof->unitvalnumber_of_proof_steps:inclusion_proof->intvalproduce_inclusion_proof:history->t->t->inclusion_proofoptionvalverify_inclusion_proof:inclusion_proof->t->t->boolendmoduletypeTREE=sigtypettypetreetypekey=stringlisttypevalue=bytesvalfind:tree->key->valueoptionLwt.tvalfind_tree:tree->key->treeoptionLwt.tvaladd:tree->key->value->treeLwt.tvalis_empty:tree->boolvalhash:tree->Context_hash.tendmoduleMakeHashingScheme(Tree:TREE):MerkelizedOperationswithtypetree=Tree.tree=structmoduleTree=Treetypetree=Tree.treetypemessages=treetypemessage=treeletadd_messageinboxpayloadmessages=letopenLwt_tzresult_syntaxinletmessage_index=inbox.message_counterinletmessage_counter=Z.succmessage_indexinletkey=key_of_messagemessage_indexinletnb_available_messages=Int64.succinbox.nb_available_messagesinlet*!messages=Tree.addmessages[key;"payload"](Bytes.of_string(payload:Sc_rollup_inbox_message_repr.serialized:>string))inletnb_messages_in_commitment_period=Int64.succinbox.nb_messages_in_commitment_periodinletinbox={inboxwithmessage_counter;nb_available_messages;nb_messages_in_commitment_period;}inreturn(messages,inbox)letget_messagemessagesmessage_index=letkey=key_of_messagemessage_indexinTree.(find_treemessages[key])letget_message_payloadmessagesmessage_index=letkey=key_of_messagemessage_indexinTree.(findmessages[key;"payload"])>|=Option.mapBytes.to_stringlethash_old_levels_messagescell=letcurrent_messages_hash=Skip_list.contentcellinletback_pointers_hashes=Skip_list.back_pointerscellinHash.to_bytescurrent_messages_hash::List.mapHash.to_bytesback_pointers_hashes|>Hash.hash_bytesmoduleInt64_map=Map.Make(Int64)typehistory={events:history_proofHash.Map.t;sequence:Hash.tInt64_map.t;bound:int64;counter:int64;}lethistory_encoding:historyData_encoding.t=letopenData_encodinginletevents_encoding=Hash.Map.encodinghistory_proof_encodinginletsequence_encoding=conv(funm->Int64_map.bindingsm)(List.fold_left(funm(k,v)->Int64_map.addkvm)Int64_map.empty)(list(tup2int64Hash.encoding))inconv(fun{events;sequence;bound;counter}->(events,sequence,bound,counter))(fun(events,sequence,bound,counter)->{events;sequence;bound;counter})(obj4(req"events"events_encoding)(req"sequence"sequence_encoding)(req"bound"int64)(req"counter"int64))letpp_historyfmthistory=Hash.Map.bindingshistory.events|>funbindings->letpp_bindingfmt(hash,history_proof)=Format.fprintffmt"@[%a -> %a@]"Hash.pphashpp_history_proofhistory_proofinFormat.pp_print_listpp_bindingfmtbindingslethistory_at_genesis~bound={events=Hash.Map.empty;sequence=Int64_map.empty;bound;counter=0L}typewithout_history_witnesstypewith_history_witnesstype_with_history=|No_history:without_history_witnesswith_history|With_history:history->with_history_witnesswith_history(** [remember_history ptr cell history] extends [history] with a new
mapping from [ptr] to [cell]. If [history] is full, the
oldest mapping is removed. If the history bound is less
or equal to zero, then this function returns [history]
untouched. *)letremember_historyptrcellhistory=ifCompare.Int64.(history.bound<=0L)thenhistoryelseletevents=Hash.Map.addptrcellhistory.eventsinletcounter=Int64.succhistory.counterinlethistory={events;sequence=Int64_map.addhistory.counterptrhistory.sequence;bound=history.bound;counter;}inifInt64.(equalhistory.counterhistory.bound)thenmatchInt64_map.min_bindinghistory.sequencewith|None->history|Some(l,h)->letsequence=Int64_map.removelhistory.sequenceinletevents=Hash.Map.removeheventsin{counter=Int64.predhistory.counter;bound=history.bound;sequence;events;}elsehistoryletremember:typehistory_witness.history_proof_hash->history_proof->history_witnesswith_history->history_witnesswith_history=funptrcellhistory->matchhistorywith|No_history->No_history|With_historyhistory->With_history(remember_historyptrcellhistory)letarchive_if_neededhistoryinboxtarget_level=letarchive_levelhistoryinbox=letprev_cell=inbox.old_levels_messagesinletprev_cell_ptr=hash_old_levels_messagesprev_cellinlethistory=rememberprev_cell_ptrprev_cellhistoryinletold_levels_messages=Skip_list.next~prev_cell~prev_cell_ptr(inbox.current_messages_hash())inletlevel=Raw_level_repr.succinbox.levelinletcurrent_messages_hash()=no_messages_hashinletinbox={rollup=inbox.rollup;nb_available_messages=inbox.nb_available_messages;nb_messages_in_commitment_period=inbox.nb_messages_in_commitment_period;starting_level_of_current_commitment_period=inbox.starting_level_of_current_commitment_period;old_levels_messages;level;current_messages_hash;message_counter=Z.zero;}in(history,inbox)inletrecaux(history,inbox)=ifRaw_level_repr.(inbox.level=target_level)then(history,inbox)elseaux(archive_levelhistoryinbox)inaux(history,inbox)lethash_messagesmessages=ifTree.is_emptymessagesthenno_messages_hashelseHash.of_context_hash@@Tree.hashmessagesletadd_messages_auxhistoryinboxlevelpayloadsmessages=letopenLwt_tzresult_syntaxinlet*()=fail_whenRaw_level_repr.(level<inbox.level)(Invalid_level_add_messageslevel)inlethistory,inbox=archive_if_neededhistoryinboxlevelinlet*messages,inbox=List.fold_left_es(fun(messages,inbox)payload->add_messageinboxpayloadmessages)(messages,inbox)payloadsinletcurrent_messages_hash()=hash_messagesmessagesinreturn(messages,history,{inboxwithcurrent_messages_hash})letadd_external_messageshistoryinboxlevelpayloadsmessages=letopenLwt_tzresult_syntaxinlet*?payloads=List.map_e(funpayload->Sc_rollup_inbox_message_repr.(to_bytes@@Externalpayload))payloadsinlet*messages,With_historyhistory,inbox=add_messages_aux(With_historyhistory)inboxlevelpayloadsmessagesinreturn(messages,history,inbox)letadd_messages_no_historyinboxlevelpayloadsmessages=letopenLwt_tzresult_syntaxinlet*messages,No_history,inbox=add_messages_auxNo_historyinboxlevelpayloadsmessagesinreturn(messages,inbox)(* An [inclusion_proof] is a path in the Merkelized skip list
showing that a given inbox history is a prefix of another one.
This path has a size logarithmic in the difference between the
levels of the two inboxes.
[Irmin.Proof.{tree_proof, stream_proof}] could not be reused here
because there is no obviously encoding of sequences in these data
structures with the same guarantee about the size of proofs. *)typeinclusion_proof=history_prooflistletinclusion_proof_encoding=letopenData_encodinginlisthistory_proof_encodingletpp_inclusion_prooffmtproof=Format.pp_print_listpp_history_prooffmtproofletnumber_of_proof_stepsproof=List.lengthproofletlift_ptr_pathhistoryptr_path=letrecauxaccu=function|[]->Some(List.revaccu)|x::xs->Option.bind(historyx)@@func->aux(c::accu)xsinaux[]ptr_pathletproduce_inclusion_proofhistoryinbox1inbox2=letcell_ptr=hash_old_levels_messagesinbox2.old_levels_messagesinlettarget_index=Skip_list.indexinbox1.old_levels_messagesinlet(With_historyhistory)=remembercell_ptrinbox2.old_levels_messages(With_historyhistory)inletderefptr=Hash.Map.find_optptrhistory.eventsinSkip_list.back_path~deref~cell_ptr~target_index|>Option.map(lift_ptr_pathderef)|>Option.joinletverify_inclusion_proofproofinbox1inbox2=letassoc=List.map(func->(hash_old_levels_messagesc,c))proofinletpath=List.splitassoc|>fstinletderef=letopenHash.Mapinletmap=of_seq(List.to_seqassoc)infunptr->find_optptrmapinletcell_ptr=hash_old_levels_messagesinbox2.old_levels_messagesinlettarget_ptr=hash_old_levels_messagesinbox1.old_levels_messagesinSkip_list.valid_back_path~equal_ptr:Hash.equal~deref~cell_ptr~target_ptrpathendinclude(MakeHashingScheme(structincludeContext.Treetypet=Context.ttypetree=Context.treetypevalue=bytestypekey=stringlistend):MerkelizedOperationswithtypetree=Context.tree)typeinbox=tmoduleProof=structtypestarting_point={inbox_level:Raw_level_repr.t;message_counter:Z.t}typet={skips:(inbox*inclusion_proof)list;(* The [skips] value in this record makes it potentially unbounded
in size. There is an issue #2997 to deal with this problem. *)level:inbox;inc:inclusion_proof;message_proof:Context.Proof.treeContext.Proof.t;}letppfmtproof=Format.fprintffmt"Inbox proof with %d skips"(List.lengthproof.skips)letencoding=letopenData_encodinginconv(fun{skips;level;inc;message_proof}->(skips,level,inc,message_proof))(fun(skips,level,inc,message_proof)->{skips;level;inc;message_proof})(obj4(req"skips"(list(tup2encodinginclusion_proof_encoding)))(req"level"encoding)(req"inc"inclusion_proof_encoding)(req"message_proof"Context.Proof_encoding.V1.Tree32.tree_proof_encoding))(* This function is for pattern matching on proofs based on whether
they involve multiple levels or if they only concern a single
level.
[split_proof proof] is [None] in the case that [proof] is a
'simple' inbox proof that only involves one level. In this case
[skips] is empty and we just check the single [level], [inc]
pair, and the [message_proof].
[split_proof proof] is [Some (level, inc, remaining_proof)] if
there are [skips]. In this case, we must check the [level] and
[inc] given, and then continue (recursively) on to the
[remaining_proof]. *)letsplit_proofproof=matchproof.skipswith|[]->None|(level,inc)::rest->Some(level,inc,{proofwithskips=rest})(* A proof might include several sub-inboxes as evidence of different
levels being empty in the actual inbox snapshot. This returns the
_lowest_ such sub-inbox for a given proof.
It's used with the function above in the recursive case of [valid].
When [split_proof proof] gives [Some (level, inc, remaining_proof)]
we have to check that [inc] is an inclusion proof between [level]
and [bottom_level remaining_proof]. *)letbottom_levelproof=matchproof.skipswith[]->proof.level|(level,_)::_->level(* The [message_proof] part of an inbox proof is a
[Context.tree_proof].
To validate this, we need a function of type
[tree -> (tree, result) Lwt.t].
For a given [n], [message_payload n] is such a function: it takes a
[Context.tree] representing the messages in a single level of the
inbox and extracts the message payload at index [n], so [result] in
this case is [string]. (It also returns the tree just to satisfy
the function [Context.verify_tree_proof]). *)letmessage_payloadntree=letopenLwt_syntaxinlet*r=get_message_payloadtreeninreturn(tree,r)letcheck_hashhashkinded_hash=matchkinded_hashwith|`Nodeh->Hash.(equal(of_context_hashh)hash)|`Valueh->Hash.(equal(of_context_hashh)hash)typeerror+=Inbox_proof_errorofstringletproof_errorreason=letopenLwt_result_syntaxinfail(Inbox_proof_errorreason)letdrop_errorpromisereason=letopenLwt_tzresult_syntaxinlet*!result=promiseinmatchresultwithOkr->returnr|Error_->proof_errorreasonletrecvalid{inbox_level=l;message_counter=n}inboxproof=assert(Z.(geqnzero));letopenLwt_result_syntaxinmatchsplit_proofproofwith|None->ifverify_inclusion_proofproof.incproof.levelinbox&&Raw_level_repr.equal(inbox_levelproof.level)l&&check_hash(proof.level.current_messages_hash())proof.message_proof.beforethenlet*(_:Context.tree),payload=drop_error(Context.verify_tree_proofproof.message_proof(message_payloadn))"message_proof invalid"inmatchpayloadwith|None->ifequalproof.levelinboxthenreturnNoneelseproof_error"payload is None, inbox proof.level not top"|Somemsg->return@@SomeSc_rollup_PVM_sem.{inbox_level=l;message_counter=n;payload=msg}elseproof_error"Inbox proof parameters don't match (message level)"|Some(level,inc,remaining_proof)->ifverify_inclusion_proofinclevel(bottom_levelremaining_proof)&&Raw_level_repr.equal(inbox_levellevel)l&&Z.equallevel.message_counternthenvalid{inbox_level=Raw_level_repr.succl;message_counter=Z.zero}inboxremaining_proofelseproof_error"Inbox proof parameters don't match (lower level)"(* TODO #2997 This needs to be implemented when the inbox structure is
improved. *)letproduce_proof__=assertfalseend