https://gitlab.com/tezos/tezos
Tip revision: ce7d752cc8d1fb72a99a96158c2a1ba4e3a8ed13 authored by Thomas Letan on 29 May 2023, 14:13:29 UTC
Teztible: Write experiments and scenarios in Yaml scripts
Teztible: Write experiments and scenarios in Yaml scripts
Tip revision: ce7d752
seed_repr.ml
(*****************************************************************************)
(* *)
(* 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 *)
type seed = B of State_hash.t
type t = T of State_hash.t
type sequence = S of State_hash.t
type nonce = bytes
type vdf_setup = Vdf.discriminant * Vdf.challenge
type vdf_solution = Vdf.result * Vdf.proof
let seed_to_bytes x =
let seed_to_state_hash (B b) = b in
State_hash.to_bytes (seed_to_state_hash x)
let vdf_setup_encoding =
let open Data_encoding in
let vdf_discriminant_encoding =
conv_with_guard
Vdf.discriminant_to_bytes
(fun b ->
Option.to_result
~none:"VDF discriminant could not be deserialised"
(Vdf.discriminant_of_bytes_opt b))
(Fixed.bytes Vdf.discriminant_size_bytes)
in
let vdf_challenge_encoding =
conv_with_guard
Vdf.challenge_to_bytes
(fun b ->
Option.to_result
~none:"VDF challenge could not be deserialised"
(Vdf.challenge_of_bytes_opt b))
(Fixed.bytes Vdf.form_size_bytes)
in
tup2 vdf_discriminant_encoding vdf_challenge_encoding
let vdf_solution_encoding =
let open Data_encoding in
let vdf_result_encoding =
conv_with_guard
Vdf.result_to_bytes
(fun b ->
Option.to_result
~none:"VDF result could not be deserialised"
(Vdf.result_of_bytes_opt b))
(Fixed.bytes Vdf.form_size_bytes)
in
let vdf_proof_encoding =
conv_with_guard
Vdf.proof_to_bytes
(fun b ->
Option.to_result
~none:"VDF proof could not be deserialised"
(Vdf.proof_of_bytes_opt b))
(Fixed.bytes Vdf.form_size_bytes)
in
tup2 vdf_result_encoding vdf_proof_encoding
let pp_solution ppf solution =
let result, proof = solution in
Format.fprintf
ppf
"@[<v 2>VDF result: %a"
Hex.pp
(Hex.of_bytes (Vdf.result_to_bytes result)) ;
Format.fprintf
ppf
"@,VDF proof: %a"
Hex.pp
(Hex.of_bytes (Vdf.proof_to_bytes proof)) ;
Format.fprintf ppf "@]"
let nonce_encoding = Data_encoding.Fixed.bytes Constants_repr.nonce_length
let zero_bytes = Bytes.make Nonce_hash.size '\000'
let state_hash_encoding =
let open Data_encoding in
conv State_hash.to_bytes State_hash.of_bytes_exn (Fixed.bytes Nonce_hash.size)
let seed_encoding =
let open Data_encoding in
conv (fun (B b) -> b) (fun b -> B b) state_hash_encoding
let update_seed (B state) nonce =
B (State_hash.hash_bytes [State_hash.to_bytes state; nonce])
let initialize_new (B state) append =
T (State_hash.hash_bytes (State_hash.to_bytes state :: zero_bytes :: append))
let xor_higher_bits i b =
let higher = TzEndian.get_int32 b 0 in
let r = Int32.logxor higher i in
let res = Bytes.copy b in
TzEndian.set_int32 res 0 r ;
res
let sequence (T state) n =
State_hash.to_bytes state |> xor_higher_bits n |> fun b ->
S (State_hash.hash_bytes [b])
let take (S state) =
let b = State_hash.to_bytes state in
let h = State_hash.hash_bytes [b] in
(State_hash.to_bytes h, S h)
let take_int32 s bound =
if Compare.Int32.(bound <= 0l) then invalid_arg "Seed_repr.take_int32"
(* FIXME *)
else
let drop_if_over =
Int32.sub Int32.max_int (Int32.rem Int32.max_int bound)
in
let rec loop s =
let bytes, s = take s in
let r = TzEndian.get_int32 bytes 0 in
(* 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. *)
let r = if Compare.Int32.(r = Int32.min_int) then 0l else Int32.abs r in
if Compare.Int32.(r >= drop_if_over) then loop s
else
let v = Int32.rem r bound in
(v, s)
in
loop s
let take_int64 s bound =
if Compare.Int64.(bound <= 0L) then invalid_arg "Seed_repr.take_int64"
(* FIXME *)
else
let drop_if_over =
Int64.sub Int64.max_int (Int64.rem Int64.max_int bound)
in
let rec loop s =
let bytes, s = take s in
let r = TzEndian.get_int64 bytes 0 in
(* 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. *)
let r = if Compare.Int64.(r = Int64.min_int) then 0L else Int64.abs r in
if Compare.Int64.(r >= drop_if_over) then loop s
else
let v = Int64.rem r bound in
(v, s)
in
loop s
type error += Unexpected_nonce_length (* `Permanent *)
let () =
register_error_kind
`Permanent
~id:"unexpected_nonce_length"
~title:"Unexpected nonce length"
~description:"Nonce length is incorrect."
~pp:(fun ppf () ->
Format.fprintf
ppf
"Nonce length is not %i bytes long as it should."
Constants_repr.nonce_length)
Data_encoding.empty
(function Unexpected_nonce_length -> Some () | _ -> None)
(fun () -> Unexpected_nonce_length)
let make_nonce nonce =
if Compare.Int.(Bytes.length nonce <> Constants_repr.nonce_length) then
error Unexpected_nonce_length
else ok nonce
let hash nonce = Nonce_hash.hash_bytes [nonce]
let check_hash nonce hash =
Compare.Int.(Bytes.length nonce = Constants_repr.nonce_length)
&& Nonce_hash.equal (Nonce_hash.hash_bytes [nonce]) hash
let nonce_hash_key_part = Nonce_hash.to_path
let initial_nonce_0 = zero_bytes
let initial_nonce_hash_0 = hash initial_nonce_0
let deterministic_seed seed = update_seed seed zero_bytes
let initial_seeds ?initial_seed n =
let rec loop acc elt i =
if Compare.Int.(i = 1) then List.rev (elt :: acc)
else loop (elt :: acc) (deterministic_seed elt) (i - 1)
in
let first_seed =
match initial_seed with
| Some initial_seed -> update_seed (B initial_seed) initial_nonce_0
| None -> B (State_hash.hash_bytes [])
in
loop [] first_seed n
let nonce_discriminant = Bytes.of_string "Tezos_generating_vdf_discriminant"
let nonce_challenge = Bytes.of_string "Tezos_generating_vdf_challenge"
let generate_vdf_setup ~seed_discriminant ~seed_challenge =
let size = Vdf.discriminant_size_bytes in
let seed =
update_seed seed_discriminant nonce_discriminant |> seed_to_bytes
in
let discriminant = Vdf.generate_discriminant ~seed size in
let input = update_seed seed_challenge nonce_challenge |> seed_to_bytes in
let challenge = Vdf.generate_challenge discriminant input in
(discriminant, challenge)
let verify (discriminant, challenge) vdf_difficulty solution =
(* We return false when getting non group elements as input *)
let result, proof = solution in
(* Note: external library call must be wrapped to ensure that
exceptions are caught. *)
Option.catch (fun () ->
Vdf.verify discriminant challenge vdf_difficulty result proof)
let vdf_to_seed seed_challenge solution =
let result, _ = solution in
update_seed seed_challenge (Vdf.result_to_bytes result)
type seed_status = RANDAO_seed | VDF_seed
let seed_status_encoding =
let to_bool = function RANDAO_seed -> false | VDF_seed -> true in
let of_bool t = if t then VDF_seed else RANDAO_seed in
Data_encoding.conv to_bool of_bool Data_encoding.bool
let compare_vdf_solution solution solution' =
let result, _ = solution in
let result', _ = solution' in
Compare.Bytes.compare
(Vdf.result_to_bytes result)
(Vdf.result_to_bytes result')