123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191open!CoremoduleKnown_protocol=Known_protocolletmax_supported_version=1_000_000letoutside_max_supported_version_rangenum=num>max_supported_versionmoduleBounded_list_in_case_someone_sends_garbage_on_the_wire=List_with_max_len.Make(structletmax_len=100letcontext=Info.of_string"Protocol_version_header"end)typet=intBounded_list_in_case_someone_sends_garbage_on_the_wire.t[@@derivingbin_io,sexp]letknown_protocol_magic_numbers=lazy(Map.key_setKnown_protocol.by_magic_number)letcreate_exn?(additional_magic_numbers=[])~protocol~supported_versions()=letprotocol_magic_number=Known_protocol.magic_numberprotocolinifList.existssupported_versions~f:outside_max_supported_version_rangethenraise_s[%message"Unable to advertise versions larger than max supported version"(max_supported_version:int)(supported_versions:intlist)];ifList.existsadditional_magic_numbers~f:(Fn.nonoutside_max_supported_version_range)thenraise_s[%message"[additional_magic_numbers] shouldn't be within [max_supported_version] range"(max_supported_version:int)(additional_magic_numbers:intlist)];ifList.existsadditional_magic_numbers~f:(Set.mem(forceknown_protocol_magic_numbers))thenraise_s[%message"[additional_magic_numbers] shouldn't be overlapping with potential \
[protocol_magic_number]s"(additional_magic_numbers:intlist)~known_protocol_magic_numbers:(forceknown_protocol_magic_numbers:Int.Set.t)];protocol_magic_number::(additional_magic_numbers@supported_versions)|>Bounded_list_in_case_someone_sends_garbage_on_the_wire.of_list_exn;;letraw_version_list(t:t)=(t:>intlist)letget_protocol(t:t)=letprotocols,versions,_additional_magic_numbers=List.partition3_map(t:>intlist)~f:(funv->matchMap.findKnown_protocol.by_magic_numbervwith|Somep->`Fstp|None->ifoutside_max_supported_version_rangevthen`Trdvelse`Sndv)inmatchprotocolswith|[]->Ok(None,Int.Set.of_listversions)|[p]->Ok(Somep,Int.Set.of_listversions)|_->Or_error.error_s[%message"[Protocol_version_header.negotiate]: multiple magic numbers seen."(protocols:Known_protocol.tlist)(versions:intlist)];;letnegotiate~allow_legacy_peer~(us:t)~(peer:t)=letopenOr_error.Let_syntaxinlet%bindus_protocol,us_versions=get_protocolusinlet%bindpeer_protocol,peer_versions=get_protocolpeerinlet%bindus_protocol=matchus_protocolwith|Somex->returnx|None->error_s[%message"No magic numbers seen"(us_versions:Int.Set.t)]inlet%bindpeer_protocol=matchpeer_protocolwith|Somex->returnx|None->(* we assume peer is speaking our protocol if [allow_legacy_peer] *)ifallow_legacy_peerthenreturnus_protocolelse(letpeer_protocol=`UnknowninOr_error.error_s[%message"[Protocol_version_header.negotiate]: conflicting magic protocol numbers"(us_protocol:Known_protocol.t)(peer_protocol:[`Unknown])])inifnot([%compare.equal:Known_protocol.t]us_protocolpeer_protocol)thenOr_error.error_s[%message"[Protocol_version_header.negotiate]: conflicting magic protocol numbers"(us_protocol:Known_protocol.t)(peer_protocol:Known_protocol.t)]else(letprotocol=us_protocolinmatchSet.max_elt(Set.interus_versionspeer_versions)with|Someversion->Okversion|None->Or_error.error_s[%message"[Protocol_version_header.negotiate]: no shared version numbers"(us_versions:Int.Set.t)(peer_versions:Int.Set.t)(protocol:Known_protocol.t)]);;letmatches_magic_prefix(t:t)~protocol=letmagic_number=Known_protocol.magic_numberprotocolinList.mem~equal:Int.equal(raw_version_listt)magic_number;;letcontains_magic_prefix~protocol=Bin_prot.Type_class.cnv_reader(matches_magic_prefix~protocol)bin_t.reader;;letany_magic_prefix=letft=List.findKnown_protocol.all~f:(funprotocol->matches_magic_prefix~protocolt)inBin_prot.Type_class.cnv_readerfbin_t.reader;;letmagic_number_bin_size=5moduleMagic_prefix_bin_repr=structtypet=int[@@derivingbin_shape,bin_write](* The bin prot representation of a protocol version header is the standard
representation for an int list:
| nat0 indicating size | element 0 | element 1 | ...
[create_exn] will always put the known protocol magic number as "element 0".
The bin size of "nat0 indicating size" and "element 0" is fixed (there are expect
tests to make sure we never change them). *)letbin_size=bin_size_tBounded_list_in_case_someone_sends_garbage_on_the_wire.max_len+magic_number_bin_size;;letbin_read_tbuf~pos_ref=let(_list_length:Bin_prot.Nat0.t)=Bin_prot.Read.bin_read_nat0buf~pos_refinBin_prot.Read.bin_read_intbuf~pos_ref;;letbin_reader_t={Bin_prot.Type_class.read=bin_read_t;vtag_read=Int.__bin_read_t__};;endletany_magic_prefix_from_six_bytes=Bin_prot.Type_class.cnv_reader(funmagic_number->(Map.findKnown_protocol.by_magic_number)magic_number)Magic_prefix_bin_repr.bin_reader_t;;letany_magic_prefix_from_six_bytes_bin_size=Magic_prefix_bin_repr.bin_sizemoduleExpert=structletraw_version_list=raw_version_listendmoduleFor_test=structmoduleMake_list_with_max_len=List_with_max_len.Makeletmagic_number_bin_size=magic_number_bin_sizeletmax_supported_version=max_supported_versionendlet%test_unit"bin sizes are not changed by accident"=(* Ensure the bin_size of Bounded_list_in_case_someone_sends_garbage_on_the_wire.max_len
is always 1. This means that regardless of how long the list of versions in the
protocol header is, the representation will be the same number of bytes. The
representation of an int jumps to 3 bytes at the value 128. *)letbounded_list_bin_size=Int.bin_size_tBounded_list_in_case_someone_sends_garbage_on_the_wire.max_leninassert(bounded_list_bin_size=1);assert(6=magic_number_bin_size+bounded_list_bin_size);assert(any_magic_prefix_from_six_bytes_bin_size=magic_number_bin_size+bounded_list_bin_size);;