1use ark_ff::{One, SquareRootField, Zero};
2
3use ledger::{proofs::transaction::legacy_input::to_bits, ToInputs};
4use mina_curves::pasta::curves::pallas::Pallas as CurvePoint;
5use mina_p2p_messages::v2::EpochSeed;
6use o1_utils::FieldHelpers;
7use poseidon::hash::{params::MINA_VRF_MESSAGE, Inputs};
8use serde::{Deserialize, Serialize};
9
10use super::{BaseField, VrfError, VrfResult};
11
12const LEDGER_DEPTH: usize = 35;
13
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15pub struct VrfMessage {
16 global_slot: u32,
17 epoch_seed: EpochSeed,
18 delegator_index: u64,
19}
20
21impl VrfMessage {
22 pub fn new(global_slot: u32, epoch_seed: EpochSeed, delegator_index: u64) -> Self {
23 Self {
24 global_slot,
25 epoch_seed,
26 delegator_index,
27 }
28 }
29
30 pub fn hash(&self) -> BaseField {
31 self.hash_with_param(&MINA_VRF_MESSAGE)
32 }
33
34 pub fn to_group(&self) -> VrfResult<CurvePoint> {
35 let two = BaseField::one() + BaseField::one();
37 let three = two + BaseField::one();
38
39 let mut projection_point_z_bytes =
41 hex::decode("1AF731EC3CA2D77CC5D13EDC8C9A0A77978CB5F4FBFCC470B5983F5B6336DB69")?;
42 projection_point_z_bytes.reverse();
43 let projection_point_z = BaseField::from_bytes(&projection_point_z_bytes)?;
44 let projection_point_y = BaseField::one();
45 let conic_c = three;
46 let u_over_2 = BaseField::one();
47 let u = two;
48
49 let t = self.hash();
50
51 let ct = conic_c * t;
53 let s =
54 two * ((ct * projection_point_y) + projection_point_z) / ((ct * t) + BaseField::one());
55 let conic_z = projection_point_z - s;
56 let conic_y = projection_point_y - (s * t);
57
58 let v = (conic_z / conic_y) - u_over_2;
60 let y = conic_y;
61
62 let x1 = v;
64 let x2 = -(u + v);
65 let x3 = u + (y * y);
66
67 let get_y = |x: BaseField| -> Option<BaseField> {
68 let five = BaseField::one()
69 + BaseField::one()
70 + BaseField::one()
71 + BaseField::one()
72 + BaseField::one();
73 let mut res = x;
74 res *= &x; res += BaseField::zero(); res *= &x; res += five; res.sqrt()
79 };
80
81 for x in [x1, x2, x3] {
82 if let Some(y) = get_y(x) {
83 return Ok(CurvePoint::new(x, y, false));
84 }
85 }
86
87 Err(VrfError::ToGroupError(t))
88 }
89}
90
91impl ToInputs for VrfMessage {
92 fn to_inputs(&self, inputs: &mut Inputs) {
93 let epoch_seed = match self.epoch_seed.to_field() {
94 Ok(epoch_seed) => epoch_seed,
95 Err(_) => {
96 mina_curves::pasta::Fp::zero()
98 }
99 };
100 inputs.append_field(epoch_seed);
101 inputs.append_u32(self.global_slot);
102 for bit in to_bits::<_, LEDGER_DEPTH>(self.delegator_index) {
103 inputs.append_bool(bit);
104 }
105 }
106}