Skip to main content

mina_node/transition_frontier/genesis/
transition_frontier_genesis_reducer.rs

1use crate::{
2    account::AccountSecretKey, block_producer::calc_epoch_seed, p2p::P2pInitializeAction,
3    transition_frontier::genesis_effectful::TransitionFrontierGenesisEffectfulAction,
4};
5use ledger::{
6    dummy::dummy_blockchain_proof, scan_state::transaction_logic::local_state::LocalState,
7};
8use mina_core::{
9    block::{genesis::genesis_and_negative_one_protocol_states, BlockWithHash},
10    constants::PROTOCOL_VERSION,
11    error,
12};
13use mina_p2p_messages::v2;
14
15use super::{
16    empty_block_body, empty_block_body_hash, empty_pending_coinbase, empty_pending_coinbase_hash,
17    TransitionFrontierGenesisAction, TransitionFrontierGenesisActionWithMetaRef,
18    TransitionFrontierGenesisState,
19};
20
21impl TransitionFrontierGenesisState {
22    pub fn reducer(
23        mut state_context: crate::Substate<Self>,
24        action: TransitionFrontierGenesisActionWithMetaRef<'_>,
25        skip_proof_verification: bool,
26    ) {
27        let Ok(state) = state_context.get_substate_mut() else {
28            // TODO: log or propagate
29            return;
30        };
31        let (action, meta) = action.split();
32
33        match action {
34            TransitionFrontierGenesisAction::LedgerLoadInit => {
35                let (dispatcher, global_state) = state_context.into_dispatcher_and_state();
36                let config = global_state.transition_frontier.config.genesis.clone();
37
38                dispatcher.push(TransitionFrontierGenesisAction::LedgerLoadPending);
39                dispatcher
40                    .push(TransitionFrontierGenesisEffectfulAction::LedgerLoadInit { config });
41            }
42            TransitionFrontierGenesisAction::LedgerLoadPending => {
43                *state = Self::LedgerLoadPending { time: meta.time() };
44            }
45            TransitionFrontierGenesisAction::LedgerLoadSuccess { data } => {
46                *state = Self::LedgerLoadSuccess {
47                    time: meta.time(),
48                    data: *data.clone(),
49                };
50
51                // Dispatch
52                let dispatcher = state_context.into_dispatcher();
53                // TODO(refactor): before this is dispatched genesis inject must be dispatched
54                dispatcher.push(TransitionFrontierGenesisAction::Produce);
55            }
56            TransitionFrontierGenesisAction::Produce => {
57                let Self::LedgerLoadSuccess { data, .. } = state else {
58                    return;
59                };
60
61                let genesis_vrf = ::mina_vrf::genesis_vrf(data.staking_epoch_seed.clone()).unwrap();
62                let genesis_vrf_hash = genesis_vrf.hash();
63
64                let Ok((negative_one, genesis, genesis_hash)) =
65                    genesis_and_negative_one_protocol_states(
66                        data.constants.clone(),
67                        data.genesis_ledger_hash.clone(),
68                        data.genesis_total_currency.clone(),
69                        data.staking_epoch_ledger_hash.clone(),
70                        data.staking_epoch_total_currency.clone(),
71                        data.next_epoch_ledger_hash.clone(),
72                        data.next_epoch_total_currency.clone(),
73                        AccountSecretKey::genesis_producer().public_key().into(),
74                        empty_pending_coinbase_hash(),
75                        (&LocalState::dummy()).into(),
76                        empty_block_body_hash(),
77                        genesis_vrf.into(),
78                        data.staking_epoch_seed.clone(),
79                        data.next_epoch_seed.clone(),
80                        calc_epoch_seed(&data.next_epoch_seed, genesis_vrf_hash), //data.next_epoch_seed.clone(),
81                    )
82                else {
83                    error!(meta.time(); "invalid negative protocol state");
84                    return;
85                };
86
87                *state = Self::Produced {
88                    time: meta.time(),
89                    negative_one,
90                    genesis,
91                    genesis_hash,
92                    genesis_producer_stake_proof: data.genesis_producer_stake_proof.clone(),
93                };
94
95                // Dispatch
96                let (dispatcher, global_state) = state_context.into_dispatcher_and_state();
97                if global_state.p2p.ready().is_none() {
98                    let TransitionFrontierGenesisState::Produced {
99                        genesis,
100                        genesis_hash,
101                        ..
102                    } = &global_state.transition_frontier.genesis
103                    else {
104                        error!(meta.time(); "incorrect state: {:?}", global_state.transition_frontier.genesis);
105                        return;
106                    };
107                    use mina_core::{constants, ChainId};
108                    let chain_id = if let Some(override_id) = &global_state.config.chain_id_override
109                    {
110                        override_id.clone()
111                    } else {
112                        let constraint_system_digests =
113                            mina_core::NetworkConfig::global().constraint_system_digests;
114                        ChainId::compute(
115                            constraint_system_digests,
116                            genesis_hash,
117                            &genesis.body.constants,
118                            constants::PROTOCOL_TRANSACTION_VERSION,
119                            constants::PROTOCOL_NETWORK_VERSION,
120                            &v2::UnsignedExtendedUInt32StableV1::from(constants::TX_POOL_MAX_SIZE),
121                        )
122                    };
123                    dispatcher.push(P2pInitializeAction::Initialize { chain_id });
124                }
125                dispatcher.push(TransitionFrontierGenesisAction::ProveInit);
126            }
127            TransitionFrontierGenesisAction::ProveInit => {
128                let TransitionFrontierGenesisState::Produced {
129                    negative_one,
130                    genesis,
131                    genesis_hash,
132                    genesis_producer_stake_proof,
133                    ..
134                } = state
135                else {
136                    return;
137                };
138
139                // When proof verification is disabled (interop testing with
140                // OCaml --proof-level none), skip real genesis proving and
141                // use a dummy proof. The real prover can panic on custom
142                // genesis configurations due to sparse ledger mismatches.
143                if skip_proof_verification {
144                    let dispatcher = state_context.into_dispatcher();
145                    dispatcher.push(TransitionFrontierGenesisAction::ProvePending);
146                    dispatcher.push(TransitionFrontierGenesisAction::ProveSuccess {
147                        proof: dummy_blockchain_proof().clone(),
148                    });
149                    return;
150                }
151
152                let genesis_hash = genesis_hash.clone();
153                let producer_pk = genesis.body.consensus_state.block_creator.clone();
154                let delegator_pk = genesis.body.consensus_state.block_stake_winner.clone();
155
156                let input = v2::ProverExtendBlockchainInputStableV2 {
157                    chain: v2::BlockchainSnarkBlockchainStableV2 {
158                        state: negative_one.clone(),
159                        proof: dummy_blockchain_proof().clone(),
160                    },
161                    next_state: genesis.clone(),
162                    block: v2::MinaStateSnarkTransitionValueStableV2 {
163                        blockchain_state: genesis.body.blockchain_state.clone(),
164                        consensus_transition: genesis
165                            .body
166                            .consensus_state
167                            .curr_global_slot_since_hard_fork
168                            .slot_number
169                            .clone(),
170                        pending_coinbase_update: v2::MinaBasePendingCoinbaseUpdateStableV1::zero(),
171                    },
172                    ledger_proof: None,
173                    prover_state: v2::ConsensusStakeProofStableV2 {
174                        delegator: v2::MinaBaseAccountIndexStableV1(0u64.into()),
175                        delegator_pk,
176                        coinbase_receiver_pk: genesis
177                            .body
178                            .consensus_state
179                            .coinbase_receiver
180                            .clone(),
181                        producer_public_key: producer_pk,
182                        producer_private_key: AccountSecretKey::genesis_producer().into(),
183                        ledger: genesis_producer_stake_proof.clone(),
184                    },
185                    pending_coinbase: v2::MinaBasePendingCoinbaseWitnessStableV2 {
186                        pending_coinbases: (&empty_pending_coinbase()).into(),
187                        is_new_stack: true,
188                    },
189                };
190
191                // Dispatch
192                let dispatcher = state_context.into_dispatcher();
193
194                dispatcher.push(TransitionFrontierGenesisAction::ProvePending);
195                dispatcher.push(TransitionFrontierGenesisEffectfulAction::ProveInit {
196                    block_hash: genesis_hash,
197                    input: input.into(),
198                });
199            }
200            TransitionFrontierGenesisAction::ProvePending => {
201                let Self::Produced {
202                    negative_one,
203                    genesis,
204                    genesis_hash,
205                    genesis_producer_stake_proof,
206                    ..
207                } = state
208                else {
209                    return;
210                };
211
212                *state = Self::ProvePending {
213                    time: meta.time(),
214                    negative_one: negative_one.clone(),
215                    genesis: genesis.clone(),
216                    genesis_hash: genesis_hash.clone(),
217                    genesis_producer_stake_proof: genesis_producer_stake_proof.clone(),
218                };
219            }
220            TransitionFrontierGenesisAction::ProveSuccess { proof } => {
221                let Self::ProvePending {
222                    genesis,
223                    genesis_hash,
224                    ..
225                } = state
226                else {
227                    return;
228                };
229
230                let block = v2::MinaBlockBlockStableV2 {
231                    header: v2::MinaBlockHeaderStableV2 {
232                        protocol_state: genesis.clone(),
233                        protocol_state_proof: proof.clone(),
234                        delta_block_chain_proof: (
235                            genesis_hash.clone(),
236                            std::iter::empty().collect(),
237                        ),
238                        current_protocol_version: PROTOCOL_VERSION.clone(),
239                        proposed_protocol_version_opt: None,
240                    },
241                    body: v2::StagedLedgerDiffBodyStableV1 {
242                        staged_ledger_diff: empty_block_body(),
243                    },
244                };
245
246                let Ok(genesis) = BlockWithHash::try_new(block.into()) else {
247                    error!(meta.time(); "invalid `genesis` block");
248                    return;
249                };
250
251                *state = Self::ProveSuccess {
252                    time: meta.time(),
253                    genesis,
254                };
255            }
256        }
257    }
258}