1use std::cmp::Ordering;
10
11use super::{msg_pool::MessagePool, provider::Provider, utils, utils::recover_sig};
12use crate::blocks::{BLOCK_MESSAGE_LIMIT, Tipset};
13use crate::message::{MessageRead as _, SignedMessage};
14use crate::message_pool::msg_chain::MsgChainNode;
15use crate::message_pool::{
16 Error,
17 msg_chain::{Chains, NodeKey, create_message_chains},
18 msgpool::MIN_GAS,
19};
20use crate::prelude::*;
21use crate::shim::crypto::{Signature, SignatureType};
22use crate::shim::{address::Address, econ::TokenAmount};
23use crate::utils::cache::SizeTrackingCache;
24use ahash::HashMap;
25use anyhow::{bail, ensure};
26use rand::prelude::SliceRandom;
27use tracing::{debug, error, warn};
28
29type Pending = HashMap<Address, HashMap<u64, SignedMessage>>;
30
31const MAX_BLOCK_MSGS: usize = 16000;
33const MAX_BLOCKS: usize = 15;
34const CBOR_GEN_LIMIT: usize = 8192; struct SelectedMessages {
41 msgs: Vec<SignedMessage>,
43 gas_limit: u64,
45 secp_limit: u64,
47 bls_limit: u64,
49}
50
51impl Default for SelectedMessages {
52 fn default() -> Self {
53 SelectedMessages {
54 msgs: Vec::new(),
55 gas_limit: crate::shim::econ::BLOCK_GAS_LIMIT,
56 secp_limit: CBOR_GEN_LIMIT as u64,
57 bls_limit: CBOR_GEN_LIMIT as u64,
58 }
59 }
60}
61
62impl SelectedMessages {
63 fn new(msgs: Vec<SignedMessage>, gas_limit: u64) -> Self {
64 SelectedMessages {
65 msgs,
66 gas_limit,
67 ..Default::default()
68 }
69 }
70
71 fn len(&self) -> usize {
73 self.msgs.len()
74 }
75
76 fn truncate(&mut self, len: usize) {
78 self.msgs.truncate(len);
79 }
80
81 fn reduce_gas_limit(&mut self, gas: u64) {
84 self.gas_limit = self.gas_limit.saturating_sub(gas);
85 }
86
87 fn reduce_bls_limit(&mut self, bls: u64) {
90 self.bls_limit = self.bls_limit.saturating_sub(bls);
91 }
92
93 fn reduce_secp_limit(&mut self, secp: u64) {
96 self.secp_limit = self.secp_limit.saturating_sub(secp);
97 }
98
99 fn extend(&mut self, msgs: Vec<SignedMessage>) {
101 self.msgs.extend(msgs);
102 }
103
104 fn try_to_add(&mut self, message_chain: MsgChainNode) -> anyhow::Result<()> {
107 let msg_chain_len = message_chain.msgs.len();
108 ensure!(
109 BLOCK_MESSAGE_LIMIT >= msg_chain_len + self.len(),
110 "Message chain is too long to fit in the block: {} messages, limit is {BLOCK_MESSAGE_LIMIT}",
111 msg_chain_len + self.len(),
112 );
113 ensure!(
114 self.gas_limit >= message_chain.gas_limit,
115 "Message chain gas limit is too high: {} gas, limit is {}",
116 message_chain.gas_limit,
117 self.gas_limit
118 );
119
120 match message_chain.sig_type {
121 Some(SignatureType::Bls) => {
122 ensure!(
123 self.bls_limit >= msg_chain_len as u64,
124 "BLS limit is too low: {msg_chain_len} messages, limit is {}",
125 self.bls_limit
126 );
127
128 self.extend(message_chain.msgs);
129 self.reduce_bls_limit(msg_chain_len as u64);
130 self.reduce_gas_limit(message_chain.gas_limit);
131 }
132 Some(SignatureType::Secp256k1) | Some(SignatureType::Delegated) => {
133 ensure!(
134 self.secp_limit >= msg_chain_len as u64,
135 "Secp256k1 limit is too low: {msg_chain_len} messages, limit is {}",
136 self.secp_limit
137 );
138
139 self.extend(message_chain.msgs);
140 self.reduce_secp_limit(msg_chain_len as u64);
141 self.reduce_gas_limit(message_chain.gas_limit);
142 }
143 None => {
144 warn!("Tried to add a message chain with no signature type");
147 }
148 }
149 Ok(())
150 }
151
152 fn try_to_add_with_deps(
155 &mut self,
156 idx: usize,
157 chains: &mut Chains,
158 base_fee: &TokenAmount,
159 ) -> anyhow::Result<()> {
160 let message_chain = chains
161 .get_mut_at(idx)
162 .context("Couldn't find required message chain")?;
163 let mut chain_gas_limit = message_chain.gas_limit;
165 let mut chain_msg_limit = message_chain.msgs.len();
166 let mut dep_gas_limit = 0;
167 let mut dep_message_limit = 0;
168 let selected_messages_limit = match message_chain.sig_type {
169 Some(SignatureType::Bls) => self.bls_limit as usize,
170 Some(SignatureType::Secp256k1) | Some(SignatureType::Delegated) => {
171 self.secp_limit as usize
172 }
173 None => {
174 bail!("Tried to add a message chain with no signature type");
177 }
178 };
179 let selected_messages_limit =
180 selected_messages_limit.min(BLOCK_MESSAGE_LIMIT.saturating_sub(self.len()));
181
182 let mut chain_deps = vec![];
183 let mut cur_chain = message_chain.prev;
184 let _ = message_chain; while let Some(cur_chn) = cur_chain {
186 let node = chains.get(cur_chn).unwrap();
187 if !node.merged {
188 chain_deps.push(cur_chn);
189 chain_gas_limit += node.gas_limit;
190 chain_msg_limit += node.msgs.len();
191 dep_gas_limit += node.gas_limit;
192 dep_message_limit += node.msgs.len();
193 cur_chain = node.prev;
194 } else {
195 break;
196 }
197 }
198
199 if chain_gas_limit > self.gas_limit || chain_msg_limit > selected_messages_limit {
201 if dep_gas_limit > self.gas_limit || dep_message_limit >= selected_messages_limit {
207 chains.invalidate(chains.get_key_at(idx));
208 } else {
209 chains.trim_msgs_at(
211 idx,
212 self.gas_limit.saturating_sub(dep_gas_limit),
213 selected_messages_limit.saturating_sub(dep_message_limit),
214 base_fee,
215 );
216 }
217
218 bail!("Chain doesn't fit in the block");
219 }
220 for dep in chain_deps.iter().rev() {
221 let cur_chain = chains.get_mut(*dep);
222 if let Some(node) = cur_chain {
223 node.merged = true;
224 self.extend(node.msgs.clone());
225 } else {
226 bail!("Couldn't find required dependent message chain");
227 }
228 }
229
230 let message_chain = chains
231 .get_mut_at(idx)
232 .context("Couldn't find required message chain")?;
233 message_chain.merged = true;
234 self.extend(message_chain.msgs.clone());
235 self.reduce_gas_limit(chain_gas_limit);
236
237 match message_chain.sig_type {
238 Some(SignatureType::Bls) => {
239 self.reduce_bls_limit(chain_msg_limit as u64);
240 }
241 Some(SignatureType::Secp256k1) | Some(SignatureType::Delegated) => {
242 self.reduce_secp_limit(chain_msg_limit as u64);
243 }
244 None => {
245 bail!("Tried to add a message chain with no signature type");
248 }
249 }
250
251 Ok(())
252 }
253
254 fn trim_chain_at(&mut self, chains: &mut Chains, idx: usize, base_fee: &TokenAmount) {
255 let message_chain = match chains.get_at(idx) {
256 Some(message_chain) => message_chain,
257 None => {
258 error!("Tried to trim a message chain that doesn't exist");
259 return;
260 }
261 };
262 let msg_limit = BLOCK_MESSAGE_LIMIT.saturating_sub(self.len());
263 let msg_limit = match message_chain.sig_type {
264 Some(SignatureType::Bls) => std::cmp::min(self.bls_limit, msg_limit as u64),
265 Some(SignatureType::Secp256k1) | Some(SignatureType::Delegated) => {
266 std::cmp::min(self.secp_limit, msg_limit as u64)
267 }
268 _ => {
269 error!("Tried to trim a message chain with no signature type");
272 return;
273 }
274 };
275
276 if message_chain.gas_limit > self.gas_limit || message_chain.msgs.len() > msg_limit as usize
277 {
278 chains.trim_msgs_at(idx, self.gas_limit, msg_limit as usize, base_fee);
279 }
280 }
281}
282
283impl<T> MessagePool<T>
284where
285 T: Provider,
286{
287 pub fn select_messages(&self, ts: &Tipset, tq: f64) -> Result<Vec<SignedMessage>, Error> {
291 let tq = if tq.is_finite() {
293 tq.clamp(0.0, 1.0)
294 } else {
295 0.0
296 };
297 let cur_ts = self.current_tipset();
298 let mut msgs = if tq > 0.84 {
304 self.select_messages_greedy(&cur_ts, ts)
305 } else {
306 self.select_messages_optimal(&cur_ts, ts, tq)
307 }?;
308
309 if msgs.len() > MAX_BLOCK_MSGS {
310 warn!(
311 "Message selection chose too many messages: {} > {MAX_BLOCK_MSGS}",
312 msgs.len(),
313 );
314 msgs.truncate(MAX_BLOCK_MSGS)
315 }
316
317 Ok(msgs.msgs)
318 }
319
320 fn select_messages_greedy(
321 &self,
322 cur_ts: &Tipset,
323 ts: &Tipset,
324 ) -> Result<SelectedMessages, Error> {
325 let base_fee = self.api.chain_compute_base_fee(ts)?;
326
327 let mut pending = self.get_pending_messages(cur_ts, ts)?;
330
331 if pending.is_empty() {
332 return Ok(SelectedMessages::default());
333 }
334
335 let selected_msgs = self.select_priority_messages(&mut pending, &base_fee, ts)?;
337
338 if selected_msgs.gas_limit < MIN_GAS || selected_msgs.len() >= BLOCK_MESSAGE_LIMIT {
340 return Ok(selected_msgs);
341 }
342
343 let mut chains = Chains::new();
346 for (actor, mset) in pending.into_iter() {
347 create_message_chains(
348 self.api.as_ref(),
349 &actor,
350 &mset,
351 &base_fee,
352 ts,
353 &mut chains,
354 &self.chain_config,
355 )?;
356 }
357
358 Ok(merge_and_trim(
359 &mut chains,
360 selected_msgs,
361 &base_fee,
362 MIN_GAS,
363 ))
364 }
365
366 #[allow(clippy::indexing_slicing)]
367 fn select_messages_optimal(
368 &self,
369 cur_ts: &Tipset,
370 target_tipset: &Tipset,
371 ticket_quality: f64,
372 ) -> Result<SelectedMessages, Error> {
373 let base_fee = self.api.chain_compute_base_fee(target_tipset)?;
374
375 let mut pending = self.get_pending_messages(cur_ts, target_tipset)?;
378
379 if pending.is_empty() {
380 return Ok(SelectedMessages::default());
381 }
382
383 let mut selected_msgs =
385 self.select_priority_messages(&mut pending, &base_fee, target_tipset)?;
386
387 if selected_msgs.gas_limit < MIN_GAS || selected_msgs.len() >= BLOCK_MESSAGE_LIMIT {
389 return Ok(selected_msgs);
390 }
391
392 let mut chains = Chains::new();
395 for (actor, mset) in pending.into_iter() {
396 create_message_chains(
397 self.api.as_ref(),
398 &actor,
399 &mset,
400 &base_fee,
401 target_tipset,
402 &mut chains,
403 &self.chain_config,
404 )?;
405 }
406
407 chains.sort(false);
409
410 if chains.get_at(0).is_some_and(|it| it.gas_perf < 0.0) {
411 tracing::warn!(
412 "all messages in mpool have non-positive gas performance {}",
413 chains[0].gas_perf
414 );
415 return Ok(selected_msgs);
416 }
417
418 let mut next_chain = 0;
423 let mut partitions: Vec<Vec<NodeKey>> = vec![vec![]; MAX_BLOCKS];
424 let mut i = 0;
425 while i < MAX_BLOCKS && next_chain < chains.len() {
426 let mut gas_limit = crate::shim::econ::BLOCK_GAS_LIMIT;
427 let mut msg_limit = BLOCK_MESSAGE_LIMIT;
428 while next_chain < chains.len() {
429 let chain_key = chains.key_vec[next_chain];
430 next_chain += 1;
431 partitions[i].push(chain_key);
432 let chain = chains.get(chain_key).unwrap();
433 let chain_gas_limit = chain.gas_limit;
434 if gas_limit < chain_gas_limit {
435 break;
436 }
437 gas_limit = gas_limit.saturating_sub(chain_gas_limit);
438 msg_limit = msg_limit.saturating_sub(chain.msgs.len());
439 if gas_limit < MIN_GAS || msg_limit == 0 {
440 break;
441 }
442 }
443 i += 1;
444 }
445
446 let block_prob = crate::message_pool::block_probabilities(ticket_quality);
450 let mut eff_chains = 0;
451 for i in 0..MAX_BLOCKS {
452 for k in &partitions[i] {
453 if let Some(node) = chains.get_mut(*k) {
454 node.eff_perf = node.gas_perf * block_prob[i];
455 }
456 }
457 eff_chains += partitions[i].len();
458 }
459
460 for i in eff_chains..chains.len() {
462 if let Some(node) = chains.get_mut_at(i) {
463 node.set_null_effective_perf();
464 }
465 }
466
467 chains.sort_effective();
469
470 let mut last = chains.len();
475 for i in 0..chains.len() {
476 if chains[i].gas_perf < 0.0 {
478 break;
479 }
480
481 if chains[i].merged {
483 continue;
484 }
485
486 match selected_msgs.try_to_add_with_deps(i, &mut chains, &base_fee) {
487 Ok(_) => {
488 if let Some(next_key) = chains[i].next {
490 let next_node = chains.get_mut(next_key).unwrap();
491 if next_node.eff_perf > 0.0 {
492 next_node.eff_perf += next_node.parent_offset;
493 let mut next_next_key = next_node.next;
494 while let Some(nnk) = next_next_key {
495 let (nn_node, prev_perfs) = chains.get_mut_with_prev_eff(nnk);
496 if let Some(nn_node) = nn_node {
497 if nn_node.eff_perf > 0.0 {
498 nn_node.set_eff_perf(prev_perfs);
499 next_next_key = nn_node.next;
500 } else {
501 break;
502 }
503 } else {
504 break;
505 }
506 }
507 }
508 }
509
510 chains.sort_range_effective(i + 1..);
514
515 continue;
516 }
517 Err(e) => {
518 debug!("Failed to add message chain with dependencies: {e:#}");
519 }
520 }
521
522 last = i;
525 break;
526 }
527
528 'tail_loop: while selected_msgs.gas_limit >= MIN_GAS && last < chains.len() {
537 if !chains[last].valid {
538 last += 1;
540 continue;
541 }
542
543 selected_msgs.trim_chain_at(&mut chains, last, &base_fee);
545
546 if chains[last].valid {
548 for i in last..chains.len() - 1 {
549 if chains[i].cmp_effective(&chains[i + 1]) == Ordering::Greater {
550 break;
551 }
552 chains.key_vec.swap(i, i + 1);
553 }
554 }
555
556 let lst = last; for i in lst..chains.len() {
559 let chain = &mut chains[i];
560 if !chain.valid {
562 continue;
563 }
564
565 if chain.merged {
567 continue;
568 }
569
570 if chain.gas_perf < 0.0 {
572 break 'tail_loop;
573 }
574
575 match selected_msgs.try_to_add_with_deps(i, &mut chains, &base_fee) {
576 Ok(_) => continue,
577 Err(e) => debug!("Failed to add message chain with dependencies: {e:#}"),
578 }
579
580 continue 'tail_loop;
581 }
582
583 break;
586 }
587
588 if selected_msgs.gas_limit >= MIN_GAS && selected_msgs.msgs.len() <= BLOCK_MESSAGE_LIMIT {
593 let pre_random_length = selected_msgs.len();
594
595 chains
596 .key_vec
597 .shuffle(&mut crate::utils::rand::forest_rng());
598
599 for i in 0..chains.len() {
600 if selected_msgs.gas_limit < MIN_GAS || selected_msgs.len() >= BLOCK_MESSAGE_LIMIT {
601 break;
602 }
603
604 if chains[i].merged || !chains[i].valid {
606 continue;
607 }
608
609 if chains[i].gas_perf < 0.0 {
611 continue;
612 }
613
614 if selected_msgs
615 .try_to_add_with_deps(i, &mut chains, &base_fee)
616 .is_ok()
617 {
618 continue;
620 }
621
622 if chains[i].valid {
623 selected_msgs
626 .try_to_add_with_deps(i, &mut chains, &base_fee)
627 .context("Failed to add message chain with dependencies")?;
628 continue;
629 }
630 }
631
632 if selected_msgs.len() != pre_random_length {
633 tracing::warn!(
634 "optimal selection failed to pack a block; picked {} messages with random selection",
635 selected_msgs.len() - pre_random_length
636 );
637 }
638 }
639
640 Ok(selected_msgs)
641 }
642
643 fn get_pending_messages(&self, cur_ts: &Tipset, ts: &Tipset) -> Result<Pending, Error> {
644 let snapshot = self.pending.snapshot();
645 let mut result: Pending = HashMap::with_capacity(snapshot.len());
646 for (a, mset) in snapshot {
647 result.insert(a, mset.msgs);
648 }
649
650 if cur_ts.epoch() == ts.epoch() && cur_ts == ts {
651 return Ok(result);
652 }
653
654 run_head_change(
656 self.api.as_ref(),
657 &self.caches.bls_sig,
658 cur_ts.clone(),
659 ts.clone(),
660 &mut result,
661 )?;
662
663 Ok(result)
664 }
665
666 fn select_priority_messages(
667 &self,
668 pending: &mut Pending,
669 base_fee: &TokenAmount,
670 ts: &Tipset,
671 ) -> Result<SelectedMessages, Error> {
672 let result = Vec::with_capacity(self.config.size_limit_low() as usize);
673 let gas_limit = crate::shim::econ::BLOCK_GAS_LIMIT;
674 let min_gas = MIN_GAS;
675
676 let priority = self.config.priority_addrs();
678 let mut chains = Chains::new();
679 for actor in priority.iter() {
680 if let Some(mset) = pending.remove(actor) {
682 create_message_chains(
684 self.api.as_ref(),
685 actor,
686 &mset,
687 base_fee,
688 ts,
689 &mut chains,
690 &self.chain_config,
691 )?;
692 }
693 }
694
695 if chains.is_empty() {
696 return Ok(SelectedMessages::new(Vec::new(), gas_limit));
697 }
698
699 Ok(merge_and_trim(
700 &mut chains,
701 SelectedMessages::new(result, gas_limit),
702 base_fee,
703 min_gas,
704 ))
705 }
706}
707
708#[allow(clippy::indexing_slicing)]
710fn merge_and_trim(
711 chains: &mut Chains,
712 mut selected_msgs: SelectedMessages,
713 base_fee: &TokenAmount,
714 min_gas: u64,
715) -> SelectedMessages {
716 if chains.is_empty() {
717 return selected_msgs;
718 }
719
720 chains.sort(true);
722
723 let first_chain_gas_perf = chains[0].gas_perf;
724
725 if !chains.is_empty() && first_chain_gas_perf < 0.0 {
726 warn!(
727 "all priority messages in mpool have negative gas performance bestGasPerf: {}",
728 first_chain_gas_perf
729 );
730 return selected_msgs;
731 }
732
733 let mut last = chains.len();
736 for i in 0..chains.len() {
737 let node = &chains[i];
738
739 if node.gas_perf < 0.0 {
740 break;
741 }
742
743 if selected_msgs.try_to_add(node.clone()).is_ok() {
744 continue;
746 }
747
748 last = i;
750 break;
751 }
752
753 'tail_loop: while selected_msgs.gas_limit >= min_gas && last < chains.len() {
754 selected_msgs.trim_chain_at(chains, last, base_fee);
756
757 let node = &chains[last];
759 if node.valid {
760 for i in last..chains.len() - 1 {
761 let cur_node = &chains[i];
763 let next_node = &chains[i + 1];
764 if cur_node.compare(next_node) == Ordering::Greater {
765 break;
766 }
767
768 chains.key_vec.swap(i, i + 1);
769 }
770 }
771
772 let lst = last; for i in lst..chains.len() {
775 let chain = chains[i].clone();
776 if !chain.valid {
777 continue;
778 }
779
780 if chain.gas_perf < 0.0 {
782 break 'tail_loop;
783 }
784
785 if selected_msgs.try_to_add(chain).is_ok() {
787 continue;
789 }
790
791 last += i;
793 continue 'tail_loop;
794 }
795
796 break;
797 }
798
799 selected_msgs
800}
801
802pub(in crate::message_pool) fn run_head_change<T>(
806 api: &T,
807 bls_sig_cache: &SizeTrackingCache<CidWrapper, Signature>,
808 from: Tipset,
809 to: Tipset,
810 rmsgs: &mut HashMap<Address, HashMap<u64, SignedMessage>>,
811) -> Result<(), Error>
812where
813 T: Provider,
814{
815 let mut left = from;
816 let mut right = to;
817 let mut left_chain = Vec::new();
818 let mut right_chain = Vec::new();
819 while left != right {
820 if left.epoch() > right.epoch() {
821 left_chain.push(left.clone());
822 let par = api.load_tipset(left.parents())?;
823 left = par;
824 } else {
825 right_chain.push(right.clone());
826 let par = api.load_tipset(right.parents())?;
827 right = par;
828 }
829 }
830 for ts in left_chain {
831 let mut msgs: Vec<SignedMessage> = Vec::new();
832 for block in ts.block_headers() {
833 let (umsg, smsgs) = api.messages_for_block(block)?;
834 msgs.extend(smsgs);
835 for msg in umsg {
836 let msg_cid = msg.cid();
837 if let Ok(smsg) = recover_sig(bls_sig_cache, msg) {
838 msgs.push(smsg);
839 } else {
840 tracing::debug!("could not recover signature for bls message {msg_cid}");
841 }
842 }
843 }
844 for msg in msgs {
845 utils::add_to_selected_msgs(msg, rmsgs);
846 }
847 }
848
849 for ts in right_chain {
850 for b in ts.block_headers() {
851 let (msgs, smsgs) = api.messages_for_block(b)?;
852
853 for msg in smsgs {
854 utils::remove_from_selected_msgs(&msg.from(), msg.sequence(), rmsgs);
855 }
856 for msg in msgs {
857 utils::remove_from_selected_msgs(&msg.from, msg.sequence, rmsgs);
858 }
859 }
860 }
861 Ok(())
862}
863
864#[cfg(test)]
865mod test_selection {
866 use std::sync::Arc;
867
868 use super::*;
869 use crate::key_management::{KeyStore, KeyStoreConfig, Wallet};
870 use crate::message_pool::msgpool::{
871 test_provider::{TestApi, mock_block},
872 tests::{create_fake_smsg, create_smsg},
873 };
874 use crate::shim::crypto::SignatureType;
875 use crate::shim::econ::BLOCK_GAS_LIMIT;
876 use tokio::task::JoinSet;
877
878 const TEST_GAS_LIMIT: i64 = 6955002;
879
880 fn make_test_mpool(joinset: &mut JoinSet<anyhow::Result<()>>) -> MessagePool<TestApi> {
881 let tma = TestApi::default();
882 let (tx, _rx) = flume::bounded(50);
883 MessagePool::new(tma, tx, Default::default(), Arc::default(), joinset).unwrap()
884 }
885
886 async fn mock_tipset(mpool: &MessagePool<TestApi>) -> Tipset {
889 let b1 = mock_block(1, 1);
890 let ts = Tipset::from(&b1);
891 mpool
892 .apply_head_change(Vec::new(), vec![Tipset::from(b1)])
893 .await
894 .unwrap();
895 ts
896 }
897
898 #[tokio::test]
899 async fn select_messages_tolerates_out_of_range_ticket_quality() {
900 let mut joinset = JoinSet::new();
901 let mpool = make_test_mpool(&mut joinset);
902
903 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
904 let mut w1 = Wallet::new(ks1);
905 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
906
907 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
908 let mut w2 = Wallet::new(ks2);
909 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
910
911 let ts = mock_tipset(&mpool).await;
912
913 mpool
914 .api
915 .set_state_balance_raw(&a1, TokenAmount::from_whole(1));
916 mpool
917 .api
918 .set_state_balance_raw(&a2, TokenAmount::from_whole(1));
919
920 for i in 0..10 {
922 let m = create_smsg(&a2, &a1, &mut w1, i, TEST_GAS_LIMIT, 2 * i + 1);
923 mpool.add(m).await.unwrap();
924 let m = create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1);
925 mpool.add(m).await.unwrap();
926 }
927
928 for tq in [
929 -1e300,
930 -4.0,
931 2.0,
932 f64::NAN,
933 f64::INFINITY,
934 f64::NEG_INFINITY,
935 ] {
936 mpool
937 .select_messages(&ts, tq)
938 .unwrap_or_else(|e| panic!("select_messages failed for tq={tq}: {e}"));
939 }
940 }
941
942 #[tokio::test]
943 async fn basic_message_selection() {
944 let mut joinset = JoinSet::new();
945 let mpool = make_test_mpool(&mut joinset);
946
947 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
948 let mut w1 = Wallet::new(ks1);
949 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
950
951 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
952 let mut w2 = Wallet::new(ks2);
953 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
954
955 let ts = mock_tipset(&mpool).await;
956
957 mpool
958 .api
959 .set_state_balance_raw(&a1, TokenAmount::from_whole(1));
960 mpool
961 .api
962 .set_state_balance_raw(&a2, TokenAmount::from_whole(1));
963
964 for i in 0..10 {
968 let m = create_smsg(&a2, &a1, &mut w1, i, TEST_GAS_LIMIT, 2 * i + 1);
969 mpool.add(m).await.unwrap();
970 }
971 for i in 0..10 {
972 let m = create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1);
973 mpool.add(m).await.unwrap();
974 }
975
976 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
977
978 assert_eq!(msgs.len(), 20, "Expected 20 messages, got {}", msgs.len());
979
980 let mut next_nonce = 0;
981 for (i, msg) in msgs.iter().enumerate().take(10) {
982 assert_eq!(
983 msg.from(),
984 a1,
985 "first 10 returned messages should be from actor a1 {i}",
986 );
987 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
988 next_nonce += 1;
989 }
990
991 next_nonce = 0;
992 for (i, msg) in msgs.iter().enumerate().take(20).skip(10) {
993 assert_eq!(
994 msg.from(),
995 a2,
996 "next 10 returned messages should be from actor a2 {i}",
997 );
998 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
999 next_nonce += 1;
1000 }
1001
1002 let b2 = mpool.api.next_block();
1004 mpool.api.set_block_messages(&b2, msgs);
1005 mpool
1006 .apply_head_change(Vec::new(), vec![Tipset::from(b2)])
1007 .await
1008 .unwrap();
1009
1010 let remaining = mpool.pending.snapshot();
1012 assert!(
1013 remaining.is_empty(),
1014 "Expected no pending messages, but got {}",
1015 remaining.len()
1016 );
1017
1018 let mut msgs = Vec::with_capacity(20);
1020 for i in 10..20 {
1021 msgs.push(create_smsg(&a2, &a1, &mut w1, i, TEST_GAS_LIMIT, 2 * i + 1));
1022 msgs.push(create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1));
1023 }
1024 let b3 = mpool.api.next_block();
1025 let ts3 = Tipset::from(&b3);
1026 mpool.api.set_block_messages(&b3, msgs);
1027
1028 mpool.api.set_state_sequence(&a1, 20);
1030 mpool.api.set_state_sequence(&a2, 20);
1031
1032 for i in 20..30 {
1034 mpool
1035 .add(create_smsg(
1036 &a2,
1037 &a1,
1038 &mut w1,
1039 i,
1040 TEST_GAS_LIMIT,
1041 2 * i + 200,
1042 ))
1043 .await
1044 .unwrap();
1045 mpool
1046 .add(create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1))
1047 .await
1048 .unwrap();
1049 }
1050 mpool.api.set_state_sequence(&a1, 10);
1054 mpool.api.set_state_sequence(&a2, 10);
1055 let msgs = mpool.select_messages(&ts3, 1.0).unwrap();
1056
1057 assert_eq!(
1058 msgs.len(),
1059 20,
1060 "Expected 20 messages, but got {}",
1061 msgs.len()
1062 );
1063
1064 let mut next_nonce = 20;
1065 for msg in msgs.iter().take(10) {
1066 assert_eq!(
1067 msg.from(),
1068 a1,
1069 "first 10 returned messages should be from actor a1"
1070 );
1071 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1072 next_nonce += 1;
1073 }
1074 next_nonce = 20;
1075 for msg in msgs.iter().take(20).skip(10) {
1076 assert_eq!(
1077 msg.from(),
1078 a2,
1079 "next 10 returned messages should be from actor a2"
1080 );
1081 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1082 next_nonce += 1;
1083 }
1084 }
1085
1086 #[tokio::test]
1087 async fn select_messages_on_non_current_tipset_should_not_update_mpool_state() {
1088 use crate::message_pool::msgpool::msg_pool::TrustPolicy;
1089 use crate::message_pool::msgpool::msg_set::StrictnessPolicy;
1090 use crate::shim::message::Message as ShimMessage;
1091 use tokio::sync::broadcast::error::TryRecvError;
1092
1093 let mut joinset = JoinSet::new();
1094 let mpool = make_test_mpool(&mut joinset);
1095
1096 let id_addr = Address::new_id(1000);
1097 let key_addr = Address::new_bls(&[3u8; 48]).unwrap();
1098 mpool.api.set_key_address_mapping(&id_addr, &key_addr);
1099 mpool
1100 .api
1101 .set_state_balance_raw(&key_addr, TokenAmount::from_whole(1));
1102 mpool.api.set_state_sequence(&key_addr, 0);
1103
1104 let b1 = mpool.api.next_block();
1107 mpool.api.set_block_messages(&b1, vec![]);
1108 let head = Tipset::from(&b1);
1109 mpool
1110 .apply_head_change(Vec::new(), vec![head.clone()])
1111 .await
1112 .unwrap();
1113
1114 let pending_msg = SignedMessage::mock_bls_signed_message(ShimMessage {
1115 from: id_addr,
1116 sequence: 0,
1117 gas_limit: TEST_GAS_LIMIT as u64,
1118 gas_fee_cap: TokenAmount::from_atto(200),
1119 gas_premium: TokenAmount::from_atto(100),
1120 ..ShimMessage::default()
1121 });
1122 mpool
1123 .add_to_pool_unchecked(
1124 &head,
1125 pending_msg.clone(),
1126 TrustPolicy::Trusted,
1127 StrictnessPolicy::Relaxed,
1128 )
1129 .await
1130 .unwrap();
1131
1132 let before = mpool.pending.snapshot();
1133 assert!(
1134 before.contains_key(&key_addr),
1135 "precondition: message is pending under the resolved key address"
1136 );
1137
1138 let b2 = mpool.api.next_block();
1141 let ts2 = Tipset::from(&b2);
1142 mpool.api.set_block_messages(&b2, vec![pending_msg.clone()]);
1143
1144 let mut rx = mpool.pending.subscriber().subscribe();
1147
1148 let _ = mpool.select_messages(&ts2, 1.0).unwrap();
1150
1151 let after = mpool.pending.snapshot();
1152 assert!(
1153 after.contains_key(&key_addr),
1154 "selecting for a non-current tipset must not remove live pending messages"
1155 );
1156 assert_eq!(
1157 before.len(),
1158 after.len(),
1159 "the live pending pool size must be unchanged by selection"
1160 );
1161 assert_eq!(
1162 after
1163 .get(&key_addr)
1164 .and_then(|mset| mset.msgs.get(&0))
1165 .map(|m| m.cid()),
1166 Some(pending_msg.cid()),
1167 "the exact pending message must survive at its nonce"
1168 );
1169 assert!(
1170 matches!(rx.try_recv(), Err(TryRecvError::Empty)),
1171 "a read-only selection simulation must not emit any MpoolUpdate events"
1172 );
1173 }
1174
1175 #[tokio::test]
1176 async fn message_selection_trimming_gas() {
1177 let mut joinset = JoinSet::new();
1178 let mpool = make_test_mpool(&mut joinset);
1179 let ts = mock_tipset(&mpool).await;
1180 let api = mpool.api.clone();
1181
1182 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1183 let mut w1 = Wallet::new(ks1);
1184 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1185
1186 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1187 let mut w2 = Wallet::new(ks2);
1188 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1189
1190 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1191 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1192
1193 let nmsgs = (crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT) + 1;
1194
1195 for i in 0..nmsgs {
1197 let bias = (nmsgs - i) / 3;
1198 let m = create_fake_smsg(
1199 &mpool,
1200 &a2,
1201 &a1,
1202 i as u64,
1203 TEST_GAS_LIMIT,
1204 (1 + i % 3 + bias) as u64,
1205 );
1206 mpool.add(m).await.unwrap();
1207 let m = create_fake_smsg(
1208 &mpool,
1209 &a1,
1210 &a2,
1211 i as u64,
1212 TEST_GAS_LIMIT,
1213 (1 + i % 3 + bias) as u64,
1214 );
1215 mpool.add(m).await.unwrap();
1216 }
1217
1218 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1219
1220 let expected = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1221 assert_eq!(msgs.len(), expected as usize);
1222 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1223 assert!(m_gas_limit <= crate::shim::econ::BLOCK_GAS_LIMIT);
1224 }
1225
1226 #[tokio::test]
1227 async fn message_selection_trimming_msgs_basic() {
1228 let mut joinset = JoinSet::new();
1229 let mpool = make_test_mpool(&mut joinset);
1230 let ts = mock_tipset(&mpool).await;
1231 let api = mpool.api.clone();
1232
1233 let keystore = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1234 let mut wallet = Wallet::new(keystore);
1235 let address = wallet.generate_addr(SignatureType::Secp256k1).unwrap();
1236
1237 api.set_state_balance_raw(&address, TokenAmount::from_whole(1));
1238
1239 for i in 0..BLOCK_MESSAGE_LIMIT {
1241 let msg = create_fake_smsg(&mpool, &address, &address, i as u64, 200_000, 100);
1242 mpool.add(msg).await.unwrap();
1243 }
1244
1245 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1246 assert_eq!(
1247 msgs.len(),
1248 CBOR_GEN_LIMIT,
1249 "Expected {CBOR_GEN_LIMIT} messages, got {}",
1250 msgs.len()
1251 );
1252
1253 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1255 assert!(
1256 m_gas_limit <= BLOCK_GAS_LIMIT,
1257 "Selected messages gas limit {m_gas_limit} exceeds block gas limit {BLOCK_GAS_LIMIT}",
1258 );
1259 }
1260
1261 #[tokio::test]
1262 async fn message_selection_trimming_msgs_two_senders() {
1263 let mut joinset = JoinSet::new();
1264 let mpool = make_test_mpool(&mut joinset);
1265 let ts = mock_tipset(&mpool).await;
1266 let api = mpool.api.clone();
1267
1268 let keystore_1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1269 let mut wallet_1 = Wallet::new(keystore_1);
1270 let address_1 = wallet_1.generate_addr(SignatureType::Secp256k1).unwrap();
1271
1272 let keystore_2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1273 let mut wallet_2 = Wallet::new(keystore_2);
1274 let address_2 = wallet_2.generate_addr(SignatureType::Bls).unwrap();
1275
1276 api.set_state_balance_raw(&address_1, TokenAmount::from_whole(1));
1277 api.set_state_balance_raw(&address_2, TokenAmount::from_whole(1));
1278
1279 for i in 0..BLOCK_MESSAGE_LIMIT {
1281 let msg = create_smsg(
1282 &address_2,
1283 &address_1,
1284 &mut wallet_1,
1285 i as u64,
1286 300_000,
1287 100,
1288 );
1289 mpool.add(msg).await.unwrap();
1290 let msg = create_smsg(
1293 &address_1,
1294 &address_2,
1295 &mut wallet_2,
1296 i as u64,
1297 300_000,
1298 1000,
1299 );
1300 mpool.add(msg).await.unwrap();
1301 }
1302 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1303 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1305 assert!(
1306 m_gas_limit <= BLOCK_GAS_LIMIT,
1307 "Selected messages gas limit {m_gas_limit} exceeds block gas limit {BLOCK_GAS_LIMIT}",
1308 );
1309 let bls_msgs = msgs.iter().filter(|m| m.is_bls()).count();
1310 assert_eq!(
1311 CBOR_GEN_LIMIT, bls_msgs,
1312 "Expected {CBOR_GEN_LIMIT} bls messages, got {bls_msgs}."
1313 );
1314 assert_eq!(
1315 msgs.len(),
1316 BLOCK_MESSAGE_LIMIT,
1317 "Expected {BLOCK_MESSAGE_LIMIT} messages, got {}",
1318 msgs.len()
1319 );
1320 }
1321
1322 #[tokio::test]
1323 async fn message_selection_trimming_msgs_two_senders_complex() {
1324 let mut joinset = JoinSet::new();
1325 let mpool = make_test_mpool(&mut joinset);
1326 let ts = mock_tipset(&mpool).await;
1327 let api = mpool.api.clone();
1328
1329 let keystore_1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1330 let mut wallet_1 = Wallet::new(keystore_1);
1331 let address_1 = wallet_1.generate_addr(SignatureType::Secp256k1).unwrap();
1332
1333 let keystore_2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1334 let mut wallet_2 = Wallet::new(keystore_2);
1335 let address_2 = wallet_2.generate_addr(SignatureType::Bls).unwrap();
1336
1337 api.set_state_balance_raw(&address_1, TokenAmount::from_whole(1));
1338 api.set_state_balance_raw(&address_2, TokenAmount::from_whole(1));
1339
1340 let mut counter = 0;
1342 for i in 0..CBOR_GEN_LIMIT {
1343 counter += 1;
1344 let msg = create_smsg(
1345 &address_2,
1346 &address_1,
1347 &mut wallet_1,
1348 i as u64,
1349 300_000,
1350 100,
1351 );
1352 mpool.add(msg).await.unwrap();
1353 let msg = create_smsg(
1356 &address_1,
1357 &address_2,
1358 &mut wallet_2,
1359 i as u64,
1360 300_000,
1361 100,
1362 );
1363 mpool.add(msg).await.unwrap();
1364 }
1365
1366 let msg = create_smsg(
1368 &address_2,
1369 &address_1,
1370 &mut wallet_1,
1371 counter as u64,
1372 300_000,
1373 1000,
1374 );
1375 mpool.add(msg).await.unwrap();
1376
1377 let msg = create_smsg(
1378 &address_1,
1379 &address_2,
1380 &mut wallet_2,
1381 counter as u64,
1382 300_000,
1383 100,
1384 );
1385 mpool.add(msg).await.unwrap();
1386
1387 counter += 1;
1388
1389 let msg = create_smsg(
1391 &address_2,
1392 &address_1,
1393 &mut wallet_1,
1394 counter as u64,
1395 400_000,
1396 1_000_000,
1397 );
1398 mpool.add(msg).await.unwrap();
1399
1400 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1401 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1403 assert!(
1404 m_gas_limit <= BLOCK_GAS_LIMIT,
1405 "Selected messages gas limit {m_gas_limit} exceeds block gas limit {BLOCK_GAS_LIMIT}",
1406 );
1407 let secps_len = msgs.iter().filter(|m| m.is_secp256k1()).count();
1409 assert_eq!(
1410 CBOR_GEN_LIMIT, secps_len,
1411 "Expected {CBOR_GEN_LIMIT} secp messages, got {secps_len}."
1412 );
1413 assert_eq!(
1415 msgs.len(),
1416 BLOCK_MESSAGE_LIMIT,
1417 "Expected {BLOCK_MESSAGE_LIMIT} messages, got {}",
1418 msgs.len()
1419 );
1420 }
1421
1422 #[tokio::test]
1423 async fn message_selection_priority() {
1424 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1425 let mut w1 = Wallet::new(ks1);
1426 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1427
1428 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1429 let mut w2 = Wallet::new(ks2);
1430 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1431
1432 let cfg = crate::message_pool::config::MpoolConfig {
1433 priority_addrs: vec![a1],
1434 ..Default::default()
1435 };
1436
1437 let mut joinset = JoinSet::new();
1438 let (tx, _rx) = flume::bounded(50);
1439 let mpool =
1440 MessagePool::new(TestApi::default(), tx, cfg, Arc::default(), &mut joinset).unwrap();
1441 let ts = mock_tipset(&mpool).await;
1442 let api = mpool.api.clone();
1443
1444 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1446 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1447
1448 let nmsgs = 10;
1449
1450 for i in 0..nmsgs {
1452 let bias = (nmsgs - i) / 3;
1453 let m = create_smsg(
1454 &a2,
1455 &a1,
1456 &mut w1,
1457 i as u64,
1458 TEST_GAS_LIMIT,
1459 (1 + i % 3 + bias) as u64,
1460 );
1461 mpool.add(m).await.unwrap();
1462 let m = create_smsg(
1463 &a1,
1464 &a2,
1465 &mut w2,
1466 i as u64,
1467 TEST_GAS_LIMIT,
1468 (1 + i % 3 + bias) as u64,
1469 );
1470 mpool.add(m).await.unwrap();
1471 }
1472
1473 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1474
1475 assert_eq!(msgs.len(), 20);
1476
1477 let mut next_nonce = 0;
1478 for msg in msgs.iter().take(10) {
1479 assert_eq!(
1480 msg.from(),
1481 a1,
1482 "first 10 returned messages should be from actor a1"
1483 );
1484 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1485 next_nonce += 1;
1486 }
1487 next_nonce = 0;
1488 for msg in msgs.iter().take(20).skip(10) {
1489 assert_eq!(
1490 msg.from(),
1491 a2,
1492 "next 10 returned messages should be from actor a2"
1493 );
1494 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1495 next_nonce += 1;
1496 }
1497 }
1498
1499 #[tokio::test]
1500 async fn test_optimal_msg_selection1() {
1501 let mut joinset = JoinSet::new();
1505 let mpool = make_test_mpool(&mut joinset);
1506 let ts = mock_tipset(&mpool).await;
1507 let api = mpool.api.clone();
1508
1509 let mut w1 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1511 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1512 let mut w2 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1513 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1514
1515 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1516 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1517
1518 let n_msgs = 10 * crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1519
1520 for i in 0..(n_msgs as usize) {
1524 let bias = (n_msgs as usize - i) / 3;
1525 let m = create_fake_smsg(
1526 &mpool,
1527 &a2,
1528 &a1,
1529 i as u64,
1530 TEST_GAS_LIMIT,
1531 (1 + i % 3 + bias) as u64,
1532 );
1533 mpool.add(m).await.unwrap();
1534 }
1535
1536 let msgs = mpool.select_messages(&ts, 0.25).unwrap();
1537
1538 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1539
1540 assert_eq!(msgs.len(), expected_msgs as usize);
1541
1542 for (next_nonce, m) in msgs.into_iter().enumerate() {
1543 assert_eq!(m.from(), a1, "Expected message from a1");
1544 assert_eq!(
1545 m.message().sequence,
1546 next_nonce as u64,
1547 "expected nonce {} but got {}",
1548 next_nonce,
1549 m.message().sequence
1550 );
1551 }
1552 }
1553
1554 #[tokio::test]
1555 async fn test_optimal_msg_selection2() {
1556 let mut joinset = JoinSet::new();
1557 let mpool = make_test_mpool(&mut joinset);
1562 let ts = mock_tipset(&mpool).await;
1563 let api = mpool.api.clone();
1564
1565 let mut w1 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1567 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1568 let mut w2 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1569 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1570
1571 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1)); api.set_state_balance_raw(&a2, TokenAmount::from_whole(1)); let n_msgs = 5 * crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1575 for i in 0..n_msgs as usize {
1576 let bias = (n_msgs as usize - i) / 3;
1577 let m = create_fake_smsg(
1578 &mpool,
1579 &a2,
1580 &a1,
1581 i as u64,
1582 TEST_GAS_LIMIT,
1583 (200000 + i % 3 + bias) as u64,
1584 );
1585 mpool.add(m).await.unwrap();
1586 let m = create_fake_smsg(
1587 &mpool,
1588 &a1,
1589 &a2,
1590 i as u64,
1591 TEST_GAS_LIMIT,
1592 (190000 + i % 3 + bias) as u64,
1593 );
1594 mpool.add(m).await.unwrap();
1595 }
1596
1597 let msgs = mpool.select_messages(&ts, 0.1).unwrap();
1598
1599 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1600 assert_eq!(
1601 msgs.len(),
1602 expected_msgs as usize,
1603 "Expected {} messages, but got {}",
1604 expected_msgs,
1605 msgs.len()
1606 );
1607
1608 let mut n_from1 = 0;
1609 let mut n_from2 = 0;
1610 let mut next_nonce1 = 0;
1611 let mut next_nonce2 = 0;
1612
1613 for m in msgs {
1614 if m.from() == a1 {
1615 if m.message.sequence != next_nonce1 {
1616 panic!(
1617 "Expected nonce {}, but got {}",
1618 next_nonce1, m.message.sequence
1619 );
1620 }
1621 next_nonce1 += 1;
1622 n_from1 += 1;
1623 } else {
1624 if m.message.sequence != next_nonce2 {
1625 panic!(
1626 "Expected nonce {}, but got {}",
1627 next_nonce2, m.message.sequence
1628 );
1629 }
1630 next_nonce2 += 1;
1631 n_from2 += 1;
1632 }
1633 }
1634
1635 if n_from1 > n_from2 {
1636 panic!("Expected more msgs from a2 than a1");
1637 }
1638 }
1639
1640 #[tokio::test]
1641 async fn test_optimal_msg_selection3() {
1642 let mut joinset = JoinSet::new();
1643 let mpool = make_test_mpool(&mut joinset);
1649 let ts = mock_tipset(&mpool).await;
1650 let api = mpool.api.clone();
1651
1652 let n_actors = 10;
1653
1654 let mut actors = vec![];
1655 let mut wallets = vec![];
1656
1657 for _ in 0..n_actors {
1658 let mut wallet = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1659 let actor = wallet.generate_addr(SignatureType::Secp256k1).unwrap();
1660
1661 actors.push(actor);
1662 wallets.push(wallet);
1663 }
1664
1665 for a in &mut actors {
1666 api.set_state_balance_raw(a, TokenAmount::from_whole(1));
1667 }
1668
1669 let n_msgs = 1 + crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1670 for i in 0..n_msgs {
1671 for j in 0..n_actors {
1672 let premium =
1673 500000 + 10000 * (n_actors - j) + (n_msgs + 2 - i) / (30 * n_actors) + i % 3;
1674 let m = create_fake_smsg(
1675 &mpool,
1676 &actors[j as usize],
1677 &actors[j as usize],
1678 i as u64,
1679 TEST_GAS_LIMIT,
1680 premium as u64,
1681 );
1682 mpool.add(m).await.unwrap();
1683 }
1684 }
1685
1686 let msgs = mpool.select_messages(&ts, 0.1).unwrap();
1687 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1688
1689 assert_eq!(
1690 msgs.len(),
1691 expected_msgs as usize,
1692 "Expected {} messages, but got {}",
1693 expected_msgs,
1694 msgs.len()
1695 );
1696
1697 let who_is = |addr| -> usize {
1698 for (i, a) in actors.iter().enumerate() {
1699 if a == &addr {
1700 return i;
1701 }
1702 }
1703 9999999
1706 };
1707
1708 let mut nonces = vec![0; n_actors as usize];
1709 for m in &msgs {
1710 let who = who_is(m.from());
1711 if who < 3 {
1712 panic!("got message from {who}th actor",);
1713 }
1714
1715 let next_nonce: u64 = nonces[who];
1716 if m.message.sequence != next_nonce {
1717 panic!(
1718 "expected nonce {} but got {}",
1719 next_nonce, m.message.sequence
1720 );
1721 }
1722 nonces[who] += 1;
1723 }
1724 }
1725}