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 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}