Skip to main content

uqoin_core/
pool.rs

1//! Manages a pool of transaction groups pending inclusion in a new block.
2//!
3//! The `pool` module maintains collections of transaction groups that are
4//! candidates for addition to the next block.
5//! Since the validity of these groups depends on the current blockchain state,
6//! it's recommended to update the pool
7//! whenever the state changes to ensure all groups remain relevant and valid.
8//! This module is essential for preparing
9//! transactions for block creation using the `prepare` function.
10
11use std::collections::HashSet;
12
13use rand::Rng;
14
15use crate::utils::*;
16use crate::transaction::{Type, Transaction, Group};
17use crate::schema::Schema;
18use crate::state::{State, OrderCoinsMap};
19
20
21/// Validator pool that keeps requested transactions.
22#[derive(Debug, Clone)]
23pub struct Pool {
24    groups: Vec<Group>,
25    senders: Vec<U256>,
26}
27
28
29impl Pool {
30    /// Create an empty pool.
31    pub fn new() -> Self {
32        Self {
33            groups: Vec::new(),
34            senders: Vec::new(),
35        }
36    }
37
38    /// Clear pool.
39    pub fn clear(&mut self) {
40        self.groups.clear();
41        self.senders.clear();
42    }
43
44    /// Add a new group. `sender` must correspond to the group sender that is
45    /// required on group creation.
46    pub fn add(&mut self, group: Group, sender: U256) {
47        self.groups.push(group);
48        self.senders.push(sender);
49    }
50
51    /// Update the pool according to the given state. Valid group in one state
52    /// may be invalid in another. This function recalculates senders based on
53    /// the state, so it may take a while.
54    pub fn update(&mut self, state: &State, schema: &Schema) {
55        let old_groups = self.groups.clone();
56        self.groups = Vec::new();
57        self.senders = Vec::new();
58        for old_group in old_groups.iter() {
59            let senders = Transaction::calc_senders(&old_group.transactions(), 
60                                                    state, schema);
61            if let Ok(group) = Group::new(old_group.transactions().to_vec(), 
62                                          state, &senders) {
63                self.add(group, senders[0].clone());
64            }
65        }
66    }
67
68    /// Prepare transactions and senders for the next block. The pool must be
69    /// updated according to this state.
70    pub fn prepare<R: Rng>(&self, rng: &mut R, state: &State, schema: &Schema,
71                           validator_key: &U256, groups_max: Option<usize>) -> 
72                           (Vec<Transaction>, Vec<U256>) {
73        // Transactions and senders to fill
74        let mut transactions = Vec::new();
75        let mut senders = Vec::new();
76
77        // Validator public
78        let validator = schema.get_public(validator_key);
79
80        // Validator resource
81        let mut validator_resource = state.get_coins(&validator).cloned()
82                                          .unwrap_or(OrderCoinsMap::new());
83
84        // Set of seen coins
85        let mut coins_seen = HashSet::new();
86
87        // Counter of added groups
88        let mut counter = 0;
89
90        // Loop for groups and corresponding senders
91        for (group, sender) in self.groups.iter().zip(self.senders.iter()) {
92            // Leave if groups_max is reached
93            if let Some(groups_max) = groups_max {
94                if counter >= groups_max {
95                    break;
96                }
97            }
98
99            // Skip if the group contains any seen coin
100            if group.transactions().iter()
101                    .any(|tr| coins_seen.contains(&tr.coin)) {
102                continue;
103            }
104
105            // Update seen coins
106            for tr in group.transactions().iter() {
107                coins_seen.insert(tr.coin.clone());
108            }
109
110            // Group senders
111            let group_senders = vec![sender.clone(); group.len()];
112
113            // Get order
114            let order = group.get_order(state, &group_senders);
115
116            // Calculate ext transactions
117            let ext_trs: Option<Vec<Transaction>> = match group.get_type() {
118                Type::Transfer => Some(vec![]),
119                Type::Merge => [order].iter().map(|ord| {
120                    let coin = Self::get_validator_coin(
121                        ord, &mut validator_resource, &coins_seen
122                    )?;
123                    let counter = state.get_coin_counter(&coin);
124                    coins_seen.insert(coin.clone());
125                    Some(Transaction::build(rng, coin, sender.clone(), 
126                                            validator_key, counter, schema))
127                }).collect(),
128                Type::Split => [order-1, order-2, order-2].iter().map(|ord| {
129                    let coin = Self::get_validator_coin(
130                        ord, &mut validator_resource, &coins_seen
131                    )?;
132                    let counter = state.get_coin_counter(&coin);
133                    coins_seen.insert(coin.clone());
134                    Some(Transaction::build(rng, coin, sender.clone(), 
135                                            validator_key, counter, schema))
136                }).collect(),
137                _ => panic!("Invalid group type"),
138            };
139
140            // Extend transactions and senders if ext was added
141            if let Some(ext_trs) = ext_trs {
142                senders.extend(group_senders);
143                senders.extend(vec![validator.clone(); ext_trs.len()]);
144
145                transactions.extend(group.transactions().iter().cloned());
146                transactions.extend(ext_trs);
147
148                counter += 1;
149            }
150        }
151
152        // Return transactions and senders
153        (transactions, senders)
154    }
155
156    /// Pop coin from the resource by order ignoring specified coins.
157    fn get_validator_coin(order: &u64, resource: &mut OrderCoinsMap, 
158                          ignore_coins: &HashSet<U256>) -> Option<U256> {
159        if let Some(set) = resource.get_mut(&order) {
160            let coin_opt = set.iter().filter(|c| !ignore_coins.contains(c))
161                              .next().cloned();
162
163            if let Some(coin) = coin_opt {
164                set.remove(&coin);
165                return Some(coin);
166            }
167        }
168        None
169    }
170}