1use ahash::{HashMap, HashMapExt};
10
11use crate::message::{MessageRead, SignedMessage};
12use crate::message_pool::errors::Error;
13use crate::message_pool::metrics;
14use crate::message_pool::msg_pool::TrustPolicy;
15use crate::message_pool::msgpool::utils::compute_rbf_min_premium;
16
17pub(in crate::message_pool) const MAX_NONCE_GAP: u64 = 4;
19
20#[derive(Clone, Copy, Debug)]
22pub struct MsgSetLimits {
23 pub trusted: u64,
25 pub untrusted: u64,
27}
28
29impl MsgSetLimits {
30 pub fn new(trusted: u64, untrusted: u64) -> Self {
31 Self { trusted, untrusted }
32 }
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37pub enum StrictnessPolicy {
38 Strict,
39 Relaxed,
40}
41
42#[derive(Clone, Default, Debug)]
45pub struct MsgSet {
46 pub(in crate::message_pool) msgs: HashMap<u64, SignedMessage>,
47 pub(in crate::message_pool) next_sequence: u64,
48}
49
50impl MsgSet {
51 pub fn new(sequence: u64) -> Self {
54 MsgSet {
55 msgs: HashMap::new(),
56 next_sequence: sequence,
57 }
58 }
59
60 pub(in crate::message_pool) fn add(
74 &mut self,
75 m: SignedMessage,
76 strictness: StrictnessPolicy,
77 trust: TrustPolicy,
78 limits: MsgSetLimits,
79 ) -> Result<(), Error> {
80 let strict = matches!(strictness, StrictnessPolicy::Strict);
81 let trusted = matches!(trust, TrustPolicy::Trusted);
82 let max_nonce_gap: u64 = if trusted { MAX_NONCE_GAP } else { 0 };
83 let max_actor_pending_messages = if trusted {
84 limits.trusted
85 } else {
86 limits.untrusted
87 };
88
89 let mut next_nonce = self.next_sequence;
90 let nonce_gap = if m.sequence() == next_nonce {
91 next_nonce += 1;
92 while self.msgs.contains_key(&next_nonce) {
93 next_nonce += 1;
94 }
95 false
96 } else if strict && m.sequence() > next_nonce + max_nonce_gap {
97 tracing::debug!(
98 nonce = m.sequence(),
99 next_nonce,
100 "message nonce has too big a gap from expected nonce"
101 );
102 return Err(Error::NonceGap);
103 } else {
104 m.sequence() > next_nonce
105 };
106
107 let has_existing = if let Some(exms) = self.msgs.get(&m.sequence()) {
108 if strict && nonce_gap {
109 tracing::debug!(
110 nonce = m.sequence(),
111 next_nonce,
112 "rejecting replace by fee because of nonce gap"
113 );
114 return Err(Error::NonceGap);
115 }
116 if m.cid() != exms.cid() {
117 let premium = &exms.message().gas_premium;
118 let min_price = compute_rbf_min_premium(premium);
119 if m.message().gas_premium < min_price {
120 return Err(Error::GasPriceTooLow);
121 }
122 } else {
123 return Err(Error::DuplicateSequence);
124 }
125 true
126 } else {
127 false
128 };
129
130 if !has_existing && self.msgs.len() as u64 >= max_actor_pending_messages {
132 return Err(Error::TooManyPendingMessages(
133 m.message.from().to_string(),
134 trusted,
135 ));
136 }
137
138 if strict && nonce_gap {
139 tracing::debug!(
140 from = %m.from(),
141 nonce = m.sequence(),
142 next_nonce,
143 "adding nonce-gapped message"
144 );
145 }
146
147 self.next_sequence = next_nonce;
148 if self.msgs.insert(m.sequence(), m).is_none() {
149 metrics::MPOOL_MESSAGE_TOTAL.inc();
150 }
151 Ok(())
152 }
153
154 pub fn rm(&mut self, sequence: u64, applied: bool) -> Option<SignedMessage> {
165 let Some(removed) = self.msgs.remove(&sequence) else {
166 if applied && sequence >= self.next_sequence {
167 self.next_sequence = sequence + 1;
168 while self.msgs.contains_key(&self.next_sequence) {
169 self.next_sequence += 1;
170 }
171 }
172 return None;
173 };
174 metrics::MPOOL_MESSAGE_TOTAL.dec();
175
176 if applied {
178 if sequence >= self.next_sequence {
181 self.next_sequence = sequence + 1;
182 while self.msgs.contains_key(&self.next_sequence) {
183 self.next_sequence += 1;
184 }
185 }
186 } else if sequence < self.next_sequence {
187 self.next_sequence = sequence;
190 }
191 Some(removed)
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use crate::shim::address::Address;
199 use crate::shim::econ::TokenAmount;
200 use crate::shim::message::Message as ShimMessage;
201
202 fn make_smsg(from: Address, seq: u64, premium: u64) -> SignedMessage {
203 SignedMessage::mock_bls_signed_message(ShimMessage {
204 from,
205 sequence: seq,
206 gas_premium: TokenAmount::from_atto(premium),
207 gas_limit: 1_000_000,
208 ..ShimMessage::default()
209 })
210 }
211
212 #[test]
215 fn rbf_at_capacity() {
216 let limits = MsgSetLimits::new(10, 10);
217 let mut mset = MsgSet::new(0);
218
219 for i in 0..10 {
221 let res = mset.add(
222 make_smsg(Address::default(), i, 100),
223 StrictnessPolicy::Relaxed,
224 TrustPolicy::Trusted,
225 limits,
226 );
227 assert!(res.is_ok(), "Failed to add message {i}");
228 }
229
230 let res = mset.add(
232 make_smsg(Address::default(), 10, 100),
233 StrictnessPolicy::Relaxed,
234 TrustPolicy::Trusted,
235 limits,
236 );
237 assert!(matches!(res, Err(Error::TooManyPendingMessages(_, _))));
238
239 let res = mset.add(
242 make_smsg(Address::default(), 5, 200),
243 StrictnessPolicy::Relaxed,
244 TrustPolicy::Trusted,
245 limits,
246 );
247 assert!(res.is_ok(), "RBF should be allowed at capacity");
248 }
249
250 #[test]
251 fn gap_filling_advances_next_sequence() {
252 let limits = MsgSetLimits::new(1000, 1000);
253 let mut mset = MsgSet::new(0);
254
255 mset.add(
256 make_smsg(Address::default(), 0, 100),
257 StrictnessPolicy::Relaxed,
258 TrustPolicy::Trusted,
259 limits,
260 )
261 .unwrap();
262 assert_eq!(mset.next_sequence, 1);
263
264 mset.add(
265 make_smsg(Address::default(), 2, 100),
266 StrictnessPolicy::Relaxed,
267 TrustPolicy::Trusted,
268 limits,
269 )
270 .unwrap();
271 assert_eq!(mset.next_sequence, 1, "gap at 1, so next_sequence stays");
272
273 mset.add(
274 make_smsg(Address::default(), 1, 100),
275 StrictnessPolicy::Relaxed,
276 TrustPolicy::Trusted,
277 limits,
278 )
279 .unwrap();
280 assert_eq!(
281 mset.next_sequence, 3,
282 "filling the gap should advance past all consecutive messages"
283 );
284 }
285
286 #[test]
287 fn trusted_allows_any_nonce_gap() {
288 let limits = MsgSetLimits::new(1000, 1000);
289 let mut mset = MsgSet::new(0);
290
291 mset.add(
292 make_smsg(Address::default(), 0, 100),
293 StrictnessPolicy::Relaxed,
294 TrustPolicy::Trusted,
295 limits,
296 )
297 .unwrap();
298 let res = mset.add(
299 make_smsg(Address::default(), 10, 100),
300 StrictnessPolicy::Relaxed,
301 TrustPolicy::Trusted,
302 limits,
303 );
304 assert!(
305 res.is_ok(),
306 "trusted adds skip nonce gap enforcement (StrictnessPolicy::Relaxed)"
307 );
308 }
309
310 #[test]
311 fn strict_allows_small_nonce_gap() {
312 let limits = MsgSetLimits::new(1000, 1000);
313 let mut mset = MsgSet::new(0);
314
315 mset.add(
317 make_smsg(Address::default(), 0, 100),
318 StrictnessPolicy::Strict,
319 TrustPolicy::Trusted,
320 limits,
321 )
322 .unwrap();
323 let res = mset.add(
324 make_smsg(Address::default(), 3, 100),
325 StrictnessPolicy::Strict,
326 TrustPolicy::Trusted,
327 limits,
328 );
329 assert!(
330 res.is_ok(),
331 "strict+trusted: gap of 2 (within MAX_NONCE_GAP=4) should succeed"
332 );
333 }
334
335 #[test]
336 fn strict_rejects_large_nonce_gap() {
337 let limits = MsgSetLimits::new(1000, 1000);
338 let mut mset = MsgSet::new(0);
339
340 mset.add(
342 make_smsg(Address::default(), 0, 100),
343 StrictnessPolicy::Strict,
344 TrustPolicy::Trusted,
345 limits,
346 )
347 .unwrap();
348 let res = mset.add(
349 make_smsg(Address::default(), 6, 100),
350 StrictnessPolicy::Strict,
351 TrustPolicy::Trusted,
352 limits,
353 );
354 assert_eq!(
355 res,
356 Err(Error::NonceGap),
357 "strict+trusted: gap of 5 (exceeds MAX_NONCE_GAP=4) should be rejected"
358 );
359 }
360
361 #[test]
362 fn strict_untrusted_rejects_any_gap() {
363 let limits = MsgSetLimits::new(1000, 1000);
364 let mut mset = MsgSet::new(0);
365
366 mset.add(
368 make_smsg(Address::default(), 0, 100),
369 StrictnessPolicy::Strict,
370 TrustPolicy::Untrusted,
371 limits,
372 )
373 .unwrap();
374 let res = mset.add(
375 make_smsg(Address::default(), 2, 100),
376 StrictnessPolicy::Strict,
377 TrustPolicy::Untrusted,
378 limits,
379 );
380 assert_eq!(
381 res,
382 Err(Error::NonceGap),
383 "strict+untrusted: any gap (maxNonceGap=0) is rejected"
384 );
385 }
386
387 #[test]
388 fn non_strict_untrusted_skips_gap_check() {
389 let limits = MsgSetLimits::new(1000, 1000);
390 let mut mset = MsgSet::new(0);
391
392 mset.add(
394 make_smsg(Address::default(), 0, 100),
395 StrictnessPolicy::Relaxed,
396 TrustPolicy::Untrusted,
397 limits,
398 )
399 .unwrap();
400 let res = mset.add(
401 make_smsg(Address::default(), 5, 100),
402 StrictnessPolicy::Relaxed,
403 TrustPolicy::Untrusted,
404 limits,
405 );
406 assert!(
407 res.is_ok(),
408 "non-strict untrusted (PushUntrusted) skips gap enforcement"
409 );
410 }
411
412 #[test]
413 fn strict_rbf_during_gap_rejected() {
414 let limits = MsgSetLimits::new(1000, 1000);
415 let mut mset = MsgSet::new(0);
416
417 mset.add(
419 make_smsg(Address::default(), 0, 100),
420 StrictnessPolicy::Relaxed,
421 TrustPolicy::Trusted,
422 limits,
423 )
424 .unwrap();
425 mset.add(
426 make_smsg(Address::default(), 2, 100),
427 StrictnessPolicy::Relaxed,
428 TrustPolicy::Trusted,
429 limits,
430 )
431 .unwrap();
432
433 let res = mset.add(
435 make_smsg(Address::default(), 2, 200),
436 StrictnessPolicy::Strict,
437 TrustPolicy::Trusted,
438 limits,
439 );
440 assert_eq!(
441 res,
442 Err(Error::NonceGap),
443 "strict RBF should be rejected when nonce gap exists"
444 );
445 }
446
447 #[test]
448 fn rbf_without_gap_still_works() {
449 let limits = MsgSetLimits::new(1000, 1000);
450 let mut mset = MsgSet::new(0);
451
452 mset.add(
453 make_smsg(Address::default(), 0, 100),
454 StrictnessPolicy::Relaxed,
455 TrustPolicy::Trusted,
456 limits,
457 )
458 .unwrap();
459 mset.add(
460 make_smsg(Address::default(), 1, 100),
461 StrictnessPolicy::Relaxed,
462 TrustPolicy::Trusted,
463 limits,
464 )
465 .unwrap();
466 mset.add(
467 make_smsg(Address::default(), 2, 100),
468 StrictnessPolicy::Relaxed,
469 TrustPolicy::Trusted,
470 limits,
471 )
472 .unwrap();
473
474 let res = mset.add(
475 make_smsg(Address::default(), 1, 200),
476 StrictnessPolicy::Relaxed,
477 TrustPolicy::Trusted,
478 limits,
479 );
480 assert!(res.is_ok(), "RBF without a nonce gap should succeed");
481 }
482
483 #[test]
484 fn rm_applied_advances_next_sequence() {
485 let limits = MsgSetLimits::new(1000, 1000);
486 let mut mset = MsgSet::new(0);
487
488 mset.add(
489 make_smsg(Address::default(), 0, 100),
490 StrictnessPolicy::Relaxed,
491 TrustPolicy::Trusted,
492 limits,
493 )
494 .unwrap();
495 assert_eq!(mset.next_sequence, 1);
496
497 mset.rm(0, true);
499 assert_eq!(
500 mset.next_sequence, 1,
501 "applied rm at seq < next_sequence does not advance further"
502 );
503
504 mset.rm(5, true);
506 assert_eq!(
507 mset.next_sequence, 6,
508 "applied rm of unknown seq >= next_sequence advances to seq+1"
509 );
510 }
511
512 #[test]
513 fn rm_applied_known_advances_past_pending_successors() {
514 let limits = MsgSetLimits::new(1000, 1000);
515 let mut mset = MsgSet::new(5);
518
519 for seq in [6, 7] {
520 mset.add(
521 make_smsg(Address::default(), seq, 100),
522 StrictnessPolicy::Relaxed,
523 TrustPolicy::Trusted,
524 limits,
525 )
526 .unwrap();
527 }
528 assert_eq!(
529 mset.next_sequence, 5,
530 "gapped adds leave next_sequence at 5"
531 );
532
533 mset.rm(6, true);
535 assert_eq!(
536 mset.next_sequence, 8,
537 "must advance past the still-pending nonce 7, not stop at 7"
538 );
539 }
540
541 #[test]
542 fn rm_pruned_rewinds_next_sequence_on_gap() {
543 let limits = MsgSetLimits::new(1000, 1000);
544 let mut mset = MsgSet::new(0);
545
546 for i in 0..3 {
548 mset.add(
549 make_smsg(Address::default(), i, 100),
550 StrictnessPolicy::Relaxed,
551 TrustPolicy::Trusted,
552 limits,
553 )
554 .unwrap();
555 }
556 assert_eq!(mset.next_sequence, 3);
557
558 mset.rm(1, false);
560 assert_eq!(
561 mset.next_sequence, 1,
562 "pruned rm creating a gap rewinds next_sequence"
563 );
564 }
565}