123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.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. *)(* *)(*****************************************************************************)(* Tezos Protocol Implementation - Random number generation *)typeseed=BofState_hash.ttypet=TofState_hash.ttypesequence=SofState_hash.ttypenonce=bytestypevdf_setup=Vdf.discriminant*Vdf.challengetypevdf_solution=Vdf.result*Vdf.proofletseed_to_bytesx=letseed_to_state_hash(Bb)=binState_hash.to_bytes(seed_to_state_hashx)letvdf_setup_encoding=letopenData_encodinginletvdf_discriminant_encoding=conv_with_guardVdf.discriminant_to_bytes(funb->Option.to_result~none:"VDF discriminant could not be deserialised"(Vdf.discriminant_of_bytes_optb))(Fixed.(bytesHex)Vdf.discriminant_size_bytes)inletvdf_challenge_encoding=conv_with_guardVdf.challenge_to_bytes(funb->Option.to_result~none:"VDF challenge could not be deserialised"(Vdf.challenge_of_bytes_optb))(Fixed.(bytesHex)Vdf.form_size_bytes)intup2vdf_discriminant_encodingvdf_challenge_encodingletvdf_solution_encoding=letopenData_encodinginletvdf_result_encoding=conv_with_guardVdf.result_to_bytes(funb->Option.to_result~none:"VDF result could not be deserialised"(Vdf.result_of_bytes_optb))(Fixed.(bytesHex)Vdf.form_size_bytes)inletvdf_proof_encoding=conv_with_guardVdf.proof_to_bytes(funb->Option.to_result~none:"VDF proof could not be deserialised"(Vdf.proof_of_bytes_optb))(Fixed.(bytesHex)Vdf.form_size_bytes)intup2vdf_result_encodingvdf_proof_encodingletpp_solutionppfsolution=letresult,proof=solutioninFormat.fprintfppf"@[<v 2>VDF result: %a"Hex.pp(Hex.of_bytes(Vdf.result_to_bytesresult));Format.fprintfppf"@,VDF proof: %a"Hex.pp(Hex.of_bytes(Vdf.proof_to_bytesproof));Format.fprintfppf"@]"letnonce_encoding=Data_encoding.Fixed.(bytesHex)Constants_repr.nonce_lengthletzero_bytes=Bytes.makeNonce_hash.size'\000'letstate_hash_encoding=letopenData_encodinginconvState_hash.to_bytesState_hash.of_bytes_exn(Fixed.(bytesHex)Nonce_hash.size)letseed_encoding=letopenData_encodinginconv(fun(Bb)->b)(funb->Bb)state_hash_encodingletupdate_seed(Bstate)nonce=B(State_hash.hash_bytes[State_hash.to_bytesstate;nonce])letinitialize_new(Bstate)append=T(State_hash.hash_bytes(State_hash.to_bytesstate::zero_bytes::append))letxor_higher_bitsib=lethigher=TzEndian.get_int32b0inletr=Int32.logxorhigheriinletres=Bytes.copybinTzEndian.set_int32res0r;resletsequence(Tstate)n=State_hash.to_bytesstate|>xor_higher_bitsn|>funb->S(State_hash.hash_bytes[b])lettake(Sstate)=letb=State_hash.to_bytesstateinleth=State_hash.hash_bytes[b]in(State_hash.to_bytesh,Sh)lettake_int32sbound=ifCompare.Int32.(bound<=0l)theninvalid_arg"Seed_repr.take_int32"(* FIXME *)elseletdrop_if_over=Int32.subInt32.max_int(Int32.remInt32.max_intbound)inletrecloops=letbytes,s=takesinletr=TzEndian.get_int32bytes0in(* The absolute value of min_int is min_int. Also, every
positive integer is represented twice (positive and negative),
but zero is only represented once. We fix both problems at
once. *)letr=ifCompare.Int32.(r=Int32.min_int)then0lelseInt32.absrinifCompare.Int32.(r>=drop_if_over)thenloopselseletv=Int32.remrboundin(v,s)inloopslettake_int64sbound=ifCompare.Int64.(bound<=0L)theninvalid_arg"Seed_repr.take_int64"(* FIXME *)elseletdrop_if_over=Int64.subInt64.max_int(Int64.remInt64.max_intbound)inletrecloops=letbytes,s=takesinletr=TzEndian.get_int64bytes0in(* The absolute value of min_int is min_int. Also, every
positive integer is represented twice (positive and negative),
but zero is only represented once. We fix both problems at
once. *)letr=ifCompare.Int64.(r=Int64.min_int)then0LelseInt64.absrinifCompare.Int64.(r>=drop_if_over)thenloopselseletv=Int64.remrboundin(v,s)inloopstypeerror+=Unexpected_nonce_length(* `Permanent *)let()=register_error_kind`Permanent~id:"unexpected_nonce_length"~title:"Unexpected nonce length"~description:"Nonce length is incorrect."~pp:(funppf()->Format.fprintfppf"Nonce length is not %i bytes long as it should."Constants_repr.nonce_length)Data_encoding.empty(functionUnexpected_nonce_length->Some()|_->None)(fun()->Unexpected_nonce_length)letmake_noncenonce=ifCompare.Int.(Bytes.lengthnonce<>Constants_repr.nonce_length)thenerrorUnexpected_nonce_lengthelseoknoncelethashnonce=Nonce_hash.hash_bytes[nonce]letcheck_hashnoncehash=Compare.Int.(Bytes.lengthnonce=Constants_repr.nonce_length)&&Nonce_hash.equal(Nonce_hash.hash_bytes[nonce])hashletnonce_hash_key_part=Nonce_hash.to_pathletinitial_nonce_0=zero_bytesletinitial_nonce_hash_0=hashinitial_nonce_0letdeterministic_seedseed=update_seedseedzero_bytesletinitial_seeds?initial_seedn=letrecloopaccelti=ifCompare.Int.(i=1)thenList.rev(elt::acc)elseloop(elt::acc)(deterministic_seedelt)(i-1)inletfirst_seed=matchinitial_seedwith|Someinitial_seed->update_seed(Binitial_seed)initial_nonce_0|None->B(State_hash.hash_bytes[])inloop[]first_seednletnonce_discriminant=Bytes.of_string"Tezos_generating_vdf_discriminant"letnonce_challenge=Bytes.of_string"Tezos_generating_vdf_challenge"letgenerate_vdf_setup~seed_discriminant~seed_challenge=letsize=Vdf.discriminant_size_bytesinletseed=update_seedseed_discriminantnonce_discriminant|>seed_to_bytesinletdiscriminant=Vdf.generate_discriminant~seedsizeinletinput=update_seedseed_challengenonce_challenge|>seed_to_bytesinletchallenge=Vdf.generate_challengediscriminantinputin(discriminant,challenge)letverify(discriminant,challenge)vdf_difficultysolution=(* We return false when getting non group elements as input *)letresult,proof=solutionin(* Note: external library call must be wrapped to ensure that
exceptions are caught. *)Option.catch(fun()->Vdf.verifydiscriminantchallengevdf_difficultyresultproof)letvdf_to_seedseed_challengesolution=letresult,_=solutioninupdate_seedseed_challenge(Vdf.result_to_bytesresult)typeseed_status=RANDAO_seed|VDF_seedletseed_status_encoding=letto_bool=functionRANDAO_seed->false|VDF_seed->trueinletof_boolt=iftthenVDF_seedelseRANDAO_seedinData_encoding.convto_boolof_boolData_encoding.boolletcompare_vdf_solutionsolutionsolution'=letresult,_=solutioninletresult',_=solution'inCompare.Bytes.compare(Vdf.result_to_bytesresult)(Vdf.result_to_bytesresult')