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