/__w/smoldot/smoldot/repo/lib/src/transactions/light_pool.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | //! Transactions pool specialized for light clients usage. |
19 | | //! |
20 | | //! See [the `pool` module](../pool) documentation for details about the transactions pool. |
21 | | //! |
22 | | //! Contrary to [`super::pool::Pool`], this data structure is opinionated towards a certain light |
23 | | //! client usage. This means: |
24 | | //! |
25 | | //! - Block bodies are initially unknown and can be added later. |
26 | | //! - Transactions included in block bodies that weren't already in the pool aren't added, and |
27 | | //! thus also don't need to be validated. |
28 | | //! - The [`LightPool`] tracks all forks, not just the best chain, so as to not require fetching |
29 | | //! again later the block bodies that are already known in case of a double re-org. |
30 | | //! |
31 | | //! # Usage |
32 | | //! |
33 | | //! A [`LightPool`] is a collection of transactions and a tree of blocks. |
34 | | //! |
35 | | //! Blocks can be added to the tree using [`LightPool::add_block`]. When a block is added, its |
36 | | //! body is unknown. You can add a body to a block using [`LightPool::set_block_body`]. The pool |
37 | | //! also tracks a best block and a finalized block. Use [`LightPool::set_best_block`] and |
38 | | //! [`LightPool::set_finalized_block`] to match the light pool with the state of the chain. |
39 | | //! |
40 | | //! Blocks that have been finalized can be removed with [`LightPool::prune_finalized_with_body`]. |
41 | | //! This method only removes blocks whose body is known. You are encouraged to track the value |
42 | | //! of [`LightPool::oldest_block_finality_lag`] and make sure that it doesn't go above a certain |
43 | | //! threshold, in order to avoid adding too many blocks to this pool. |
44 | | //! |
45 | | //! Each transaction in the pool exposes three properties: |
46 | | //! |
47 | | //! - Whether or not it has been validated, and if yes, the block against which it has been |
48 | | //! validated and the characteristics of the transaction (as provided by the runtime): the tags it |
49 | | //! provides and requires, its longevity, and its priority. See [the `validate` module](../validate) |
50 | | //! for more information. |
51 | | //! - The block of the best chain, if any, in which the transaction has been included. |
52 | | //! - A so-called user data, an opaque field controller by the API user, of type `TTx`. |
53 | | //! |
54 | | //! Use [`LightPool::add_unvalidated`] to add to the pool a transaction that should be included in |
55 | | //! a block at a later point in time. |
56 | | //! |
57 | | //! Use [`LightPool::unvalidated_transactions`] to obtain the list of transactions that should be |
58 | | //! validated. Validation should be performed using the [`validate`](../validate) module, and |
59 | | //! the result reported with [`LightPool::set_validation_result`]. |
60 | | //! |
61 | | |
62 | | use super::validate::ValidTransaction; |
63 | | use crate::chain::fork_tree; |
64 | | |
65 | | use alloc::{ |
66 | | collections::{BTreeMap, BTreeSet}, |
67 | | vec::Vec, |
68 | | }; |
69 | | use core::{fmt, iter}; |
70 | | |
71 | | mod tests; |
72 | | |
73 | | /// Configuration for [`LightPool::new`]. |
74 | | pub struct Config { |
75 | | /// Number of transactions to initially allocate memory for. |
76 | | pub transactions_capacity: usize, |
77 | | |
78 | | /// Number of blocks to initially allocate memory for. |
79 | | pub blocks_capacity: usize, |
80 | | |
81 | | /// Hash of the finalized block at initialization. |
82 | | pub finalized_block_hash: [u8; 32], |
83 | | } |
84 | | |
85 | | /// Identifier of a transaction stored within the [`LightPool`]. |
86 | | /// |
87 | | /// Identifiers can be re-used by the pool. In other words, a transaction id can compare equal to |
88 | | /// an older transaction id that is no longer in the pool. |
89 | | // |
90 | | // Implementation note: corresponds to indices within [`LightPool::transactions`]. |
91 | | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
92 | | pub struct TransactionId(usize); |
93 | | |
94 | | /// Data structure containing transactions. See the module-level documentation for more info. |
95 | | pub struct LightPool<TTx, TBl, TErr> { |
96 | | /// Actual list of transactions. |
97 | | transactions: slab::Slab<Transaction<TTx, TErr>>, |
98 | | |
99 | | /// Holds tuples of `(block_hash, transaction_id)`. When an entry is present in this set, it |
100 | | /// means that this transaction has been found in the body of this block. The value contains |
101 | | /// the index in that body where the transaction is present. |
102 | | /// |
103 | | /// Blocks are guaranteed to be found in [`LightPool::blocks_tree`]. |
104 | | /// |
105 | | /// It is guaranteed that when a `(block_hash, transaction_id)` combination is in this |
106 | | /// container, no other parent and child of this block also includes this transaction. In |
107 | | /// other words, a transaction is always included only in the earliest block of any given |
108 | | /// fork. |
109 | | transactions_by_inclusion: BTreeMap<([u8; 32], TransactionId), usize>, |
110 | | |
111 | | /// Symmetry of [`LightPool::transactions_by_inclusion`]. |
112 | | included_transactions: BTreeSet<(TransactionId, [u8; 32])>, |
113 | | |
114 | | /// Holds tuples of `(block_hash, transaction_id)`. When an entry is present in this set, it |
115 | | /// means that this transaction has been validated against this block. Contains the result of |
116 | | /// this validation. |
117 | | /// |
118 | | /// Blocks are guaranteed to be found in [`LightPool::blocks_tree`]. |
119 | | transaction_validations: BTreeMap<(TransactionId, [u8; 32]), Result<Validation, TErr>>, |
120 | | |
121 | | /// Symmetry of [`LightPool::transaction_validations`]. |
122 | | transactions_by_validation: BTreeSet<([u8; 32], TransactionId)>, |
123 | | |
124 | | /// Transaction ids (i.e. indices within [`LightPool::transactions`]) indexed by the BLAKE2 |
125 | | /// hash of the bytes of the transaction. |
126 | | by_hash: BTreeSet<([u8; 32], TransactionId)>, |
127 | | |
128 | | /// Tree of all the non-finalized and finalized blocks. This is necessary in case of a re-org |
129 | | /// (i.e. the new best block is a nephew of the previous best block) in order to know which |
130 | | /// transactions that were present in the previous best chain are still present in the new |
131 | | /// best chain. |
132 | | blocks_tree: fork_tree::ForkTree<Block<TBl>>, |
133 | | |
134 | | /// Contains all blocks in [`LightPool::blocks_tree`], indexed by their hash. |
135 | | blocks_by_id: hashbrown::HashMap<[u8; 32], fork_tree::NodeIndex, fnv::FnvBuildHasher>, |
136 | | |
137 | | /// Index of the best block in [`LightPool::blocks_tree`]. `None` iff the tree is empty |
138 | | /// or if the best block is [`LightPool::blocks_tree_root_hash`]. |
139 | | best_block_index: Option<fork_tree::NodeIndex>, |
140 | | |
141 | | /// Index of the finalized block in [`LightPool::blocks_tree`]. `None` if the tree is empty |
142 | | /// or if the finalized block is [`LightPool::blocks_tree_root_hash`]. |
143 | | finalized_block_index: Option<fork_tree::NodeIndex>, |
144 | | |
145 | | /// Hash of the block that serves as root of all the blocks in [`LightPool::blocks_tree`]. |
146 | | /// Always a finalized block. |
147 | | blocks_tree_root_hash: [u8; 32], |
148 | | |
149 | | /// Height of the block that serves as root of all the blocks in [`LightPool::blocks_tree`] |
150 | | /// minus height of the block that was passed as [`Config::finalized_block_hash`]. |
151 | | /// Always a finalized block. |
152 | | blocks_tree_root_relative_height: u64, |
153 | | } |
154 | | |
155 | | impl<TTx, TBl, TErr> LightPool<TTx, TBl, TErr> |
156 | | where |
157 | | TErr: Clone, |
158 | | { |
159 | | /// Initializes a new transactions pool. |
160 | 6 | pub fn new(config: Config) -> Self { |
161 | 6 | LightPool { |
162 | 6 | transactions: slab::Slab::with_capacity(config.transactions_capacity), |
163 | 6 | transactions_by_inclusion: BTreeMap::new(), |
164 | 6 | included_transactions: BTreeSet::new(), |
165 | 6 | transaction_validations: BTreeMap::new(), |
166 | 6 | transactions_by_validation: BTreeSet::new(), |
167 | 6 | by_hash: BTreeSet::new(), |
168 | 6 | blocks_tree: fork_tree::ForkTree::with_capacity(config.blocks_capacity), |
169 | 6 | blocks_by_id: hashbrown::HashMap::with_capacity_and_hasher( |
170 | 6 | config.blocks_capacity, |
171 | 6 | Default::default(), |
172 | 6 | ), |
173 | 6 | best_block_index: None, |
174 | 6 | finalized_block_index: None, |
175 | 6 | blocks_tree_root_hash: config.finalized_block_hash, |
176 | 6 | blocks_tree_root_relative_height: 0, |
177 | 6 | } |
178 | 6 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE3newB6_ Line | Count | Source | 160 | 6 | pub fn new(config: Config) -> Self { | 161 | 6 | LightPool { | 162 | 6 | transactions: slab::Slab::with_capacity(config.transactions_capacity), | 163 | 6 | transactions_by_inclusion: BTreeMap::new(), | 164 | 6 | included_transactions: BTreeSet::new(), | 165 | 6 | transaction_validations: BTreeMap::new(), | 166 | 6 | transactions_by_validation: BTreeSet::new(), | 167 | 6 | by_hash: BTreeSet::new(), | 168 | 6 | blocks_tree: fork_tree::ForkTree::with_capacity(config.blocks_capacity), | 169 | 6 | blocks_by_id: hashbrown::HashMap::with_capacity_and_hasher( | 170 | 6 | config.blocks_capacity, | 171 | 6 | Default::default(), | 172 | 6 | ), | 173 | 6 | best_block_index: None, | 174 | 6 | finalized_block_index: None, | 175 | 6 | blocks_tree_root_hash: config.finalized_block_hash, | 176 | 6 | blocks_tree_root_relative_height: 0, | 177 | 6 | } | 178 | 6 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE3newB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE3newB6_ |
179 | | |
180 | | /// Returns the number of transactions in the pool. |
181 | 0 | pub fn num_transactions(&self) -> usize { |
182 | 0 | self.transactions.len() |
183 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE16num_transactionsB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE16num_transactionsB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE16num_transactionsB6_ |
184 | | |
185 | | /// Inserts a new unverified transaction in the pool. |
186 | | /// |
187 | | /// Must be passed as parameter the SCALE-encoded transaction. |
188 | 6 | pub fn add_unvalidated(&mut self, scale_encoded: Vec<u8>, user_data: TTx) -> TransactionId { |
189 | 6 | let hash = blake2_hash(scale_encoded.as_ref()); |
190 | 6 | |
191 | 6 | let tx_id = TransactionId(self.transactions.insert(Transaction { |
192 | 6 | scale_encoded, |
193 | 6 | user_data, |
194 | 6 | finalized_chain_validation: None, |
195 | 6 | best_chain_validation: None, |
196 | 6 | })); |
197 | 6 | |
198 | 6 | let _was_inserted = self.by_hash.insert((hash, tx_id)); |
199 | 6 | debug_assert!(_was_inserted); |
200 | | |
201 | 6 | tx_id |
202 | 6 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE15add_unvalidatedB6_ Line | Count | Source | 188 | 6 | pub fn add_unvalidated(&mut self, scale_encoded: Vec<u8>, user_data: TTx) -> TransactionId { | 189 | 6 | let hash = blake2_hash(scale_encoded.as_ref()); | 190 | 6 | | 191 | 6 | let tx_id = TransactionId(self.transactions.insert(Transaction { | 192 | 6 | scale_encoded, | 193 | 6 | user_data, | 194 | 6 | finalized_chain_validation: None, | 195 | 6 | best_chain_validation: None, | 196 | 6 | })); | 197 | 6 | | 198 | 6 | let _was_inserted = self.by_hash.insert((hash, tx_id)); | 199 | 6 | debug_assert!(_was_inserted); | 200 | | | 201 | 6 | tx_id | 202 | 6 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE15add_unvalidatedB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE15add_unvalidatedB6_ |
203 | | |
204 | | /// Removes from the pool the transaction with the given identifier. |
205 | | /// |
206 | | /// # Panic |
207 | | /// |
208 | | /// Panics if the identifier is invalid. |
209 | | /// |
210 | | #[track_caller] |
211 | 0 | pub fn remove_transaction(&mut self, id: TransactionId) -> (Vec<u8>, TTx) { |
212 | 0 | let tx = self.transactions.remove(id.0); // Panics if `id` is invalid. |
213 | 0 |
|
214 | 0 | let blocks_included = self |
215 | 0 | .included_transactions |
216 | 0 | .range((id, [0; 32])..=(id, [0xff; 32])) |
217 | 0 | .map(|(_, block)| *block) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPoolpppE18remove_transaction0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE18remove_transaction0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE18remove_transaction0B8_ |
218 | 0 | .collect::<Vec<_>>(); |
219 | 0 |
|
220 | 0 | let blocks_validated = self |
221 | 0 | .transaction_validations |
222 | 0 | .range((id, [0; 32])..=(id, [0xff; 32])) |
223 | 0 | .map(|((_, block), _)| *block) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPoolpppE18remove_transactions_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE18remove_transactions_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE18remove_transactions_0B8_ |
224 | 0 | .collect::<Vec<_>>(); |
225 | | |
226 | 0 | for block_hash in blocks_included { |
227 | 0 | let _removed = self.included_transactions.remove(&(id, block_hash)); |
228 | 0 | debug_assert!(_removed); |
229 | 0 | let _removed = self.transactions_by_inclusion.remove(&(block_hash, id)); |
230 | 0 | debug_assert!(_removed.is_some()); |
231 | | } |
232 | | |
233 | 0 | for block_hash in blocks_validated { |
234 | 0 | let _removed = self.transaction_validations.remove(&(id, block_hash)); |
235 | 0 | debug_assert!(_removed.is_some()); |
236 | 0 | let _removed = self.transactions_by_validation.remove(&(block_hash, id)); |
237 | 0 | debug_assert!(_removed); |
238 | | } |
239 | | |
240 | 0 | let _removed = self.by_hash.remove(&(blake2_hash(&tx.scale_encoded), id)); |
241 | 0 | debug_assert!(_removed); |
242 | | |
243 | 0 | (tx.scale_encoded, tx.user_data) |
244 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE18remove_transactionB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE18remove_transactionB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE18remove_transactionB6_ |
245 | | |
246 | | /// Returns a list of transactions whose state is "not validated", and their user data. |
247 | | /// |
248 | | /// These transactions should always be validated against the current best block. |
249 | 11 | pub fn unvalidated_transactions( |
250 | 11 | &'_ self, |
251 | 11 | ) -> impl Iterator<Item = (TransactionId, &'_ TTx)> + '_ { |
252 | 11 | let best_block_relative_height = match self.best_block_index { |
253 | 10 | Some(idx) => self.blocks_tree.get(idx).unwrap().relative_block_height, |
254 | 1 | None => self.blocks_tree_root_relative_height, |
255 | | }; |
256 | | |
257 | | // Note that this iterates over all transactions every time, which seems unoptimal, but |
258 | | // is also way easier to implement and probably doesn't cost too much in practice. |
259 | 11 | self.transactions |
260 | 11 | .iter() |
261 | 11 | .filter(move |(_, tx)| match &tx.best_chain_validation9 { |
262 | 2 | None => true, |
263 | 9 | Some(Ok(v)) => v.longevity_relative_block_height < best_block_relative_height, |
264 | 0 | Some(Err(_)) => false, |
265 | 11 | }) _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE24unvalidated_transactions0B8_ Line | Count | Source | 261 | 11 | .filter(move |(_, tx)| match &tx.best_chain_validation9 { | 262 | 2 | None => true, | 263 | 9 | Some(Ok(v)) => v.longevity_relative_block_height < best_block_relative_height, | 264 | 0 | Some(Err(_)) => false, | 265 | 11 | }) |
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE24unvalidated_transactions0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE24unvalidated_transactions0B8_ |
266 | 11 | .map(move |(tx_id, _)| { |
267 | 5 | let tx = self.transactions.get(tx_id).unwrap(); |
268 | 5 | (TransactionId(tx_id), &tx.user_data) |
269 | 11 | }) _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE24unvalidated_transactionss_0B8_ Line | Count | Source | 266 | 5 | .map(move |(tx_id, _)| { | 267 | 5 | let tx = self.transactions.get(tx_id).unwrap(); | 268 | 5 | (TransactionId(tx_id), &tx.user_data) | 269 | 5 | }) |
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE24unvalidated_transactionss_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE24unvalidated_transactionss_0B8_ |
270 | 11 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE24unvalidated_transactionsB6_ Line | Count | Source | 249 | 11 | pub fn unvalidated_transactions( | 250 | 11 | &'_ self, | 251 | 11 | ) -> impl Iterator<Item = (TransactionId, &'_ TTx)> + '_ { | 252 | 11 | let best_block_relative_height = match self.best_block_index { | 253 | 10 | Some(idx) => self.blocks_tree.get(idx).unwrap().relative_block_height, | 254 | 1 | None => self.blocks_tree_root_relative_height, | 255 | | }; | 256 | | | 257 | | // Note that this iterates over all transactions every time, which seems unoptimal, but | 258 | | // is also way easier to implement and probably doesn't cost too much in practice. | 259 | 11 | self.transactions | 260 | 11 | .iter() | 261 | 11 | .filter(move |(_, tx)| match &tx.best_chain_validation { | 262 | | None => true, | 263 | | Some(Ok(v)) => v.longevity_relative_block_height < best_block_relative_height, | 264 | | Some(Err(_)) => false, | 265 | 11 | }) | 266 | 11 | .map(move |(tx_id, _)| { | 267 | | let tx = self.transactions.get(tx_id).unwrap(); | 268 | | (TransactionId(tx_id), &tx.user_data) | 269 | 11 | }) | 270 | 11 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE24unvalidated_transactionsB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE24unvalidated_transactionsB6_ |
271 | | |
272 | | /// Returns the list of all transactions within the pool. |
273 | 0 | pub fn transactions_iter(&'_ self) -> impl Iterator<Item = (TransactionId, &'_ TTx)> + '_ { |
274 | 0 | self.transactions |
275 | 0 | .iter() |
276 | 0 | .map(|(id, tx)| (TransactionId(id), &tx.user_data)) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPoolpppE17transactions_iter0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE17transactions_iter0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE17transactions_iter0B8_ |
277 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE17transactions_iterB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE17transactions_iterB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE17transactions_iterB6_ |
278 | | |
279 | | /// Returns the list of all transactions within the pool. |
280 | 0 | pub fn transactions_iter_mut( |
281 | 0 | &'_ mut self, |
282 | 0 | ) -> impl Iterator<Item = (TransactionId, &'_ mut TTx)> + '_ { |
283 | 0 | self.transactions |
284 | 0 | .iter_mut() |
285 | 0 | .map(|(id, tx)| (TransactionId(id), &mut tx.user_data)) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPoolpppE21transactions_iter_mut0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE21transactions_iter_mut0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE21transactions_iter_mut0B8_ |
286 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE21transactions_iter_mutB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE21transactions_iter_mutB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE21transactions_iter_mutB6_ |
287 | | |
288 | | /// Returns the user data associated with a given transaction. |
289 | | /// |
290 | | /// Returns `None` if the identifier is invalid. |
291 | 0 | pub fn transaction_user_data(&self, id: TransactionId) -> Option<&TTx> { |
292 | 0 | Some(&self.transactions.get(id.0)?.user_data) |
293 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE21transaction_user_dataB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE21transaction_user_dataB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE21transaction_user_dataB6_ |
294 | | |
295 | | /// Returns the user data associated with a given transaction. |
296 | | /// |
297 | | /// Returns `None` if the identifier is invalid. |
298 | 0 | pub fn transaction_user_data_mut(&mut self, id: TransactionId) -> Option<&mut TTx> { |
299 | 0 | Some(&mut self.transactions.get_mut(id.0)?.user_data) |
300 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE25transaction_user_data_mutB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE25transaction_user_data_mutB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE25transaction_user_data_mutB6_ |
301 | | |
302 | | /// Returns the bytes associated with a given transaction. |
303 | | /// |
304 | | /// Returns `None` if the identifier is invalid. |
305 | 0 | pub fn scale_encoding(&self, id: TransactionId) -> Option<&[u8]> { |
306 | 0 | Some(&self.transactions.get(id.0)?.scale_encoded) |
307 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE14scale_encodingB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE14scale_encodingB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE14scale_encodingB6_ |
308 | | |
309 | | /// Tries to find the transactions in the pool whose bytes are `scale_encoded`. |
310 | 0 | pub fn find_transaction( |
311 | 0 | &'_ self, |
312 | 0 | scale_encoded: &[u8], |
313 | 0 | ) -> impl Iterator<Item = TransactionId> + '_ { |
314 | 0 | let hash = blake2_hash(scale_encoded); |
315 | 0 | self.by_hash |
316 | 0 | .range((hash, TransactionId(usize::MIN))..=(hash, TransactionId(usize::MAX))) |
317 | 0 | .map(|(_, tx_id)| *tx_id) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPoolpppE16find_transaction0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE16find_transaction0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE16find_transaction0B8_ |
318 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE16find_transactionB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE16find_transactionB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE16find_transactionB6_ |
319 | | |
320 | | /// Returns `true` if the given transaction has been included in the past in an ancestor of |
321 | | /// the current best block. |
322 | | /// |
323 | | /// # Panic |
324 | | /// |
325 | | /// Panics if the transaction with the given id is invalid. |
326 | | /// |
327 | 0 | pub fn is_included_best_chain(&self, id: TransactionId) -> bool { |
328 | 0 | let mut iter = self |
329 | 0 | .included_transactions |
330 | 0 | .range((id, [0; 32])..=(id, [0xff; 32])) |
331 | 0 | .filter(|(_, block_hash)| { |
332 | 0 | let block_index = *self.blocks_by_id.get(block_hash).unwrap(); |
333 | 0 | self.best_block_index.map_or(false, |best_idx| { |
334 | 0 | self.blocks_tree.is_ancestor(block_index, best_idx) |
335 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB6_9LightPoolpppE22is_included_best_chain00Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB6_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1f_5BlockNtB1f_14InvalidOrErrorE22is_included_best_chain00B2u_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB6_9LightPoolpppE22is_included_best_chain00Ba_ |
336 | 0 | }); Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPoolpppE22is_included_best_chain0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE22is_included_best_chain0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE22is_included_best_chain0B8_ |
337 | 0 |
|
338 | 0 | let outcome = iter.next().is_some(); |
339 | 0 | if outcome { |
340 | 0 | debug_assert!(iter.next().is_none()); |
341 | 0 | } |
342 | 0 | outcome |
343 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE22is_included_best_chainB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE22is_included_best_chainB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE22is_included_best_chainB6_ |
344 | | |
345 | | /// Returns `true` if the given transaction has been successfully validated in the past against |
346 | | /// an ancestor of the best block and is still within its longevity period. |
347 | | /// |
348 | | /// Returns `false` either if the given transaction hasn't been validated, or if its validation |
349 | | /// resulted in an error. |
350 | | /// |
351 | | /// > **Note**: This function might return `true` or `false` independently of whether or not |
352 | | /// > the transaction has already been included in the best chain. You might want |
353 | | /// > to call [`LightPool::is_included_best_chain`] as well. |
354 | | /// |
355 | | /// # Panic |
356 | | /// |
357 | | /// Panics if the transaction with the given id is invalid. |
358 | | /// |
359 | 11 | pub fn is_valid_against_best_block(&self, id: TransactionId) -> bool { |
360 | 11 | let best_block_relative_height = match self.best_block_index { |
361 | 10 | Some(idx) => self.blocks_tree.get(idx).unwrap().relative_block_height, |
362 | 1 | None => self.blocks_tree_root_relative_height, |
363 | | }; |
364 | | |
365 | 11 | match &self.transactions[id.0].best_chain_validation { |
366 | 2 | None => false, |
367 | 9 | Some(Ok(v)) => v.longevity_relative_block_height >= best_block_relative_height, |
368 | 0 | Some(Err(_)) => false, |
369 | | } |
370 | 11 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE27is_valid_against_best_blockB6_ Line | Count | Source | 359 | 11 | pub fn is_valid_against_best_block(&self, id: TransactionId) -> bool { | 360 | 11 | let best_block_relative_height = match self.best_block_index { | 361 | 10 | Some(idx) => self.blocks_tree.get(idx).unwrap().relative_block_height, | 362 | 1 | None => self.blocks_tree_root_relative_height, | 363 | | }; | 364 | | | 365 | 11 | match &self.transactions[id.0].best_chain_validation { | 366 | 2 | None => false, | 367 | 9 | Some(Ok(v)) => v.longevity_relative_block_height >= best_block_relative_height, | 368 | 0 | Some(Err(_)) => false, | 369 | | } | 370 | 11 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE27is_valid_against_best_blockB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE27is_valid_against_best_blockB6_ |
371 | | |
372 | | /// Returns a list of transactions which have been validated against an ancestor of the current |
373 | | /// best block but have encountered an error during their validation (i.e. |
374 | | /// [`LightPool::set_validation_result`] was called with `Err`). |
375 | | /// |
376 | | /// > **Note**: Contrary to [`LightPool::invalid_transactions_finalized_block`], it is |
377 | | /// > possible for the transaction to become valid again in the future if a reorg |
378 | | /// > happens and removes the block the transaction was validated against. |
379 | 0 | pub fn invalid_transactions_best_block( |
380 | 0 | &'_ self, |
381 | 0 | ) -> impl Iterator<Item = (TransactionId, &'_ TTx, &'_ TErr)> + '_ { |
382 | 0 | // Note that this iterates over all transactions every time, which seems unoptimal, but |
383 | 0 | // is also way easier to implement and probably doesn't cost too much in practice. |
384 | 0 | self.transactions |
385 | 0 | .iter() |
386 | 0 | .filter_map(move |(tx_id, tx)| match &tx.best_chain_validation { |
387 | 0 | Some(Err(err)) => Some((TransactionId(tx_id), &tx.user_data, err)), |
388 | 0 | _ => None, |
389 | 0 | }) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPoolpppE31invalid_transactions_best_block0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE31invalid_transactions_best_block0B8_ |
390 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE31invalid_transactions_best_blockB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE31invalid_transactions_best_blockB6_ |
391 | | |
392 | | /// Returns a list of transactions which have been validated against an ancestor of the current |
393 | | /// finalized block but have encountered an error during their validation (i.e. |
394 | | /// [`LightPool::set_validation_result`] was called with `Err`). |
395 | | /// |
396 | | /// > **Note**: Once a transaction is considered as invalid, it can be assumed that this |
397 | | /// > transaction will be invalid if verified against any of the descendants of the |
398 | | /// > block it was verified against. In other words, it can assumed that transactions |
399 | | /// > returned here will never be valid. |
400 | 0 | pub fn invalid_transactions_finalized_block( |
401 | 0 | &'_ self, |
402 | 0 | ) -> impl Iterator<Item = (TransactionId, &'_ TTx, &'_ TErr)> + '_ { |
403 | 0 | // Note that this iterates over all transactions every time, which seems unoptimal, but |
404 | 0 | // is also way easier to implement and probably doesn't cost too much in practice. |
405 | 0 | self.transactions.iter().filter_map(move |(tx_id, tx)| { |
406 | 0 | match &tx.finalized_chain_validation { |
407 | 0 | Some((_, Err(err))) => Some((TransactionId(tx_id), &tx.user_data, err)), |
408 | 0 | _ => None, |
409 | | } |
410 | 0 | }) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPoolpppE36invalid_transactions_finalized_block0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE36invalid_transactions_finalized_block0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE36invalid_transactions_finalized_block0B8_ |
411 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE36invalid_transactions_finalized_blockB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE36invalid_transactions_finalized_blockB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE36invalid_transactions_finalized_blockB6_ |
412 | | |
413 | | /// Sets the outcome of validating the transaction with the given identifier. |
414 | | /// |
415 | | /// The block hash must be the block hash against which the transaction has been |
416 | | /// validated. |
417 | | /// |
418 | | /// # Panic |
419 | | /// |
420 | | /// Panics if the transaction with the given id is invalid. |
421 | | /// Panics if no block with that hash has been inserted before, or the block has been pruned. |
422 | | /// |
423 | 3 | pub fn set_validation_result( |
424 | 3 | &mut self, |
425 | 3 | id: TransactionId, |
426 | 3 | block_hash_validated_against: &[u8; 32], |
427 | 3 | result: Result<ValidTransaction, TErr>, |
428 | 3 | ) { |
429 | | // Make sure that the block exists. |
430 | 3 | let block_index = if *block_hash_validated_against == self.blocks_tree_root_hash { |
431 | 1 | None |
432 | | } else { |
433 | 2 | Some(*self.blocks_by_id.get(block_hash_validated_against).unwrap()) |
434 | | }; |
435 | | |
436 | | // Height of `block_index`, minus the height of the finalized block passed in the |
437 | | // original `Config`. |
438 | 3 | let block_relative_height = match block_index { |
439 | 2 | Some(block_index) => { |
440 | 2 | self.blocks_tree |
441 | 2 | .get(block_index) |
442 | 2 | .unwrap() |
443 | 2 | .relative_block_height |
444 | | } |
445 | 1 | None => self.blocks_tree_root_relative_height, |
446 | | }; |
447 | | |
448 | | // Make sure that the transaction exists. |
449 | 3 | assert!(self.transactions.contains(id.0)); |
450 | | |
451 | | // Determine if block the transaction was validated against is best and/or finalized. |
452 | 3 | let block_is_in_best_chain = match (self.best_block_index, block_index) { |
453 | 1 | (None, None) => true, |
454 | 0 | (Some(_), None) => true, |
455 | 0 | (None, Some(_)) => false, |
456 | 2 | (Some(b), Some(i)) => self.blocks_tree.is_ancestor(i, b), |
457 | | }; |
458 | 3 | let block_is_finalized = match (self.finalized_block_index, block_index) { |
459 | 1 | (None, None) => true, |
460 | 0 | (Some(_), None) => true, |
461 | 2 | (None, Some(_)) => false, |
462 | 0 | (Some(b), Some(i)) => self.blocks_tree.is_ancestor(i, b), |
463 | | }; |
464 | 3 | debug_assert!(block_is_in_best_chain || !block_is_finalized0 ); |
465 | | |
466 | | // Convert the validation result into something more concise and useful for this data |
467 | | // structure. |
468 | 3 | let result = match result { |
469 | 0 | Err(err) => Err(err), |
470 | 3 | Ok(v) => Ok(Validation { |
471 | 3 | longevity_relative_block_height: block_relative_height |
472 | 3 | .saturating_add(v.longevity.get()), |
473 | 3 | propagate: v.propagate, |
474 | 3 | }), |
475 | | }; |
476 | | |
477 | | // Update the transaction's validation status. |
478 | 3 | if block_is_finalized { |
479 | 1 | self.transactions[id.0].finalized_chain_validation = |
480 | 1 | Some((block_relative_height, result.clone())); |
481 | 2 | } |
482 | | |
483 | 3 | if block_is_in_best_chain { |
484 | 3 | // TODO: no /!\ there could be another block with a validation that is even higher |
485 | 3 | self.transactions[id.0].best_chain_validation = Some(result.clone()); |
486 | 3 | }0 |
487 | | |
488 | 3 | self.transaction_validations |
489 | 3 | .insert((id, *block_hash_validated_against), result); |
490 | 3 | self.transactions_by_validation |
491 | 3 | .insert((*block_hash_validated_against, id)); |
492 | 3 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE21set_validation_resultB6_ Line | Count | Source | 423 | 3 | pub fn set_validation_result( | 424 | 3 | &mut self, | 425 | 3 | id: TransactionId, | 426 | 3 | block_hash_validated_against: &[u8; 32], | 427 | 3 | result: Result<ValidTransaction, TErr>, | 428 | 3 | ) { | 429 | | // Make sure that the block exists. | 430 | 3 | let block_index = if *block_hash_validated_against == self.blocks_tree_root_hash { | 431 | 1 | None | 432 | | } else { | 433 | 2 | Some(*self.blocks_by_id.get(block_hash_validated_against).unwrap()) | 434 | | }; | 435 | | | 436 | | // Height of `block_index`, minus the height of the finalized block passed in the | 437 | | // original `Config`. | 438 | 3 | let block_relative_height = match block_index { | 439 | 2 | Some(block_index) => { | 440 | 2 | self.blocks_tree | 441 | 2 | .get(block_index) | 442 | 2 | .unwrap() | 443 | 2 | .relative_block_height | 444 | | } | 445 | 1 | None => self.blocks_tree_root_relative_height, | 446 | | }; | 447 | | | 448 | | // Make sure that the transaction exists. | 449 | 3 | assert!(self.transactions.contains(id.0)); | 450 | | | 451 | | // Determine if block the transaction was validated against is best and/or finalized. | 452 | 3 | let block_is_in_best_chain = match (self.best_block_index, block_index) { | 453 | 1 | (None, None) => true, | 454 | 0 | (Some(_), None) => true, | 455 | 0 | (None, Some(_)) => false, | 456 | 2 | (Some(b), Some(i)) => self.blocks_tree.is_ancestor(i, b), | 457 | | }; | 458 | 3 | let block_is_finalized = match (self.finalized_block_index, block_index) { | 459 | 1 | (None, None) => true, | 460 | 0 | (Some(_), None) => true, | 461 | 2 | (None, Some(_)) => false, | 462 | 0 | (Some(b), Some(i)) => self.blocks_tree.is_ancestor(i, b), | 463 | | }; | 464 | 3 | debug_assert!(block_is_in_best_chain || !block_is_finalized0 ); | 465 | | | 466 | | // Convert the validation result into something more concise and useful for this data | 467 | | // structure. | 468 | 3 | let result = match result { | 469 | 0 | Err(err) => Err(err), | 470 | 3 | Ok(v) => Ok(Validation { | 471 | 3 | longevity_relative_block_height: block_relative_height | 472 | 3 | .saturating_add(v.longevity.get()), | 473 | 3 | propagate: v.propagate, | 474 | 3 | }), | 475 | | }; | 476 | | | 477 | | // Update the transaction's validation status. | 478 | 3 | if block_is_finalized { | 479 | 1 | self.transactions[id.0].finalized_chain_validation = | 480 | 1 | Some((block_relative_height, result.clone())); | 481 | 2 | } | 482 | | | 483 | 3 | if block_is_in_best_chain { | 484 | 3 | // TODO: no /!\ there could be another block with a validation that is even higher | 485 | 3 | self.transactions[id.0].best_chain_validation = Some(result.clone()); | 486 | 3 | }0 | 487 | | | 488 | 3 | self.transaction_validations | 489 | 3 | .insert((id, *block_hash_validated_against), result); | 490 | 3 | self.transactions_by_validation | 491 | 3 | .insert((*block_hash_validated_against, id)); | 492 | 3 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE21set_validation_resultB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE21set_validation_resultB6_ |
493 | | |
494 | | /// Adds a block to the collection of blocks. |
495 | | /// |
496 | | /// Has no effect if that block was already present in the collection. |
497 | | /// |
498 | | /// If there is no transaction in the pool, then the block is marked as "doesn't need a body", |
499 | | /// meaning that it will not be returned by [`LightPool::missing_block_bodies`]. |
500 | | /// |
501 | | /// # Panic |
502 | | /// |
503 | | /// Panics if the parent block cannot be found in the collection. |
504 | | /// |
505 | 15 | pub fn add_block(&mut self, hash: [u8; 32], parent_hash: &[u8; 32], user_data: TBl) { |
506 | 15 | let (parent_index_in_tree, parent_relative_height) = |
507 | 15 | if *parent_hash == self.blocks_tree_root_hash { |
508 | 7 | (None, self.blocks_tree_root_relative_height) |
509 | | } else { |
510 | | // The transactions service tracks all new blocks. |
511 | | // The parent of each new best block must therefore already be in the tree. |
512 | 8 | let idx = *self.blocks_by_id.get(parent_hash).unwrap(); |
513 | 8 | ( |
514 | 8 | Some(idx), |
515 | 8 | self.blocks_tree.get(idx).unwrap().relative_block_height, |
516 | 8 | ) |
517 | | }; |
518 | | |
519 | 15 | let entry = match self.blocks_by_id.entry(hash) { |
520 | 0 | hashbrown::hash_map::Entry::Occupied(_) => return, |
521 | 15 | hashbrown::hash_map::Entry::Vacant(e) => e, |
522 | | }; |
523 | | |
524 | 15 | let block_index = self.blocks_tree.insert( |
525 | 15 | parent_index_in_tree, |
526 | 15 | Block { |
527 | 15 | hash, |
528 | 15 | body: if self.transactions.is_empty() { |
529 | 0 | BodyState::NotNeeded |
530 | | } else { |
531 | 15 | BodyState::Needed |
532 | | }, |
533 | 15 | relative_block_height: parent_relative_height + 1, |
534 | 15 | user_data, |
535 | 15 | }, |
536 | 15 | ); |
537 | 15 | |
538 | 15 | entry.insert(block_index); |
539 | 15 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE9add_blockB6_ Line | Count | Source | 505 | 15 | pub fn add_block(&mut self, hash: [u8; 32], parent_hash: &[u8; 32], user_data: TBl) { | 506 | 15 | let (parent_index_in_tree, parent_relative_height) = | 507 | 15 | if *parent_hash == self.blocks_tree_root_hash { | 508 | 7 | (None, self.blocks_tree_root_relative_height) | 509 | | } else { | 510 | | // The transactions service tracks all new blocks. | 511 | | // The parent of each new best block must therefore already be in the tree. | 512 | 8 | let idx = *self.blocks_by_id.get(parent_hash).unwrap(); | 513 | 8 | ( | 514 | 8 | Some(idx), | 515 | 8 | self.blocks_tree.get(idx).unwrap().relative_block_height, | 516 | 8 | ) | 517 | | }; | 518 | | | 519 | 15 | let entry = match self.blocks_by_id.entry(hash) { | 520 | 0 | hashbrown::hash_map::Entry::Occupied(_) => return, | 521 | 15 | hashbrown::hash_map::Entry::Vacant(e) => e, | 522 | | }; | 523 | | | 524 | 15 | let block_index = self.blocks_tree.insert( | 525 | 15 | parent_index_in_tree, | 526 | 15 | Block { | 527 | 15 | hash, | 528 | 15 | body: if self.transactions.is_empty() { | 529 | 0 | BodyState::NotNeeded | 530 | | } else { | 531 | 15 | BodyState::Needed | 532 | | }, | 533 | 15 | relative_block_height: parent_relative_height + 1, | 534 | 15 | user_data, | 535 | 15 | }, | 536 | 15 | ); | 537 | 15 | | 538 | 15 | entry.insert(block_index); | 539 | 15 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE9add_blockB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE9add_blockB6_ |
540 | | |
541 | | /// Sets the passed block as the new best block of the chain. |
542 | | /// |
543 | | /// # Panic |
544 | | /// |
545 | | /// Panics if no block with the given hash has been inserted before. |
546 | | /// |
547 | | #[must_use] |
548 | 16 | pub fn set_best_block(&mut self, new_best_block_hash: &[u8; 32]) -> SetBestBlock { |
549 | | // Index of the provided block within the tree. `None` if equal to `blocks_tree_root_hash`. |
550 | 16 | let new_best_block_index = if *new_best_block_hash == self.blocks_tree_root_hash { |
551 | 0 | None |
552 | | } else { |
553 | 16 | Some(*self.blocks_by_id.get(new_best_block_hash).unwrap()) |
554 | | }; |
555 | | |
556 | | // Iterators over the potential re-org. Used below to report the transaction status |
557 | | // updates. |
558 | 16 | let (old_best_to_common_ancestor, common_ancestor_to_new_best) = |
559 | 16 | match (self.best_block_index, new_best_block_index) { |
560 | 10 | (Some(old_best_index), Some(new_best_block_index)) => { |
561 | 10 | let (ascend, descend) = self |
562 | 10 | .blocks_tree |
563 | 10 | .ascend_and_descend(old_best_index, new_best_block_index); |
564 | 10 | ( |
565 | 10 | either::Left(either::Left(ascend)), |
566 | 10 | either::Left(either::Left(descend)), |
567 | 10 | ) |
568 | | } |
569 | 0 | (Some(old_best_index), None) => { |
570 | 0 | let ascend = self.blocks_tree.node_to_root_path(old_best_index); |
571 | 0 | let descend = iter::empty::<fork_tree::NodeIndex>(); |
572 | 0 | (either::Left(either::Right(ascend)), either::Right(descend)) |
573 | | } |
574 | 6 | (None, Some(new_best_block_index)) => { |
575 | 6 | let ascend = iter::empty::<fork_tree::NodeIndex>(); |
576 | 6 | let descend = self.blocks_tree.root_to_node_path(new_best_block_index); |
577 | 6 | (either::Right(ascend), either::Left(either::Right(descend))) |
578 | | } |
579 | | (None, None) => { |
580 | 0 | let ascend = iter::empty::<fork_tree::NodeIndex>(); |
581 | 0 | let descend = iter::empty::<fork_tree::NodeIndex>(); |
582 | 0 | (either::Right(ascend), either::Right(descend)) |
583 | | } |
584 | | }; |
585 | | |
586 | 16 | let mut retracted_transactions = Vec::new(); |
587 | 18 | for to_retract_index2 in old_best_to_common_ancestor { |
588 | 2 | let retracted = self.blocks_tree.get(to_retract_index).unwrap(); |
589 | | |
590 | 2 | for ((_, tx_id), index1 ) in self.transactions_by_inclusion.range( |
591 | 2 | (retracted.hash, TransactionId(usize::MIN)) |
592 | 2 | ..=(retracted.hash, TransactionId(usize::MAX)), |
593 | 2 | ) { |
594 | 1 | retracted_transactions.push((*tx_id, retracted.hash, *index)); |
595 | 1 | } |
596 | | |
597 | 2 | for (_, tx_id0 ) in self.transactions_by_validation.range( |
598 | 2 | (retracted.hash, TransactionId(usize::MIN)) |
599 | 2 | ..=(retracted.hash, TransactionId(usize::MAX)), |
600 | 2 | ) { |
601 | 0 | self.transactions[tx_id.0].best_chain_validation = self.transactions[tx_id.0] |
602 | 0 | .finalized_chain_validation |
603 | 0 | .as_ref() |
604 | 0 | .map(|(_, v)| v.clone()); Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE14set_best_block0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE14set_best_block0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE14set_best_block0B8_ |
605 | 0 |
|
606 | 0 | // TODO: check if any block between finalized and to_retract_index has a validation |
607 | 0 | } |
608 | | } |
609 | | |
610 | 16 | let mut included_transactions = Vec::new(); |
611 | 32 | for to_include_index16 in common_ancestor_to_new_best { |
612 | 16 | let included = self.blocks_tree.get(to_include_index).unwrap(); |
613 | | |
614 | 16 | for ((_, tx_id), index3 ) in self.transactions_by_inclusion.range( |
615 | 16 | (included.hash, TransactionId(usize::MIN)) |
616 | 16 | ..=(included.hash, TransactionId(usize::MAX)), |
617 | 16 | ) { |
618 | 3 | included_transactions.push((*tx_id, included.hash, *index)); |
619 | 3 | } |
620 | | |
621 | 16 | for (_, tx_id0 ) in self.transactions_by_validation.range( |
622 | 16 | (included.hash, TransactionId(usize::MIN)) |
623 | 16 | ..=(included.hash, TransactionId(usize::MAX)), |
624 | 16 | ) { |
625 | 0 | let validation = self |
626 | 0 | .transaction_validations |
627 | 0 | .get(&(*tx_id, included.hash)) |
628 | 0 | .unwrap() |
629 | 0 | .clone(); |
630 | 0 | self.transactions[tx_id.0].best_chain_validation = Some(validation); |
631 | 0 | } |
632 | | } |
633 | | |
634 | 16 | self.best_block_index = new_best_block_index; |
635 | 16 | |
636 | 16 | SetBestBlock { |
637 | 16 | retracted_transactions, |
638 | 16 | included_transactions, |
639 | 16 | } |
640 | 16 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE14set_best_blockB6_ Line | Count | Source | 548 | 16 | pub fn set_best_block(&mut self, new_best_block_hash: &[u8; 32]) -> SetBestBlock { | 549 | | // Index of the provided block within the tree. `None` if equal to `blocks_tree_root_hash`. | 550 | 16 | let new_best_block_index = if *new_best_block_hash == self.blocks_tree_root_hash { | 551 | 0 | None | 552 | | } else { | 553 | 16 | Some(*self.blocks_by_id.get(new_best_block_hash).unwrap()) | 554 | | }; | 555 | | | 556 | | // Iterators over the potential re-org. Used below to report the transaction status | 557 | | // updates. | 558 | 16 | let (old_best_to_common_ancestor, common_ancestor_to_new_best) = | 559 | 16 | match (self.best_block_index, new_best_block_index) { | 560 | 10 | (Some(old_best_index), Some(new_best_block_index)) => { | 561 | 10 | let (ascend, descend) = self | 562 | 10 | .blocks_tree | 563 | 10 | .ascend_and_descend(old_best_index, new_best_block_index); | 564 | 10 | ( | 565 | 10 | either::Left(either::Left(ascend)), | 566 | 10 | either::Left(either::Left(descend)), | 567 | 10 | ) | 568 | | } | 569 | 0 | (Some(old_best_index), None) => { | 570 | 0 | let ascend = self.blocks_tree.node_to_root_path(old_best_index); | 571 | 0 | let descend = iter::empty::<fork_tree::NodeIndex>(); | 572 | 0 | (either::Left(either::Right(ascend)), either::Right(descend)) | 573 | | } | 574 | 6 | (None, Some(new_best_block_index)) => { | 575 | 6 | let ascend = iter::empty::<fork_tree::NodeIndex>(); | 576 | 6 | let descend = self.blocks_tree.root_to_node_path(new_best_block_index); | 577 | 6 | (either::Right(ascend), either::Left(either::Right(descend))) | 578 | | } | 579 | | (None, None) => { | 580 | 0 | let ascend = iter::empty::<fork_tree::NodeIndex>(); | 581 | 0 | let descend = iter::empty::<fork_tree::NodeIndex>(); | 582 | 0 | (either::Right(ascend), either::Right(descend)) | 583 | | } | 584 | | }; | 585 | | | 586 | 16 | let mut retracted_transactions = Vec::new(); | 587 | 18 | for to_retract_index2 in old_best_to_common_ancestor { | 588 | 2 | let retracted = self.blocks_tree.get(to_retract_index).unwrap(); | 589 | | | 590 | 2 | for ((_, tx_id), index1 ) in self.transactions_by_inclusion.range( | 591 | 2 | (retracted.hash, TransactionId(usize::MIN)) | 592 | 2 | ..=(retracted.hash, TransactionId(usize::MAX)), | 593 | 2 | ) { | 594 | 1 | retracted_transactions.push((*tx_id, retracted.hash, *index)); | 595 | 1 | } | 596 | | | 597 | 2 | for (_, tx_id0 ) in self.transactions_by_validation.range( | 598 | 2 | (retracted.hash, TransactionId(usize::MIN)) | 599 | 2 | ..=(retracted.hash, TransactionId(usize::MAX)), | 600 | 2 | ) { | 601 | 0 | self.transactions[tx_id.0].best_chain_validation = self.transactions[tx_id.0] | 602 | 0 | .finalized_chain_validation | 603 | 0 | .as_ref() | 604 | 0 | .map(|(_, v)| v.clone()); | 605 | 0 |
| 606 | 0 | // TODO: check if any block between finalized and to_retract_index has a validation | 607 | 0 | } | 608 | | } | 609 | | | 610 | 16 | let mut included_transactions = Vec::new(); | 611 | 32 | for to_include_index16 in common_ancestor_to_new_best { | 612 | 16 | let included = self.blocks_tree.get(to_include_index).unwrap(); | 613 | | | 614 | 16 | for ((_, tx_id), index3 ) in self.transactions_by_inclusion.range( | 615 | 16 | (included.hash, TransactionId(usize::MIN)) | 616 | 16 | ..=(included.hash, TransactionId(usize::MAX)), | 617 | 16 | ) { | 618 | 3 | included_transactions.push((*tx_id, included.hash, *index)); | 619 | 3 | } | 620 | | | 621 | 16 | for (_, tx_id0 ) in self.transactions_by_validation.range( | 622 | 16 | (included.hash, TransactionId(usize::MIN)) | 623 | 16 | ..=(included.hash, TransactionId(usize::MAX)), | 624 | 16 | ) { | 625 | 0 | let validation = self | 626 | 0 | .transaction_validations | 627 | 0 | .get(&(*tx_id, included.hash)) | 628 | 0 | .unwrap() | 629 | 0 | .clone(); | 630 | 0 | self.transactions[tx_id.0].best_chain_validation = Some(validation); | 631 | 0 | } | 632 | | } | 633 | | | 634 | 16 | self.best_block_index = new_best_block_index; | 635 | 16 | | 636 | 16 | SetBestBlock { | 637 | 16 | retracted_transactions, | 638 | 16 | included_transactions, | 639 | 16 | } | 640 | 16 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE14set_best_blockB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE14set_best_blockB6_ |
641 | | |
642 | | /// Returns `true` if the block with the given hash is present in the pool. |
643 | 0 | pub fn has_block(&self, hash: &[u8; 32]) -> bool { |
644 | 0 | self.blocks_by_id.contains_key(hash) |
645 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE9has_blockB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE9has_blockB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE9has_blockB6_ |
646 | | |
647 | | /// Returns the hash of the best block. |
648 | | /// |
649 | | /// Please note that the block with the given hash might not have an associated user data in |
650 | | /// case the best block is equal to the finalized block and all finalized blocks have been |
651 | | /// pruned. |
652 | 0 | pub fn best_block_hash(&self) -> &[u8; 32] { |
653 | 0 | match self.best_block_index { |
654 | 0 | Some(idx) => &self.blocks_tree.get(idx).unwrap().hash, |
655 | 0 | None => &self.blocks_tree_root_hash, |
656 | | } |
657 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE15best_block_hashB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE15best_block_hashB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE15best_block_hashB6_ |
658 | | |
659 | | /// Returns the user data associated with a given block. |
660 | | /// |
661 | | /// Returns `None` if the block hash doesn't correspond to a known block. |
662 | 0 | pub fn block_user_data(&self, hash: &[u8; 32]) -> Option<&TBl> { |
663 | 0 | let index = *self.blocks_by_id.get(hash)?; |
664 | 0 | Some(&self.blocks_tree.get(index).unwrap().user_data) |
665 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE15block_user_dataB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE15block_user_dataB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE15block_user_dataB6_ |
666 | | |
667 | | /// Returns the user data associated with a given block. |
668 | | /// |
669 | | /// Returns `None` if the block hash doesn't correspond to a known block. |
670 | 0 | pub fn block_user_data_mut(&mut self, hash: &[u8; 32]) -> Option<&mut TBl> { |
671 | 0 | let index = *self.blocks_by_id.get(hash)?; |
672 | 0 | Some(&mut self.blocks_tree.get_mut(index).unwrap().user_data) |
673 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE19block_user_data_mutB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE19block_user_data_mutB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE19block_user_data_mutB6_ |
674 | | |
675 | | /// Sets the list of single-SCALE-encoded transactions that are present in the body of a block. |
676 | | /// |
677 | | /// If the block is part of the best chain, returns the list of transactions that are in the |
678 | | /// pool, that were found in the body, and that weren't part of the best chain before, |
679 | | /// alongside with their index in the body. |
680 | | /// |
681 | | /// # Panic |
682 | | /// |
683 | | /// Panics if no block with the given hash has been inserted before. |
684 | | /// |
685 | | // TODO: return something more precise in case the block in which a transaction is included is updated? |
686 | | #[must_use = "`set_block_body` returns the list of transactions that are now included in the chain"] |
687 | 4 | pub fn set_block_body( |
688 | 4 | &'_ mut self, |
689 | 4 | block_hash: &[u8; 32], |
690 | 4 | body: impl Iterator<Item = impl AsRef<[u8]>>, |
691 | 4 | ) -> impl Iterator<Item = (TransactionId, usize)> + '_ { |
692 | 4 | let block_index = *self.blocks_by_id.get(block_hash).unwrap(); |
693 | 4 | |
694 | 4 | // TODO: what if body was already known? this will trigger the `debug_assert!(_was_included)` below |
695 | 4 | // TODO: right now we just panic |
696 | 4 | assert!(!matches!( |
697 | 4 | self.blocks_tree.get_mut(block_index).unwrap().body, |
698 | | BodyState::Known |
699 | | )); |
700 | 4 | self.blocks_tree.get_mut(block_index).unwrap().body = BodyState::Known; |
701 | 4 | |
702 | 4 | let is_in_best_chain = self.best_block_index.map_or(false, |best_block_index| { |
703 | 1 | self.blocks_tree.is_ancestor(block_index, best_block_index) |
704 | 4 | }); _RNCINvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB5_9LightPooluuuE14set_block_bodyINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1x_9into_iter8IntoIterB1u_EE0B9_ Line | Count | Source | 702 | 1 | let is_in_best_chain = self.best_block_index.map_or(false, |best_block_index| { | 703 | 1 | self.blocks_tree.is_ancestor(block_index, best_block_index) | 704 | 1 | }); |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB5_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1e_5BlockNtB1e_14InvalidOrErrorE14set_block_bodyINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB4f_9into_iter8IntoIterB4c_EE0B2t_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB5_9LightPoolpppE14set_block_bodyppE0B9_ |
705 | 4 | |
706 | 4 | // Value returned from the function. |
707 | 4 | // TODO: optimize by not having Vec |
708 | 4 | let mut included_transactions = Vec::new(); |
709 | | |
710 | 4 | for (included_body_index, included_body3 ) in body.into_iter().enumerate() { |
711 | 3 | let included_body = included_body.as_ref(); |
712 | 3 | let hash = blake2_hash(included_body); |
713 | | |
714 | 3 | 'tx_in_pool: for (_, known_tx_id) in self |
715 | 3 | .by_hash |
716 | 3 | .range((hash, TransactionId(usize::MIN))..=(hash, TransactionId(usize::MAX))) |
717 | | { |
718 | 3 | let mut now_included = is_in_best_chain; |
719 | | |
720 | | // Check in which other blocks this transaction has been seen before. |
721 | 3 | for (_, existing_included_block0 ) in self |
722 | 3 | .included_transactions |
723 | 3 | .range((*known_tx_id, [0x0; 32])..=(*known_tx_id, [0xff; 32])) |
724 | 3 | .cloned() |
725 | 3 | .collect::<Vec<_>>() |
726 | | { |
727 | 0 | let existing_included_block_idx = |
728 | 0 | *self.blocks_by_id.get(&existing_included_block).unwrap(); |
729 | 0 |
|
730 | 0 | // Skip this transaction if it has already been found in a parent. |
731 | 0 | if self |
732 | 0 | .blocks_tree |
733 | 0 | .is_ancestor(existing_included_block_idx, block_index) |
734 | | { |
735 | 0 | continue 'tx_in_pool; |
736 | 0 | } |
737 | 0 |
|
738 | 0 | // If the transaction is found in a children, un-include it from the child. |
739 | 0 | if self |
740 | 0 | .blocks_tree |
741 | 0 | .is_ancestor(block_index, existing_included_block_idx) |
742 | | { |
743 | 0 | let _was_removed = self |
744 | 0 | .transactions_by_inclusion |
745 | 0 | .remove(&(existing_included_block, *known_tx_id)); |
746 | 0 | debug_assert!(_was_removed.is_some()); |
747 | | |
748 | 0 | let _was_removed = self |
749 | 0 | .included_transactions |
750 | 0 | .remove(&(*known_tx_id, existing_included_block)); |
751 | 0 | debug_assert!(_was_removed); |
752 | | |
753 | | // If `existing_included_block_idx` is in the best chain, set |
754 | | // `now_included` to false. |
755 | 0 | if self.best_block_index.map_or(false, |best_block_index| { |
756 | 0 | self.blocks_tree |
757 | 0 | .is_ancestor(existing_included_block_idx, best_block_index) |
758 | 0 | }) { Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB5_9LightPooluuuE14set_block_bodyINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1x_9into_iter8IntoIterB1u_EEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB5_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1e_5BlockNtB1e_14InvalidOrErrorE14set_block_bodyINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB4f_9into_iter8IntoIterB4c_EEs_0B2t_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB5_9LightPoolpppE14set_block_bodyppEs_0B9_ |
759 | 0 | now_included = false; |
760 | 0 | } |
761 | 0 | } |
762 | | } |
763 | | |
764 | 3 | let _was_present = self |
765 | 3 | .transactions_by_inclusion |
766 | 3 | .insert((*block_hash, *known_tx_id), included_body_index); |
767 | 3 | debug_assert!(_was_present.is_none()); |
768 | | |
769 | 3 | let _was_included = self |
770 | 3 | .included_transactions |
771 | 3 | .insert((*known_tx_id, *block_hash)); |
772 | 3 | debug_assert!(_was_included); |
773 | | |
774 | 3 | if now_included { |
775 | 1 | included_transactions.push((*known_tx_id, included_body_index)); |
776 | 2 | } |
777 | | } |
778 | | } |
779 | | |
780 | 4 | included_transactions.into_iter() |
781 | 4 | } _RINvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB3_9LightPooluuuE14set_block_bodyINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1v_9into_iter8IntoIterB1s_EEB7_ Line | Count | Source | 687 | 4 | pub fn set_block_body( | 688 | 4 | &'_ mut self, | 689 | 4 | block_hash: &[u8; 32], | 690 | 4 | body: impl Iterator<Item = impl AsRef<[u8]>>, | 691 | 4 | ) -> impl Iterator<Item = (TransactionId, usize)> + '_ { | 692 | 4 | let block_index = *self.blocks_by_id.get(block_hash).unwrap(); | 693 | 4 | | 694 | 4 | // TODO: what if body was already known? this will trigger the `debug_assert!(_was_included)` below | 695 | 4 | // TODO: right now we just panic | 696 | 4 | assert!(!matches!( | 697 | 4 | self.blocks_tree.get_mut(block_index).unwrap().body, | 698 | | BodyState::Known | 699 | | )); | 700 | 4 | self.blocks_tree.get_mut(block_index).unwrap().body = BodyState::Known; | 701 | 4 | | 702 | 4 | let is_in_best_chain = self.best_block_index.map_or(false, |best_block_index| { | 703 | | self.blocks_tree.is_ancestor(block_index, best_block_index) | 704 | 4 | }); | 705 | 4 | | 706 | 4 | // Value returned from the function. | 707 | 4 | // TODO: optimize by not having Vec | 708 | 4 | let mut included_transactions = Vec::new(); | 709 | | | 710 | 4 | for (included_body_index, included_body3 ) in body.into_iter().enumerate() { | 711 | 3 | let included_body = included_body.as_ref(); | 712 | 3 | let hash = blake2_hash(included_body); | 713 | | | 714 | 3 | 'tx_in_pool: for (_, known_tx_id) in self | 715 | 3 | .by_hash | 716 | 3 | .range((hash, TransactionId(usize::MIN))..=(hash, TransactionId(usize::MAX))) | 717 | | { | 718 | 3 | let mut now_included = is_in_best_chain; | 719 | | | 720 | | // Check in which other blocks this transaction has been seen before. | 721 | 3 | for (_, existing_included_block0 ) in self | 722 | 3 | .included_transactions | 723 | 3 | .range((*known_tx_id, [0x0; 32])..=(*known_tx_id, [0xff; 32])) | 724 | 3 | .cloned() | 725 | 3 | .collect::<Vec<_>>() | 726 | | { | 727 | 0 | let existing_included_block_idx = | 728 | 0 | *self.blocks_by_id.get(&existing_included_block).unwrap(); | 729 | 0 |
| 730 | 0 | // Skip this transaction if it has already been found in a parent. | 731 | 0 | if self | 732 | 0 | .blocks_tree | 733 | 0 | .is_ancestor(existing_included_block_idx, block_index) | 734 | | { | 735 | 0 | continue 'tx_in_pool; | 736 | 0 | } | 737 | 0 |
| 738 | 0 | // If the transaction is found in a children, un-include it from the child. | 739 | 0 | if self | 740 | 0 | .blocks_tree | 741 | 0 | .is_ancestor(block_index, existing_included_block_idx) | 742 | | { | 743 | 0 | let _was_removed = self | 744 | 0 | .transactions_by_inclusion | 745 | 0 | .remove(&(existing_included_block, *known_tx_id)); | 746 | 0 | debug_assert!(_was_removed.is_some()); | 747 | | | 748 | 0 | let _was_removed = self | 749 | 0 | .included_transactions | 750 | 0 | .remove(&(*known_tx_id, existing_included_block)); | 751 | 0 | debug_assert!(_was_removed); | 752 | | | 753 | | // If `existing_included_block_idx` is in the best chain, set | 754 | | // `now_included` to false. | 755 | 0 | if self.best_block_index.map_or(false, |best_block_index| { | 756 | | self.blocks_tree | 757 | | .is_ancestor(existing_included_block_idx, best_block_index) | 758 | 0 | }) { | 759 | 0 | now_included = false; | 760 | 0 | } | 761 | 0 | } | 762 | | } | 763 | | | 764 | 3 | let _was_present = self | 765 | 3 | .transactions_by_inclusion | 766 | 3 | .insert((*block_hash, *known_tx_id), included_body_index); | 767 | 3 | debug_assert!(_was_present.is_none()); | 768 | | | 769 | 3 | let _was_included = self | 770 | 3 | .included_transactions | 771 | 3 | .insert((*known_tx_id, *block_hash)); | 772 | 3 | debug_assert!(_was_included); | 773 | | | 774 | 3 | if now_included { | 775 | 1 | included_transactions.push((*known_tx_id, included_body_index)); | 776 | 2 | } | 777 | | } | 778 | | } | 779 | | | 780 | 4 | included_transactions.into_iter() | 781 | 4 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB3_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1c_5BlockNtB1c_14InvalidOrErrorE14set_block_bodyINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB4d_9into_iter8IntoIterB4a_EEB2r_ Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB3_9LightPoolpppE14set_block_bodyppEB7_ |
782 | | |
783 | | /// Returns the list of blocks whose bodies aren't present in this data structure. |
784 | | /// |
785 | | /// Blocks that were inserted when there wasn't any transaction in the pool are never |
786 | | /// returned. |
787 | | // TODO: return whether in best chain |
788 | 5 | pub fn missing_block_bodies(&'_ self) -> impl Iterator<Item = (&'_ [u8; 32], &'_ TBl)> + '_ { |
789 | 5 | self.blocks_tree |
790 | 5 | .iter_unordered() |
791 | 5 | .filter_map(move |(_, block)| {2 |
792 | 2 | if !matches!1 (block.body, BodyState::Needed) { |
793 | 1 | return None; |
794 | 1 | } |
795 | 1 | |
796 | 1 | Some((&block.hash, &block.user_data)) |
797 | 5 | }2 ) _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE20missing_block_bodies0B8_ Line | Count | Source | 791 | 2 | .filter_map(move |(_, block)| { | 792 | 2 | if !matches!1 (block.body, BodyState::Needed) { | 793 | 1 | return None; | 794 | 1 | } | 795 | 1 | | 796 | 1 | Some((&block.hash, &block.user_data)) | 797 | 2 | }) |
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE20missing_block_bodies0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE20missing_block_bodies0B8_ |
798 | 5 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE20missing_block_bodiesB6_ Line | Count | Source | 788 | 5 | pub fn missing_block_bodies(&'_ self) -> impl Iterator<Item = (&'_ [u8; 32], &'_ TBl)> + '_ { | 789 | 5 | self.blocks_tree | 790 | 5 | .iter_unordered() | 791 | 5 | .filter_map(move |(_, block)| { | 792 | | if !matches!(block.body, BodyState::Needed) { | 793 | | return None; | 794 | | } | 795 | | | 796 | | Some((&block.hash, &block.user_data)) | 797 | 5 | }) | 798 | 5 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE20missing_block_bodiesB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE20missing_block_bodiesB6_ |
799 | | |
800 | | /// Sets the finalized block of the chain. |
801 | | /// |
802 | | /// Removes and returns the blocks that are not part of the finalized chain. Please note that |
803 | | /// the finalized chain itself, however, isn't removed. |
804 | | /// |
805 | | /// The current best block (set using [`LightPool::set_best_block`]) must be a descendant of |
806 | | /// or equal to the node passed as parameter. This guarantees that no transaction gets |
807 | | /// retracted. |
808 | | /// |
809 | | /// # Panic |
810 | | /// |
811 | | /// Panics if no block with the given hash has been inserted before. |
812 | | /// Panics if the current best block isn't a descendant of or equal to the new finalized |
813 | | /// block. |
814 | | /// Panics if the current finalized block isn't an ancestor of or equal to the new finalized |
815 | | /// block. |
816 | | /// |
817 | 3 | pub fn set_finalized_block( |
818 | 3 | &mut self, |
819 | 3 | new_finalized_block_hash: &[u8; 32], |
820 | 3 | ) -> impl Iterator<Item = ([u8; 32], TBl)> { |
821 | 3 | let new_finalized_block_index = if *new_finalized_block_hash == self.blocks_tree_root_hash { |
822 | 0 | assert!(self.finalized_block_index.is_none()); |
823 | 0 | return Vec::new().into_iter(); |
824 | | } else { |
825 | 3 | let index = *self.blocks_by_id.get(new_finalized_block_hash).unwrap(); |
826 | 3 | // TODO: check ancestry of previously finalized too |
827 | 3 | assert!(self |
828 | 3 | .blocks_tree |
829 | 3 | .is_ancestor(index, self.best_block_index.unwrap())); |
830 | 3 | index |
831 | | }; |
832 | | |
833 | | // Update `finalized_chain_validation` in transactions. |
834 | | { |
835 | 3 | let old_finalized_to_new_finalized = match self.finalized_block_index { |
836 | 0 | Some(old_fin_index) => { |
837 | 0 | let (_ascend, descend) = self |
838 | 0 | .blocks_tree |
839 | 0 | .ascend_and_descend(old_fin_index, new_finalized_block_index); |
840 | 0 | debug_assert_eq!(_ascend.count(), 0); |
841 | 0 | either::Left(descend) |
842 | | } |
843 | | None => { |
844 | 3 | let iter = self |
845 | 3 | .blocks_tree |
846 | 3 | .root_to_node_path(new_finalized_block_index); |
847 | 3 | either::Right(iter) |
848 | | } |
849 | | }; |
850 | | |
851 | 9 | for block_index6 in old_finalized_to_new_finalized { |
852 | 6 | let block = self.blocks_tree.get(block_index).unwrap(); |
853 | 6 | |
854 | 6 | let validated_txs = self |
855 | 6 | .transactions_by_validation |
856 | 6 | .range( |
857 | 6 | (block.hash, TransactionId(usize::MIN)) |
858 | 6 | ..=(block.hash, TransactionId(usize::MAX)), |
859 | 6 | ) |
860 | 6 | .map(|(_, tx)| *tx1 ) _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE19set_finalized_block0B8_ Line | Count | Source | 860 | 1 | .map(|(_, tx)| *tx) |
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE19set_finalized_block0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE19set_finalized_block0B8_ |
861 | 6 | .collect::<Vec<_>>(); |
862 | | |
863 | 7 | for tx_id1 in validated_txs { |
864 | 1 | let validation = self |
865 | 1 | .transaction_validations |
866 | 1 | .get(&(tx_id, block.hash)) |
867 | 1 | .unwrap() |
868 | 1 | .clone(); |
869 | 1 | self.transactions[tx_id.0].finalized_chain_validation = |
870 | 1 | Some((block.relative_block_height, validation)); |
871 | 1 | } |
872 | | } |
873 | | } |
874 | | |
875 | | // Now update `self` because we don't need the old value anymore. |
876 | 3 | self.finalized_block_index = Some(new_finalized_block_index); |
877 | 3 | |
878 | 3 | // TODO: don't allocate a Vec here |
879 | 3 | let mut out = Vec::new(); |
880 | | |
881 | 3 | for pruned_block0 in self.blocks_tree.prune_uncles(new_finalized_block_index) { |
882 | 0 | debug_assert!(!pruned_block.is_prune_target_ancestor); |
883 | | |
884 | 0 | let _expected_index = self.blocks_by_id.remove(&pruned_block.user_data.hash); |
885 | 0 | debug_assert_eq!(_expected_index, Some(pruned_block.index)); |
886 | | |
887 | 0 | let included_txs = self |
888 | 0 | .transactions_by_inclusion |
889 | 0 | .range( |
890 | 0 | (pruned_block.user_data.hash, TransactionId(usize::MIN)) |
891 | 0 | ..=(pruned_block.user_data.hash, TransactionId(usize::MAX)), |
892 | 0 | ) |
893 | 0 | .map(|((_, tx), _)| *tx) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE19set_finalized_blocks_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE19set_finalized_blocks_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE19set_finalized_blocks_0B8_ |
894 | 0 | .collect::<Vec<_>>(); |
895 | | |
896 | 0 | for tx_id in included_txs { |
897 | 0 | let _was_removed = self |
898 | 0 | .transactions_by_inclusion |
899 | 0 | .remove(&(pruned_block.user_data.hash, tx_id)); |
900 | 0 | debug_assert!(_was_removed.is_some()); |
901 | 0 | let _was_removed = self |
902 | 0 | .included_transactions |
903 | 0 | .remove(&(tx_id, pruned_block.user_data.hash)); |
904 | 0 | debug_assert!(_was_removed); |
905 | | } |
906 | | |
907 | 0 | let validated_txs = self |
908 | 0 | .transactions_by_validation |
909 | 0 | .range( |
910 | 0 | (pruned_block.user_data.hash, TransactionId(usize::MIN)) |
911 | 0 | ..=(pruned_block.user_data.hash, TransactionId(usize::MAX)), |
912 | 0 | ) |
913 | 0 | .map(|(_, tx)| *tx) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE19set_finalized_blocks0_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE19set_finalized_blocks0_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE19set_finalized_blocks0_0B8_ |
914 | 0 | .collect::<Vec<_>>(); |
915 | | |
916 | 0 | for tx_id in validated_txs { |
917 | 0 | let _was_removed = self |
918 | 0 | .transactions_by_validation |
919 | 0 | .remove(&(pruned_block.user_data.hash, tx_id)); |
920 | 0 | debug_assert!(_was_removed); |
921 | 0 | let _was_removed = self |
922 | 0 | .transaction_validations |
923 | 0 | .remove(&(tx_id, pruned_block.user_data.hash)); |
924 | 0 | debug_assert!(_was_removed.is_some()); |
925 | | } |
926 | | |
927 | 0 | out.push(( |
928 | 0 | pruned_block.user_data.hash, |
929 | 0 | pruned_block.user_data.user_data, |
930 | 0 | )); |
931 | | } |
932 | | |
933 | 3 | out.into_iter() |
934 | 3 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE19set_finalized_blockB6_ Line | Count | Source | 817 | 3 | pub fn set_finalized_block( | 818 | 3 | &mut self, | 819 | 3 | new_finalized_block_hash: &[u8; 32], | 820 | 3 | ) -> impl Iterator<Item = ([u8; 32], TBl)> { | 821 | 3 | let new_finalized_block_index = if *new_finalized_block_hash == self.blocks_tree_root_hash { | 822 | 0 | assert!(self.finalized_block_index.is_none()); | 823 | 0 | return Vec::new().into_iter(); | 824 | | } else { | 825 | 3 | let index = *self.blocks_by_id.get(new_finalized_block_hash).unwrap(); | 826 | 3 | // TODO: check ancestry of previously finalized too | 827 | 3 | assert!(self | 828 | 3 | .blocks_tree | 829 | 3 | .is_ancestor(index, self.best_block_index.unwrap())); | 830 | 3 | index | 831 | | }; | 832 | | | 833 | | // Update `finalized_chain_validation` in transactions. | 834 | | { | 835 | 3 | let old_finalized_to_new_finalized = match self.finalized_block_index { | 836 | 0 | Some(old_fin_index) => { | 837 | 0 | let (_ascend, descend) = self | 838 | 0 | .blocks_tree | 839 | 0 | .ascend_and_descend(old_fin_index, new_finalized_block_index); | 840 | 0 | debug_assert_eq!(_ascend.count(), 0); | 841 | 0 | either::Left(descend) | 842 | | } | 843 | | None => { | 844 | 3 | let iter = self | 845 | 3 | .blocks_tree | 846 | 3 | .root_to_node_path(new_finalized_block_index); | 847 | 3 | either::Right(iter) | 848 | | } | 849 | | }; | 850 | | | 851 | 9 | for block_index6 in old_finalized_to_new_finalized { | 852 | 6 | let block = self.blocks_tree.get(block_index).unwrap(); | 853 | 6 | | 854 | 6 | let validated_txs = self | 855 | 6 | .transactions_by_validation | 856 | 6 | .range( | 857 | 6 | (block.hash, TransactionId(usize::MIN)) | 858 | 6 | ..=(block.hash, TransactionId(usize::MAX)), | 859 | 6 | ) | 860 | 6 | .map(|(_, tx)| *tx) | 861 | 6 | .collect::<Vec<_>>(); | 862 | | | 863 | 7 | for tx_id1 in validated_txs { | 864 | 1 | let validation = self | 865 | 1 | .transaction_validations | 866 | 1 | .get(&(tx_id, block.hash)) | 867 | 1 | .unwrap() | 868 | 1 | .clone(); | 869 | 1 | self.transactions[tx_id.0].finalized_chain_validation = | 870 | 1 | Some((block.relative_block_height, validation)); | 871 | 1 | } | 872 | | } | 873 | | } | 874 | | | 875 | | // Now update `self` because we don't need the old value anymore. | 876 | 3 | self.finalized_block_index = Some(new_finalized_block_index); | 877 | 3 | | 878 | 3 | // TODO: don't allocate a Vec here | 879 | 3 | let mut out = Vec::new(); | 880 | | | 881 | 3 | for pruned_block0 in self.blocks_tree.prune_uncles(new_finalized_block_index) { | 882 | 0 | debug_assert!(!pruned_block.is_prune_target_ancestor); | 883 | | | 884 | 0 | let _expected_index = self.blocks_by_id.remove(&pruned_block.user_data.hash); | 885 | 0 | debug_assert_eq!(_expected_index, Some(pruned_block.index)); | 886 | | | 887 | 0 | let included_txs = self | 888 | 0 | .transactions_by_inclusion | 889 | 0 | .range( | 890 | 0 | (pruned_block.user_data.hash, TransactionId(usize::MIN)) | 891 | 0 | ..=(pruned_block.user_data.hash, TransactionId(usize::MAX)), | 892 | 0 | ) | 893 | 0 | .map(|((_, tx), _)| *tx) | 894 | 0 | .collect::<Vec<_>>(); | 895 | | | 896 | 0 | for tx_id in included_txs { | 897 | 0 | let _was_removed = self | 898 | 0 | .transactions_by_inclusion | 899 | 0 | .remove(&(pruned_block.user_data.hash, tx_id)); | 900 | 0 | debug_assert!(_was_removed.is_some()); | 901 | 0 | let _was_removed = self | 902 | 0 | .included_transactions | 903 | 0 | .remove(&(tx_id, pruned_block.user_data.hash)); | 904 | 0 | debug_assert!(_was_removed); | 905 | | } | 906 | | | 907 | 0 | let validated_txs = self | 908 | 0 | .transactions_by_validation | 909 | 0 | .range( | 910 | 0 | (pruned_block.user_data.hash, TransactionId(usize::MIN)) | 911 | 0 | ..=(pruned_block.user_data.hash, TransactionId(usize::MAX)), | 912 | 0 | ) | 913 | 0 | .map(|(_, tx)| *tx) | 914 | 0 | .collect::<Vec<_>>(); | 915 | | | 916 | 0 | for tx_id in validated_txs { | 917 | 0 | let _was_removed = self | 918 | 0 | .transactions_by_validation | 919 | 0 | .remove(&(pruned_block.user_data.hash, tx_id)); | 920 | 0 | debug_assert!(_was_removed); | 921 | 0 | let _was_removed = self | 922 | 0 | .transaction_validations | 923 | 0 | .remove(&(tx_id, pruned_block.user_data.hash)); | 924 | 0 | debug_assert!(_was_removed.is_some()); | 925 | | } | 926 | | | 927 | 0 | out.push(( | 928 | 0 | pruned_block.user_data.hash, | 929 | 0 | pruned_block.user_data.user_data, | 930 | 0 | )); | 931 | | } | 932 | | | 933 | 3 | out.into_iter() | 934 | 3 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE19set_finalized_blockB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE19set_finalized_blockB6_ |
935 | | |
936 | | /// Removes from the pool as many blocks as possible from the finalized chain. Blocks are |
937 | | /// removed from parent to child until either the first non-finalized block or a block whose |
938 | | /// body is missing is encountered. |
939 | | /// |
940 | | /// Also removes the transactions from the pool that were included in these blocks. |
941 | 3 | pub fn prune_finalized_with_body( |
942 | 3 | &'_ mut self, |
943 | 3 | ) -> impl Iterator<Item = PruneBodyFinalized<TTx, TBl>> + '_ { |
944 | | // TODO: optimize? |
945 | | |
946 | 3 | let finalized_block_index = match self.finalized_block_index { |
947 | 3 | Some(idx) => idx, |
948 | 0 | None => return either::Right(iter::empty()), |
949 | | }; |
950 | | |
951 | | // Find highest finalized block that can be pruned. |
952 | 1 | let (num_blocks_to_remove, upmost_to_remove) = { |
953 | 3 | let search = self |
954 | 3 | .blocks_tree |
955 | 3 | .root_to_node_path(finalized_block_index) |
956 | 3 | .take_while(|idx| { |
957 | 3 | !matches!1 (self.blocks_tree.get(*idx).unwrap().body, BodyState::Needed) |
958 | 3 | }) _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE25prune_finalized_with_body0B8_ Line | Count | Source | 956 | 3 | .take_while(|idx| { | 957 | 3 | !matches!1 (self.blocks_tree.get(*idx).unwrap().body, BodyState::Needed) | 958 | 3 | }) |
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE25prune_finalized_with_body0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE25prune_finalized_with_body0B8_ |
959 | 3 | .enumerate() |
960 | 3 | .map(|(n, b)| (n + 1, b)1 ) _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE25prune_finalized_with_bodys_0B8_ Line | Count | Source | 960 | 1 | .map(|(n, b)| (n + 1, b)) |
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE25prune_finalized_with_bodys_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE25prune_finalized_with_bodys_0B8_ |
961 | 3 | .last(); |
962 | 3 | match search { |
963 | 1 | Some(idx) => idx, |
964 | 2 | None => return either::Right(iter::empty()), |
965 | | } |
966 | | }; |
967 | | |
968 | | // Some internal state update. |
969 | 1 | if upmost_to_remove == finalized_block_index { |
970 | 1 | self.finalized_block_index = None; |
971 | 1 | self.blocks_tree_root_hash = self.blocks_tree.get(upmost_to_remove).unwrap().hash; |
972 | 1 | }0 |
973 | | |
974 | | // Return value of the function. |
975 | 1 | let mut return_value = Vec::with_capacity(num_blocks_to_remove); |
976 | | |
977 | | // Do the actual pruning. |
978 | 1 | for pruned in self.blocks_tree.prune_ancestors(upmost_to_remove) { |
979 | | // Since all the blocks that we removed are already finalized, we shouldn't find any |
980 | | // sibling when pruning. |
981 | 1 | debug_assert!(pruned.is_prune_target_ancestor); |
982 | | |
983 | 1 | let _removed = self.blocks_by_id.remove(&pruned.user_data.hash); |
984 | 1 | debug_assert_eq!(_removed, Some(pruned.index)); |
985 | | |
986 | | // List of transactions that were included in this block. |
987 | 1 | let included_transactions_ids = self |
988 | 1 | .transactions_by_inclusion |
989 | 1 | .range( |
990 | 1 | (pruned.user_data.hash, TransactionId(usize::MIN)) |
991 | 1 | ..=(pruned.user_data.hash, TransactionId(usize::MAX)), |
992 | 1 | ) |
993 | 1 | .map(|((_, tx_id), index)| (*tx_id, *index)) _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE25prune_finalized_with_bodys0_0B8_ Line | Count | Source | 993 | 1 | .map(|((_, tx_id), index)| (*tx_id, *index)) |
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE25prune_finalized_with_bodys0_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE25prune_finalized_with_bodys0_0B8_ |
994 | 1 | .collect::<Vec<_>>(); |
995 | 1 | let mut included_transactions = Vec::with_capacity(included_transactions_ids.len()); |
996 | | |
997 | 2 | for (tx_id, index_in_block1 ) in &included_transactions_ids { |
998 | | // Completely remove this transaction from the pool, similar to what |
999 | | // `remove_transaction` does. |
1000 | 1 | let tx = self.transactions.remove(tx_id.0); |
1001 | 1 | |
1002 | 1 | let blocks_included = self |
1003 | 1 | .included_transactions |
1004 | 1 | .range((*tx_id, [0; 32])..=(*tx_id, [0xff; 32])) |
1005 | 1 | .map(|(_, block)| *block) _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE25prune_finalized_with_bodys1_0B8_ Line | Count | Source | 1005 | 1 | .map(|(_, block)| *block) |
Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE25prune_finalized_with_bodys1_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE25prune_finalized_with_bodys1_0B8_ |
1006 | 1 | .collect::<Vec<_>>(); |
1007 | 1 | |
1008 | 1 | let blocks_validated = self |
1009 | 1 | .transaction_validations |
1010 | 1 | .range((*tx_id, [0; 32])..=(*tx_id, [0xff; 32])) |
1011 | 1 | .map(|((_, block), _)| *block0 ) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE25prune_finalized_with_bodys2_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE25prune_finalized_with_bodys2_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE25prune_finalized_with_bodys2_0B8_ |
1012 | 1 | .collect::<Vec<_>>(); |
1013 | | |
1014 | 2 | for block_hash1 in blocks_included { |
1015 | 1 | let _removed = self.included_transactions.remove(&(*tx_id, block_hash)); |
1016 | 1 | debug_assert!(_removed); |
1017 | 1 | let _removed = self.transactions_by_inclusion.remove(&(block_hash, *tx_id)); |
1018 | 1 | debug_assert!(_removed.is_some()); |
1019 | | } |
1020 | | |
1021 | 1 | for block_hash0 in blocks_validated { |
1022 | 0 | let _removed = self.transaction_validations.remove(&(*tx_id, block_hash)); |
1023 | 0 | debug_assert!(_removed.is_some()); |
1024 | 0 | let _removed = self |
1025 | 0 | .transactions_by_validation |
1026 | 0 | .remove(&(block_hash, *tx_id)); |
1027 | 0 | debug_assert!(_removed); |
1028 | | } |
1029 | | |
1030 | 1 | let _removed = self |
1031 | 1 | .by_hash |
1032 | 1 | .remove(&(blake2_hash(&tx.scale_encoded), *tx_id)); |
1033 | 1 | debug_assert!(_removed); |
1034 | | |
1035 | 1 | included_transactions.push(RemovedTransaction { |
1036 | 1 | id: *tx_id, |
1037 | 1 | index_in_block: *index_in_block, |
1038 | 1 | scale_encoding: tx.scale_encoded, |
1039 | 1 | user_data: tx.user_data, |
1040 | 1 | }); |
1041 | | } |
1042 | | |
1043 | | // Purge the state from any validation information about that block. |
1044 | 1 | let validated_txs = self |
1045 | 1 | .transactions_by_validation |
1046 | 1 | .range( |
1047 | 1 | (pruned.user_data.hash, TransactionId(usize::MIN)) |
1048 | 1 | ..=(pruned.user_data.hash, TransactionId(usize::MAX)), |
1049 | 1 | ) |
1050 | 1 | .map(|(_, tx)| *tx0 ) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB4_9LightPooluuuE25prune_finalized_with_bodys3_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1d_5BlockNtB1d_14InvalidOrErrorE25prune_finalized_with_bodys3_0B2s_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB4_9LightPoolpppE25prune_finalized_with_bodys3_0B8_ |
1051 | 1 | .collect::<Vec<_>>(); |
1052 | | |
1053 | 1 | for tx_id0 in validated_txs { |
1054 | 0 | let _was_removed = self |
1055 | 0 | .transactions_by_validation |
1056 | 0 | .remove(&(pruned.user_data.hash, tx_id)); |
1057 | 0 | debug_assert!(_was_removed); |
1058 | 0 | let _was_removed = self |
1059 | 0 | .transaction_validations |
1060 | 0 | .remove(&(tx_id, pruned.user_data.hash)); |
1061 | 0 | debug_assert!(_was_removed.is_some()); |
1062 | | } |
1063 | | |
1064 | 1 | return_value.push(PruneBodyFinalized { |
1065 | 1 | block_hash: pruned.user_data.hash, |
1066 | 1 | included_transactions, |
1067 | 1 | user_data: pruned.user_data.user_data, |
1068 | 1 | }); |
1069 | | } |
1070 | | |
1071 | | // We returned earlier in the function if `finalized_node_index` is `None`. Consequently, |
1072 | | // `best_block_index` can't be `None` either. |
1073 | 1 | if self.best_block_index.unwrap() == upmost_to_remove { |
1074 | 1 | self.best_block_index = None; |
1075 | 1 | }0 |
1076 | | |
1077 | | // Success. |
1078 | 1 | either::Left(return_value.into_iter()) |
1079 | 3 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPooluuuE25prune_finalized_with_bodyB6_ Line | Count | Source | 941 | 3 | pub fn prune_finalized_with_body( | 942 | 3 | &'_ mut self, | 943 | 3 | ) -> impl Iterator<Item = PruneBodyFinalized<TTx, TBl>> + '_ { | 944 | | // TODO: optimize? | 945 | | | 946 | 3 | let finalized_block_index = match self.finalized_block_index { | 947 | 3 | Some(idx) => idx, | 948 | 0 | None => return either::Right(iter::empty()), | 949 | | }; | 950 | | | 951 | | // Find highest finalized block that can be pruned. | 952 | 1 | let (num_blocks_to_remove, upmost_to_remove) = { | 953 | 3 | let search = self | 954 | 3 | .blocks_tree | 955 | 3 | .root_to_node_path(finalized_block_index) | 956 | 3 | .take_while(|idx| { | 957 | | !matches!(self.blocks_tree.get(*idx).unwrap().body, BodyState::Needed) | 958 | 3 | }) | 959 | 3 | .enumerate() | 960 | 3 | .map(|(n, b)| (n + 1, b)) | 961 | 3 | .last(); | 962 | 3 | match search { | 963 | 1 | Some(idx) => idx, | 964 | 2 | None => return either::Right(iter::empty()), | 965 | | } | 966 | | }; | 967 | | | 968 | | // Some internal state update. | 969 | 1 | if upmost_to_remove == finalized_block_index { | 970 | 1 | self.finalized_block_index = None; | 971 | 1 | self.blocks_tree_root_hash = self.blocks_tree.get(upmost_to_remove).unwrap().hash; | 972 | 1 | }0 | 973 | | | 974 | | // Return value of the function. | 975 | 1 | let mut return_value = Vec::with_capacity(num_blocks_to_remove); | 976 | | | 977 | | // Do the actual pruning. | 978 | 1 | for pruned in self.blocks_tree.prune_ancestors(upmost_to_remove) { | 979 | | // Since all the blocks that we removed are already finalized, we shouldn't find any | 980 | | // sibling when pruning. | 981 | 1 | debug_assert!(pruned.is_prune_target_ancestor); | 982 | | | 983 | 1 | let _removed = self.blocks_by_id.remove(&pruned.user_data.hash); | 984 | 1 | debug_assert_eq!(_removed, Some(pruned.index)); | 985 | | | 986 | | // List of transactions that were included in this block. | 987 | 1 | let included_transactions_ids = self | 988 | 1 | .transactions_by_inclusion | 989 | 1 | .range( | 990 | 1 | (pruned.user_data.hash, TransactionId(usize::MIN)) | 991 | 1 | ..=(pruned.user_data.hash, TransactionId(usize::MAX)), | 992 | 1 | ) | 993 | 1 | .map(|((_, tx_id), index)| (*tx_id, *index)) | 994 | 1 | .collect::<Vec<_>>(); | 995 | 1 | let mut included_transactions = Vec::with_capacity(included_transactions_ids.len()); | 996 | | | 997 | 2 | for (tx_id, index_in_block1 ) in &included_transactions_ids { | 998 | | // Completely remove this transaction from the pool, similar to what | 999 | | // `remove_transaction` does. | 1000 | 1 | let tx = self.transactions.remove(tx_id.0); | 1001 | 1 | | 1002 | 1 | let blocks_included = self | 1003 | 1 | .included_transactions | 1004 | 1 | .range((*tx_id, [0; 32])..=(*tx_id, [0xff; 32])) | 1005 | 1 | .map(|(_, block)| *block) | 1006 | 1 | .collect::<Vec<_>>(); | 1007 | 1 | | 1008 | 1 | let blocks_validated = self | 1009 | 1 | .transaction_validations | 1010 | 1 | .range((*tx_id, [0; 32])..=(*tx_id, [0xff; 32])) | 1011 | 1 | .map(|((_, block), _)| *block) | 1012 | 1 | .collect::<Vec<_>>(); | 1013 | | | 1014 | 2 | for block_hash1 in blocks_included { | 1015 | 1 | let _removed = self.included_transactions.remove(&(*tx_id, block_hash)); | 1016 | 1 | debug_assert!(_removed); | 1017 | 1 | let _removed = self.transactions_by_inclusion.remove(&(block_hash, *tx_id)); | 1018 | 1 | debug_assert!(_removed.is_some()); | 1019 | | } | 1020 | | | 1021 | 1 | for block_hash0 in blocks_validated { | 1022 | 0 | let _removed = self.transaction_validations.remove(&(*tx_id, block_hash)); | 1023 | 0 | debug_assert!(_removed.is_some()); | 1024 | 0 | let _removed = self | 1025 | 0 | .transactions_by_validation | 1026 | 0 | .remove(&(block_hash, *tx_id)); | 1027 | 0 | debug_assert!(_removed); | 1028 | | } | 1029 | | | 1030 | 1 | let _removed = self | 1031 | 1 | .by_hash | 1032 | 1 | .remove(&(blake2_hash(&tx.scale_encoded), *tx_id)); | 1033 | 1 | debug_assert!(_removed); | 1034 | | | 1035 | 1 | included_transactions.push(RemovedTransaction { | 1036 | 1 | id: *tx_id, | 1037 | 1 | index_in_block: *index_in_block, | 1038 | 1 | scale_encoding: tx.scale_encoded, | 1039 | 1 | user_data: tx.user_data, | 1040 | 1 | }); | 1041 | | } | 1042 | | | 1043 | | // Purge the state from any validation information about that block. | 1044 | 1 | let validated_txs = self | 1045 | 1 | .transactions_by_validation | 1046 | 1 | .range( | 1047 | 1 | (pruned.user_data.hash, TransactionId(usize::MIN)) | 1048 | 1 | ..=(pruned.user_data.hash, TransactionId(usize::MAX)), | 1049 | 1 | ) | 1050 | 1 | .map(|(_, tx)| *tx) | 1051 | 1 | .collect::<Vec<_>>(); | 1052 | | | 1053 | 1 | for tx_id0 in validated_txs { | 1054 | 0 | let _was_removed = self | 1055 | 0 | .transactions_by_validation | 1056 | 0 | .remove(&(pruned.user_data.hash, tx_id)); | 1057 | 0 | debug_assert!(_was_removed); | 1058 | 0 | let _was_removed = self | 1059 | 0 | .transaction_validations | 1060 | 0 | .remove(&(tx_id, pruned.user_data.hash)); | 1061 | 0 | debug_assert!(_was_removed.is_some()); | 1062 | | } | 1063 | | | 1064 | 1 | return_value.push(PruneBodyFinalized { | 1065 | 1 | block_hash: pruned.user_data.hash, | 1066 | 1 | included_transactions, | 1067 | 1 | user_data: pruned.user_data.user_data, | 1068 | 1 | }); | 1069 | | } | 1070 | | | 1071 | | // We returned earlier in the function if `finalized_node_index` is `None`. Consequently, | 1072 | | // `best_block_index` can't be `None` either. | 1073 | 1 | if self.best_block_index.unwrap() == upmost_to_remove { | 1074 | 1 | self.best_block_index = None; | 1075 | 1 | }0 | 1076 | | | 1077 | | // Success. | 1078 | 1 | either::Left(return_value.into_iter()) | 1079 | 3 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE25prune_finalized_with_bodyB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE25prune_finalized_with_bodyB6_ |
1080 | | |
1081 | | /// Returns the number of blocks between the oldest block stored in this data structure and |
1082 | | /// the finalized block. |
1083 | 0 | pub fn oldest_block_finality_lag(&self) -> usize { |
1084 | 0 | if let Some(finalized_block_index) = self.finalized_block_index { |
1085 | 0 | self.blocks_tree |
1086 | 0 | .root_to_node_path(finalized_block_index) |
1087 | 0 | .count() |
1088 | | } else { |
1089 | 0 | 0 |
1090 | | } |
1091 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot12transactions10light_poolINtB2_9LightPoolpppE25oldest_block_finality_lagB6_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolINtNtCsih6EgvAwZF2_13smoldot_light20transactions_service18PendingTransactionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefENtB1b_5BlockNtB1b_14InvalidOrErrorE25oldest_block_finality_lagB2q_ Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot12transactions10light_poolINtB2_9LightPoolpppE25oldest_block_finality_lagB6_ |
1092 | | } |
1093 | | |
1094 | | impl<TTx: fmt::Debug, TBl, TErr> fmt::Debug for LightPool<TTx, TBl, TErr> { |
1095 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1096 | 0 | f.debug_list() |
1097 | 0 | .entries( |
1098 | 0 | self.transactions |
1099 | 0 | .iter() |
1100 | 0 | .map(|t| (TransactionId(t.0), &t.1.user_data)), Unexecuted instantiation: _RNCNvXININtNtCsN16ciHI6Qf_7smoldot12transactions10light_pools_0pppEINtB7_9LightPoolpppENtNtCsaYZPK01V26L_4core3fmt5Debug3fmt0Bb_ Unexecuted instantiation: _RNCNvXININtNtCseuYC0Zibziv_7smoldot12transactions10light_pools_0pppEINtB7_9LightPoolpppENtNtCsaYZPK01V26L_4core3fmt5Debug3fmt0Bb_ |
1101 | 0 | ) |
1102 | 0 | .finish() |
1103 | 0 | } Unexecuted instantiation: _RNvXININtNtCsN16ciHI6Qf_7smoldot12transactions10light_pools_0pppEINtB5_9LightPoolpppENtNtCsaYZPK01V26L_4core3fmt5Debug3fmtB9_ Unexecuted instantiation: _RNvXININtNtCseuYC0Zibziv_7smoldot12transactions10light_pools_0pppEINtB5_9LightPoolpppENtNtCsaYZPK01V26L_4core3fmt5Debug3fmtB9_ |
1104 | | } |
1105 | | |
1106 | | /// See [`LightPool::prune_finalized_with_body`]. |
1107 | | pub struct PruneBodyFinalized<TTx, TBl> { |
1108 | | /// Hash of the finalized block. |
1109 | | pub block_hash: [u8; 32], |
1110 | | |
1111 | | /// User data associated to this block. |
1112 | | pub user_data: TBl, |
1113 | | |
1114 | | /// List of transactions that were included in this block, alongside with their index within |
1115 | | /// that block, SCALE encoding, and user data. These transactions have been removed from the |
1116 | | /// pool. |
1117 | | pub included_transactions: Vec<RemovedTransaction<TTx>>, |
1118 | | } |
1119 | | |
1120 | | /// See [`PruneBodyFinalized`]. |
1121 | | #[derive(Debug, Clone, PartialEq, Eq)] |
1122 | | pub struct RemovedTransaction<TTx> { |
1123 | | /// Id of this transaction in the state machine. |
1124 | | pub id: TransactionId, |
1125 | | |
1126 | | /// Index of the transaction within the finalized block. |
1127 | | pub index_in_block: usize, |
1128 | | |
1129 | | /// SCALE-encoded transaction. |
1130 | | pub scale_encoding: Vec<u8>, |
1131 | | |
1132 | | /// Opaque user data that was insert alongside with the transaction. |
1133 | | pub user_data: TTx, |
1134 | | } |
1135 | | |
1136 | | /// See [`LightPool::set_best_block`]. |
1137 | | #[derive(Debug, Clone)] |
1138 | | pub struct SetBestBlock { |
1139 | | /// List of transactions that were included in a block of the best chain but no longer are, |
1140 | | /// the hash of the block in which it was, and the index of the transaction in that block's |
1141 | | /// body. |
1142 | | /// |
1143 | | /// Can share some entries with [`SetBestBlock::included_transactions`] in case a transaction |
1144 | | /// has been retracted then included. |
1145 | | pub retracted_transactions: Vec<(TransactionId, [u8; 32], usize)>, |
1146 | | |
1147 | | /// List of transactions that weren't included in a block of the best chain but now are, the |
1148 | | /// hash of the block in which it was found, and the index of the transaction in that block's |
1149 | | /// body. |
1150 | | /// |
1151 | | /// Can share some entries with [`SetBestBlock::retracted_transactions`] in case a transaction |
1152 | | /// has been retracted then included. |
1153 | | pub included_transactions: Vec<(TransactionId, [u8; 32], usize)>, |
1154 | | } |
1155 | | |
1156 | | /// Entry in [`LightPool::transactions`]. |
1157 | | struct Transaction<TTx, TErr> { |
1158 | | /// Bytes corresponding to the SCALE-encoded transaction. |
1159 | | scale_encoded: Vec<u8>, |
1160 | | |
1161 | | /// User data chosen by the user. |
1162 | | user_data: TTx, |
1163 | | |
1164 | | /// Relative block height and status of the transaction validation against the highest |
1165 | | /// finalized block. |
1166 | | finalized_chain_validation: Option<(u64, Result<Validation, TErr>)>, |
1167 | | |
1168 | | /// Cache of the validation status of the transaction against the highest block of the best |
1169 | | /// chain that has one. |
1170 | | best_chain_validation: Option<Result<Validation, TErr>>, |
1171 | | } |
1172 | | |
1173 | | #[derive(Debug, Clone, PartialEq, Eq)] |
1174 | | struct Validation { |
1175 | | longevity_relative_block_height: u64, |
1176 | | propagate: bool, |
1177 | | } |
1178 | | |
1179 | | struct Block<TBl> { |
1180 | | /// Height of this block minus height of the block that was passed as |
1181 | | /// [`Config::finalized_block_hash`]. |
1182 | | /// |
1183 | | /// All the heights manipulated by the [`LightPool`] are relative to the height of the block |
1184 | | /// passed as [`Config::finalized_block_hash`], making it possible to compare and subtract |
1185 | | /// them. |
1186 | | relative_block_height: u64, |
1187 | | hash: [u8; 32], |
1188 | | body: BodyState, |
1189 | | user_data: TBl, |
1190 | | } |
1191 | | |
1192 | | enum BodyState { |
1193 | | Needed, |
1194 | | NotNeeded, |
1195 | | Known, |
1196 | | } |
1197 | | |
1198 | | /// Utility. Calculates the BLAKE2 hash of the given bytes. |
1199 | 10 | fn blake2_hash(bytes: &[u8]) -> [u8; 32] { |
1200 | 10 | <[u8; 32]>::try_from(blake2_rfc::blake2b::blake2b(32, &[], bytes).as_bytes()).unwrap() |
1201 | 10 | } _RNvNtNtCsN16ciHI6Qf_7smoldot12transactions10light_pool11blake2_hash Line | Count | Source | 1199 | 10 | fn blake2_hash(bytes: &[u8]) -> [u8; 32] { | 1200 | 10 | <[u8; 32]>::try_from(blake2_rfc::blake2b::blake2b(32, &[], bytes).as_bytes()).unwrap() | 1201 | 10 | } |
Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot12transactions10light_pool11blake2_hash |