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