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