Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/transactions/light_pool/tests.rs
Line
Count
Source
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
#![cfg(test)]
19
20
use core::num::NonZeroU64;
21
22
use super::super::validate;
23
use super::{Config, LightPool};
24
25
#[test]
26
1
fn regular_path() {
27
1
    let mut pool = LightPool::<_, _, ()>::new(Config {
28
1
        blocks_capacity: 16,
29
1
        finalized_block_hash: [0; 32],
30
1
        transactions_capacity: 16,
31
1
    });
32
1
33
1
    assert_eq!(pool.missing_block_bodies().count(), 0);
34
35
1
    let tx_id = pool.add_unvalidated(vec![0], ());
36
1
37
1
    pool.add_block([1; 32], &[0; 32], ());
38
1
    let set_best_block = pool.set_best_block(&[1; 32]);
39
1
    assert!(set_best_block.included_transactions.is_empty());
40
1
    assert!(set_best_block.retracted_transactions.is_empty());
41
1
    assert_eq!(pool.missing_block_bodies().count(), 1);
42
43
1
    let included_txs = pool
44
1
        .set_block_body(&[1; 32], vec![vec![0]].into_iter())
45
1
        .collect::<Vec<_>>();
46
1
    assert_eq!(included_txs, vec![(tx_id, 0)]);
47
1
    assert_eq!(pool.missing_block_bodies().count(), 0);
48
49
1
    let mut non_finalized_iter = pool.set_finalized_block(&[1; 32]);
50
1
    assert!(non_finalized_iter.next().is_none());
51
52
1
    let mut iter = pool.prune_finalized_with_body();
53
1
    let pruned = iter.next().unwrap();
54
1
    assert_eq!(pruned.block_hash, [1; 32]);
55
1
    assert_eq!(
56
1
        pruned.included_transactions,
57
1
        vec![super::RemovedTransaction {
58
1
            id: tx_id,
59
1
            index_in_block: 0,
60
1
            scale_encoding: vec![0],
61
1
            user_data: ()
62
1
        }]
63
1
    );
64
1
}
65
66
#[test]
67
1
fn included_after_set_best() {
68
1
    let mut pool = LightPool::<_, _, ()>::new(Config {
69
1
        blocks_capacity: 16,
70
1
        finalized_block_hash: [0; 32],
71
1
        transactions_capacity: 16,
72
1
    });
73
1
74
1
    assert_eq!(pool.missing_block_bodies().count(), 0);
75
76
1
    let tx_id = pool.add_unvalidated(vec![0], ());
77
1
78
1
    pool.add_block([1; 32], &[0; 32], ());
79
1
    let included_txs = pool
80
1
        .set_block_body(&[1; 32], vec![vec![0]].into_iter())
81
1
        .collect::<Vec<_>>();
82
1
    assert!(included_txs.is_empty());
83
84
1
    let set_best_block = pool.set_best_block(&[1; 32]);
85
1
    assert_eq!(
86
1
        set_best_block.included_transactions,
87
1
        vec![(tx_id, [1; 32], 0)]
88
1
    );
89
1
    assert!(set_best_block.retracted_transactions.is_empty());
90
1
}
91
92
#[test]
93
1
fn transaction_retracted_after_reorg() {
94
1
    let mut pool = LightPool::<_, _, ()>::new(Config {
95
1
        blocks_capacity: 16,
96
1
        finalized_block_hash: [0; 32],
97
1
        transactions_capacity: 16,
98
1
    });
99
1
100
1
    assert_eq!(pool.missing_block_bodies().count(), 0);
101
102
1
    let tx_id = pool.add_unvalidated(vec![0], ());
103
1
104
1
    // Add blocks 1 and 2, both children of block 0.
105
1
    pool.add_block([1; 32], &[0; 32], ());
106
1
    pool.add_block([2; 32], &[0; 32], ());
107
1
108
1
    // Block 1 contains the transaction we want, while block 2 doesn't.
109
1
    let included_txs = pool
110
1
        .set_block_body(&[1; 32], vec![vec![0]].into_iter())
111
1
        .collect::<Vec<_>>();
112
1
    assert!(included_txs.is_empty());
113
1
    let included_txs = pool
114
1
        .set_block_body(&[2; 32], Vec::<Vec<u8>>::new().into_iter())
115
1
        .collect::<Vec<_>>();
116
1
    assert!(included_txs.is_empty());
117
118
    // Set block 1 as the best block. Transaction must be included.
119
1
    let set_best_block = pool.set_best_block(&[1; 32]);
120
1
    assert_eq!(
121
1
        set_best_block.included_transactions,
122
1
        vec![(tx_id, [1; 32], 0)]
123
1
    );
124
1
    assert!(set_best_block.retracted_transactions.is_empty());
125
126
    // Set block 2 as the best block. Transaction must be retracted.
127
1
    let set_best_block = pool.set_best_block(&[2; 32]);
128
1
    assert!(set_best_block.included_transactions.is_empty());
129
1
    assert_eq!(
130
1
        set_best_block.retracted_transactions,
131
1
        vec![(tx_id, [1; 32], 0)]
132
1
    );
133
134
    // Set block 1 as the best block again. Transaction must be included.
135
1
    let set_best_block = pool.set_best_block(&[1; 32]);
136
1
    assert_eq!(
137
1
        set_best_block.included_transactions,
138
1
        vec![(tx_id, [1; 32], 0)]
139
1
    );
140
1
    assert!(set_best_block.retracted_transactions.is_empty());
141
1
}
142
143
#[test]
144
1
fn longevity_works_non_finalized() {
145
1
    let mut pool = LightPool::<_, _, ()>::new(Config {
146
1
        blocks_capacity: 16,
147
1
        finalized_block_hash: [0; 32],
148
1
        transactions_capacity: 16,
149
1
    });
150
1
151
1
    let tx_id = pool.add_unvalidated(vec![0], ());
152
1
153
1
    // Add one base block.
154
1
    pool.add_block([1; 32], &[0; 32], ());
155
1
    let _ = pool.set_best_block(&[1; 32]);
156
1
    assert!(!pool.is_valid_against_best_block(tx_id));
157
1
    assert_eq!(
158
1
        pool.unvalidated_transactions()
159
1
            .map(|(id, _)| id)
160
1
            .collect::<Vec<_>>(),
161
1
        vec![tx_id]
162
1
    );
163
164
    // Validate transaction against that block.
165
1
    pool.set_validation_result(
166
1
        tx_id,
167
1
        &[1; 32],
168
1
        Ok(validate::ValidTransaction {
169
1
            longevity: NonZeroU64::new(2).unwrap(),
170
1
            priority: 1,
171
1
            propagate: true,
172
1
            provides: Vec::new(),
173
1
            requires: Vec::new(),
174
1
        }),
175
1
    );
176
1
    assert!(pool.is_valid_against_best_block(tx_id));
177
1
    assert_eq!(pool.unvalidated_transactions().count(), 0);
178
179
    // Add more blocks on top of the best chain.
180
1
    pool.add_block([2; 32], &[1; 32], ());
181
1
    let _ = pool.set_best_block(&[2; 32]);
182
1
    pool.add_block([3; 32], &[2; 32], ());
183
1
    let _ = pool.set_best_block(&[3; 32]);
184
1
185
1
    // The transaction is still valid.
186
1
    assert!(pool.is_valid_against_best_block(tx_id));
187
1
    assert_eq!(pool.unvalidated_transactions().count(), 0);
188
189
    // One more block.
190
1
    pool.add_block([4; 32], &[3; 32], ());
191
1
    let _ = pool.set_best_block(&[4; 32]);
192
1
193
1
    // Transaction is no longer valid because its longevity has expired.
194
1
    assert!(!pool.is_valid_against_best_block(tx_id));
195
1
    assert_eq!(
196
1
        pool.unvalidated_transactions()
197
1
            .map(|(id, _)| id)
198
1
            .collect::<Vec<_>>(),
199
1
        vec![tx_id]
200
1
    );
201
1
}
202
203
#[test]
204
1
fn longevity_works_finalized() {
205
1
    let mut pool = LightPool::<_, _, ()>::new(Config {
206
1
        blocks_capacity: 16,
207
1
        finalized_block_hash: [0; 32],
208
1
        transactions_capacity: 16,
209
1
    });
210
1
211
1
    let tx_id = pool.add_unvalidated(vec![0], ());
212
1
213
1
    // Add one base block.
214
1
    pool.add_block([1; 32], &[0; 32], ());
215
1
    let _ = pool.set_best_block(&[1; 32]);
216
1
    assert!(!pool.is_valid_against_best_block(tx_id));
217
1
    assert_eq!(
218
1
        pool.unvalidated_transactions()
219
1
            .map(|(id, _)| id)
220
1
            .collect::<Vec<_>>(),
221
1
        vec![tx_id]
222
1
    );
223
224
    // Validate transaction against that block.
225
1
    pool.set_validation_result(
226
1
        tx_id,
227
1
        &[1; 32],
228
1
        Ok(validate::ValidTransaction {
229
1
            longevity: NonZeroU64::new(2).unwrap(),
230
1
            priority: 1,
231
1
            propagate: true,
232
1
            provides: Vec::new(),
233
1
            requires: Vec::new(),
234
1
        }),
235
1
    );
236
1
    assert!(pool.is_valid_against_best_block(tx_id));
237
1
    assert_eq!(pool.unvalidated_transactions().count(), 0);
238
239
    // Add more blocks on top of the best chain.
240
1
    pool.add_block([2; 32], &[1; 32], ());
241
1
    let _ = pool.set_best_block(&[2; 32]);
242
1
    pool.add_block([3; 32], &[2; 32], ());
243
1
    let _ = pool.set_best_block(&[3; 32]);
244
1
245
1
    // Finalize the latest block added.
246
1
    let _ = pool.set_finalized_block(&[3; 32]);
247
1
    let _ = pool.prune_finalized_with_body();
248
1
249
1
    // The transaction is still valid.
250
1
    assert!(pool.is_valid_against_best_block(tx_id));
251
1
    assert_eq!(pool.unvalidated_transactions().count(), 0);
252
253
    // One more block.
254
1
    pool.add_block([4; 32], &[3; 32], ());
255
1
    let _ = pool.set_best_block(&[4; 32]);
256
1
257
1
    // Transaction is no longer valid because its longevity has expired.
258
1
    assert!(!pool.is_valid_against_best_block(tx_id));
259
1
    assert_eq!(
260
1
        pool.unvalidated_transactions()
261
1
            .map(|(id, _)| id)
262
1
            .collect::<Vec<_>>(),
263
1
        vec![tx_id]
264
1
    );
265
1
}
266
267
#[test]
268
1
fn longevity_works_finalized_base() {
269
1
    let mut pool = LightPool::<_, _, ()>::new(Config {
270
1
        blocks_capacity: 16,
271
1
        finalized_block_hash: [0; 32],
272
1
        transactions_capacity: 16,
273
1
    });
274
1
275
1
    let tx_id = pool.add_unvalidated(vec![0], ());
276
1
277
1
    // Validate transaction against that block.
278
1
    pool.set_validation_result(
279
1
        tx_id,
280
1
        &[0; 32],
281
1
        Ok(validate::ValidTransaction {
282
1
            longevity: NonZeroU64::new(2).unwrap(),
283
1
            priority: 1,
284
1
            propagate: true,
285
1
            provides: Vec::new(),
286
1
            requires: Vec::new(),
287
1
        }),
288
1
    );
289
1
    assert!(pool.is_valid_against_best_block(tx_id));
290
1
    assert_eq!(pool.unvalidated_transactions().count(), 0);
291
292
    // Add more blocks on top of the best chain.
293
1
    pool.add_block([1; 32], &[0; 32], ());
294
1
    let _ = pool.set_best_block(&[1; 32]);
295
1
    pool.add_block([2; 32], &[1; 32], ());
296
1
    let _ = pool.set_best_block(&[2; 32]);
297
1
298
1
    // Finalize the latest block added.
299
1
    let _ = pool.set_finalized_block(&[2; 32]);
300
1
    let _ = pool.prune_finalized_with_body();
301
1
302
1
    // The transaction is still valid.
303
1
    assert!(pool.is_valid_against_best_block(tx_id));
304
1
    assert_eq!(pool.unvalidated_transactions().count(), 0);
305
306
    // One more block.
307
1
    pool.add_block([3; 32], &[2; 32], ());
308
1
    let _ = pool.set_best_block(&[3; 32]);
309
1
310
1
    // Transaction is no longer valid because its longevity has expired.
311
1
    assert!(!pool.is_valid_against_best_block(tx_id));
312
1
    assert_eq!(
313
1
        pool.unvalidated_transactions()
314
1
            .map(|(id, _)| id)
315
1
            .collect::<Vec<_>>(),
316
1
        vec![tx_id]
317
1
    );
318
1
}
319
320
// TODO: more tests