123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2022 Trili Tech <contact@trili.tech> *)(* *)(* 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_contextopenProtocol_client_contexttypeerror+=|Slot_size_is_too_bigof{actual_size:int;max_size:int}|Wrong_slot_frame_versionof{expected:int;provided:int}|Could_not_deserialize_slotlet()=register_error_kind`Permanent~id:"slot_size_is_too_big"~title:"Slot size is too big"~description:"Slot cannot fit in maximum size"~pp:(funppf(actual_size,max_size)->Format.fprintfppf"Actual size: %d, Maximum size: %d"actual_sizemax_size)Data_encoding.(obj2(req"actual_size"int31)(req"maximum_size"int31))(function|Slot_size_is_too_big{actual_size;max_size}->Some(actual_size,max_size)|_->None)(fun(actual_size,max_size)->Slot_size_is_too_big{actual_size;max_size});register_error_kind`Permanent~id:"wrong_slot_frame_version"~title:"Wrong slot frame version"~description:"Wrong slot frame version"~pp:(funppf(expected,provided)->Format.fprintfppf"Version expected: %d, Version provided: %d"expectedprovided)Data_encoding.(obj2(req"version_expected"uint8)(req"version_provided"uint8))(function|Wrong_slot_frame_version{expected;provided}->Some(expected,provided)|_->None)(fun(expected,provided)->Wrong_slot_frame_version{expected;provided});register_error_kind`Permanent~id:"could_not_deserialize_slot"~title:"Slot could not be deserialized"~description:"Error when recovering slot contents from binary"~pp:(funppf()->Format.fprintfppf"Error when recovering slot contents from binary")Data_encoding.(unit)(functionCould_not_deserialize_slot->Some()|_->None)(fun()->Could_not_deserialize_slot)typeversion=inttypemessage=stringmoduleRollups_map=Map.Make(Sc_rollup.Address)typet=messagelistRollups_map.tmoduletypeSlot_version=sigvalversion_prefix:versionvalexpected_slot_size:t->intvalserialize:max_size:int->t->stringtzresultLwt.tvaldeserialize:max_size:int->string->ttzresultLwt.tendletversion_encoding=Data_encoding.uint8moduleV0=structletversion_prefix=0(* Binary representation of string uses 4 bytes as a header,
containing the string length. This conforms to the specification
given for messages. *)letmessage_encoding=Data_encoding.string(* Binary representation of lists uses 4 bytes as a header, containing
the length of the list in bytes. This conforms to the
specification given for messages frames. *)letmessages_frame_encoding=Data_encoding.(listmessage_encoding)(* Binary representation of lists uses 4 bytes as a header, containing
the size of the encoded list contents in bytes.
`all_messages_frame_encoding` encodes not only the encoded messages
frames, but also the 4 bytes containing the length of all messages
frames that separate the rollups frame from the messages frame.
*)letall_messages_frames_encoding=Data_encoding.(listmessages_frame_encoding)(* Binary representation of a [Sc_rollup.Address.t] uses 20 bytes,
while the encoding of a [int32] uses 4 bytes. These are
concatenated together by `rollup_offset_entry_encoding`. *)letrollup_offset_entry_encoding=Data_encoding.(tup2Sc_rollup.Address.encodingint32)(* Binary representation of lists uses 4 bytes as a header, containing
the size of the encoded list contents in bytes.
the size in bytes of the rollups frame. containing
the length of the list in bytes. Thus the rollups_frame_encoding
conforms to the specification given*)letrollups_frame_encoding=Data_encoding.(listrollup_offset_entry_encoding)letslot_encoding~max_size=Data_encoding.(check_sizemax_size@@tup3version_encodingrollups_frame_encodingall_messages_frames_encoding)moduleInternal=structletsize_of_optsize=matchsizewithNone->assertfalse|Somen->nletmessage_size=Data_encoding.Binary.lengthmessage_encodingletmessages_frame_size=Data_encoding.Binary.lengthmessages_frame_encodingletall_messages_frames_size=Data_encoding.Binary.lengthall_messages_frames_encodingletframe_prefix_size=size_of_optData_encoding.(Binary.fixed_lengthint32)letframe_version_size=size_of_optData_encoding.(Binary.fixed_lengthuint8)letrollup_entry_size=size_of_opt@@Data_encoding.Binary.fixed_lengthrollup_offset_entry_encodingletrollups_frame_sizenumber_of_rollups=(rollup_entry_size*number_of_rollups)+frame_prefix_sizeendletexpected_slot_sizeall_rollups_messages=letbindings=Rollups_map.bindingsall_rollups_messagesinletnumber_of_rollups=bindings|>List.lengthinletmessages=List.mapsndbindingsinInternal.(frame_version_size+rollups_frame_sizenumber_of_rollups+all_messages_frames_sizemessages)letserialize~max_sizeall_rollups_messages=letopenLwt_result_syntaxinletfirst_messages_frame_offset=Internal.(frame_version_size+rollups_frame_size(Rollups_map.bindingsall_rollups_messages|>List.length)+frame_prefix_size)inletrev_rollups_frame,rev_messages_frames,expected_slot_size=Rollups_map.fold(funrollupmessages(rollups_frame,messages_frames,next_offset)->letrollups_frame=(rollup,Int32.of_intnext_offset)::rollups_frameinletmessages_frames=messages::messages_framesinletnext_offset=next_offset+Internal.messages_frame_sizemessagesin(rollups_frame,messages_frames,next_offset))all_rollups_messages([],[],first_messages_frame_offset)inlet*()=fail_unless(expected_slot_size<=max_size)(Slot_size_is_too_big{actual_size=expected_slot_size;max_size})inletrollups_frame=List.revrev_rollups_frameinletmessages_frames=List.revrev_messages_framesinlet*?result=Data_encoding.Binary.to_string(slot_encoding~max_size)(version_prefix,rollups_frame,messages_frames)|>Result.map_error(fun_->assertfalse)inreturnresult(* Deserialization of slot contents will be done by WASM PVM kernels, as
per #3374. However, we should still provide a function to
deserialize the contents of a slot, as other components other than the
PVM kernel may need to inspect it. *)letdeserialize~max_sizeserialized=letopenLwt_result_syntaxinletactual_size=String.lengthserializedinlet*()=fail_unless(actual_size<=max_size)(Slot_size_is_too_big{actual_size;max_size})inletversion_prefix,rollups_frame,messages_frames=Data_encoding.Binary.of_string_exn(slot_encoding~max_size)serializedinlet*()=fail_when(version_prefix<>0)(Wrong_slot_frame_version{expected=0;provided=version_prefix})inlet*()=fail_when(List.compare_length_withmessages_frames@@List.lengthrollups_frame<>0)Could_not_deserialize_slotinlet*?deserialized=List.map2~when_different_lengths:[](fun(rollup,_offset)messages->(rollup,messages))rollups_framemessages_frames|>Result.map_error(fun_->[Could_not_deserialize_slot])|>Result.map(funrollups_with_messages->Rollups_map.add_seq(List.to_seqrollups_with_messages)Rollups_map.empty)inreturndeserializedend