12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)(* Copyright (c) 2018-2021 Nomadic Labs <contact@nomadic-labs.com> *)(* Copyright (c) 2018-2020 Tarides <contact@tarides.com> *)(* Copyright (c) 2020 Metastate AG <hello@metastate.dev> *)(* Copyright (c) 2021 DaiLambda, Inc. <contact@dailambda.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. *)(* *)(*****************************************************************************)moduleProof=Tezos_context_sigs.Context.Proof_types(* Errors *)typeerror+=|Cannot_create_fileofstring|Cannot_open_fileofstring|Cannot_find_protocol|Suspicious_fileofintlet()=register_error_kind`Permanent~id:"context_dump.write.cannot_open"~title:"Cannot open file for context dump"~description:""~pp:(funppfuerr->Format.fprintfppf"@[Error while opening file for context dumping: %s@]"uerr)Data_encoding.(obj1(req"context_dump_cannot_open"string))(functionCannot_create_filee->Somee|_->None)(fune->Cannot_create_filee);register_error_kind`Permanent~id:"context_dump.read.cannot_open"~title:"Cannot open file for context restoring"~description:""~pp:(funppfuerr->Format.fprintfppf"@[Error while opening file for context restoring: %s@]"uerr)Data_encoding.(obj1(req"context_restore_cannot_open"string))(functionCannot_open_filee->Somee|_->None)(fune->Cannot_open_filee);register_error_kind`Permanent~id:"context_dump.cannot_find_protocol"~title:"Cannot find protocol"~description:""~pp:(funppf()->Format.fprintfppf"@[Cannot find protocol in context@]")Data_encoding.unit(functionCannot_find_protocol->Some()|_->None)(fun()->Cannot_find_protocol);register_error_kind`Permanent~id:"context_dump.read.suspicious"~title:"Suspicious file: data after end"~description:""~pp:(funppfuerr->Format.fprintfppf"@[Remaining bytes in file after context restoring: %d@]"uerr)Data_encoding.(obj1(req"context_restore_suspicious"int31))(functionSuspicious_filee->Somee|_->None)(fune->Suspicious_filee)moduletypeTEZOS_CONTEXT_UNIX=sigtypeerror+=|Cannot_create_fileofstring|Cannot_open_fileofstring|Cannot_find_protocol|Suspicious_fileofintincludeTezos_context_sigs.Context.TEZOS_CONTEXTwithtypememory_context_tree:=Tezos_context_memory.Context.tree(** Sync the context with disk. Only useful for read-only instances.
Does not fail when the context is not in read-only mode. *)valsync:index->unitLwt.tvalflush:t->tLwt.t(** {2 Context dumping} *)(** Rebuild a context from a given snapshot. *)valrestore_context:index->expected_context_hash:Context_hash.t->nb_context_elements:int->fd:Lwt_unix.file_descr->in_memory:bool->progress_display_mode:Animation.progress_display_mode->unittzresultLwt.t(** Offline integrity checking and statistics for contexts. *)moduleChecks:sigmodulePack:Irmin_pack_unix.Checks.SmoduleIndex:Index.Checks.Sendendletreporter()=letreportsrclevel~overkmsgf=letk_=over();k()inletwith_stamph_tagskfmt=letdt=Time.Monotonic.Span.to_float_us(Mtime_clock.elapsed())inFmt.kpfkFmt.stderr("%+04.0fus %a %a @["^^fmt^^"@]@.")dtFmt.(styled`Magentastring)(Logs.Src.namesrc)Logs_fmt.pp_header(level,h)inmsgf@@fun?header?tagsfmt->with_stampheadertagskfmtin{Logs.report}let()=matchTezos_context_helpers.Env.(v.verbosity)with|`Info->Logs.set_level(SomeLogs.Info);Logs.set_reporter(reporter())|`Debug->Logs.set_level(SomeLogs.Debug);Logs.set_reporter(reporter())|`Default->()moduleEvents=structincludeInternal_event.Simpleletsection=["node";"context";"disk"]letinit_context=declare_3~section~level:Info~name:"init_context"~msg:"initializing context (readonly: {readonly}, index_log_size: \
{index_log_size}, lru_size: {lru_size})"~pp1:Format.pp_print_bool("readonly",Data_encoding.bool)~pp2:Format.pp_print_int("index_log_size",Data_encoding.int31)~pp3:Format.pp_print_int("lru_size",Data_encoding.int31)letstarting_gc=declare_1~section~level:Info~name:"starting_gc"~msg:"starting context garbage collection for commit {context_hash}"~pp1:Context_hash.pp("context_hash",Context_hash.encoding)letending_gc=declare_2~section~level:Info~name:"ending_gc"~msg:"context garbage collection finished in {duration} (finalised in \
{finalisation})"~pp1:Time.System.Span.pp_hum("duration",Time.System.Span.encoding)~pp2:Time.System.Span.pp_hum("finalisation",Time.System.Span.encoding)letsplit_context=declare_0~section~level:Debug~name:"split_context"~msg:"splitting context into a new chunk"()letgc_failure=declare_1~section~level:Warning~name:"gc_failure"~msg:"context garbage collection failed: {error}"("error",Data_encoding.string)letgc_launch_failure=declare_1~section~level:Warning~name:"gc_launch_failure"~msg:"context garbage collection launch failed: {error}"("error",Data_encoding.string)endmoduleMake(Encoding:moduletypeofTezos_context_encoding.Context)=structtypeerror+=|Cannot_create_file=Cannot_create_file|Cannot_open_file=Cannot_open_file|Cannot_find_protocol=Cannot_find_protocol|Suspicious_file=Suspicious_fileopenEncoding(** Tezos - Versioned (key x value) store (over Irmin) *)moduleStore=structmoduleMaker=Irmin_pack_unix.Maker(Conf)includeMaker.Make(Schema)moduleSchema=Tezos_context_encoding.Context.SchemaendmoduleInfo=Store.InfomoduleP=Store.BackendmoduleChecks=structmoduleConf=structincludeConfendmoduleMaker=structmoduleMaker=Irmin_pack_unix.Maker(Conf)includeMaker.Make(Schema)endmodulePack:Irmin_pack_unix.Checks.S=Irmin_pack_unix.Checks.Make(Maker)moduleIndex=structmoduleI=Irmin_pack_unix.Index.Make(Hash)includeI.Checksendendtypeindex={path:string;repo:Store.Repo.t;patch_context:(context->contexttzresultLwt.t)option;readonly:bool;}andcontext={index:index;parents:Store.Commit.tlist;tree:Store.tree;(* number of [remove], [add_tree] and [add] calls, not yet flushed *)ops:int;}typet=contextletindex{index;_}=index(*-- Version Access and Update -----------------------------------------------*)letcurrent_protocol_key=["protocol"]letcurrent_test_chain_key=["test_chain"]letcurrent_predecessor_block_metadata_hash_key=["predecessor_block_metadata_hash"]letcurrent_predecessor_ops_metadata_hash_key=["predecessor_ops_metadata_hash"]letsyncindex=ifindex.readonlythenStore.reloadindex.repo;Lwt.return_unitletexistsindexkey=letopenLwt_syntaxinlet*()=syncindexinlet+o=Store.Commit.of_hashindex.repo(Hash.of_context_hashkey)inOption.is_someoletcheckoutindexkey=letopenLwt_syntaxinlet*()=syncindexinlet*o=Store.Commit.of_hashindex.repo(Hash.of_context_hashkey)inmatchowith|None->return_none|Somecommit->lettree=Store.Commit.treecommitinreturn_some{index;tree;parents=[commit];ops=0}letcheckout_exnindexkey=letopenLwt_syntaxinlet*o=checkoutindexkeyinmatchowithNone->Lwt.failNot_found|Somep->Lwt.returnp(* unshallow possible 1-st level objects from previous partial
checkouts ; might be better to pass directly the list of shallow
objects. *)letunshallowcontext=letopenLwt_syntaxinlet*children=Store.Tree.listcontext.tree[]inP.Repo.batchcontext.index.repo(funxy_->List.iter_s(fun(s,k)->matchStore.Tree.destructkwith|`Contents_->Lwt.return_unit|`Node_->let*tree=Store.Tree.get_treecontext.tree[s]inlet+_=Store.save_tree~clear:truecontext.index.repoxytreein())children)letget_hash_version_c=Context_hash.Version.of_int0letset_hash_versioncv=letopenLwt_result_syntaxinifContext_hash.Version.(of_int0=v)thenreturncelsetzfail(Tezos_context_helpers.Context.Unsupported_context_hash_versionv)letraw_commit~time?(message="")context=letopenLwt_syntaxinletinfo=Info.v~author:"Tezos"(Time.Protocol.to_secondstime)~messageinletparents=List.mapStore.Commit.keycontext.parentsinlet*()=unshallowcontextinlet+c=Store.Commit.vcontext.index.repo~info~parentscontext.treeinStore.Tree.clearcontext.tree;cmoduleCommit_hash=Irmin.Hash.Typed(Hash)(P.Commit_portable)lethash~time?(message="")context=letinfo=Info.v~author:"Tezos"(Time.Protocol.to_secondstime)~messageinletparents=List.map(func->Store.Commit.hashc)context.parentsinletnode=Store.Tree.hashcontext.treeinletcommit=P.Commit_portable.v~parents~node~infoinHash.to_context_hash(Commit_hash.hashcommit)letcommit~time?messagecontext=letopenLwt_syntaxinlet+commit=raw_commit~time?messagecontextinHash.to_context_hash(Store.Commit.hashcommit)letgcindexcontext_hash=letopenLwt_syntaxinletrepo=index.repoinlet*commit_opt=Store.Commit.of_hashindex.repo(Hash.of_context_hashcontext_hash)inmatchcommit_optwith|None->Fmt.failwith"%a: unknown context hash"Context_hash.ppcontext_hash|Somecommit->(let*()=Events.(emitstarting_gc)context_hashinLogs.info(funm->m"Launch GC for commit %a@."Context_hash.ppcontext_hash);letfinished=function|Ok(stats:Irmin_pack_unix.Stats.Latest_gc.stats)->lettotal_duration=Irmin_pack_unix.Stats.Latest_gc.total_durationstatsinletfinalise_duration=Irmin_pack_unix.Stats.Latest_gc.finalise_durationstatsinEvents.(emitending_gc)(Time.System.Span.of_seconds_exntotal_duration,Time.System.Span.of_seconds_exnfinalise_duration)|Error(`Msgerr)->Events.(emitgc_failure)errinletcommit_key=Store.Commit.keycommitinlet*launch_result=Store.Gc.run~finishedrepocommit_keyinmatchlaunch_resultwith|Ok_->return_unit|Error(`Msgerr)->let*()=Events.(emitgc_launch_failure)errinreturn_unit)letwait_gc_completionindex=letopenLwt_syntaxinlet*()=syncindexinlet*r=Store.Gc.waitindex.repoinmatchrwith|Ok_stats_opt->return_unit|Error(`Msg_msg)->(* Logs will be printed by the [gc] caller. *)return_unitletis_gc_allowedindex=Store.Gc.is_allowedindex.repoletsplitindex=letopenLwt_syntaxinlet*()=Events.(emitsplit_context())inStore.splitindex.repo;Lwt.return_unitletexport_snapshotindexcontext_hash~path=letopenLwt_syntaxinlet*commit_opt=Store.Commit.of_hashindex.repo(Hash.of_context_hashcontext_hash)inmatchcommit_optwith|None->Fmt.failwith"%a: unknown context hash"Context_hash.ppcontext_hash|Somecommit->leth=Store.Commit.keycommitinStore.create_one_commit_storeindex.repohpath(*-- Generic Store Primitives ------------------------------------------------*)letdata_key=Tezos_context_sigs.Context.data_keytypekey=stringlisttypevalue=bytestypetree=Store.treetypenode_key=Store.node_keytypevalue_key=Store.contents_keytypekinded_key=[`Nodeofnode_key|`Valueofvalue_key]moduleTree=Tezos_context_helpers.Context.Make_tree(Conf)(Store)includeTezos_context_helpers.Context.Make_config(Conf)includeTezos_context_helpers.Context.Make_proof(Store)(Conf)letmemctxtkey=Tree.memctxt.tree(data_keykey)letmem_treectxtkey=Tree.mem_treectxt.tree(data_keykey)letraw_findctxtkey=Tree.findctxt.treekeyletlistctxt?offset?lengthkey=Tree.listctxt.tree?offset?length(data_keykey)letlengthctxtkey=Tree.lengthctxt.tree(data_keykey)letfindctxtkey=raw_findctxt(data_keykey)letincr_opsctxt={ctxtwithops=ctxt.ops+1}letraw_addctxtkeydata=letopenLwt_syntaxinlet+tree=Tree.addctxt.treekeydatainincr_ops{ctxtwithtree}letaddctxtkeydata=raw_addctxt(data_keykey)dataletraw_removectxtk=letopenLwt_syntaxinlet+tree=Tree.removectxt.treekinincr_ops{ctxtwithtree}letremovectxtkey=raw_removectxt(data_keykey)letfind_treectxtkey=Tree.find_treectxt.tree(data_keykey)letflushcontext=letopenLwt_syntaxinlet+_=P.Repo.batchcontext.index.repo(funxy_->Store.save_tree~clear:truecontext.index.repoxycontext.tree)in{contextwithops=0}letmay_flushcontext=if(notcontext.index.readonly)&&context.ops>=Tezos_context_helpers.Env.(v.auto_flush)thenflushcontextelseLwt.returncontextletadd_treectxtkeytree=letopenLwt_syntaxinlet*ctxt=may_flushctxtinlet+tree=Tree.add_treectxt.tree(data_keykey)treeinincr_ops{ctxtwithtree}letfold?depthctxtkey~order~init~f=Tree.fold?depthctxt.tree(data_keykey)~order~init~f(** The light mode relies on the implementation of this
function, because it uses Irmin.Type.of_string to rebuild values
of type Irmin.Hash.t. This is a temporary workaround until we
do that in a type safe manner when there are less moving pieces. *)letmerkle_hash_to_string=Irmin.Type.to_stringStore.Hash.tletrectree_to_raw_contexttree=letopenLwt_syntaxinmatchStore.Tree.destructtreewith|`Contents(v,_)->let+v=Store.Tree.Contents.force_exnvinProof.Keyv|`Node_->let*kvs=Store.Tree.listtree[]inletfacc(key,_)=(* get_tree is safe, because we iterate over keys *)let*tree=Store.Tree.get_treetree[key]inlet+sub_raw_context=tree_to_raw_contexttreeinString.Map.addkeysub_raw_contextaccinlet+res=List.fold_left_sfString.Map.emptykvsinProof.Dirreslettree_to_memory_tree(tree:tree):Tezos_context_memory.Context.treeLwt.t=letcontentspathbytesacc=Tezos_context_memory.Context.Tree.addaccpathbytesinStore.Tree.fold~force:`True~order:`Undefined~cache:false~uniq:`False~contentstree(Tezos_context_memory.Context.make_empty_tree())letto_memory_tree(ctxt:t)(key:stringlist):Tezos_context_memory.Context.treeoptionLwt.t=letopenLwt_option_syntaxinlet*ctxt_tree=find_treectxtkeyinlet*!c=tree_to_memory_treectxt_treeinreturncletmerkle_hashtree=letmerkle_hash_kind=matchStore.Tree.destructtreewith|`Contents_->Proof.Contents|`Node_->Proof.Nodeinlethash_str=Store.Tree.hashtree|>merkle_hash_to_stringinProof.Hash(merkle_hash_kind,hash_str)letmerkle_treetleaf_kindkey=letopenLwt_syntaxinlet*subtree_opt=Store.Tree.find_treet.tree(data_key[])inmatchsubtree_optwith|None->Lwt.returnString.Map.empty|Somesubtree->letkey_to_stringk=String.concat";"kinletreckey_to_merkle_treettarget=match(Store.Tree.destructt,target)with|_,[]->(* We cannot use this case as the base case, because a merkle_node
is a map from string to something. In this case, we have
no key to put in the map's domain. *)raise(Invalid_argument(Printf.sprintf"Reached end of key (top-level key was: %s)"@@key_to_stringkey))|_,[hd]->letfinallykey=(* get_tree is safe because we iterate on keys *)let*tree=Store.Tree.get_treet[key]inifkey=hdthen(* on the target path: the final leaf *)matchleaf_kindwith|Proof.Hole->Lwt.return@@merkle_hashtree|Proof.Raw_context->let+raw_context=tree_to_raw_contexttreeinProof.Dataraw_contextelse(* a sibling of the target path: return a hash *)Lwt.return@@merkle_hashtreeinlet*l=Store.Tree.listt[]inList.fold_left_s(funacc(key,_)->let+v=finallykeyinString.Map.addkeyvacc)String.Map.emptyl|`Node_,target_hd::target_tl->letcontinuekey=(* get_tree is safe because we iterate on keys *)let*tree=Store.Tree.get_treet[key]inifkey=target_hdthen(* on the target path: recurse *)let+sub=key_to_merkle_treetreetarget_tlinProof.Continuesubelse(* a sibling of the target path: return a hash *)Lwt.return@@merkle_hashtreeinlet*l=Store.Tree.listt[]inList.fold_left_s(funacc(key,_)->let+atom=continuekeyinString.Map.addkeyatomacc)String.Map.emptyl|`Contents_,_->raise(Invalid_argument(Printf.sprintf"(`Contents _, l) when l <> [_] (in other words: found a \
leaf node whereas key %s (top-level key: %s) wasn't \
fully consumed)"(key_to_stringtarget)(key_to_stringkey)))inkey_to_merkle_treesubtreekeyletproduce_tree_proofindex=produce_tree_proofindex.repoletproduce_stream_proofindex=produce_stream_proofindex.repomoduleStorelike=structtypekey=stringlisttypetree=Store.treetypevalue=bytesletfind=Tree.findletfind_tree=Tree.find_treeletunshallow=Tree.unshallowendmoduleGet_data=Tezos_context_sigs.Context.With_get_data((Storelike:Tezos_context_sigs.Context.Storelike))letmerkle_tree_v2ctxleaf_kindkey=letopenLwt_syntaxinmatchTree.kinded_keyctx.treewith|None->raise(Invalid_argument"On-disk context.tree has no kinded_key")|Somekinded_key->let*proof,_=produce_tree_proofctx.indexkinded_key(Get_data.get_dataleaf_kind[key])inreturnproof(*-- Predefined Fields -------------------------------------------------------*)moduleRoot_tree=structletget_protocolt=letopenLwt_syntaxinlet+o=Tree.findtcurrent_protocol_keyinletdata=WithExceptions.Option.to_exn_f~none:(fun()->assertfalse)oinProtocol_hash.of_bytes_exndataletadd_protocoltv=letv=Protocol_hash.to_bytesvinTree.addtcurrent_protocol_keyvletget_test_chaint=letopenLwt_syntaxinlet*o=Tree.findtcurrent_test_chain_keyinletdata=WithExceptions.Option.to_exn~none:(Failure"Unexpected error (Context.get_test_chain)")oinmatchData_encoding.Binary.of_bytesTest_chain_status.encodingdatawith|Errorre->Format.kasprintf(funs->Lwt.fail(Failures))"Error in Context.get_test_chain: %a"Data_encoding.Binary.pp_read_errorre|Okr->Lwt.returnrletadd_test_chaintid=letid=Data_encoding.Binary.to_bytes_exnTest_chain_status.encodingidinTree.addtcurrent_test_chain_keyidletfind_predecessor_block_metadata_hasht=letopenLwt_syntaxinlet*o=Tree.findtcurrent_predecessor_block_metadata_hash_keyinmatchowith|None->return_none|Somedata->(matchData_encoding.Binary.of_bytes_optBlock_metadata_hash.encodingdatawith|None->raise(Failure"Unexpected error \
(Context.get_predecessor_block_metadata_hash)")|Somer->return_somer)letadd_predecessor_block_metadata_hashthash=letdata=Data_encoding.Binary.to_bytes_exnBlock_metadata_hash.encodinghashinTree.addtcurrent_predecessor_block_metadata_hash_keydataletfind_predecessor_ops_metadata_hasht=letopenLwt_syntaxinlet*o=Tree.findtcurrent_predecessor_ops_metadata_hash_keyinmatchowith|None->return_none|Somedata->(matchData_encoding.Binary.of_bytes_optOperation_metadata_list_list_hash.encodingdatawith|None->raise(Failure"Unexpected error \
(Context.get_predecessor_ops_metadata_hash)")|Somer->return_somer)letadd_predecessor_ops_metadata_hashthash=letdata=Data_encoding.Binary.to_bytes_exnOperation_metadata_list_list_hash.encodinghashinTree.addtcurrent_predecessor_ops_metadata_hash_keydataendletget_protocolctxt=Root_tree.get_protocolctxt.treeletget_test_chainctxt=Root_tree.get_test_chainctxt.treeletfind_predecessor_block_metadata_hashctxt=Root_tree.find_predecessor_block_metadata_hashctxt.treeletfind_predecessor_ops_metadata_hashctxt=Root_tree.find_predecessor_ops_metadata_hashctxt.treeletlift_tree_add_to_ctxttree_addctxtv=letopenLwt_syntaxinlet+tree=tree_addctxt.treevinincr_ops{ctxtwithtree}letadd_protocol=lift_tree_add_to_ctxtRoot_tree.add_protocolletadd_test_chain=lift_tree_add_to_ctxtRoot_tree.add_test_chainletadd_predecessor_block_metadata_hash=lift_tree_add_to_ctxtRoot_tree.add_predecessor_block_metadata_hashletadd_predecessor_ops_metadata_hash=lift_tree_add_to_ctxtRoot_tree.add_predecessor_ops_metadata_hashletremove_test_chainv=raw_removevcurrent_test_chain_keyletfork_test_chainv~protocol~expiration=add_test_chainv(Forking{protocol;expiration})(*-- Initialisation ----------------------------------------------------------*)letinit?patch_context?(readonly=false)?index_log_size:tbl_log_sizeroot=letopenLwt_syntaxin(* Forces the context to use the minimal indexing strategy. *)letindexing_strategy=Irmin_pack.Indexing_strategy.minimalinlet+repo=letenv=Tezos_context_helpers.Env.vinletindex_log_size=Option.valuetbl_log_size~default:Tezos_context_helpers.Env.(env.index_log_size)inletlru_size=env.lru_sizeinlet*()=Events.(emitinit_context(readonly,index_log_size,lru_size))inStore.Repo.v(Irmin_pack.config~readonly~indexing_strategy~index_log_size~lru_sizeroot)in{path=root;repo;patch_context;readonly}letcloseindex=let_interrupted_gc=Store.Gc.cancelindex.repoinStore.Repo.closeindex.repoletget_branchchain_id=Format.asprintf"%a"Chain_id.ppchain_idletemptyindex={index;tree=Store.Tree.empty();parents=[];ops=0}letis_empty{tree;_}=Store.Tree.is_emptytreeletcommit_genesisindex~chain_id~time~protocol=letopenLwt_result_syntaxinletctxt=emptyindexinlet*ctxt=matchindex.patch_contextwith|None->returnctxt|Somepatch_context->patch_contextctxtinlet*!ctxt=add_protocolctxtprotocolinlet*!ctxt=add_test_chainctxtNot_runninginlet*!commit=raw_commit~time~message:"Genesis"ctxtinlet*!()=Store.Branch.setindex.repo(get_branchchain_id)commitinreturn(Hash.to_context_hash(Store.Commit.hashcommit))letcompute_testchain_chain_idgenesis=letgenesis_hash=Block_hash.hash_bytes[Block_hash.to_bytesgenesis]inChain_id.of_block_hashgenesis_hashletcompute_testchain_genesisforked_block=letgenesis=Block_hash.hash_bytes[Block_hash.to_bytesforked_block]ingenesisletcommit_test_chain_genesisctxt(forked_header:Block_header.t)=letopenLwt_syntaxinletmessage=Format.asprintf"Forking testchain at level %ld."forked_header.shell.levelinlet*commit=raw_commit~time:forked_header.shell.timestamp~messagectxtinletfaked_shell_header:Block_header.shell_header={forked_header.shellwithproto_level=succforked_header.shell.proto_level;predecessor=Block_hash.zero;validation_passes=0;operations_hash=Operation_list_list_hash.empty;context=Hash.to_context_hash(Store.Commit.hashcommit);}inletforked_block=Block_header.hashforked_headerinletgenesis_hash=compute_testchain_genesisforked_blockinletchain_id=compute_testchain_chain_idgenesis_hashinletgenesis_header:Block_header.t={shell={faked_shell_headerwithpredecessor=genesis_hash};protocol_data=Bytes.create0;}inletbranch=get_branchchain_idinlet+()=Store.Branch.setctxt.index.repobranchcommitingenesis_headerletclear_test_chainindexchain_id=(* TODO remove commits... ??? *)letbranch=get_branchchain_idinStore.Branch.removeindex.repobranchletset_headindexchain_idcommit=letopenLwt_syntaxinletbranch=get_branchchain_idinlet*o=Store.Commit.of_hashindex.repo(Hash.of_context_hashcommit)inmatchowith|None->assertfalse|Somecommit->Store.Branch.setindex.repobranchcommitletset_masterindexcommit=letopenLwt_syntaxinlet*o=Store.Commit.of_hashindex.repo(Hash.of_context_hashcommit)inmatchowith|None->assertfalse|Somecommit->Store.Branch.setindex.repoStore.Branch.maincommit(* Context dumping *)moduleDumpable_context=structtypenonrecindex=indextypenonreccontext=contexttypetree=Store.treetypehash=Store.hashmoduleKinded_hash=structtypet=[`Blobofhash|`Nodeofhash]letencoding:tData_encoding.t=letopenData_encodinginletkind_encoding=string_enum[("node",`Node);("blob",`Blob)]inconv(function|`Blobh->(`Blob,Context_hash.to_bytes(Hash.to_context_hashh))|`Nodeh->(`Node,Context_hash.to_bytes(Hash.to_context_hashh)))(function|`Blob,h->`Blob(Hash.of_context_hash(Context_hash.of_bytes_exnh))|`Node,h->`Node(Hash.of_context_hash(Context_hash.of_bytes_exnh)))(obj2(req"kind"kind_encoding)(req"value"bytes))endtypecommit_info=Info.ttypebatch=|BatchofStore.repo*[`Read|`Write]P.Contents.t*[`Read|`Write]P.Node.tletbatchindexf=P.Repo.batchindex.repo(funxy_->f(Batch(index.repo,x,y)))letcommit_info_encoding=letopenData_encodinginconv(funirmin_info->letauthor=Info.authorirmin_infoinletmessage=Info.messageirmin_infoinletdate=Info.dateirmin_infoin(author,message,date))(fun(author,message,date)->Info.v~authordate~message)(obj3(req"author"string)(req"message"string)(req"date"int64))lethash_equal(h1:hash)(h2:hash)=h1=h2letcontext_parentsctxt=matchctxtwith|{parents=[commit];_}->letparents=Store.Commit.parentscommitinletparents=List.map(funk->P.Commit.Key.to_hashk|>Hash.to_context_hash)parentsinList.sortContext_hash.compareparents|_->assertfalseletcontext_info=function|{parents=[c];_}->Store.Commit.infoc|_->assertfalseletcheckoutidxh=checkoutidxhletset_context~info~parentsctxtcontext_hash=letopenLwt_syntaxinletparents=List.sortContext_hash.compareparentsinletparents=(* All commit objects in the context are indexed, so it's safe to build a
hash-only key referencing them. *)List.map(funh->Hash.of_context_hashh|>Irmin_pack_unix.Pack_key.v_indexed)parentsinlet+c=Store.Commit.vctxt.index.repo~info~parentsctxt.treeinleth=Store.Commit.hashcinContext_hash.equalcontext_hash(Hash.to_context_hashh)letcontext_treectxt=ctxt.treemoduleSnapshot=structincludeStore.Snapshotletkinded_hash_encoding:kinded_hashData_encoding.t=letopenData_encodinginletkind_encoding=string_enum[("node",`Node);("contents",`Contents)]inconv(function|Contents(h,())->(`Contents,Context_hash.to_bytes(Hash.to_context_hashh))|Nodeh->(`Node,Context_hash.to_bytes(Hash.to_context_hashh)))(function|`Contents,h->leth=Hash.of_context_hash(Context_hash.of_bytes_exnh)inContents(h,())|`Node,h->Node(Hash.of_context_hash(Context_hash.of_bytes_exnh)))(obj2(req"kind"kind_encoding)(req"value"bytes))lethash_encoding:hashData_encoding.t=letopenData_encodinginconv(funh->Context_hash.to_bytes(Hash.to_context_hashh))(funh->Hash.of_context_hash(Context_hash.of_bytes_exnh))bytesletentry_encoding:entryData_encoding.t=letopenData_encodinginconv(fun{step;hash}->(step,hash))(fun(step,hash)->{step;hash})(obj2(req"name"string)(req"hash"kinded_hash_encoding))letinode_tree_encoding:inode_treeData_encoding.t=letopenData_encodinginletpair_encoding=obj2(req"index"uint16)(req"hash"hash_encoding)inconv(fun{depth;length;pointers}->(Int32.of_intdepth,Int32.of_intlength,pointers))(fun(depth,length,pointers)->{depth=Int32.to_intdepth;length=Int32.to_intlength;pointers})(obj3(req"depth"int32)(req"length"int32)(req"pointers"(listpair_encoding)))letv_encoding:vData_encoding.t=letopenData_encodinginletinode_tree_case=case~title:"tree"(Tag(Char.code't'))inode_tree_encoding(functionInode_treet->Somet|_->None)(funt->Inode_treet)inletinode_value_case=case~title:"value"(Tag(Char.code'v'))(listentry_encoding)(functionInode_valuet->Somet|_->None)(funt->Inode_valuet)inData_encoding.union~tag_size:`Uint8[inode_tree_case;inode_value_case]letencoding:inodeData_encoding.t=letopenData_encodinginconv(fun{v;root}->(v,root))(fun(v,root)->{v;root})(obj2(req"v"v_encoding)(req"root"bool))endlettree_iteri_unique?(on_disk=false)indexftree=letroot_key=matchStore.Tree.keytreewithNone->assertfalse|Somekey->keyinleton_disk=ifon_diskthenletpath=Filename.concatindex.path"index_snapshot"inSome(`Pathpath)elseNoneinSnapshot.export?on_diskindex.repof~root_keytypeimport=Snapshot.Import.processletv_import?(in_memory=false)idx=leton_disk=ifin_memorythenNoneelse(* by default the import is using an on-disk index. *)letindex_on_disk=Filename.concatidx.path"index_snapshot"inSome(`Pathindex_on_disk)inSnapshot.Import.v?on_diskidx.repoletsave_inodeidximportsnapshot=letopenLwt_syntaxinlet*key=Snapshot.Import.save_eltimportsnapshotinStore.Tree.of_keyidx.repo(`Nodekey)letclose_importimportindex=Snapshot.Import.closeimportindex.repoletmake_contextindex=emptyindexletupdate_contextcontexttree={contextwithtree}letadd_hash(Batch(repo,_,_))treekeyhash=letopenLwt_syntaxinletirmin_hash=matchhashwith`Blobhash->`Contents(hash,())|`Node_asn->ninlet*o=Store.Tree.of_hashrepoirmin_hashinmatchowith|None->Lwt.return_none|Somet->let+v=Store.Tree.add_treetreekey(t:>tree)inSomevletadd_bytes(Batch(_,t,_))bytes=letopenLwt_syntaxin(* Save the contents in the store *)let+_=Store.save_contentstbytesinStore.Tree.of_contentsbytesletadd_dirbatchl=letopenLwt_result_syntaxinletaddsub_tree(step,hash)=matchsub_treewith|None->Lwt.return_some(Store.Tree.empty())|Somesub_tree->add_hashbatchsub_tree[step]hashinlet*o=Seq_es.S.fold_leftadd(Some(Store.Tree.empty()))linmatchowith|None->return_none|Sometree->let(Batch(repo,x,y))=batchin(* Save the node in the store ... *)let*!_=Store.save_tree~clear:truerepoxytreeinreturn_sometreemoduleCommit_hash=Context_hashmoduleBlock_header=Block_headerend(* Context dumper *)openTezos_context_dumpmoduleContext_dumper=Context_dump.Make(Dumpable_context)letrestore_contextidx~expected_context_hash~nb_context_elements~fd~in_memory~progress_display_mode=Context_dumper.restore_context_fdidx~in_memory~expected_context_hash~fd~nb_context_elements~progress_display_modeend