Coverage Report

Created: 2024-05-16 12:16

/__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