node/transaction_pool/
transaction_pool_state.rs

1use ledger::{
2    scan_state::{
3        currency::{Amount, Nonce, Slot},
4        transaction_logic::valid::UserCommand,
5    },
6    transaction_pool::{Config, ValidCommandWithHash},
7    AccountId,
8};
9use mina_p2p_messages::v2::{self, TransactionHash};
10use openmina_core::{consensus::ConsensusConstants, distributed_pool::DistributedPool};
11use serde::{Deserialize, Serialize};
12use std::collections::{BTreeMap, HashMap};
13
14use super::{candidate::TransactionPoolCandidatesState, TransactionPoolAction};
15
16pub(super) type PendingId = u32;
17
18#[derive(Serialize, Deserialize, Debug)]
19pub struct TransactionPoolState {
20    pub candidates: TransactionPoolCandidatesState,
21    // TODO(binier): ideally this and `.pool` should be merged together.
22    pub(super) dpool: DistributedPool<TransactionState, v2::TransactionHash>,
23    pub(super) pool: ledger::transaction_pool::TransactionPool,
24    pub(super) pending_actions: BTreeMap<PendingId, TransactionPoolAction>,
25    pub(super) pending_id: PendingId,
26    pub(super) best_tip_hash: Option<v2::LedgerHash>,
27    /// For debug only
28    #[serde(skip)]
29    pub(super) file: Option<std::fs::File>,
30}
31
32#[derive(Serialize, Deserialize, Debug, Clone)]
33pub struct TransactionState {
34    pub time: redux::Timestamp,
35    pub hash: TransactionHash,
36}
37
38impl AsRef<TransactionHash> for TransactionState {
39    fn as_ref(&self) -> &TransactionHash {
40        &self.hash
41    }
42}
43
44impl Clone for TransactionPoolState {
45    fn clone(&self) -> Self {
46        Self {
47            candidates: self.candidates.clone(),
48            dpool: self.dpool.clone(),
49            pool: self.pool.clone(),
50            pending_actions: self.pending_actions.clone(),
51            pending_id: self.pending_id,
52            best_tip_hash: self.best_tip_hash.clone(),
53            file: None,
54        }
55    }
56}
57
58impl TransactionPoolState {
59    pub fn new(config: Config, consensus_constants: &ConsensusConstants) -> Self {
60        Self {
61            candidates: Default::default(),
62            dpool: Default::default(),
63            pool: ledger::transaction_pool::TransactionPool::new(config, consensus_constants),
64            pending_actions: Default::default(),
65            pending_id: 0,
66            best_tip_hash: None,
67            file: None,
68        }
69    }
70
71    pub fn size(&self) -> usize {
72        self.pool.size()
73    }
74
75    pub fn for_propagation_size(&self) -> usize {
76        self.dpool.len()
77    }
78
79    pub fn contains(&self, hash: &TransactionHash) -> bool {
80        self.get(hash).is_some()
81    }
82
83    pub fn get(&self, hash: &TransactionHash) -> Option<&UserCommand> {
84        self.pool.pool.get(hash).map(|v| &v.data)
85    }
86
87    pub fn transactions(&mut self, limit: usize) -> Vec<ValidCommandWithHash> {
88        self.pool.transactions(limit)
89    }
90
91    pub fn list_includable_transactions(&self, limit: usize) -> Vec<ValidCommandWithHash> {
92        self.pool.list_includable_transactions(limit)
93    }
94
95    pub fn get_all_transactions(&self) -> Vec<ValidCommandWithHash> {
96        self.pool.get_all_transactions()
97    }
98
99    pub fn get_pending_amount_and_nonce(&self) -> HashMap<AccountId, (Option<Nonce>, Amount)> {
100        self.pool.get_pending_amount_and_nonce()
101    }
102
103    fn next_pending_id(&mut self) -> PendingId {
104        let id = self.pending_id;
105        self.pending_id = self.pending_id.wrapping_add(1);
106        id
107    }
108
109    pub(super) fn make_action_pending(&mut self, action: &TransactionPoolAction) -> PendingId {
110        let id = self.next_pending_id();
111        self.pending_actions.insert(id, action.clone());
112        id
113    }
114
115    #[allow(dead_code)]
116    fn save_actions(state: &mut crate::Substate<Self>) {
117        let substate = state.get_substate_mut().unwrap();
118        if substate.file.is_none() {
119            let mut file = std::fs::File::create("/tmp/pool.bin").unwrap();
120            postcard::to_io(&state.unsafe_get_state(), &mut file).unwrap();
121            let substate = state.get_substate_mut().unwrap();
122            substate.file = Some(file);
123        }
124    }
125
126    pub(super) fn global_slots(state: &crate::State) -> Option<(Slot, Slot)> {
127        Some((
128            Slot::from_u32(state.cur_global_slot()?),
129            Slot::from_u32(state.cur_global_slot_since_genesis()?),
130        ))
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::{super::TransactionPoolActionWithMeta, *};
137    use crate::State;
138    use redux::Dispatcher;
139
140    #[allow(unused)]
141    #[test]
142    fn test_replay_pool() {
143        let vec = std::fs::read("/tmp/pool.bin").unwrap();
144        let slice = vec.as_slice();
145
146        let (mut state, rest) = postcard::take_from_bytes::<State>(slice).unwrap();
147        let mut slice = rest;
148
149        while let Ok((action, rest)) =
150            postcard::take_from_bytes::<TransactionPoolActionWithMeta>(slice)
151        {
152            slice = rest;
153
154            let mut dispatcher = Dispatcher::new();
155            let state = crate::Substate::<TransactionPoolState>::new(&mut state, &mut dispatcher);
156            let (action, meta) = action.split();
157
158            TransactionPoolState::handle_action(state, meta.with_action(&action));
159        }
160    }
161}