123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2023 Nomadic Labs, <contact@nomadic-labs.com> *)(* Copyright (c) 2023 Functori, <contact@functori.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. *)(* *)(*****************************************************************************)openGossipsub_intftypetopic={slot_index:int;pkh:Signature.Public_key_hash.t}(* FIXME: https://gitlab.com/tezos/tezos/-/issues/5543
Refine the GS message_id to save bandwidth.
With the defintion below: commitment * level * slot_index * shard_index *
attestor => BW = About 48 + 4 + 2 + 2 + 20 (non bls pkh) = 76 bytes.
However,
1. we could compute the pkh when needed from L1 information instead of
providing it;
2. we could give the payload round instead of the commitment. Together with
the level, it could identify the commitment (except if there is a double
baking);
3. we could also provide the first characters of the commitment.
With 1 and 2, we would get:
BW' = BW - 48 - 20 + 1 (Z.n on small numbers up 127) = 9 bytes
*)typemessage_id={commitment:Cryptobox.Commitment.t;level:int32;slot_index:int;shard_index:int;pkh:Signature.Public_key_hash.t;}typemessage={share:Cryptobox.share;shard_proof:Cryptobox.shard_proof}typepeer=P2p_peer.Id.t(* FIXME: https://gitlab.com/tezos/tezos/-/issues/5607
Bound / add checks for bounds of these encodings *)lettopic_encoding:topicData_encoding.t=letopenData_encodinginconv(fun({slot_index;pkh}:topic)->(slot_index,pkh))(fun(slot_index,pkh)->{slot_index;pkh})(obj2(req"slot_index"uint8)(req"pkh"Signature.Public_key_hash.encoding))letmessage_id_encoding:message_idData_encoding.t=letopenData_encodinginconv(fun{level;slot_index;commitment;shard_index;pkh}->(level,slot_index,commitment,shard_index,pkh))(fun(level,slot_index,commitment,shard_index,pkh)->{level;slot_index;commitment;shard_index;pkh})(obj5(req"level"int32)(req"slot_index"uint8)(req"commitment"Cryptobox.Commitment.encoding)(req"shard_index"uint16)(req"pkh"Signature.Public_key_hash.encoding))letmessage_encoding:messageData_encoding.t=letopenData_encodinginconv(fun{share;shard_proof}->(share,shard_proof))(fun(share,shard_proof)->{share;shard_proof})(obj2(req"share"Cryptobox.share_encoding)(req"shard_proof"Cryptobox.shard_proof_encoding))(* Modules needed to instantiate the Gossipsub worker. *)moduleIterable(Cmp:sigtypetvalcompare:t->t->intend)=structincludeCompare.Make(Cmp)moduleSet=Set.Make(Cmp)moduleMap=Map.Make(Cmp)endmoduleTopic=structtypet=topic={slot_index:int;pkh:Signature.Public_key_hash.t}includeIterable(structtypenonrect=tletcomparetopic{slot_index;pkh}=letc=Int.comparetopic.slot_indexslot_indexinifc<>0thencelseSignature.Public_key_hash.comparetopic.pkhpkhend)letppfmt{pkh;slot_index}=Format.fprintffmt"{ pkh=%a; slot_index=%d }"Signature.Public_key_hash.pppkhslot_indexendmoduleMessage_id=structtypet=message_idincludeIterable(structtypenonrect=tletcompareid{level;slot_index;commitment;shard_index;pkh}=letc=Int32.compareid.levellevelinifc<>0thencelseletc=Int.compareid.shard_indexshard_indexinifc<>0thencelseletc=Cryptobox.Commitment.compareid.commitmentcommitmentinifc<>0thencelseTopic.compare{slot_index=id.slot_index;pkh=id.pkh}{slot_index;pkh}end)letppfmt{level;slot_index;commitment;shard_index;pkh}=Format.fprintffmt"{ level=%ld; shard_index=%d; commitment=%a; topic=%a }"levelshard_indexCryptobox.Commitment.ppcommitmentTopic.pp{slot_index;pkh}letget_topic{slot_index;pkh;_}={slot_index;pkh}endmoduleValidate_message_hook=struct(* FIXME: https://gitlab.com/tezos/tezos/-/issues/5674
Refactor gossipsub integration to avoid this mutable hook in the lib. *)letcheck=ref(fun_msg_msg_id->Format.eprintf"Gs interface: messages validate function is not set@.";`Unknown)letsetfunc=check:=funcendmoduleMessage=structtypet=messageletppfmt{share;shard_proof}=Format.fprintffmt"{ share=%s; shard_proof=%s }"(Data_encoding.Binary.to_string_exnCryptobox.share_encodingshare)(Data_encoding.Binary.to_string_exnCryptobox.shard_proof_encodingshard_proof)letvalidmsgmsg_id=!Validate_message_hook.checkmsgmsg_idendmodulePeer=structtypet=peerincludeIterable(structtypenonrect=tletcomparep1p2=P2p_peer.Id.comparep1p2end)letpp=P2p_peer.Id.ppendletget_value~__LOC__func=Option.value_f~default:(fun()->Stdlib.failwith(Format.sprintf"%s: Unexpected overflow in %s"__LOC__func))moduleSpan:Gossipsub_intf.SPANwithtypet=Ptime.Span.t=structtypet=Ptime.Span.tincludeCompare.Make(Ptime.Span)letzero=Ptime.Span.zeroletto_int_st=Ptime.Span.to_int_st|>get_value~__LOC____FUNCTION__letto_float_st=Ptime.Span.to_float_stletof_int_s=Ptime.Span.of_int_sletof_float_sf=Ptime.Span.of_float_sf|>get_value~__LOC____FUNCTION__letmulspann=to_float_sspan*.floatn|>of_float_sletpp=Ptime.Span.ppendmoduleTime=structtypespan=Span.ttypet=Ptime.tincludeCompare.Make(Ptime)letpp=Ptime.ppletnow=Ptime_clock.nowletaddtspan=Ptime.add_spantspan|>get_value~__LOC____FUNCTION__letsubtspan=Ptime.sub_spantspan|>get_value~__LOC____FUNCTION__letto_span=Ptime.to_spanendmoduleAutomaton_config:AUTOMATON_CONFIGwithtypeTime.t=Ptime.tandmoduleSpan=SpanandtypeSubconfig.Peer.t=peerandtypeSubconfig.Topic.t=topicandtypeSubconfig.Message_id.t=message_idandtypeSubconfig.Message.t=message=structmoduleSpan=SpanmoduleTime=TimemoduleSubconfig=structmodulePeer=PeermoduleTopic=TopicmoduleMessage_id=Message_idmoduleMessage=MessageendendmoduleMonad=structtype'at='aLwt.tlet(let*)=Lwt.bindletreturn=Lwt.returnletsleep(span:Span.t)=Lwt_unix.sleep@@Span.to_float_sspanend(** Instantiate the worker functor *)moduleWorker_config:Gossipsub_intf.WORKER_CONFIGURATIONwithtypeGS.Topic.t=topicandtypeGS.Message_id.t=message_idandtypeGS.Message.t=messageandtypeGS.Peer.t=peerandmoduleGS.Span=SpanandmoduleMonad=Monad=structmoduleGS=Tezos_gossipsub.Make(Automaton_config)moduleMonad=Monad(* TODO: https://gitlab.com/tezos/tezos/-/issues/5596
Use Seq_s instead of Lwt_stream to implement module Stream. *)moduleStream=structtype'at={stream:'aLwt_stream.t;pusher:'aoption->unit}letempty()=letstream,pusher=Lwt_stream.create()in{stream;pusher}letpushet=t.pusher(Somee)letpopt=letopenLwt_syntaxinlet*r=Lwt_stream.gett.streaminmatchrwith|Somer->Lwt.returnr|None->Stdlib.failwith"Invariant: None values are never pushed in the stream"letget_availablet=Lwt_stream.get_availablet.streamendendletspan_encoding:Span.tData_encoding.t=letopenData_encodingin(* We limit the size of a {!Span.t} value to 2 bytes. It is sufficient for the
spans sent via the network by Gossipsub, while avoiding overflows when
adding them to values of type {!Time.t}. *)letspan_size=2incheck_sizespan_size@@conv(funspan->Span.to_int_sspan)(funspan->Span.of_int_sspan)(obj1(req"span"int16))moduleWorker_instance=Tezos_gossipsub.Worker(Worker_config)