Skip to main content

mina_node/ledger/write/
mod.rs

1mod ledger_write_actions;
2use ledger::{scan_state::transaction_logic::valid, Account, AccountId, AccountIndex, TokenId};
3pub use ledger_write_actions::*;
4
5mod ledger_write_state;
6pub use ledger_write_state::*;
7use mina_core::block::AppliedBlock;
8
9mod ledger_write_reducer;
10
11use std::{
12    collections::{BTreeMap, BTreeSet},
13    sync::Arc,
14};
15
16use ledger::scan_state::scan_state::{transaction_snark::OneOrTwo, AvailableJobMessage};
17use mina_p2p_messages::v2::{self, StateBodyHash};
18use serde::{Deserialize, Serialize};
19
20use crate::{
21    block_producer_effectful::StagedLedgerDiffCreateOutput,
22    core::{
23        block::ArcBlockWithHash,
24        snark::{Snark, SnarkJobId},
25    },
26    transition_frontier::sync::{
27        ledger::staged::StagedLedgerAuxAndPendingCoinbasesValid,
28        TransitionFrontierRootSnarkedLedgerUpdates,
29    },
30};
31
32#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, Copy)]
33#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
34pub enum LedgerWriteKind {
35    StagedLedgerReconstruct,
36    StagedLedgerDiffCreate,
37    BlockApply,
38    Commit,
39}
40
41#[derive(Serialize, Deserialize, Debug, Clone)]
42pub enum LedgerWriteRequest {
43    StagedLedgerReconstruct {
44        snarked_ledger_hash: v2::LedgerHash,
45        parts: Option<Arc<StagedLedgerAuxAndPendingCoinbasesValid>>,
46    },
47    StagedLedgerDiffCreate {
48        pred_block: AppliedBlock,
49        global_slot_since_genesis: v2::MinaNumbersGlobalSlotSinceGenesisMStableV1,
50        is_new_epoch: bool,
51        producer: v2::NonZeroCurvePoint,
52        delegator: v2::NonZeroCurvePoint,
53        coinbase_receiver: v2::NonZeroCurvePoint,
54        completed_snarks: BTreeMap<SnarkJobId, Snark>,
55        supercharge_coinbase: bool,
56        transactions_by_fee: Vec<valid::UserCommand>,
57    },
58    BlockApply {
59        block: ArcBlockWithHash,
60        pred_block: AppliedBlock,
61        skip_verification: bool,
62    },
63    Commit {
64        ledgers_to_keep: LedgersToKeep,
65        root_snarked_ledger_updates: TransitionFrontierRootSnarkedLedgerUpdates,
66        needed_protocol_states: BTreeMap<v2::StateHash, v2::MinaStateProtocolStateValueStableV2>,
67        new_root: AppliedBlock,
68        new_best_tip: AppliedBlock,
69    },
70}
71
72#[derive(Serialize, Deserialize, Debug, Clone)]
73pub enum LedgerWriteResponse {
74    StagedLedgerReconstruct {
75        staged_ledger_hash: v2::LedgerHash,
76        result: Result<(), String>,
77    },
78    StagedLedgerDiffCreate {
79        pred_block_hash: v2::StateHash,
80        global_slot_since_genesis: v2::MinaNumbersGlobalSlotSinceGenesisMStableV1,
81        result: Result<Arc<StagedLedgerDiffCreateOutput>, String>,
82    },
83    BlockApply {
84        block_hash: v2::StateHash,
85        result: Result<BlockApplyResult, String>,
86    },
87    Commit {
88        best_tip_hash: v2::StateHash,
89        result: CommitResult,
90    },
91}
92
93#[derive(Serialize, Deserialize, Debug, Clone)]
94pub struct BlockApplyResult {
95    pub block: ArcBlockWithHash,
96    pub just_emitted_a_proof: bool,
97    pub archive_data: Option<BlockApplyResultArchive>,
98}
99
100#[derive(Serialize, Deserialize, Debug, Clone)]
101pub struct BlockApplyResultArchive {
102    pub accounts_accessed: Vec<(AccountIndex, Account)>,
103    pub accounts_created: Vec<(AccountId, u64)>,
104    pub tokens_used: BTreeSet<(TokenId, Option<AccountId>)>,
105    pub sender_receipt_chains_from_parent_ledger: Vec<(AccountId, v2::ReceiptChainHash)>,
106}
107
108impl TryFrom<BlockApplyResult> for v2::ArchiveTransitionFrontierDiff {
109    type Error = String;
110
111    fn try_from(value: BlockApplyResult) -> Result<Self, Self::Error> {
112        (&value).try_into()
113    }
114}
115
116impl TryFrom<&BlockApplyResult> for v2::ArchiveTransitionFrontierDiff {
117    type Error = String;
118
119    fn try_from(value: &BlockApplyResult) -> Result<Self, Self::Error> {
120        if let Some(archive_data) = &value.archive_data {
121            let res = Self::BreadcrumbAdded {
122                // TODO(adonagy): check if we need the StateBodyHash, if no keep the None
123                block: (
124                    (*value.block.block).clone(),
125                    (
126                        value
127                            .block
128                            .header()
129                            .protocol_state
130                            .body
131                            .try_hash()
132                            .ok()
133                            .map(StateBodyHash::from),
134                        value.block.hash().clone(),
135                    ),
136                ),
137                accounts_accessed: archive_data
138                    .accounts_accessed
139                    .iter()
140                    .map(|(index, account)| (index.0.into(), account.into()))
141                    .collect(),
142                accounts_created: archive_data
143                    .accounts_created
144                    .iter()
145                    .map(|(account_id, fee)| {
146                        (
147                            (*account_id).clone().into(),
148                            v2::CurrencyFeeStableV1((*fee).into()),
149                        )
150                    })
151                    .collect(),
152                tokens_used: archive_data
153                    .tokens_used
154                    .iter()
155                    .map(|(token_id, account_id)| {
156                        (
157                            token_id.into(),
158                            account_id.clone().map(|account_id| account_id.into()),
159                        )
160                    })
161                    .collect(),
162                sender_receipt_chains_from_parent_ledger: archive_data
163                    .sender_receipt_chains_from_parent_ledger
164                    .iter()
165                    .map(|(account_id, receipt_chain_hash)| {
166                        (
167                            (*account_id).clone().into(),
168                            receipt_chain_hash.clone().into_inner(),
169                        )
170                    })
171                    .collect(),
172            };
173            Ok(res)
174        } else {
175            Err("Archive data not available, not running in archive mode".to_string())
176        }
177    }
178}
179
180impl TryFrom<&BlockApplyResult> for v2::PrecomputedBlock {
181    type Error = String;
182
183    fn try_from(value: &BlockApplyResult) -> Result<Self, Self::Error> {
184        let archive_transition_frontier_diff: v2::ArchiveTransitionFrontierDiff =
185            value.try_into()?;
186
187        let res = Self {
188            scheduled_time: value
189                .block
190                .header()
191                .protocol_state
192                .body
193                .blockchain_state
194                .timestamp,
195            protocol_state: value.block.header().protocol_state.clone(),
196            protocol_state_proof: value
197                .block
198                .header()
199                .protocol_state_proof
200                .as_ref()
201                .clone()
202                .into(),
203            staged_ledger_diff: value.block.body().staged_ledger_diff.clone(),
204            delta_transition_chain_proof: value.block.header().delta_block_chain_proof.clone(),
205            protocol_version: value.block.header().current_protocol_version.clone(),
206            proposed_protocol_version: None,
207            accounts_accessed: archive_transition_frontier_diff.accounts_accessed(),
208            accounts_created: archive_transition_frontier_diff.accounts_created(),
209            tokens_used: archive_transition_frontier_diff.tokens_used(),
210        };
211
212        Ok(res)
213    }
214}
215
216#[derive(Serialize, Deserialize, Debug, Ord, PartialOrd, Eq, PartialEq, Default, Clone)]
217pub struct LedgersToKeep {
218    snarked: BTreeSet<v2::LedgerHash>,
219    staged: BTreeSet<Arc<v2::MinaBaseStagedLedgerHashStableV1>>,
220}
221
222impl LedgersToKeep {
223    pub fn new() -> Self {
224        Self::default()
225    }
226
227    pub fn contains<'a, T>(&self, key: T) -> bool
228    where
229        T: 'a + Into<LedgerToKeep<'a>>,
230    {
231        match key.into() {
232            LedgerToKeep::Snarked(hash) => self.snarked.contains(hash),
233            LedgerToKeep::Staged(hash) => self.staged.contains(hash),
234        }
235    }
236
237    pub fn add_snarked(&mut self, hash: v2::LedgerHash) -> bool {
238        self.snarked.insert(hash)
239    }
240
241    pub fn add_staged(&mut self, hash: Arc<v2::MinaBaseStagedLedgerHashStableV1>) -> bool {
242        self.staged.insert(hash)
243    }
244}
245
246impl<'a> FromIterator<&'a ArcBlockWithHash> for LedgersToKeep {
247    fn from_iter<T: IntoIterator<Item = &'a ArcBlockWithHash>>(iter: T) -> Self {
248        let mut res = Self::new();
249        let best_tip = iter.into_iter().fold(None, |best_tip, block| {
250            res.add_snarked(block.snarked_ledger_hash().clone());
251            res.add_staged(Arc::new(block.staged_ledger_hashes().clone()));
252            match best_tip {
253                None => Some(block),
254                Some(tip) if tip.height() < block.height() => Some(block),
255                old => old,
256            }
257        });
258
259        if let Some(best_tip) = best_tip {
260            res.add_snarked(best_tip.staking_epoch_ledger_hash().clone());
261            res.add_snarked(best_tip.next_epoch_ledger_hash().clone());
262        }
263
264        res
265    }
266}
267
268#[derive(derive_more::From)]
269pub enum LedgerToKeep<'a> {
270    Snarked(&'a v2::LedgerHash),
271    Staged(&'a v2::MinaBaseStagedLedgerHashStableV1),
272}
273
274impl TryFrom<BlockApplyResult> for v2::PrecomputedBlock {
275    type Error = String;
276
277    fn try_from(value: BlockApplyResult) -> Result<Self, Self::Error> {
278        (&value).try_into()
279    }
280}
281
282#[derive(Serialize, Deserialize, Debug, Default, Clone)]
283pub struct CommitResult {
284    pub alive_masks: usize,
285    pub available_jobs: Arc<Vec<OneOrTwo<AvailableJobMessage>>>,
286    pub needed_protocol_states: BTreeSet<v2::StateHash>,
287}
288
289impl LedgerWriteRequest {
290    pub fn kind(&self) -> LedgerWriteKind {
291        match self {
292            Self::StagedLedgerReconstruct { .. } => LedgerWriteKind::StagedLedgerReconstruct,
293            Self::StagedLedgerDiffCreate { .. } => LedgerWriteKind::StagedLedgerDiffCreate,
294            Self::BlockApply { .. } => LedgerWriteKind::BlockApply,
295            Self::Commit { .. } => LedgerWriteKind::Commit,
296        }
297    }
298}
299
300impl LedgerWriteResponse {
301    pub fn kind(&self) -> LedgerWriteKind {
302        match self {
303            Self::StagedLedgerReconstruct { .. } => LedgerWriteKind::StagedLedgerReconstruct,
304            Self::StagedLedgerDiffCreate { .. } => LedgerWriteKind::StagedLedgerDiffCreate,
305            Self::BlockApply { .. } => LedgerWriteKind::BlockApply,
306            Self::Commit { .. } => LedgerWriteKind::Commit,
307        }
308    }
309}