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
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 for msg in umsg {
842 let msg_cid = msg.cid();
843 if let Ok(smsg) = recover_sig(bls_sig_cache, msg) {
844 msgs.push(smsg);
845 } else {
846 tracing::debug!("could not recover signature for bls message {msg_cid}");
847 }
848 }
849 }
850 for msg in msgs {
851 utils::add_to_selected_msgs(msg, rmsgs);
852 }
853 }
854
855 for ts in right_chain {
856 for b in ts.block_headers() {
857 let (msgs, smsgs) = api.messages_for_block(b)?;
858
859 for msg in smsgs {
860 utils::remove_from_selected_msgs(&msg.from(), msg.sequence(), rmsgs);
861 }
862 for msg in msgs {
863 utils::remove_from_selected_msgs(&msg.from, msg.sequence, rmsgs);
864 }
865 }
866 }
867 Ok(())
868}
869
870#[cfg(test)]
871mod test_selection {
872 use std::sync::Arc;
873
874 use super::*;
875 use crate::key_management::{KeyStore, KeyStoreConfig, Wallet};
876 use crate::message_pool::msgpool::{
877 test_provider::{TestApi, mock_block},
878 tests::{create_fake_smsg, create_smsg},
879 };
880 use crate::shim::crypto::SignatureType;
881 use crate::shim::econ::BLOCK_GAS_LIMIT;
882 use tokio::task::JoinSet;
883
884 const TEST_GAS_LIMIT: i64 = 6955002;
885
886 fn make_test_mpool(joinset: &mut JoinSet<anyhow::Result<()>>) -> MessagePool<TestApi> {
887 let tma = TestApi::default();
888 let (tx, _rx) = flume::bounded(50);
889 MessagePool::new(tma, tx, Default::default(), Arc::default(), joinset).unwrap()
890 }
891
892 async fn mock_tipset(mpool: &MessagePool<TestApi>) -> Tipset {
895 let b1 = mock_block(1, 1);
896 let ts = Tipset::from(&b1);
897 mpool
898 .apply_head_change(Vec::new(), vec![Tipset::from(b1)])
899 .await
900 .unwrap();
901 ts
902 }
903
904 #[tokio::test]
905 async fn select_messages_tolerates_out_of_range_ticket_quality() {
906 let mut joinset = JoinSet::new();
907 let mpool = make_test_mpool(&mut joinset);
908
909 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
910 let mut w1 = Wallet::new(ks1);
911 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
912
913 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
914 let mut w2 = Wallet::new(ks2);
915 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
916
917 let ts = mock_tipset(&mpool).await;
918
919 mpool
920 .api
921 .set_state_balance_raw(&a1, TokenAmount::from_whole(1));
922 mpool
923 .api
924 .set_state_balance_raw(&a2, TokenAmount::from_whole(1));
925
926 for i in 0..10 {
928 let m = create_smsg(&a2, &a1, &mut w1, i, TEST_GAS_LIMIT, 2 * i + 1);
929 mpool.add(m).await.unwrap();
930 let m = create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1);
931 mpool.add(m).await.unwrap();
932 }
933
934 for tq in [
935 -1e300,
936 -4.0,
937 2.0,
938 f64::NAN,
939 f64::INFINITY,
940 f64::NEG_INFINITY,
941 ] {
942 mpool
943 .select_messages(&ts, tq)
944 .unwrap_or_else(|e| panic!("select_messages failed for tq={tq}: {e}"));
945 }
946 }
947
948 #[tokio::test]
949 async fn basic_message_selection() {
950 let mut joinset = JoinSet::new();
951 let mpool = make_test_mpool(&mut joinset);
952
953 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
954 let mut w1 = Wallet::new(ks1);
955 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
956
957 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
958 let mut w2 = Wallet::new(ks2);
959 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
960
961 let ts = mock_tipset(&mpool).await;
962
963 mpool
964 .api
965 .set_state_balance_raw(&a1, TokenAmount::from_whole(1));
966 mpool
967 .api
968 .set_state_balance_raw(&a2, TokenAmount::from_whole(1));
969
970 for i in 0..10 {
974 let m = create_smsg(&a2, &a1, &mut w1, i, TEST_GAS_LIMIT, 2 * i + 1);
975 mpool.add(m).await.unwrap();
976 }
977 for i in 0..10 {
978 let m = create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1);
979 mpool.add(m).await.unwrap();
980 }
981
982 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
983
984 assert_eq!(msgs.len(), 20, "Expected 20 messages, got {}", msgs.len());
985
986 let mut next_nonce = 0;
987 for (i, msg) in msgs.iter().enumerate().take(10) {
988 assert_eq!(
989 msg.from(),
990 a1,
991 "first 10 returned messages should be from actor a1 {i}",
992 );
993 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
994 next_nonce += 1;
995 }
996
997 next_nonce = 0;
998 for (i, msg) in msgs.iter().enumerate().take(20).skip(10) {
999 assert_eq!(
1000 msg.from(),
1001 a2,
1002 "next 10 returned messages should be from actor a2 {i}",
1003 );
1004 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1005 next_nonce += 1;
1006 }
1007
1008 let b2 = mpool.api.next_block();
1010 mpool.api.set_block_messages(&b2, msgs);
1011 mpool
1012 .apply_head_change(Vec::new(), vec![Tipset::from(b2)])
1013 .await
1014 .unwrap();
1015
1016 let remaining = mpool.pending.snapshot();
1018 assert!(
1019 remaining.is_empty(),
1020 "Expected no pending messages, but got {}",
1021 remaining.len()
1022 );
1023
1024 let mut msgs = Vec::with_capacity(20);
1026 for i in 10..20 {
1027 msgs.push(create_smsg(&a2, &a1, &mut w1, i, TEST_GAS_LIMIT, 2 * i + 1));
1028 msgs.push(create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1));
1029 }
1030 let b3 = mpool.api.next_block();
1031 let ts3 = Tipset::from(&b3);
1032 mpool.api.set_block_messages(&b3, msgs);
1033
1034 mpool.api.set_state_sequence(&a1, 20);
1036 mpool.api.set_state_sequence(&a2, 20);
1037
1038 for i in 20..30 {
1040 mpool
1041 .add(create_smsg(
1042 &a2,
1043 &a1,
1044 &mut w1,
1045 i,
1046 TEST_GAS_LIMIT,
1047 2 * i + 200,
1048 ))
1049 .await
1050 .unwrap();
1051 mpool
1052 .add(create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1))
1053 .await
1054 .unwrap();
1055 }
1056 mpool.api.set_state_sequence(&a1, 10);
1060 mpool.api.set_state_sequence(&a2, 10);
1061 let msgs = mpool.select_messages(&ts3, 1.0).unwrap();
1062
1063 assert_eq!(
1064 msgs.len(),
1065 20,
1066 "Expected 20 messages, but got {}",
1067 msgs.len()
1068 );
1069
1070 let mut next_nonce = 20;
1071 for msg in msgs.iter().take(10) {
1072 assert_eq!(
1073 msg.from(),
1074 a1,
1075 "first 10 returned messages should be from actor a1"
1076 );
1077 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1078 next_nonce += 1;
1079 }
1080 next_nonce = 20;
1081 for msg in msgs.iter().take(20).skip(10) {
1082 assert_eq!(
1083 msg.from(),
1084 a2,
1085 "next 10 returned messages should be from actor a2"
1086 );
1087 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1088 next_nonce += 1;
1089 }
1090 }
1091
1092 #[tokio::test]
1093 async fn select_messages_on_non_current_tipset_should_not_update_mpool_state() {
1094 use crate::message_pool::msgpool::msg_pool::TrustPolicy;
1095 use crate::message_pool::msgpool::msg_set::StrictnessPolicy;
1096 use crate::shim::message::Message as ShimMessage;
1097 use tokio::sync::broadcast::error::TryRecvError;
1098
1099 let mut joinset = JoinSet::new();
1100 let mpool = make_test_mpool(&mut joinset);
1101
1102 let id_addr = Address::new_id(1000);
1103 let key_addr = Address::new_bls(&[3u8; 48]).unwrap();
1104 mpool.api.set_key_address_mapping(&id_addr, &key_addr);
1105 mpool
1106 .api
1107 .set_state_balance_raw(&key_addr, TokenAmount::from_whole(1));
1108 mpool.api.set_state_sequence(&key_addr, 0);
1109
1110 let b1 = mpool.api.next_block();
1113 mpool.api.set_block_messages(&b1, vec![]);
1114 let head = Tipset::from(&b1);
1115 mpool
1116 .apply_head_change(Vec::new(), vec![head.clone()])
1117 .await
1118 .unwrap();
1119
1120 let pending_msg = SignedMessage::mock_bls_signed_message(ShimMessage {
1121 from: id_addr,
1122 sequence: 0,
1123 gas_limit: TEST_GAS_LIMIT as u64,
1124 gas_fee_cap: TokenAmount::from_atto(200),
1125 gas_premium: TokenAmount::from_atto(100),
1126 ..ShimMessage::default()
1127 });
1128 mpool
1129 .add_to_pool_unchecked(
1130 &head,
1131 pending_msg.clone(),
1132 TrustPolicy::Trusted,
1133 StrictnessPolicy::Relaxed,
1134 )
1135 .await
1136 .unwrap();
1137
1138 let before = mpool.pending.snapshot();
1139 assert!(
1140 before.contains_key(&key_addr),
1141 "precondition: message is pending under the resolved key address"
1142 );
1143
1144 let b2 = mpool.api.next_block();
1147 let ts2 = Tipset::from(&b2);
1148 mpool.api.set_block_messages(&b2, vec![pending_msg.clone()]);
1149
1150 let mut rx = mpool.pending.subscriber().subscribe();
1153
1154 let _ = mpool.select_messages(&ts2, 1.0).unwrap();
1156
1157 let after = mpool.pending.snapshot();
1158 assert!(
1159 after.contains_key(&key_addr),
1160 "selecting for a non-current tipset must not remove live pending messages"
1161 );
1162 assert_eq!(
1163 before.len(),
1164 after.len(),
1165 "the live pending pool size must be unchanged by selection"
1166 );
1167 assert_eq!(
1168 after
1169 .get(&key_addr)
1170 .and_then(|mset| mset.msgs.get(&0))
1171 .map(|m| m.cid()),
1172 Some(pending_msg.cid()),
1173 "the exact pending message must survive at its nonce"
1174 );
1175 assert!(
1176 matches!(rx.try_recv(), Err(TryRecvError::Empty)),
1177 "a read-only selection simulation must not emit any MpoolUpdate events"
1178 );
1179 }
1180
1181 #[tokio::test]
1182 async fn message_selection_trimming_gas() {
1183 let mut joinset = JoinSet::new();
1184 let mpool = make_test_mpool(&mut joinset);
1185 let ts = mock_tipset(&mpool).await;
1186 let api = mpool.api.clone();
1187
1188 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1189 let mut w1 = Wallet::new(ks1);
1190 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1191
1192 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1193 let mut w2 = Wallet::new(ks2);
1194 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1195
1196 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1197 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1198
1199 let nmsgs = (crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT) + 1;
1200
1201 for i in 0..nmsgs {
1203 let bias = (nmsgs - i) / 3;
1204 let m = create_fake_smsg(
1205 &mpool,
1206 &a2,
1207 &a1,
1208 i as u64,
1209 TEST_GAS_LIMIT,
1210 (1 + i % 3 + bias) as u64,
1211 );
1212 mpool.add(m).await.unwrap();
1213 let m = create_fake_smsg(
1214 &mpool,
1215 &a1,
1216 &a2,
1217 i as u64,
1218 TEST_GAS_LIMIT,
1219 (1 + i % 3 + bias) as u64,
1220 );
1221 mpool.add(m).await.unwrap();
1222 }
1223
1224 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1225
1226 let expected = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1227 assert_eq!(msgs.len(), expected as usize);
1228 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1229 assert!(m_gas_limit <= crate::shim::econ::BLOCK_GAS_LIMIT);
1230 }
1231
1232 #[tokio::test]
1233 async fn message_selection_trimming_msgs_basic() {
1234 let mut joinset = JoinSet::new();
1235 let mpool = make_test_mpool(&mut joinset);
1236 let ts = mock_tipset(&mpool).await;
1237 let api = mpool.api.clone();
1238
1239 let keystore = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1240 let mut wallet = Wallet::new(keystore);
1241 let address = wallet.generate_addr(SignatureType::Secp256k1).unwrap();
1242
1243 api.set_state_balance_raw(&address, TokenAmount::from_whole(1));
1244
1245 for i in 0..BLOCK_MESSAGE_LIMIT {
1247 let msg = create_fake_smsg(&mpool, &address, &address, i as u64, 200_000, 100);
1248 mpool.add(msg).await.unwrap();
1249 }
1250
1251 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1252 assert_eq!(
1253 msgs.len(),
1254 CBOR_GEN_LIMIT,
1255 "Expected {CBOR_GEN_LIMIT} messages, got {}",
1256 msgs.len()
1257 );
1258
1259 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1261 assert!(
1262 m_gas_limit <= BLOCK_GAS_LIMIT,
1263 "Selected messages gas limit {m_gas_limit} exceeds block gas limit {BLOCK_GAS_LIMIT}",
1264 );
1265 }
1266
1267 #[tokio::test]
1268 async fn message_selection_trimming_msgs_two_senders() {
1269 let mut joinset = JoinSet::new();
1270 let mpool = make_test_mpool(&mut joinset);
1271 let ts = mock_tipset(&mpool).await;
1272 let api = mpool.api.clone();
1273
1274 let keystore_1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1275 let mut wallet_1 = Wallet::new(keystore_1);
1276 let address_1 = wallet_1.generate_addr(SignatureType::Secp256k1).unwrap();
1277
1278 let keystore_2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1279 let mut wallet_2 = Wallet::new(keystore_2);
1280 let address_2 = wallet_2.generate_addr(SignatureType::Bls).unwrap();
1281
1282 api.set_state_balance_raw(&address_1, TokenAmount::from_whole(1));
1283 api.set_state_balance_raw(&address_2, TokenAmount::from_whole(1));
1284
1285 for i in 0..BLOCK_MESSAGE_LIMIT {
1287 let msg = create_smsg(
1288 &address_2,
1289 &address_1,
1290 &mut wallet_1,
1291 i as u64,
1292 300_000,
1293 100,
1294 );
1295 mpool.add(msg).await.unwrap();
1296 let msg = create_smsg(
1299 &address_1,
1300 &address_2,
1301 &mut wallet_2,
1302 i as u64,
1303 300_000,
1304 1000,
1305 );
1306 mpool.add(msg).await.unwrap();
1307 }
1308 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1309 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1311 assert!(
1312 m_gas_limit <= BLOCK_GAS_LIMIT,
1313 "Selected messages gas limit {m_gas_limit} exceeds block gas limit {BLOCK_GAS_LIMIT}",
1314 );
1315 let bls_msgs = msgs.iter().filter(|m| m.is_bls()).count();
1316 assert_eq!(
1317 CBOR_GEN_LIMIT, bls_msgs,
1318 "Expected {CBOR_GEN_LIMIT} bls messages, got {bls_msgs}."
1319 );
1320 assert_eq!(
1321 msgs.len(),
1322 BLOCK_MESSAGE_LIMIT,
1323 "Expected {BLOCK_MESSAGE_LIMIT} messages, got {}",
1324 msgs.len()
1325 );
1326 }
1327
1328 #[tokio::test]
1329 async fn message_selection_trimming_msgs_two_senders_complex() {
1330 let mut joinset = JoinSet::new();
1331 let mpool = make_test_mpool(&mut joinset);
1332 let ts = mock_tipset(&mpool).await;
1333 let api = mpool.api.clone();
1334
1335 let keystore_1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1336 let mut wallet_1 = Wallet::new(keystore_1);
1337 let address_1 = wallet_1.generate_addr(SignatureType::Secp256k1).unwrap();
1338
1339 let keystore_2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1340 let mut wallet_2 = Wallet::new(keystore_2);
1341 let address_2 = wallet_2.generate_addr(SignatureType::Bls).unwrap();
1342
1343 api.set_state_balance_raw(&address_1, TokenAmount::from_whole(1));
1344 api.set_state_balance_raw(&address_2, TokenAmount::from_whole(1));
1345
1346 let mut counter = 0;
1348 for i in 0..CBOR_GEN_LIMIT {
1349 counter += 1;
1350 let msg = create_smsg(
1351 &address_2,
1352 &address_1,
1353 &mut wallet_1,
1354 i as u64,
1355 300_000,
1356 100,
1357 );
1358 mpool.add(msg).await.unwrap();
1359 let msg = create_smsg(
1362 &address_1,
1363 &address_2,
1364 &mut wallet_2,
1365 i as u64,
1366 300_000,
1367 100,
1368 );
1369 mpool.add(msg).await.unwrap();
1370 }
1371
1372 let msg = create_smsg(
1374 &address_2,
1375 &address_1,
1376 &mut wallet_1,
1377 counter as u64,
1378 300_000,
1379 1000,
1380 );
1381 mpool.add(msg).await.unwrap();
1382
1383 let msg = create_smsg(
1384 &address_1,
1385 &address_2,
1386 &mut wallet_2,
1387 counter as u64,
1388 300_000,
1389 100,
1390 );
1391 mpool.add(msg).await.unwrap();
1392
1393 counter += 1;
1394
1395 let msg = create_smsg(
1397 &address_2,
1398 &address_1,
1399 &mut wallet_1,
1400 counter as u64,
1401 400_000,
1402 1_000_000,
1403 );
1404 mpool.add(msg).await.unwrap();
1405
1406 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1407 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1409 assert!(
1410 m_gas_limit <= BLOCK_GAS_LIMIT,
1411 "Selected messages gas limit {m_gas_limit} exceeds block gas limit {BLOCK_GAS_LIMIT}",
1412 );
1413 let secps_len = msgs.iter().filter(|m| m.is_secp256k1()).count();
1415 assert_eq!(
1416 CBOR_GEN_LIMIT, secps_len,
1417 "Expected {CBOR_GEN_LIMIT} secp messages, got {secps_len}."
1418 );
1419 assert_eq!(
1421 msgs.len(),
1422 BLOCK_MESSAGE_LIMIT,
1423 "Expected {BLOCK_MESSAGE_LIMIT} messages, got {}",
1424 msgs.len()
1425 );
1426 }
1427
1428 #[tokio::test]
1429 async fn message_selection_priority() {
1430 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1431 let mut w1 = Wallet::new(ks1);
1432 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1433
1434 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1435 let mut w2 = Wallet::new(ks2);
1436 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1437
1438 let cfg = crate::message_pool::config::MpoolConfig {
1439 priority_addrs: vec![a1],
1440 ..Default::default()
1441 };
1442
1443 let mut joinset = JoinSet::new();
1444 let (tx, _rx) = flume::bounded(50);
1445 let mpool =
1446 MessagePool::new(TestApi::default(), tx, cfg, Arc::default(), &mut joinset).unwrap();
1447 let ts = mock_tipset(&mpool).await;
1448 let api = mpool.api.clone();
1449
1450 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1452 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1453
1454 let nmsgs = 10;
1455
1456 for i in 0..nmsgs {
1458 let bias = (nmsgs - i) / 3;
1459 let m = create_smsg(
1460 &a2,
1461 &a1,
1462 &mut w1,
1463 i as u64,
1464 TEST_GAS_LIMIT,
1465 (1 + i % 3 + bias) as u64,
1466 );
1467 mpool.add(m).await.unwrap();
1468 let m = create_smsg(
1469 &a1,
1470 &a2,
1471 &mut w2,
1472 i as u64,
1473 TEST_GAS_LIMIT,
1474 (1 + i % 3 + bias) as u64,
1475 );
1476 mpool.add(m).await.unwrap();
1477 }
1478
1479 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1480
1481 assert_eq!(msgs.len(), 20);
1482
1483 let mut next_nonce = 0;
1484 for msg in msgs.iter().take(10) {
1485 assert_eq!(
1486 msg.from(),
1487 a1,
1488 "first 10 returned messages should be from actor a1"
1489 );
1490 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1491 next_nonce += 1;
1492 }
1493 next_nonce = 0;
1494 for msg in msgs.iter().take(20).skip(10) {
1495 assert_eq!(
1496 msg.from(),
1497 a2,
1498 "next 10 returned messages should be from actor a2"
1499 );
1500 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1501 next_nonce += 1;
1502 }
1503 }
1504
1505 #[tokio::test]
1506 async fn test_optimal_msg_selection1() {
1507 let mut joinset = JoinSet::new();
1511 let mpool = make_test_mpool(&mut joinset);
1512 let ts = mock_tipset(&mpool).await;
1513 let api = mpool.api.clone();
1514
1515 let mut w1 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1517 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1518 let mut w2 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1519 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1520
1521 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1522 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1523
1524 let n_msgs = 10 * crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1525
1526 for i in 0..(n_msgs as usize) {
1530 let bias = (n_msgs as usize - i) / 3;
1531 let m = create_fake_smsg(
1532 &mpool,
1533 &a2,
1534 &a1,
1535 i as u64,
1536 TEST_GAS_LIMIT,
1537 (1 + i % 3 + bias) as u64,
1538 );
1539 mpool.add(m).await.unwrap();
1540 }
1541
1542 let msgs = mpool.select_messages(&ts, 0.25).unwrap();
1543
1544 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1545
1546 assert_eq!(msgs.len(), expected_msgs as usize);
1547
1548 for (next_nonce, m) in msgs.into_iter().enumerate() {
1549 assert_eq!(m.from(), a1, "Expected message from a1");
1550 assert_eq!(
1551 m.message().sequence,
1552 next_nonce as u64,
1553 "expected nonce {} but got {}",
1554 next_nonce,
1555 m.message().sequence
1556 );
1557 }
1558 }
1559
1560 #[tokio::test]
1561 async fn test_optimal_msg_selection2() {
1562 let mut joinset = JoinSet::new();
1563 let mpool = make_test_mpool(&mut joinset);
1568 let ts = mock_tipset(&mpool).await;
1569 let api = mpool.api.clone();
1570
1571 let mut w1 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1573 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1574 let mut w2 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1575 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1576
1577 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;
1581 for i in 0..n_msgs as usize {
1582 let bias = (n_msgs as usize - i) / 3;
1583 let m = create_fake_smsg(
1584 &mpool,
1585 &a2,
1586 &a1,
1587 i as u64,
1588 TEST_GAS_LIMIT,
1589 (200000 + i % 3 + bias) as u64,
1590 );
1591 mpool.add(m).await.unwrap();
1592 let m = create_fake_smsg(
1593 &mpool,
1594 &a1,
1595 &a2,
1596 i as u64,
1597 TEST_GAS_LIMIT,
1598 (190000 + i % 3 + bias) as u64,
1599 );
1600 mpool.add(m).await.unwrap();
1601 }
1602
1603 let msgs = mpool.select_messages(&ts, 0.1).unwrap();
1604
1605 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1606 assert_eq!(
1607 msgs.len(),
1608 expected_msgs as usize,
1609 "Expected {} messages, but got {}",
1610 expected_msgs,
1611 msgs.len()
1612 );
1613
1614 let mut n_from1 = 0;
1615 let mut n_from2 = 0;
1616 let mut next_nonce1 = 0;
1617 let mut next_nonce2 = 0;
1618
1619 for m in msgs {
1620 if m.from() == a1 {
1621 if m.message.sequence != next_nonce1 {
1622 panic!(
1623 "Expected nonce {}, but got {}",
1624 next_nonce1, m.message.sequence
1625 );
1626 }
1627 next_nonce1 += 1;
1628 n_from1 += 1;
1629 } else {
1630 if m.message.sequence != next_nonce2 {
1631 panic!(
1632 "Expected nonce {}, but got {}",
1633 next_nonce2, m.message.sequence
1634 );
1635 }
1636 next_nonce2 += 1;
1637 n_from2 += 1;
1638 }
1639 }
1640
1641 if n_from1 > n_from2 {
1642 panic!("Expected more msgs from a2 than a1");
1643 }
1644 }
1645
1646 #[tokio::test]
1647 async fn test_optimal_msg_selection3() {
1648 let mut joinset = JoinSet::new();
1649 let mpool = make_test_mpool(&mut joinset);
1655 let ts = mock_tipset(&mpool).await;
1656 let api = mpool.api.clone();
1657
1658 let n_actors = 10;
1659
1660 let mut actors = vec![];
1661 let mut wallets = vec![];
1662
1663 for _ in 0..n_actors {
1664 let mut wallet = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1665 let actor = wallet.generate_addr(SignatureType::Secp256k1).unwrap();
1666
1667 actors.push(actor);
1668 wallets.push(wallet);
1669 }
1670
1671 for a in &mut actors {
1672 api.set_state_balance_raw(a, TokenAmount::from_whole(1));
1673 }
1674
1675 let n_msgs = 1 + crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1676 for i in 0..n_msgs {
1677 for j in 0..n_actors {
1678 let premium =
1679 500000 + 10000 * (n_actors - j) + (n_msgs + 2 - i) / (30 * n_actors) + i % 3;
1680 let m = create_fake_smsg(
1681 &mpool,
1682 &actors[j as usize],
1683 &actors[j as usize],
1684 i as u64,
1685 TEST_GAS_LIMIT,
1686 premium as u64,
1687 );
1688 mpool.add(m).await.unwrap();
1689 }
1690 }
1691
1692 let msgs = mpool.select_messages(&ts, 0.1).unwrap();
1693 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1694
1695 assert_eq!(
1696 msgs.len(),
1697 expected_msgs as usize,
1698 "Expected {} messages, but got {}",
1699 expected_msgs,
1700 msgs.len()
1701 );
1702
1703 let who_is = |addr| -> usize {
1704 for (i, a) in actors.iter().enumerate() {
1705 if a == &addr {
1706 return i;
1707 }
1708 }
1709 9999999
1712 };
1713
1714 let mut nonces = vec![0; n_actors as usize];
1715 for m in &msgs {
1716 let who = who_is(m.from());
1717 if who < 3 {
1718 panic!("got message from {who}th actor",);
1719 }
1720
1721 let next_nonce: u64 = nonces[who];
1722 if m.message.sequence != next_nonce {
1723 panic!(
1724 "expected nonce {} but got {}",
1725 next_nonce, m.message.sequence
1726 );
1727 }
1728 nonces[who] += 1;
1729 }
1730 }
1731}