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 cur_ts = self.current_tipset();
292 let mut msgs = if tq > 0.84 {
298 self.select_messages_greedy(&cur_ts, ts)
299 } else {
300 self.select_messages_optimal(&cur_ts, ts, tq)
301 }?;
302
303 if msgs.len() > MAX_BLOCK_MSGS {
304 warn!(
305 "Message selection chose too many messages: {} > {MAX_BLOCK_MSGS}",
306 msgs.len(),
307 );
308 msgs.truncate(MAX_BLOCK_MSGS)
309 }
310
311 Ok(msgs.msgs)
312 }
313
314 fn select_messages_greedy(
315 &self,
316 cur_ts: &Tipset,
317 ts: &Tipset,
318 ) -> Result<SelectedMessages, Error> {
319 let base_fee = self.api.chain_compute_base_fee(ts)?;
320
321 let mut pending = self.get_pending_messages(cur_ts, ts)?;
324
325 if pending.is_empty() {
326 return Ok(SelectedMessages::default());
327 }
328
329 let selected_msgs = self.select_priority_messages(&mut pending, &base_fee, ts)?;
331
332 if selected_msgs.gas_limit < MIN_GAS || selected_msgs.len() >= BLOCK_MESSAGE_LIMIT {
334 return Ok(selected_msgs);
335 }
336
337 let mut chains = Chains::new();
340 for (actor, mset) in pending.into_iter() {
341 create_message_chains(
342 self.api.as_ref(),
343 &actor,
344 &mset,
345 &base_fee,
346 ts,
347 &mut chains,
348 &self.chain_config,
349 )?;
350 }
351
352 Ok(merge_and_trim(
353 &mut chains,
354 selected_msgs,
355 &base_fee,
356 MIN_GAS,
357 ))
358 }
359
360 #[allow(clippy::indexing_slicing)]
361 fn select_messages_optimal(
362 &self,
363 cur_ts: &Tipset,
364 target_tipset: &Tipset,
365 ticket_quality: f64,
366 ) -> Result<SelectedMessages, Error> {
367 let base_fee = self.api.chain_compute_base_fee(target_tipset)?;
368
369 let mut pending = self.get_pending_messages(cur_ts, target_tipset)?;
372
373 if pending.is_empty() {
374 return Ok(SelectedMessages::default());
375 }
376
377 let mut selected_msgs =
379 self.select_priority_messages(&mut pending, &base_fee, target_tipset)?;
380
381 if selected_msgs.gas_limit < MIN_GAS || selected_msgs.len() >= BLOCK_MESSAGE_LIMIT {
383 return Ok(selected_msgs);
384 }
385
386 let mut chains = Chains::new();
389 for (actor, mset) in pending.into_iter() {
390 create_message_chains(
391 self.api.as_ref(),
392 &actor,
393 &mset,
394 &base_fee,
395 target_tipset,
396 &mut chains,
397 &self.chain_config,
398 )?;
399 }
400
401 chains.sort(false);
403
404 if chains.get_at(0).is_some_and(|it| it.gas_perf < 0.0) {
405 tracing::warn!(
406 "all messages in mpool have non-positive gas performance {}",
407 chains[0].gas_perf
408 );
409 return Ok(selected_msgs);
410 }
411
412 let mut next_chain = 0;
417 let mut partitions: Vec<Vec<NodeKey>> = vec![vec![]; MAX_BLOCKS];
418 let mut i = 0;
419 while i < MAX_BLOCKS && next_chain < chains.len() {
420 let mut gas_limit = crate::shim::econ::BLOCK_GAS_LIMIT;
421 let mut msg_limit = BLOCK_MESSAGE_LIMIT;
422 while next_chain < chains.len() {
423 let chain_key = chains.key_vec[next_chain];
424 next_chain += 1;
425 partitions[i].push(chain_key);
426 let chain = chains.get(chain_key).unwrap();
427 let chain_gas_limit = chain.gas_limit;
428 if gas_limit < chain_gas_limit {
429 break;
430 }
431 gas_limit = gas_limit.saturating_sub(chain_gas_limit);
432 msg_limit = msg_limit.saturating_sub(chain.msgs.len());
433 if gas_limit < MIN_GAS || msg_limit == 0 {
434 break;
435 }
436 }
437 i += 1;
438 }
439
440 let block_prob = crate::message_pool::block_probabilities(ticket_quality);
444 let mut eff_chains = 0;
445 for i in 0..MAX_BLOCKS {
446 for k in &partitions[i] {
447 if let Some(node) = chains.get_mut(*k) {
448 node.eff_perf = node.gas_perf * block_prob[i];
449 }
450 }
451 eff_chains += partitions[i].len();
452 }
453
454 for i in eff_chains..chains.len() {
456 if let Some(node) = chains.get_mut_at(i) {
457 node.set_null_effective_perf();
458 }
459 }
460
461 chains.sort_effective();
463
464 let mut last = chains.len();
469 for i in 0..chains.len() {
470 if chains[i].gas_perf < 0.0 {
472 break;
473 }
474
475 if chains[i].merged {
477 continue;
478 }
479
480 match selected_msgs.try_to_add_with_deps(i, &mut chains, &base_fee) {
481 Ok(_) => {
482 if let Some(next_key) = chains[i].next {
484 let next_node = chains.get_mut(next_key).unwrap();
485 if next_node.eff_perf > 0.0 {
486 next_node.eff_perf += next_node.parent_offset;
487 let mut next_next_key = next_node.next;
488 while let Some(nnk) = next_next_key {
489 let (nn_node, prev_perfs) = chains.get_mut_with_prev_eff(nnk);
490 if let Some(nn_node) = nn_node {
491 if nn_node.eff_perf > 0.0 {
492 nn_node.set_eff_perf(prev_perfs);
493 next_next_key = nn_node.next;
494 } else {
495 break;
496 }
497 } else {
498 break;
499 }
500 }
501 }
502 }
503
504 chains.sort_range_effective(i + 1..);
508
509 continue;
510 }
511 Err(e) => {
512 debug!("Failed to add message chain with dependencies: {e:#}");
513 }
514 }
515
516 last = i;
519 break;
520 }
521
522 'tail_loop: while selected_msgs.gas_limit >= MIN_GAS && last < chains.len() {
531 if !chains[last].valid {
532 last += 1;
534 continue;
535 }
536
537 selected_msgs.trim_chain_at(&mut chains, last, &base_fee);
539
540 if chains[last].valid {
542 for i in last..chains.len() - 1 {
543 if chains[i].cmp_effective(&chains[i + 1]) == Ordering::Greater {
544 break;
545 }
546 chains.key_vec.swap(i, i + 1);
547 }
548 }
549
550 let lst = last; for i in lst..chains.len() {
553 let chain = &mut chains[i];
554 if !chain.valid {
556 continue;
557 }
558
559 if chain.merged {
561 continue;
562 }
563
564 if chain.gas_perf < 0.0 {
566 break 'tail_loop;
567 }
568
569 match selected_msgs.try_to_add_with_deps(i, &mut chains, &base_fee) {
570 Ok(_) => continue,
571 Err(e) => debug!("Failed to add message chain with dependencies: {e:#}"),
572 }
573
574 continue 'tail_loop;
575 }
576
577 break;
580 }
581
582 if selected_msgs.gas_limit >= MIN_GAS && selected_msgs.msgs.len() <= BLOCK_MESSAGE_LIMIT {
587 let pre_random_length = selected_msgs.len();
588
589 chains
590 .key_vec
591 .shuffle(&mut crate::utils::rand::forest_rng());
592
593 for i in 0..chains.len() {
594 if selected_msgs.gas_limit < MIN_GAS || selected_msgs.len() >= BLOCK_MESSAGE_LIMIT {
595 break;
596 }
597
598 if chains[i].merged || !chains[i].valid {
600 continue;
601 }
602
603 if chains[i].gas_perf < 0.0 {
605 continue;
606 }
607
608 if selected_msgs
609 .try_to_add_with_deps(i, &mut chains, &base_fee)
610 .is_ok()
611 {
612 continue;
614 }
615
616 if chains[i].valid {
617 selected_msgs
620 .try_to_add_with_deps(i, &mut chains, &base_fee)
621 .context("Failed to add message chain with dependencies")?;
622 continue;
623 }
624 }
625
626 if selected_msgs.len() != pre_random_length {
627 tracing::warn!(
628 "optimal selection failed to pack a block; picked {} messages with random selection",
629 selected_msgs.len() - pre_random_length
630 );
631 }
632 }
633
634 Ok(selected_msgs)
635 }
636
637 fn get_pending_messages(&self, cur_ts: &Tipset, ts: &Tipset) -> Result<Pending, Error> {
638 let snapshot = self.pending.snapshot();
639 let mut result: Pending = HashMap::with_capacity(snapshot.len());
640 for (a, mset) in snapshot {
641 result.insert(a, mset.msgs);
642 }
643
644 if cur_ts.epoch() == ts.epoch() && cur_ts == ts {
645 return Ok(result);
646 }
647
648 run_head_change(
650 self.api.as_ref(),
651 &self.caches.bls_sig,
652 cur_ts.clone(),
653 ts.clone(),
654 &mut result,
655 )?;
656
657 Ok(result)
658 }
659
660 fn select_priority_messages(
661 &self,
662 pending: &mut Pending,
663 base_fee: &TokenAmount,
664 ts: &Tipset,
665 ) -> Result<SelectedMessages, Error> {
666 let result = Vec::with_capacity(self.config.size_limit_low() as usize);
667 let gas_limit = crate::shim::econ::BLOCK_GAS_LIMIT;
668 let min_gas = MIN_GAS;
669
670 let priority = self.config.priority_addrs();
672 let mut chains = Chains::new();
673 for actor in priority.iter() {
674 if let Some(mset) = pending.remove(actor) {
676 create_message_chains(
678 self.api.as_ref(),
679 actor,
680 &mset,
681 base_fee,
682 ts,
683 &mut chains,
684 &self.chain_config,
685 )?;
686 }
687 }
688
689 if chains.is_empty() {
690 return Ok(SelectedMessages::new(Vec::new(), gas_limit));
691 }
692
693 Ok(merge_and_trim(
694 &mut chains,
695 SelectedMessages::new(result, gas_limit),
696 base_fee,
697 min_gas,
698 ))
699 }
700}
701
702#[allow(clippy::indexing_slicing)]
704fn merge_and_trim(
705 chains: &mut Chains,
706 mut selected_msgs: SelectedMessages,
707 base_fee: &TokenAmount,
708 min_gas: u64,
709) -> SelectedMessages {
710 if chains.is_empty() {
711 return selected_msgs;
712 }
713
714 chains.sort(true);
716
717 let first_chain_gas_perf = chains[0].gas_perf;
718
719 if !chains.is_empty() && first_chain_gas_perf < 0.0 {
720 warn!(
721 "all priority messages in mpool have negative gas performance bestGasPerf: {}",
722 first_chain_gas_perf
723 );
724 return selected_msgs;
725 }
726
727 let mut last = chains.len();
730 for i in 0..chains.len() {
731 let node = &chains[i];
732
733 if node.gas_perf < 0.0 {
734 break;
735 }
736
737 if selected_msgs.try_to_add(node.clone()).is_ok() {
738 continue;
740 }
741
742 last = i;
744 break;
745 }
746
747 'tail_loop: while selected_msgs.gas_limit >= min_gas && last < chains.len() {
748 selected_msgs.trim_chain_at(chains, last, base_fee);
750
751 let node = &chains[last];
753 if node.valid {
754 for i in last..chains.len() - 1 {
755 let cur_node = &chains[i];
757 let next_node = &chains[i + 1];
758 if cur_node.compare(next_node) == Ordering::Greater {
759 break;
760 }
761
762 chains.key_vec.swap(i, i + 1);
763 }
764 }
765
766 let lst = last; for i in lst..chains.len() {
769 let chain = chains[i].clone();
770 if !chain.valid {
771 continue;
772 }
773
774 if chain.gas_perf < 0.0 {
776 break 'tail_loop;
777 }
778
779 if selected_msgs.try_to_add(chain).is_ok() {
781 continue;
783 }
784
785 last += i;
787 continue 'tail_loop;
788 }
789
790 break;
791 }
792
793 selected_msgs
794}
795
796pub(in crate::message_pool) fn run_head_change<T>(
800 api: &T,
801 bls_sig_cache: &SizeTrackingCache<CidWrapper, Signature>,
802 from: Tipset,
803 to: Tipset,
804 rmsgs: &mut HashMap<Address, HashMap<u64, SignedMessage>>,
805) -> Result<(), Error>
806where
807 T: Provider,
808{
809 let mut left = from;
810 let mut right = to;
811 let mut left_chain = Vec::new();
812 let mut right_chain = Vec::new();
813 while left != right {
814 if left.epoch() > right.epoch() {
815 left_chain.push(left.clone());
816 let par = api.load_tipset(left.parents())?;
817 left = par;
818 } else {
819 right_chain.push(right.clone());
820 let par = api.load_tipset(right.parents())?;
821 right = par;
822 }
823 }
824 for ts in left_chain {
825 let mut msgs: Vec<SignedMessage> = Vec::new();
826 for block in ts.block_headers() {
827 let (umsg, smsgs) = api.messages_for_block(block)?;
828 msgs.extend(smsgs);
829 for msg in umsg {
830 let msg_cid = msg.cid();
831 if let Ok(smsg) = recover_sig(bls_sig_cache, msg) {
832 msgs.push(smsg);
833 } else {
834 tracing::debug!("could not recover signature for bls message {msg_cid}");
835 }
836 }
837 }
838 for msg in msgs {
839 utils::add_to_selected_msgs(msg, rmsgs);
840 }
841 }
842
843 for ts in right_chain {
844 for b in ts.block_headers() {
845 let (msgs, smsgs) = api.messages_for_block(b)?;
846
847 for msg in smsgs {
848 utils::remove_from_selected_msgs(&msg.from(), msg.sequence(), rmsgs);
849 }
850 for msg in msgs {
851 utils::remove_from_selected_msgs(&msg.from, msg.sequence, rmsgs);
852 }
853 }
854 }
855 Ok(())
856}
857
858#[cfg(test)]
859mod test_selection {
860 use std::sync::Arc;
861
862 use super::*;
863 use crate::key_management::{KeyStore, KeyStoreConfig, Wallet};
864 use crate::message_pool::msgpool::{
865 test_provider::{TestApi, mock_block},
866 tests::{create_fake_smsg, create_smsg},
867 };
868 use crate::shim::crypto::SignatureType;
869 use crate::shim::econ::BLOCK_GAS_LIMIT;
870 use tokio::task::JoinSet;
871
872 const TEST_GAS_LIMIT: i64 = 6955002;
873
874 fn make_test_mpool(joinset: &mut JoinSet<anyhow::Result<()>>) -> MessagePool<TestApi> {
875 let tma = TestApi::default();
876 let (tx, _rx) = flume::bounded(50);
877 MessagePool::new(tma, tx, Default::default(), Arc::default(), joinset).unwrap()
878 }
879
880 async fn mock_tipset(mpool: &MessagePool<TestApi>) -> Tipset {
883 let b1 = mock_block(1, 1);
884 let ts = Tipset::from(&b1);
885 mpool
886 .apply_head_change(Vec::new(), vec![Tipset::from(b1)])
887 .await
888 .unwrap();
889 ts
890 }
891
892 #[tokio::test]
893 async fn basic_message_selection() {
894 let mut joinset = JoinSet::new();
895 let mpool = make_test_mpool(&mut joinset);
896
897 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
898 let mut w1 = Wallet::new(ks1);
899 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
900
901 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
902 let mut w2 = Wallet::new(ks2);
903 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
904
905 let ts = mock_tipset(&mpool).await;
906
907 mpool
908 .api
909 .set_state_balance_raw(&a1, TokenAmount::from_whole(1));
910 mpool
911 .api
912 .set_state_balance_raw(&a2, TokenAmount::from_whole(1));
913
914 for i in 0..10 {
918 let m = create_smsg(&a2, &a1, &mut w1, i, TEST_GAS_LIMIT, 2 * i + 1);
919 mpool.add(m).await.unwrap();
920 }
921 for i in 0..10 {
922 let m = create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1);
923 mpool.add(m).await.unwrap();
924 }
925
926 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
927
928 assert_eq!(msgs.len(), 20, "Expected 20 messages, got {}", msgs.len());
929
930 let mut next_nonce = 0;
931 for (i, msg) in msgs.iter().enumerate().take(10) {
932 assert_eq!(
933 msg.from(),
934 a1,
935 "first 10 returned messages should be from actor a1 {i}",
936 );
937 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
938 next_nonce += 1;
939 }
940
941 next_nonce = 0;
942 for (i, msg) in msgs.iter().enumerate().take(20).skip(10) {
943 assert_eq!(
944 msg.from(),
945 a2,
946 "next 10 returned messages should be from actor a2 {i}",
947 );
948 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
949 next_nonce += 1;
950 }
951
952 let b2 = mpool.api.next_block();
954 mpool.api.set_block_messages(&b2, msgs);
955 mpool
956 .apply_head_change(Vec::new(), vec![Tipset::from(b2)])
957 .await
958 .unwrap();
959
960 let remaining = mpool.pending.snapshot();
962 assert!(
963 remaining.is_empty(),
964 "Expected no pending messages, but got {}",
965 remaining.len()
966 );
967
968 let mut msgs = Vec::with_capacity(20);
970 for i in 10..20 {
971 msgs.push(create_smsg(&a2, &a1, &mut w1, i, TEST_GAS_LIMIT, 2 * i + 1));
972 msgs.push(create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1));
973 }
974 let b3 = mpool.api.next_block();
975 let ts3 = Tipset::from(&b3);
976 mpool.api.set_block_messages(&b3, msgs);
977
978 mpool.api.set_state_sequence(&a1, 20);
980 mpool.api.set_state_sequence(&a2, 20);
981
982 for i in 20..30 {
984 mpool
985 .add(create_smsg(
986 &a2,
987 &a1,
988 &mut w1,
989 i,
990 TEST_GAS_LIMIT,
991 2 * i + 200,
992 ))
993 .await
994 .unwrap();
995 mpool
996 .add(create_smsg(&a1, &a2, &mut w2, i, TEST_GAS_LIMIT, i + 1))
997 .await
998 .unwrap();
999 }
1000 mpool.api.set_state_sequence(&a1, 10);
1004 mpool.api.set_state_sequence(&a2, 10);
1005 let msgs = mpool.select_messages(&ts3, 1.0).unwrap();
1006
1007 assert_eq!(
1008 msgs.len(),
1009 20,
1010 "Expected 20 messages, but got {}",
1011 msgs.len()
1012 );
1013
1014 let mut next_nonce = 20;
1015 for msg in msgs.iter().take(10) {
1016 assert_eq!(
1017 msg.from(),
1018 a1,
1019 "first 10 returned messages should be from actor a1"
1020 );
1021 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1022 next_nonce += 1;
1023 }
1024 next_nonce = 20;
1025 for msg in msgs.iter().take(20).skip(10) {
1026 assert_eq!(
1027 msg.from(),
1028 a2,
1029 "next 10 returned messages should be from actor a2"
1030 );
1031 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1032 next_nonce += 1;
1033 }
1034 }
1035
1036 #[tokio::test]
1037 async fn select_messages_on_non_current_tipset_should_not_update_mpool_state() {
1038 use crate::message_pool::msgpool::msg_pool::TrustPolicy;
1039 use crate::message_pool::msgpool::msg_set::StrictnessPolicy;
1040 use crate::shim::message::Message as ShimMessage;
1041 use tokio::sync::broadcast::error::TryRecvError;
1042
1043 let mut joinset = JoinSet::new();
1044 let mpool = make_test_mpool(&mut joinset);
1045
1046 let id_addr = Address::new_id(1000);
1047 let key_addr = Address::new_bls(&[3u8; 48]).unwrap();
1048 mpool.api.set_key_address_mapping(&id_addr, &key_addr);
1049 mpool
1050 .api
1051 .set_state_balance_raw(&key_addr, TokenAmount::from_whole(1));
1052 mpool.api.set_state_sequence(&key_addr, 0);
1053
1054 let b1 = mpool.api.next_block();
1057 mpool.api.set_block_messages(&b1, vec![]);
1058 let head = Tipset::from(&b1);
1059 mpool
1060 .apply_head_change(Vec::new(), vec![head.clone()])
1061 .await
1062 .unwrap();
1063
1064 let pending_msg = SignedMessage::mock_bls_signed_message(ShimMessage {
1065 from: id_addr,
1066 sequence: 0,
1067 gas_limit: TEST_GAS_LIMIT as u64,
1068 gas_fee_cap: TokenAmount::from_atto(200),
1069 gas_premium: TokenAmount::from_atto(100),
1070 ..ShimMessage::default()
1071 });
1072 mpool
1073 .add_to_pool_unchecked(
1074 &head,
1075 pending_msg.clone(),
1076 TrustPolicy::Trusted,
1077 StrictnessPolicy::Relaxed,
1078 )
1079 .await
1080 .unwrap();
1081
1082 let before = mpool.pending.snapshot();
1083 assert!(
1084 before.contains_key(&key_addr),
1085 "precondition: message is pending under the resolved key address"
1086 );
1087
1088 let b2 = mpool.api.next_block();
1091 let ts2 = Tipset::from(&b2);
1092 mpool.api.set_block_messages(&b2, vec![pending_msg.clone()]);
1093
1094 let mut rx = mpool.pending.subscriber().subscribe();
1097
1098 let _ = mpool.select_messages(&ts2, 1.0).unwrap();
1100
1101 let after = mpool.pending.snapshot();
1102 assert!(
1103 after.contains_key(&key_addr),
1104 "selecting for a non-current tipset must not remove live pending messages"
1105 );
1106 assert_eq!(
1107 before.len(),
1108 after.len(),
1109 "the live pending pool size must be unchanged by selection"
1110 );
1111 assert_eq!(
1112 after
1113 .get(&key_addr)
1114 .and_then(|mset| mset.msgs.get(&0))
1115 .map(|m| m.cid()),
1116 Some(pending_msg.cid()),
1117 "the exact pending message must survive at its nonce"
1118 );
1119 assert!(
1120 matches!(rx.try_recv(), Err(TryRecvError::Empty)),
1121 "a read-only selection simulation must not emit any MpoolUpdate events"
1122 );
1123 }
1124
1125 #[tokio::test]
1126 async fn message_selection_trimming_gas() {
1127 let mut joinset = JoinSet::new();
1128 let mpool = make_test_mpool(&mut joinset);
1129 let ts = mock_tipset(&mpool).await;
1130 let api = mpool.api.clone();
1131
1132 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1133 let mut w1 = Wallet::new(ks1);
1134 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1135
1136 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1137 let mut w2 = Wallet::new(ks2);
1138 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1139
1140 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1141 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1142
1143 let nmsgs = (crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT) + 1;
1144
1145 for i in 0..nmsgs {
1147 let bias = (nmsgs - i) / 3;
1148 let m = create_fake_smsg(
1149 &mpool,
1150 &a2,
1151 &a1,
1152 i as u64,
1153 TEST_GAS_LIMIT,
1154 (1 + i % 3 + bias) as u64,
1155 );
1156 mpool.add(m).await.unwrap();
1157 let m = create_fake_smsg(
1158 &mpool,
1159 &a1,
1160 &a2,
1161 i as u64,
1162 TEST_GAS_LIMIT,
1163 (1 + i % 3 + bias) as u64,
1164 );
1165 mpool.add(m).await.unwrap();
1166 }
1167
1168 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1169
1170 let expected = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1171 assert_eq!(msgs.len(), expected as usize);
1172 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1173 assert!(m_gas_limit <= crate::shim::econ::BLOCK_GAS_LIMIT);
1174 }
1175
1176 #[tokio::test]
1177 async fn message_selection_trimming_msgs_basic() {
1178 let mut joinset = JoinSet::new();
1179 let mpool = make_test_mpool(&mut joinset);
1180 let ts = mock_tipset(&mpool).await;
1181 let api = mpool.api.clone();
1182
1183 let keystore = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1184 let mut wallet = Wallet::new(keystore);
1185 let address = wallet.generate_addr(SignatureType::Secp256k1).unwrap();
1186
1187 api.set_state_balance_raw(&address, TokenAmount::from_whole(1));
1188
1189 for i in 0..BLOCK_MESSAGE_LIMIT {
1191 let msg = create_fake_smsg(&mpool, &address, &address, i as u64, 200_000, 100);
1192 mpool.add(msg).await.unwrap();
1193 }
1194
1195 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1196 assert_eq!(
1197 msgs.len(),
1198 CBOR_GEN_LIMIT,
1199 "Expected {CBOR_GEN_LIMIT} messages, got {}",
1200 msgs.len()
1201 );
1202
1203 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1205 assert!(
1206 m_gas_limit <= BLOCK_GAS_LIMIT,
1207 "Selected messages gas limit {m_gas_limit} exceeds block gas limit {BLOCK_GAS_LIMIT}",
1208 );
1209 }
1210
1211 #[tokio::test]
1212 async fn message_selection_trimming_msgs_two_senders() {
1213 let mut joinset = JoinSet::new();
1214 let mpool = make_test_mpool(&mut joinset);
1215 let ts = mock_tipset(&mpool).await;
1216 let api = mpool.api.clone();
1217
1218 let keystore_1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1219 let mut wallet_1 = Wallet::new(keystore_1);
1220 let address_1 = wallet_1.generate_addr(SignatureType::Secp256k1).unwrap();
1221
1222 let keystore_2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1223 let mut wallet_2 = Wallet::new(keystore_2);
1224 let address_2 = wallet_2.generate_addr(SignatureType::Bls).unwrap();
1225
1226 api.set_state_balance_raw(&address_1, TokenAmount::from_whole(1));
1227 api.set_state_balance_raw(&address_2, TokenAmount::from_whole(1));
1228
1229 for i in 0..BLOCK_MESSAGE_LIMIT {
1231 let msg = create_smsg(
1232 &address_2,
1233 &address_1,
1234 &mut wallet_1,
1235 i as u64,
1236 300_000,
1237 100,
1238 );
1239 mpool.add(msg).await.unwrap();
1240 let msg = create_smsg(
1243 &address_1,
1244 &address_2,
1245 &mut wallet_2,
1246 i as u64,
1247 300_000,
1248 1000,
1249 );
1250 mpool.add(msg).await.unwrap();
1251 }
1252 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
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 let bls_msgs = msgs.iter().filter(|m| m.is_bls()).count();
1260 assert_eq!(
1261 CBOR_GEN_LIMIT, bls_msgs,
1262 "Expected {CBOR_GEN_LIMIT} bls messages, got {bls_msgs}."
1263 );
1264 assert_eq!(
1265 msgs.len(),
1266 BLOCK_MESSAGE_LIMIT,
1267 "Expected {BLOCK_MESSAGE_LIMIT} messages, got {}",
1268 msgs.len()
1269 );
1270 }
1271
1272 #[tokio::test]
1273 async fn message_selection_trimming_msgs_two_senders_complex() {
1274 let mut joinset = JoinSet::new();
1275 let mpool = make_test_mpool(&mut joinset);
1276 let ts = mock_tipset(&mpool).await;
1277 let api = mpool.api.clone();
1278
1279 let keystore_1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1280 let mut wallet_1 = Wallet::new(keystore_1);
1281 let address_1 = wallet_1.generate_addr(SignatureType::Secp256k1).unwrap();
1282
1283 let keystore_2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1284 let mut wallet_2 = Wallet::new(keystore_2);
1285 let address_2 = wallet_2.generate_addr(SignatureType::Bls).unwrap();
1286
1287 api.set_state_balance_raw(&address_1, TokenAmount::from_whole(1));
1288 api.set_state_balance_raw(&address_2, TokenAmount::from_whole(1));
1289
1290 let mut counter = 0;
1292 for i in 0..CBOR_GEN_LIMIT {
1293 counter += 1;
1294 let msg = create_smsg(
1295 &address_2,
1296 &address_1,
1297 &mut wallet_1,
1298 i as u64,
1299 300_000,
1300 100,
1301 );
1302 mpool.add(msg).await.unwrap();
1303 let msg = create_smsg(
1306 &address_1,
1307 &address_2,
1308 &mut wallet_2,
1309 i as u64,
1310 300_000,
1311 100,
1312 );
1313 mpool.add(msg).await.unwrap();
1314 }
1315
1316 let msg = create_smsg(
1318 &address_2,
1319 &address_1,
1320 &mut wallet_1,
1321 counter as u64,
1322 300_000,
1323 1000,
1324 );
1325 mpool.add(msg).await.unwrap();
1326
1327 let msg = create_smsg(
1328 &address_1,
1329 &address_2,
1330 &mut wallet_2,
1331 counter as u64,
1332 300_000,
1333 100,
1334 );
1335 mpool.add(msg).await.unwrap();
1336
1337 counter += 1;
1338
1339 let msg = create_smsg(
1341 &address_2,
1342 &address_1,
1343 &mut wallet_1,
1344 counter as u64,
1345 400_000,
1346 1_000_000,
1347 );
1348 mpool.add(msg).await.unwrap();
1349
1350 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1351 let m_gas_limit = msgs.iter().map(|m| m.gas_limit()).sum::<u64>();
1353 assert!(
1354 m_gas_limit <= BLOCK_GAS_LIMIT,
1355 "Selected messages gas limit {m_gas_limit} exceeds block gas limit {BLOCK_GAS_LIMIT}",
1356 );
1357 let secps_len = msgs.iter().filter(|m| m.is_secp256k1()).count();
1359 assert_eq!(
1360 CBOR_GEN_LIMIT, secps_len,
1361 "Expected {CBOR_GEN_LIMIT} secp messages, got {secps_len}."
1362 );
1363 assert_eq!(
1365 msgs.len(),
1366 BLOCK_MESSAGE_LIMIT,
1367 "Expected {BLOCK_MESSAGE_LIMIT} messages, got {}",
1368 msgs.len()
1369 );
1370 }
1371
1372 #[tokio::test]
1373 async fn message_selection_priority() {
1374 let ks1 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1375 let mut w1 = Wallet::new(ks1);
1376 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1377
1378 let ks2 = KeyStore::new(KeyStoreConfig::Memory).unwrap();
1379 let mut w2 = Wallet::new(ks2);
1380 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1381
1382 let cfg = crate::message_pool::config::MpoolConfig {
1383 priority_addrs: vec![a1],
1384 ..Default::default()
1385 };
1386
1387 let mut joinset = JoinSet::new();
1388 let (tx, _rx) = flume::bounded(50);
1389 let mpool =
1390 MessagePool::new(TestApi::default(), tx, cfg, Arc::default(), &mut joinset).unwrap();
1391 let ts = mock_tipset(&mpool).await;
1392 let api = mpool.api.clone();
1393
1394 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1396 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1397
1398 let nmsgs = 10;
1399
1400 for i in 0..nmsgs {
1402 let bias = (nmsgs - i) / 3;
1403 let m = create_smsg(
1404 &a2,
1405 &a1,
1406 &mut w1,
1407 i as u64,
1408 TEST_GAS_LIMIT,
1409 (1 + i % 3 + bias) as u64,
1410 );
1411 mpool.add(m).await.unwrap();
1412 let m = create_smsg(
1413 &a1,
1414 &a2,
1415 &mut w2,
1416 i as u64,
1417 TEST_GAS_LIMIT,
1418 (1 + i % 3 + bias) as u64,
1419 );
1420 mpool.add(m).await.unwrap();
1421 }
1422
1423 let msgs = mpool.select_messages(&ts, 1.0).unwrap();
1424
1425 assert_eq!(msgs.len(), 20);
1426
1427 let mut next_nonce = 0;
1428 for msg in msgs.iter().take(10) {
1429 assert_eq!(
1430 msg.from(),
1431 a1,
1432 "first 10 returned messages should be from actor a1"
1433 );
1434 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1435 next_nonce += 1;
1436 }
1437 next_nonce = 0;
1438 for msg in msgs.iter().take(20).skip(10) {
1439 assert_eq!(
1440 msg.from(),
1441 a2,
1442 "next 10 returned messages should be from actor a2"
1443 );
1444 assert_eq!(msg.sequence(), next_nonce, "nonce should be in order");
1445 next_nonce += 1;
1446 }
1447 }
1448
1449 #[tokio::test]
1450 async fn test_optimal_msg_selection1() {
1451 let mut joinset = JoinSet::new();
1455 let mpool = make_test_mpool(&mut joinset);
1456 let ts = mock_tipset(&mpool).await;
1457 let api = mpool.api.clone();
1458
1459 let mut w1 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1461 let a1 = w1.generate_addr(SignatureType::Secp256k1).unwrap();
1462 let mut w2 = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1463 let a2 = w2.generate_addr(SignatureType::Secp256k1).unwrap();
1464
1465 api.set_state_balance_raw(&a1, TokenAmount::from_whole(1));
1466 api.set_state_balance_raw(&a2, TokenAmount::from_whole(1));
1467
1468 let n_msgs = 10 * crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1469
1470 for i in 0..(n_msgs as usize) {
1474 let bias = (n_msgs as usize - i) / 3;
1475 let m = create_fake_smsg(
1476 &mpool,
1477 &a2,
1478 &a1,
1479 i as u64,
1480 TEST_GAS_LIMIT,
1481 (1 + i % 3 + bias) as u64,
1482 );
1483 mpool.add(m).await.unwrap();
1484 }
1485
1486 let msgs = mpool.select_messages(&ts, 0.25).unwrap();
1487
1488 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1489
1490 assert_eq!(msgs.len(), expected_msgs as usize);
1491
1492 for (next_nonce, m) in msgs.into_iter().enumerate() {
1493 assert_eq!(m.from(), a1, "Expected message from a1");
1494 assert_eq!(
1495 m.message().sequence,
1496 next_nonce as u64,
1497 "expected nonce {} but got {}",
1498 next_nonce,
1499 m.message().sequence
1500 );
1501 }
1502 }
1503
1504 #[tokio::test]
1505 async fn test_optimal_msg_selection2() {
1506 let mut joinset = JoinSet::new();
1507 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)); 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;
1525 for i in 0..n_msgs as usize {
1526 let bias = (n_msgs as usize - i) / 3;
1527 let m = create_fake_smsg(
1528 &mpool,
1529 &a2,
1530 &a1,
1531 i as u64,
1532 TEST_GAS_LIMIT,
1533 (200000 + i % 3 + bias) as u64,
1534 );
1535 mpool.add(m).await.unwrap();
1536 let m = create_fake_smsg(
1537 &mpool,
1538 &a1,
1539 &a2,
1540 i as u64,
1541 TEST_GAS_LIMIT,
1542 (190000 + i % 3 + bias) as u64,
1543 );
1544 mpool.add(m).await.unwrap();
1545 }
1546
1547 let msgs = mpool.select_messages(&ts, 0.1).unwrap();
1548
1549 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1550 assert_eq!(
1551 msgs.len(),
1552 expected_msgs as usize,
1553 "Expected {} messages, but got {}",
1554 expected_msgs,
1555 msgs.len()
1556 );
1557
1558 let mut n_from1 = 0;
1559 let mut n_from2 = 0;
1560 let mut next_nonce1 = 0;
1561 let mut next_nonce2 = 0;
1562
1563 for m in msgs {
1564 if m.from() == a1 {
1565 if m.message.sequence != next_nonce1 {
1566 panic!(
1567 "Expected nonce {}, but got {}",
1568 next_nonce1, m.message.sequence
1569 );
1570 }
1571 next_nonce1 += 1;
1572 n_from1 += 1;
1573 } else {
1574 if m.message.sequence != next_nonce2 {
1575 panic!(
1576 "Expected nonce {}, but got {}",
1577 next_nonce2, m.message.sequence
1578 );
1579 }
1580 next_nonce2 += 1;
1581 n_from2 += 1;
1582 }
1583 }
1584
1585 if n_from1 > n_from2 {
1586 panic!("Expected more msgs from a2 than a1");
1587 }
1588 }
1589
1590 #[tokio::test]
1591 async fn test_optimal_msg_selection3() {
1592 let mut joinset = JoinSet::new();
1593 let mpool = make_test_mpool(&mut joinset);
1599 let ts = mock_tipset(&mpool).await;
1600 let api = mpool.api.clone();
1601
1602 let n_actors = 10;
1603
1604 let mut actors = vec![];
1605 let mut wallets = vec![];
1606
1607 for _ in 0..n_actors {
1608 let mut wallet = Wallet::new(KeyStore::new(KeyStoreConfig::Memory).unwrap());
1609 let actor = wallet.generate_addr(SignatureType::Secp256k1).unwrap();
1610
1611 actors.push(actor);
1612 wallets.push(wallet);
1613 }
1614
1615 for a in &mut actors {
1616 api.set_state_balance_raw(a, TokenAmount::from_whole(1));
1617 }
1618
1619 let n_msgs = 1 + crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1620 for i in 0..n_msgs {
1621 for j in 0..n_actors {
1622 let premium =
1623 500000 + 10000 * (n_actors - j) + (n_msgs + 2 - i) / (30 * n_actors) + i % 3;
1624 let m = create_fake_smsg(
1625 &mpool,
1626 &actors[j as usize],
1627 &actors[j as usize],
1628 i as u64,
1629 TEST_GAS_LIMIT,
1630 premium as u64,
1631 );
1632 mpool.add(m).await.unwrap();
1633 }
1634 }
1635
1636 let msgs = mpool.select_messages(&ts, 0.1).unwrap();
1637 let expected_msgs = crate::shim::econ::BLOCK_GAS_LIMIT as i64 / TEST_GAS_LIMIT;
1638
1639 assert_eq!(
1640 msgs.len(),
1641 expected_msgs as usize,
1642 "Expected {} messages, but got {}",
1643 expected_msgs,
1644 msgs.len()
1645 );
1646
1647 let who_is = |addr| -> usize {
1648 for (i, a) in actors.iter().enumerate() {
1649 if a == &addr {
1650 return i;
1651 }
1652 }
1653 9999999
1656 };
1657
1658 let mut nonces = vec![0; n_actors as usize];
1659 for m in &msgs {
1660 let who = who_is(m.from());
1661 if who < 3 {
1662 panic!("got message from {who}th actor",);
1663 }
1664
1665 let next_nonce: u64 = nonces[who];
1666 if m.message.sequence != next_nonce {
1667 panic!(
1668 "expected nonce {} but got {}",
1669 next_nonce, m.message.sequence
1670 );
1671 }
1672 nonces[who] += 1;
1673 }
1674 }
1675}