node/block_producer_effectful/
block_producer_effectful_effects.rs

1use crate::{
2    block_producer::BlockProducerCurrentState,
3    ledger::write::{LedgerWriteAction, LedgerWriteRequest},
4    BlockProducerAction, Store,
5};
6use mina_p2p_messages::v2::{
7    BlockchainSnarkBlockchainStableV2, ConsensusStakeProofStableV2,
8    MinaStateSnarkTransitionValueStableV2, ProverExtendBlockchainInputStableV2,
9};
10use openmina_node_account::AccountSecretKey;
11use redux::ActionWithMeta;
12
13use super::BlockProducerEffectfulAction;
14
15pub fn block_producer_effects<S: crate::Service>(
16    store: &mut Store<S>,
17    action: ActionWithMeta<BlockProducerEffectfulAction>,
18) {
19    let (action, meta) = action.split();
20
21    match action {
22        BlockProducerEffectfulAction::VrfEvaluator(a) => {
23            a.effects(&meta, store);
24        }
25        BlockProducerEffectfulAction::WonSlot { won_slot } => {
26            if let Some(stats) = store.service.stats() {
27                stats.block_producer().scheduled(meta.time(), &won_slot);
28            }
29            if !store.dispatch(BlockProducerAction::WonSlotWait) {
30                store.dispatch(BlockProducerAction::WonSlotProduceInit);
31            }
32        }
33        BlockProducerEffectfulAction::StagedLedgerDiffCreateInit => {
34            if let Some(stats) = store.service.stats() {
35                stats
36                    .block_producer()
37                    .staged_ledger_diff_create_start(meta.time());
38            }
39            let state = store.state.get();
40            let Some((won_slot, pred_block, producer, coinbase_receiver)) = None.or_else(|| {
41                let pred_block = state.block_producer.current_parent_chain()?.last()?;
42                let won_slot = state.block_producer.current_won_slot()?;
43                let config = state.block_producer.config()?;
44                Some((
45                    won_slot,
46                    pred_block,
47                    &config.pub_key,
48                    config.coinbase_receiver(),
49                ))
50            }) else {
51                return;
52            };
53
54            let completed_snarks = state
55                .snark_pool
56                .completed_snarks_iter()
57                .map(|snark| (snark.job_id(), snark.clone()))
58                .collect();
59            // TODO(binier)
60            let supercharge_coinbase = true;
61            // We want to know if this is a new epoch to decide which staking ledger to use
62            // (staking epoch ledger or next epoch ledger).
63            let is_new_epoch = won_slot.epoch()
64                > pred_block
65                    .header()
66                    .protocol_state
67                    .body
68                    .consensus_state
69                    .epoch_count
70                    .as_u32();
71
72            let transactions_by_fee = state.block_producer.pending_transactions();
73
74            store.dispatch(LedgerWriteAction::Init {
75                request: LedgerWriteRequest::StagedLedgerDiffCreate {
76                    pred_block: pred_block.clone(),
77                    global_slot_since_genesis: won_slot
78                        .global_slot_since_genesis(pred_block.global_slot_diff()),
79                    is_new_epoch,
80                    producer: producer.clone(),
81                    delegator: won_slot.delegator.0.clone(),
82                    coinbase_receiver: coinbase_receiver.clone(),
83                    completed_snarks,
84                    supercharge_coinbase,
85                    transactions_by_fee,
86                },
87                on_init: redux::callback!(
88                    on_staged_ledger_diff_create_init(_request: LedgerWriteRequest) -> crate::Action {
89                        BlockProducerAction::StagedLedgerDiffCreatePending
90                    }
91                ),
92            });
93        }
94        BlockProducerEffectfulAction::StagedLedgerDiffCreateSuccess => {
95            if let Some(stats) = store.service.stats() {
96                stats
97                    .block_producer()
98                    .staged_ledger_diff_create_end(meta.time());
99            }
100            store.dispatch(BlockProducerAction::BlockUnprovenBuild);
101        }
102        BlockProducerEffectfulAction::BlockUnprovenBuild => {
103            if let Some(stats) = store.service.stats() {
104                let bp = &store.state.get().block_producer;
105                if let Some((block_hash, block, just_emitted_ledger_proof)) =
106                    bp.with(None, |bp| match &bp.current {
107                        BlockProducerCurrentState::BlockUnprovenBuilt {
108                            block,
109                            block_hash,
110                            emitted_ledger_proof,
111                            ..
112                        } => Some((block_hash, block, emitted_ledger_proof.is_some())),
113                        _ => None,
114                    })
115                {
116                    let ps = &block.protocol_state;
117                    let cs = &ps.body.consensus_state;
118                    let previous_state_hash = ps.previous_state_hash.to_string();
119                    let state_hash = block_hash.to_string();
120                    let global_slot_since_genesis = cs.global_slot_since_genesis.as_u32();
121                    let height = cs.blockchain_length.as_u32();
122                    openmina_core::info!(
123                        meta.time();
124                        message = "Unproven block built",
125                        state_hash = state_hash,
126                        previous_state_hash = previous_state_hash,
127                        global_slot_since_genesis = global_slot_since_genesis,
128                        height = height,
129                        just_emitted_ledger_proof = just_emitted_ledger_proof,
130                    );
131
132                    stats
133                        .block_producer()
134                        .produced(meta.time(), block_hash, block);
135                }
136            }
137
138            store.dispatch(BlockProducerAction::BlockProveInit);
139        }
140        BlockProducerEffectfulAction::BlockProveInit => {
141            let service = &mut store.service;
142
143            if let Some(stats) = service.stats() {
144                stats.block_producer().proof_create_start(meta.time());
145            }
146            let Some((block_hash, input)) = store.state.get().block_producer.with(None, |bp| {
147                let BlockProducerCurrentState::BlockUnprovenBuilt {
148                    won_slot,
149                    chain,
150                    emitted_ledger_proof,
151                    pending_coinbase_update,
152                    pending_coinbase_witness,
153                    stake_proof_sparse_ledger,
154                    block,
155                    block_hash,
156                    ..
157                } = &bp.current
158                else {
159                    return None;
160                };
161
162                let pred_block = chain.last()?;
163
164                let producer_public_key = block
165                    .protocol_state
166                    .body
167                    .consensus_state
168                    .block_creator
169                    .clone();
170
171                let input = Box::new(ProverExtendBlockchainInputStableV2 {
172                    chain: BlockchainSnarkBlockchainStableV2 {
173                        state: pred_block.header().protocol_state.clone(),
174                        proof: pred_block.header().protocol_state_proof.clone(),
175                    },
176                    next_state: block.protocol_state.clone(),
177                    block: MinaStateSnarkTransitionValueStableV2 {
178                        blockchain_state: block.protocol_state.body.blockchain_state.clone(),
179                        consensus_transition: block
180                            .protocol_state
181                            .body
182                            .consensus_state
183                            .curr_global_slot_since_hard_fork
184                            .slot_number
185                            .clone(),
186                        pending_coinbase_update: pending_coinbase_update.clone(),
187                    },
188                    ledger_proof: emitted_ledger_proof.clone(),
189                    prover_state: ConsensusStakeProofStableV2 {
190                        delegator: won_slot.delegator.1.into(),
191                        delegator_pk: won_slot.delegator.0.clone(),
192                        coinbase_receiver_pk: block
193                            .protocol_state
194                            .body
195                            .consensus_state
196                            .coinbase_receiver
197                            .clone(),
198                        ledger: stake_proof_sparse_ledger.clone(),
199                        // it is replaced with correct keys in the service.
200                        producer_private_key: AccountSecretKey::genesis_producer().into(),
201                        producer_public_key,
202                    },
203                    pending_coinbase: pending_coinbase_witness.clone(),
204                });
205                Some((block_hash.clone(), input))
206            }) else {
207                return;
208            };
209            service.prove(block_hash, input);
210            store.dispatch(BlockProducerAction::BlockProvePending);
211        }
212        BlockProducerEffectfulAction::BlockProveSuccess => {
213            if let Some(stats) = store.service.stats() {
214                stats.block_producer().proof_create_end(meta.time());
215            }
216            store.dispatch(BlockProducerAction::BlockProduced);
217        }
218        BlockProducerEffectfulAction::WonSlotDiscard { reason } => {
219            if let Some(stats) = store.service.stats() {
220                stats.block_producer().discarded(meta.time(), reason);
221            }
222            store.dispatch(BlockProducerAction::WonSlotSearch);
223        }
224        BlockProducerEffectfulAction::BlockProduced { block } => {
225            if let Some(stats) = store.service.stats() {
226                stats.block_producer().last_produced_block = Some(block.clone());
227            }
228        }
229    }
230}