123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281(*****************************************************************************)(* *)(* 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. *)(* *)(*****************************************************************************)openProtocolopenAlpha_contextletwrap=Environment.wrap_tzresultmodulePlugin=structmoduleProto=Registerer.Registeredtypeblock_info=Protocol_client_context.Alpha_block_services.block_infoletparametric_constantschainblockctxt=letcpctxt=newProtocol_client_context.wrap_rpc_contextctxtinProtocol.Constants_services.parametriccpctxt(chain,block)letget_constantschainblockctxt=letopenLwt_result_syntaxinlet*parametric=parametric_constantschainblockctxtinlet{Constants.Parametric.feature_enable;incentives_enable;number_of_slots;attestation_lag;attestation_threshold;cryptobox_parameters;}=parametric.dalinreturn{Dal_plugin.feature_enable;incentives_enable;number_of_slots;attestation_lag;attestation_threshold;cryptobox_parameters;sc_rollup_challenge_window_in_blocks=parametric.sc_rollup.challenge_window_in_blocks;commitment_period_in_blocks=parametric.sc_rollup.commitment_period_in_blocks;dal_attested_slots_validity_lag=parametric.sc_rollup.reveal_activation_level.dal_attested_slots_validity_lag;}letblock_info?chain?block~metadatactxt=letcpctxt=newProtocol_client_context.wrap_rpc_contextctxtinProtocol_client_context.Alpha_block_services.infocpctxt?chain?block~metadata()letblock_shell_header(block_info:block_info)=block_info.header.shellletget_roundfitness=letopenResult_syntaxinlet*round=Fitness.round_from_rawfitness|>wrapinreturn@@Round.to_int32round(* Turn the given value of type {!Protocol.Apply_operation_result.operation_result}
into a value of type {!Dal_plugin.operation_application_result}. *)letstatus_of_result=function|Protocol.Apply_operation_result.Applied_->Dal_plugin.Succeeded|_->Dal_plugin.Failedletget_published_slot_headers(block:block_info)=letopenLwt_result_syntaxinletopenProtocol.Alpha_contextinletapply_internalacc~source:__op_res=accinletapply(typekind)acc~source:_(op:kindmanager_operation)(result:(kind,_,_)Protocol.Apply_operation_result.operation_result)=matchopwith|Dal_publish_commitmentoperation->(operation.slot_index,operation.commitment,status_of_resultresult)::acc|_->accinLayer1_services.(process_manager_operations[]block.operations{apply;apply_internal})|>List.map_es(fun(slot_index,commitment,status)->letpublished_level=block.header.shell.levelinletslot_index=Dal.Slot_index.to_intslot_indexinreturnDal_plugin.({published_level;slot_index;commitment},status))letget_committeectxt~level=letopenLwt_result_syntaxinletcpctxt=newProtocol_client_context.wrap_rpc_contextctxtinlet*?level=Raw_level.of_int32level|>wrapinlet+pkh_to_shards=Plugin.RPC.Dal.dal_shardscpctxt(`Main,`Head0)~level()inList.fold_left(funacc({delegate;indexes}:Plugin.RPC.Dal.S.shards_assignment)->Signature.Public_key_hash.Map.adddelegateindexesacc)Signature.Public_key_hash.Map.emptypkh_to_shardsletattested_slot_headers(block:block_info)~number_of_slots=letopenResult_syntaxinlet*metadata=Option.to_resultblock.metadata~none:(TzTrace.make@@Layer1_services.Cannot_read_block_metadatablock.hash)inletconfirmed_slots=metadata.protocol_data.dal_attestationinlet*all_slots=Dal.Slot_index.slots_range~number_of_slots~lower:0~upper:(number_of_slots-1)|>wrapinList.filter(Dal.Attestation.is_attestedconfirmed_slots)all_slots|>Dal.Slot_index.to_int_list|>return(* Section of helpers for Skip lists *)moduleSkip_list=structtypecell=Dal.Slots_history.ttypehash=Dal.Slots_history.Pointer_hash.tletcell_encoding=Dal.Slots_history.encodinglethash_encoding=Dal.Slots_history.Pointer_hash.encodingletcell_equal=Dal.Slots_history.equallethash_equal=Dal.Slots_history.Pointer_hash.equalletcell_hash=Dal.Slots_history.hash(*
This function mimics what the protocol does in
{!Dal_slot_storage.finalize_pending_slot_headers}. Given a block_info and
an RPC context, this function computes the cells produced by the DAL skip
list during the level L of block_info using:
- The information telling which slot headers were waiting for attestation
at level [L - attestation_lag];
- The bitset of attested slots at level [L] in the block's metadata.
The ordering of the elements in the returned list is not relevant.
*)letcells_of_level(block_info:block_info)ctxt=letopenLwt_result_syntaxin(* 0. Let's call [level] the block's level. *)letlevel=block_info.header.shell.levelinlet*dal_constants=get_constants`Main(`Levellevel)ctxtinletpublished_level=Int32.sublevel(Int32.of_intdal_constants.attestation_lag)in(* 1. Before it's possible to attest the slots at the first published
levels, the list of cells is empty. *)ifpublished_level<=1lthenreturn[]elselet*publication_level_dal_constants=(* published_level - 1 is positive. *)get_constants`Main(`Level(Int32.predpublished_level))ctxtin(* Is DAL activated at at published_level - 1 *)ifnotpublication_level_dal_constants.feature_enablethenreturn[]elseletcpctxt=newProtocol_client_context.wrap_rpc_contextctxtin(* 2. We retrieve the slot headers published at level [level -
attestation_lag] from the context. *)let*published_slot_headers=Plugin.RPC.Dal.dal_published_slot_headerscpctxt(`Main,`Levelpublished_level)()inlet*?published_level=Raw_level.of_int32published_level|>Environment.wrap_tzresultin(* 3. We retrieve the last cell of the DAL skip list from the context,
if any. It's the one stored in the context at [level - 1]. If no cell
is stored yet, we return the genesis cell. *)let*previous_cell=let*previous_cell_opt=(* Should not be negative as attestation_lag > 0. *)letprev_level=Int32.predlevelinPlugin.RPC.Dal.dal_commitments_historycpctxt(`Main,`Levelprev_level)inreturn@@Option.valueprevious_cell_opt~default:Dal.Slots_history.genesisin(* 4. We retrieve the bitset of attested slots at level [level]. *)let*attested_slots=let*?metadata=Option.to_resultblock_info.metadata~none:(TzTrace.make@@Layer1_services.Cannot_read_block_metadatablock_info.hash)inreturnmetadata.protocol_data.dal_attestationinletis_slot_attestedslot=Dal.Attestation.is_attestedattested_slotsslot.Dal.Slot.Header.id.indexin(* 5. We filter the list of slot headers published at [level -
attestation_lag] and keep only those attested at level [level]. *)letattested_slot_headers,_attested_slots_bitset=Dal.Slot.compute_attested_slot_headers~is_slot_attestedpublished_slot_headersin(* 6. Starting from the [previous_cell], we insert the successive cells
of level [level] in the skip list thanks to function
{!add_confirmed_slot_headers}. The function is fed with an empty
history cache, so the returned [cache] contains exactly the cells
produced for this [level]. *)let*?_last_cell,cache=letcapacity=Int64.of_int@@maxpublication_level_dal_constants.number_of_slotsdal_constants.number_of_slotsinDal.Slots_history.add_confirmed_slot_headersprevious_cell(Dal.Slots_history.History_cache.empty~capacity)published_level(* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/3997
Not resilient to DAL parameters change. *)~number_of_slots:dal_constants.number_of_slotsattested_slot_headers|>Environment.wrap_tzresultin(* 7. We finally export and return the cells alongside their hashes as a
list. *)letlast_cells=letopenDal.Slots_history.History_cacheinviewcache|>Map.bindingsinreturnlast_cellsendmoduleRPC=structletdirectoryskip_list_cells_store=RPC_directory.directoryskip_list_cells_storeendendlet()=Dal_plugin.register(modulePlugin)