1use ark_ec::short_weierstrass_jacobian::GroupAffine;
2use ark_ff::{BigInteger, BigInteger256, PrimeField};
3use ledger::{proofs::transaction::field_to_bits, AppendToInputs, ToInputs};
4use mina_p2p_messages::v2::ConsensusVrfOutputTruncatedStableV1;
5use num::{BigInt, BigRational, One, ToPrimitive};
6use o1_utils::FieldHelpers;
7use poseidon::hash::params::MINA_VRF_OUTPUT;
8use serde::{Deserialize, Serialize};
9use sha2::{Digest, Sha256};
10
11use crate::{BaseField, BigInt2048, ScalarField};
12
13use super::serialize::{ark_deserialize, ark_serialize};
14
15use super::{message::VrfMessage, CurvePoint};
16
17#[derive(Clone, Debug)]
18pub struct VrfOutputHashInput {
19 message: VrfMessage,
20 g: CurvePoint,
21}
22
23impl VrfOutputHashInput {
24 pub fn new(message: VrfMessage, g: CurvePoint) -> Self {
25 Self { message, g }
26 }
27}
28
29impl ToInputs for VrfOutputHashInput {
30 fn to_inputs(&self, inputs: &mut poseidon::hash::Inputs) {
31 let Self {
32 message,
33 g: GroupAffine { x, y, .. },
34 } = self;
35
36 inputs.append(message);
37 inputs.append(x);
38 inputs.append(y);
39 }
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
43pub struct VrfOutput {
44 message: VrfMessage,
45 #[serde(serialize_with = "ark_serialize", deserialize_with = "ark_deserialize")]
46 output: CurvePoint,
47}
48
49impl VrfOutput {
50 pub fn new(message: VrfMessage, output: CurvePoint) -> Self {
51 Self { message, output }
52 }
53
54 pub fn raw(&self) -> CurvePoint {
55 self.output
56 }
57
58 pub fn hash(&self) -> BaseField {
59 let hash_input = VrfOutputHashInput::new(self.message.clone(), self.output);
60 hash_input.hash_with_param(&MINA_VRF_OUTPUT)
61 }
62
63 pub fn truncated(&self) -> ScalarField {
64 let hash = self.hash();
65 let bits = field_to_bits::<_, 256>(hash);
66
67 let repr = BigInteger256::from_bits_le(&bits[..bits.len() - 3]);
68 ScalarField::from_repr(repr).unwrap()
69 }
70
71 pub fn truncated_with_prefix_and_checksum(&self) -> Vec<u8> {
72 let mut output_bytes = Vec::new();
73 let prefix = vec![0x15, 0x20];
74
75 output_bytes.extend(prefix);
76
77 output_bytes.extend(self.truncated().to_bytes());
78
79 let checksum_hash = Sha256::digest(&Sha256::digest(&output_bytes[..])[..]);
81 output_bytes.extend(&checksum_hash[..4]);
82
83 output_bytes
84 }
85
86 pub fn fractional(&self) -> f64 {
87 let two_tpo_256 = BigInt::one() << 253u32;
91
92 let vrf_out: BigInt2048 = BigInt2048::from_bytes_be(
93 num::bigint::Sign::Plus,
94 &self.truncated().into_repr().to_bytes_be(),
95 );
96
97 BigRational::new(vrf_out, two_tpo_256).to_f64().unwrap()
98 }
99
100 pub fn to_base_58(&self) -> String {
101 let bytes = self.truncated_with_prefix_and_checksum();
102 bs58::encode(bytes).into_string()
103 }
104}
105
106impl std::fmt::Display for VrfOutput {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 let encoded = self.to_base_58();
109 write!(f, "{encoded}")
110 }
111}
112
113impl From<&VrfOutput> for ConsensusVrfOutputTruncatedStableV1 {
114 fn from(value: &VrfOutput) -> Self {
115 let bytes = value.truncated().to_bytes();
116 Self(bytes.into())
117 }
118}
119
120impl From<VrfOutput> for ConsensusVrfOutputTruncatedStableV1 {
121 fn from(value: VrfOutput) -> Self {
122 Self::from(&value)
123 }
124}
125
126#[cfg(test)]
127mod test {
128 use mina_p2p_messages::v2::ConsensusVrfOutputTruncatedStableV1;
129
130 use mina_p2p_messages::{
131 bigint::BigInt as MinaBigInt,
132 v2::{EpochSeed, MinaBaseEpochSeedStableV1},
133 };
134
135 use crate::{genesis_vrf, output::VrfOutput};
136
137 #[test]
138 fn test_serialization() {
139 let vrf_output = genesis_vrf(EpochSeed::from(MinaBaseEpochSeedStableV1(
140 MinaBigInt::zero(),
141 )))
142 .unwrap();
143
144 let serialized = serde_json::to_string(&vrf_output).unwrap();
145 let deserialized: VrfOutput = serde_json::from_str(&serialized).unwrap();
146
147 assert_eq!(vrf_output, deserialized);
148 }
149
150 #[test]
151 fn test_conv_to_mina_type() {
152 let vrf_output = genesis_vrf(EpochSeed::from(MinaBaseEpochSeedStableV1(
153 MinaBigInt::zero(),
154 )))
155 .unwrap();
156
157 let converted = ConsensusVrfOutputTruncatedStableV1::from(vrf_output);
158 let converted_string = serde_json::to_string_pretty(&converted).unwrap();
159 let converted_string_deser: String = serde_json::from_str(&converted_string).unwrap();
160 let expected = String::from("39cyg4ZmMtnb_aFUIerNAoAJV8qtkfOpq0zFzPspjgM=");
161
162 assert_eq!(expected, converted_string_deser);
163 }
164}