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