node/block_producer/
mod.rs1pub mod vrf_evaluator;
2
3mod block_producer_config;
4use std::sync::Arc;
5
6pub use block_producer_config::*;
7
8mod block_producer_state;
9pub use block_producer_state::*;
10
11mod block_producer_event;
12pub use block_producer_event::*;
13
14mod block_producer_actions;
15pub use block_producer_actions::*;
16
17mod block_producer_reducer;
18
19use ledger::AccountIndex;
20use mina_p2p_messages::{list::List, v2};
21use openmina_core::{block::ArcBlockWithHash, constants::constraint_constants};
22use poseidon::hash::params::MINA_EPOCH_SEED;
23use serde::{Deserialize, Serialize};
24use vrf::output::VrfOutput;
25
26use self::vrf_evaluator::VrfWonSlotWithHash;
27
28#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
29pub struct BlockProducerWonSlot {
30 pub slot_time: redux::Timestamp,
31 pub delegator: (v2::NonZeroCurvePoint, AccountIndex),
32 pub global_slot: v2::ConsensusGlobalSlotStableV1,
33 pub vrf_output: Box<VrfOutput>,
34 pub value_with_threshold: Option<(f64, f64)>,
35 pub staking_ledger_hash: v2::LedgerHash,
37}
38
39#[derive(Serialize, Deserialize, Debug, Clone)]
40pub struct BlockWithoutProof {
41 pub protocol_state: v2::MinaStateProtocolStateValueStableV2,
42 pub delta_block_chain_proof: (v2::StateHash, List<v2::StateBodyHash>),
43 pub current_protocol_version: v2::ProtocolVersionStableV2,
44 pub proposed_protocol_version_opt: Option<v2::ProtocolVersionStableV2>,
45 pub body: v2::StagedLedgerDiffBodyStableV1,
46}
47
48impl BlockProducerWonSlot {
49 pub fn from_vrf_won_slot(
50 won_slot_with_hash: &VrfWonSlotWithHash,
51 genesis_timestamp: redux::Timestamp,
52 ) -> Self {
53 let VrfWonSlotWithHash {
54 won_slot,
55 staking_ledger_hash,
56 } = won_slot_with_hash;
57
58 let slot_time = Self::calculate_slot_time(genesis_timestamp, won_slot.global_slot);
59
60 let delegator = (
61 won_slot.winner_account.clone().into(),
62 won_slot.account_index,
63 );
64 let global_slot = v2::ConsensusGlobalSlotStableV1 {
65 slot_number: v2::MinaNumbersGlobalSlotSinceHardForkMStableV1::SinceHardFork(
66 won_slot.global_slot.into(),
67 ),
68 slots_per_epoch: 7140.into(), };
70
71 Self {
72 slot_time,
73 delegator,
74 global_slot,
75 vrf_output: won_slot.vrf_output.clone(),
76 value_with_threshold: won_slot.value_with_threshold,
77 staking_ledger_hash: staking_ledger_hash.clone(),
78 }
79 }
80
81 fn calculate_slot_time(genesis_timestamp: redux::Timestamp, slot: u32) -> redux::Timestamp {
82 let per_block_ns = constraint_constants()
83 .block_window_duration_ms
84 .saturating_mul(1_000_000);
85 genesis_timestamp
86 .checked_add((slot as u64).checked_mul(per_block_ns).expect("overflow"))
87 .expect("overflow")
88 }
89
90 pub fn global_slot(&self) -> u32 {
91 self.global_slot.slot_number.as_u32()
92 }
93
94 pub fn epoch(&self) -> u32 {
95 self.global_slot()
96 .checked_div(self.global_slot.slots_per_epoch.as_u32())
97 .expect("division by 0")
98 }
99
100 pub fn global_slot_since_genesis(
101 &self,
102 slot_diff: u32,
103 ) -> v2::MinaNumbersGlobalSlotSinceGenesisMStableV1 {
104 let slot = self.global_slot().checked_add(slot_diff).expect("overflow");
105 v2::MinaNumbersGlobalSlotSinceGenesisMStableV1::SinceGenesis(slot.into())
106 }
107
108 pub fn timestamp(&self) -> v2::BlockTimeTimeStableV1 {
109 let ms = u64::from(self.slot_time) / 1_000_000;
110 v2::BlockTimeTimeStableV1(v2::UnsignedExtendedUInt64Int64ForVersionTagsStableV1(
111 ms.into(),
112 ))
113 }
114
115 pub fn next_slot_time(&self) -> redux::Timestamp {
116 self.slot_time
117 .checked_add(3u64.saturating_mul(60).saturating_mul(1_000_000_000_u64))
118 .expect("overflow")
119 }
120}
121
122impl PartialOrd for BlockProducerWonSlot {
123 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
124 Some(self.global_slot().cmp(&other.global_slot()).then_with(|| {
125 v2::ConsensusVrfOutputTruncatedStableV1::from(&*self.vrf_output)
126 .blake2b()
127 .cmp(&v2::ConsensusVrfOutputTruncatedStableV1::from(&*other.vrf_output).blake2b())
128 }))
129 }
130}
131
132impl PartialEq<ArcBlockWithHash> for BlockProducerWonSlot {
133 fn eq(&self, other: &ArcBlockWithHash) -> bool {
134 self.partial_cmp(other).is_some_and(|ord| ord.is_eq())
135 }
136}
137
138impl PartialOrd<ArcBlockWithHash> for BlockProducerWonSlot {
139 fn partial_cmp(&self, other: &ArcBlockWithHash) -> Option<std::cmp::Ordering> {
140 Some(self.global_slot().cmp(&other.global_slot()).then_with(|| {
142 v2::ConsensusVrfOutputTruncatedStableV1::from(&*self.vrf_output)
143 .blake2b()
144 .cmp(
145 &other
146 .header()
147 .protocol_state
148 .body
149 .consensus_state
150 .last_vrf_output
151 .blake2b(),
152 )
153 }))
154 }
155}
156
157pub fn to_epoch_and_slot(global_slot: &v2::ConsensusGlobalSlotStableV1) -> (u32, u32) {
158 let epoch = global_slot
159 .slot_number
160 .as_u32()
161 .checked_div(global_slot.slots_per_epoch.as_u32())
162 .expect("division by 0");
163 let slot = global_slot
164 .slot_number
165 .as_u32()
166 .checked_rem(global_slot.slots_per_epoch.as_u32())
167 .expect("division by 0");
168 (epoch, slot)
169}
170
171pub fn next_epoch_first_slot(global_slot: &v2::ConsensusGlobalSlotStableV1) -> u32 {
172 let (epoch, _) = to_epoch_and_slot(global_slot);
173 (epoch.saturating_add(1))
174 .checked_mul(global_slot.slots_per_epoch.as_u32())
175 .expect("overflow")
176}
177
178impl BlockWithoutProof {
187 pub fn with_hash_and_proof(
188 self,
189 hash: v2::StateHash,
190 proof: Arc<v2::MinaBaseProofStableV2>,
191 ) -> ArcBlockWithHash {
192 let block = v2::MinaBlockBlockStableV2 {
193 header: v2::MinaBlockHeaderStableV2 {
194 protocol_state: self.protocol_state,
195 protocol_state_proof: proof,
196 delta_block_chain_proof: self.delta_block_chain_proof,
197 current_protocol_version: self.current_protocol_version,
198 proposed_protocol_version_opt: self.proposed_protocol_version_opt,
199 },
200 body: self.body,
201 };
202
203 ArcBlockWithHash {
204 block: block.into(),
205 hash,
206 }
207 }
208}
209
210pub fn calc_epoch_seed(
211 prev_epoch_seed: &v2::EpochSeed,
212 vrf_hash: mina_curves::pasta::Fp,
213) -> v2::EpochSeed {
214 let old_seed = prev_epoch_seed.to_field().unwrap();
216 let new_seed = poseidon::hash::hash_with_kimchi(&MINA_EPOCH_SEED, &[old_seed, vrf_hash]);
217 v2::MinaBaseEpochSeedStableV1(new_seed.into()).into()
218}