1use crate::blocks::{CachingBlockHeader, Tipset, TipsetKey};
10use crate::chain::{HeadChanges, MINIMUM_BASE_FEE};
11use crate::eth::is_valid_eth_tx_for_sending;
12use crate::libp2p::{NetworkMessage, PUBSUB_MSG_STR, Topic};
13use crate::message::{ChainMessage, MessageRead as _, SignedMessage, valid_for_block_inclusion};
14use crate::message_pool::{
15 config::MpoolConfig,
16 errors::Error,
17 msgpool::{
18 BASE_FEE_LOWER_BOUND_FACTOR_CONSERVATIVE, events::MpoolSubscriber,
19 pending_store::PendingStore, recovered_bls_messages, republish::RepublishState,
20 },
21 provider::{Provider, ProviderExt},
22 utils::get_base_fee_lower_bound,
23};
24use crate::networks::{ChainConfig, NEWEST_NETWORK_VERSION};
25use crate::prelude::*;
26use crate::rpc::eth::types::EthAddress;
27use crate::shim::{
28 address::{Address, Protocol},
29 crypto::{Signature, SignatureType},
30 econ::TokenAmount,
31 gas::{Gas, price_list_by_network_version},
32 state_tree::ActorState,
33};
34use crate::state_manager::IdToAddressCache;
35use crate::state_manager::utils::is_valid_for_sending;
36use crate::utils::cache::SizeTrackingCache;
37use ahash::HashSet;
38use futures::StreamExt;
39use fvm_ipld_encoding::to_vec;
40use get_size2::GetSize;
41use itertools::Itertools;
42use nonzero_ext::nonzero;
43use parking_lot::RwLock as SyncRwLock;
44use std::num::NonZeroUsize;
45use std::time::Duration;
46use tokio::{task::JoinSet, time::interval};
47use tracing::warn;
48
49const MAX_MESSAGE_SIZE: usize = 64 << 10; pub(in crate::message_pool) const MAX_ACTOR_PENDING_MESSAGES: u64 = 1000;
54pub(in crate::message_pool) const MAX_UNTRUSTED_ACTOR_PENDING_MESSAGES: u64 = 100;
55
56const BLS_SIG_CACHE_SIZE: NonZeroUsize = nonzero!(40000usize);
58const SIG_VAL_CACHE_SIZE: NonZeroUsize = nonzero!(32000usize);
59const KEY_CACHE_SIZE: NonZeroUsize = nonzero!(1_048_576usize);
60const STATE_NONCE_CACHE_SIZE: NonZeroUsize = nonzero!(32768usize);
61
62#[derive(Clone, Debug, Hash, PartialEq, Eq, GetSize)]
63pub(in crate::message_pool) struct StateNonceCacheKey {
64 tipset_key: TipsetKey,
65 addr: Address,
66}
67
68#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub(in crate::message_pool) enum TrustPolicy {
72 Trusted,
73 Untrusted,
74}
75
76pub use super::msg_set::{MsgSetLimits, StrictnessPolicy};
77
78pub(in crate::message_pool) struct Caches {
80 pub(in crate::message_pool) bls_sig: SizeTrackingCache<CidWrapper, Signature>,
81 pub(in crate::message_pool) sig_val: SizeTrackingCache<CidWrapper, ()>,
82 pub(in crate::message_pool) key: IdToAddressCache,
83 pub(in crate::message_pool) state_nonce: SizeTrackingCache<StateNonceCacheKey, u64>,
84}
85
86impl Caches {
87 pub(in crate::message_pool) fn new() -> Self {
88 Self {
89 bls_sig: SizeTrackingCache::new_with_metrics("bls_sig", BLS_SIG_CACHE_SIZE),
90 sig_val: SizeTrackingCache::new_with_metrics("sig_val", SIG_VAL_CACHE_SIZE),
91 key: SizeTrackingCache::new_with_metrics("mpool_key", KEY_CACHE_SIZE),
92 state_nonce: SizeTrackingCache::new_with_metrics("state_nonce", STATE_NONCE_CACHE_SIZE),
93 }
94 }
95}
96
97impl ShallowClone for Caches {
98 fn shallow_clone(&self) -> Self {
99 Self {
100 bls_sig: self.bls_sig.shallow_clone(),
101 sig_val: self.sig_val.shallow_clone(),
102 key: self.key.shallow_clone(),
103 state_nonce: self.state_nonce.shallow_clone(),
104 }
105 }
106}
107
108pub struct MessagePool<T> {
112 pub(in crate::message_pool) pending: PendingStore,
115 pub(in crate::message_pool) caches: Caches,
116 pub(in crate::message_pool) local_addrs: Arc<SyncRwLock<HashSet<Address>>>,
118 pub(in crate::message_pool) cur_tipset: Arc<SyncRwLock<Tipset>>,
120 pub(in crate::message_pool) api: Arc<T>,
122 pub(in crate::message_pool) network_sender: flume::Sender<NetworkMessage>,
124 pub(in crate::message_pool) republish: Arc<RepublishState>,
126 pub(in crate::message_pool) config: Arc<MpoolConfig>,
128 pub(in crate::message_pool) chain_config: Arc<ChainConfig>,
130}
131
132impl<T> ShallowClone for MessagePool<T> {
133 fn shallow_clone(&self) -> Self {
134 Self {
135 pending: self.pending.shallow_clone(),
136 caches: self.caches.shallow_clone(),
137 local_addrs: self.local_addrs.shallow_clone(),
138 cur_tipset: self.cur_tipset.shallow_clone(),
139 api: self.api.shallow_clone(),
140 network_sender: self.network_sender.clone(),
141 republish: self.republish.shallow_clone(),
142 config: self.config.shallow_clone(),
143 chain_config: self.chain_config.shallow_clone(),
144 }
145 }
146}
147
148pub(in crate::message_pool) async fn resolve_to_key<T: Provider + Send + Sync + 'static>(
151 api: &Arc<T>,
152 key_cache: &IdToAddressCache,
153 addr: &Address,
154 cur_ts: &Tipset,
155) -> Result<Address, Error> {
156 if matches!(
157 addr.protocol(),
158 Protocol::BLS | Protocol::Secp256k1 | Protocol::Delegated
159 ) {
160 return Ok(*addr);
161 }
162 let id = addr.id().ok();
163 if let Some(id) = &id
164 && let Some(resolved) = key_cache.get(id)
165 {
166 return Ok(resolved);
167 }
168 let resolved = api
169 .resolve_to_deterministic_address_at_finality_async(*addr, cur_ts.clone())
170 .await?;
171 if let Some(id) = id {
172 key_cache.insert(id, resolved);
173 }
174 Ok(resolved)
175}
176
177impl<T> MessagePool<T>
178where
179 T: Provider,
180{
181 pub fn current_tipset(&self) -> Tipset {
183 self.cur_tipset.read().clone()
184 }
185
186 pub(in crate::message_pool) async fn resolve_to_key(
187 &self,
188 addr: &Address,
189 cur_ts: &Tipset,
190 ) -> Result<Address, Error>
191 where
192 T: Send + Sync + 'static,
193 {
194 resolve_to_key(&self.api, &self.caches.key, addr, cur_ts).await
195 }
196
197 async fn add_local(&self, m: &SignedMessage) -> Result<(), Error>
200 where
201 T: Send + Sync + 'static,
202 {
203 let cur_ts = self.current_tipset();
204 let resolved = self.resolve_to_key(&m.from(), &cur_ts).await?;
205 self.local_addrs.write().insert(resolved);
206 Ok(())
207 }
208
209 async fn push_internal(
212 &self,
213 msg: SignedMessage,
214 trust_policy: TrustPolicy,
215 ) -> Result<Cid, Error>
216 where
217 T: Send + Sync + 'static,
218 {
219 let cid = msg.cid();
220 let publish = self.add_to_pool(msg.clone(), true, trust_policy).await?;
221 self.add_local(&msg).await?;
222 if publish {
223 self.publish_pubsub(&msg).await?;
224 }
225 Ok(cid)
226 }
227
228 pub(in crate::message_pool) async fn publish_pubsub(
230 &self,
231 msg: &SignedMessage,
232 ) -> Result<(), Error> {
233 let message = to_vec(msg)?;
234 let network_name = self.chain_config.network.genesis_name();
235 self.network_sender
236 .send_async(NetworkMessage::PubsubMessage {
237 topic: Topic::new(format!("{PUBSUB_MSG_STR}/{network_name}")),
238 message,
239 })
240 .await
241 .map_err(|_| Error::Other("Network receiver dropped".to_string()))
242 }
243
244 pub async fn push(&self, msg: SignedMessage) -> Result<Cid, Error>
246 where
247 T: Send + Sync + 'static,
248 {
249 self.push_internal(msg, TrustPolicy::Trusted).await
250 }
251
252 pub async fn push_untrusted(&self, msg: SignedMessage) -> Result<Cid, Error>
254 where
255 T: Send + Sync + 'static,
256 {
257 self.push_internal(msg, TrustPolicy::Untrusted).await
258 }
259
260 pub async fn add(&self, msg: SignedMessage) -> Result<(), Error>
263 where
264 T: Send + Sync + 'static,
265 {
266 self.add_to_pool(msg, false, TrustPolicy::Trusted).await?;
267 Ok(())
268 }
269
270 pub(in crate::message_pool) async fn validate_for_pool(
276 &self,
277 msg: &SignedMessage,
278 cur_ts: &Tipset,
279 local: bool,
280 ) -> Result<bool, Error>
281 where
282 T: Send + Sync + 'static,
283 {
284 validate_static(msg)?;
285 validate_signature(msg, &self.caches.sig_val, self.chain_config.eth_chain_id)?;
286
287 let expected_sequence = self.get_state_sequence(&msg.from(), cur_ts).await?;
288 let sender_actor = self.api.get_actor_after(&msg.from(), cur_ts)?;
289
290 validate_with_state(
291 msg,
292 &self.chain_config,
293 cur_ts,
294 &sender_actor,
295 expected_sequence,
296 local,
297 )
298 }
299
300 pub(in crate::message_pool) async fn add_to_pool(
304 &self,
305 msg: SignedMessage,
306 local: bool,
307 trust_policy: TrustPolicy,
308 ) -> Result<bool, Error>
309 where
310 T: Send + Sync + 'static,
311 {
312 let cur_ts = self.current_tipset();
313 let publish = self.validate_for_pool(&msg, &cur_ts, local).await?;
314 let strictness = if local {
315 StrictnessPolicy::Relaxed
316 } else {
317 StrictnessPolicy::Strict
318 };
319 self.add_to_pool_unchecked(&cur_ts, msg, trust_policy, strictness)
320 .await?;
321 Ok(publish)
322 }
323
324 pub(in crate::message_pool) async fn add_to_pool_unchecked(
334 &self,
335 cur_ts: &Tipset,
336 msg: SignedMessage,
337 trust_policy: TrustPolicy,
338 strictness: StrictnessPolicy,
339 ) -> Result<(), Error>
340 where
341 T: Send + Sync + 'static,
342 {
343 if msg.signature().signature_type() == SignatureType::Bls {
344 self.caches
345 .bls_sig
346 .insert(msg.cid().into(), msg.signature().clone());
347 }
348
349 self.api
350 .put_message(&ChainMessage::Signed(msg.clone().into()))?;
351 self.api
352 .put_message(&ChainMessage::Unsigned(msg.message().clone().into()))?;
353
354 let sequence = self.get_state_sequence(&msg.from(), cur_ts).await?;
355 let resolved_from = self.resolve_to_key(&msg.from(), cur_ts).await?;
356 self.pending
357 .insert(resolved_from, msg, sequence, trust_policy, strictness)
358 }
359
360 pub async fn get_sequence(&self, addr: &Address) -> Result<u64, Error>
363 where
364 T: Send + Sync + 'static,
365 {
366 let cur_ts = self.current_tipset();
367
368 let sequence = self.get_state_sequence(addr, &cur_ts).await?;
369
370 let resolved = self.resolve_to_key(addr, &cur_ts).await.ok();
371 let mset = resolved
372 .and_then(|r| self.pending.snapshot_for(&r))
373 .or_else(|| self.pending.snapshot_for(addr));
374 match mset {
375 Some(mset) => {
376 if sequence > mset.next_sequence {
377 return Ok(sequence);
378 }
379 Ok(mset.next_sequence)
380 }
381 None => Ok(sequence),
382 }
383 }
384
385 pub(in crate::message_pool) async fn get_state_sequence(
389 &self,
390 addr: &Address,
391 cur_ts: &Tipset,
392 ) -> Result<u64, Error>
393 where
394 T: Send + Sync + 'static,
395 {
396 let nk = StateNonceCacheKey {
397 tipset_key: cur_ts.key().clone(),
398 addr: *addr,
399 };
400
401 if let Some(cached) = self.caches.state_nonce.get(&nk) {
402 return Ok(cached);
403 }
404
405 let actor = self.api.get_actor_after(addr, cur_ts)?;
406 let mut next_nonce = actor.sequence;
407
408 let resolved = self
409 .resolve_to_key(addr, cur_ts)
410 .await
411 .inspect_err(|e| tracing::warn!(%addr, "failed to resolve address to key: {e:#}"));
412 let messages = self
413 .api
414 .messages_for_tipset(cur_ts)
415 .inspect_err(|e| tracing::warn!("failed to get messages for tipset: {e:#}"));
416 if let (Ok(resolved), Ok(messages)) = (resolved, messages) {
417 for msg in messages.iter() {
418 if let Ok(from) = self.resolve_to_key(&msg.from(), cur_ts).await.inspect_err(
419 |e| tracing::warn!(from = %msg.from(), "failed to resolve message sender: {e:#}"),
420 ) && from == resolved
421 {
422 let n = msg.sequence() + 1;
423 if n > next_nonce {
424 next_nonce = n;
425 }
426 }
427 }
428 }
429
430 self.caches.state_nonce.insert(nk, next_nonce);
431 Ok(next_nonce)
432 }
433
434 pub fn pending(&self) -> (Vec<SignedMessage>, Tipset) {
437 let cur_ts = self.current_tipset();
438 let snapshot = self.pending.snapshot();
439 let len = snapshot.values().map(|mset| mset.msgs.len()).sum();
440 let mut out = Vec::with_capacity(len);
441
442 for mset in snapshot.into_values() {
443 out.extend(
444 mset.msgs
445 .into_values()
446 .sorted_unstable_by_key(|m| m.message().sequence),
447 );
448 }
449
450 (out, cur_ts)
451 }
452
453 pub async fn pending_for(&self, a: &Address) -> Option<Vec<SignedMessage>>
457 where
458 T: Send + Sync + 'static,
459 {
460 let cur_ts = self.current_tipset();
461 let resolved = self
462 .resolve_to_key(a, &cur_ts)
463 .await
464 .inspect_err(|e| tracing::debug!(%a, "pending_for: failed to resolve address: {e:#}"))
465 .ok()?;
466 let mset = self.pending.snapshot_for(&resolved)?;
467 if mset.msgs.is_empty() {
468 return None;
469 }
470
471 Some(
472 mset.msgs
473 .into_values()
474 .sorted_by_key(|v| v.message().sequence)
475 .collect(),
476 )
477 }
478
479 pub fn subscriber(&self) -> MpoolSubscriber {
482 self.pending.subscriber()
483 }
484
485 pub fn messages_for_blocks<'a>(
487 &self,
488 blks: impl Iterator<Item = &'a CachingBlockHeader>,
489 ) -> Result<Vec<SignedMessage>, Error> {
490 let mut msg_vec: Vec<SignedMessage> = Vec::new();
491
492 for block in blks {
493 let (umsg, mut smsgs) = self.api.messages_for_block(block)?;
494
495 msg_vec.append(smsgs.as_mut());
496 msg_vec.extend(recovered_bls_messages(&self.caches.bls_sig, umsg));
497 }
498 Ok(msg_vec)
499 }
500
501 pub fn gas_limit_overestimation(&self) -> f64 {
502 self.config.gas_limit_overestimation
503 }
504
505 pub fn config(&self) -> MpoolConfig {
506 (*self.config).clone()
507 }
508}
509
510impl<T> MessagePool<T>
511where
512 T: Provider + Send + Sync + 'static,
513{
514 pub fn new(
516 api: T,
517 network_sender: flume::Sender<NetworkMessage>,
518 config: MpoolConfig,
519 chain_config: Arc<ChainConfig>,
520 services: &mut JoinSet<anyhow::Result<()>>,
521 ) -> Result<Self, Error>
522 where
523 T: Provider,
524 {
525 let pending = PendingStore::new(MsgSetLimits::new(
528 api.max_actor_pending_messages(),
529 api.max_untrusted_actor_pending_messages(),
530 ));
531 let cur_tipset = Arc::new(SyncRwLock::new(api.get_heaviest_tipset()));
532 let republish_interval =
533 u64::from(10 * chain_config.block_delay_secs + chain_config.propagation_delay_secs);
534 let (republish, repub_trigger_rx) = RepublishState::new();
535
536 let mp = MessagePool {
537 pending,
538 caches: Caches::new(),
539 local_addrs: Arc::new(SyncRwLock::new(HashSet::default())),
540 republish: Arc::new(republish),
541 cur_tipset,
542 api: Arc::new(api),
543 network_sender,
544 config: Arc::new(config),
545 chain_config,
546 };
547
548 {
550 let mp = mp.shallow_clone();
551 let head_changes_rx = mp.api.subscribe_head_changes();
552 services.spawn(async move {
553 while let Ok(HeadChanges { reverts, applies }) = head_changes_rx.recv_async().await
554 {
555 if let Err(e) = mp.apply_head_change(reverts, applies).await {
556 tracing::warn!("Error changing head: {e}");
557 }
558 }
559 Ok(())
560 });
561 }
562
563 {
565 let mp = mp.shallow_clone();
566 services.spawn(async move {
567 let mut repub_trigger_rx = repub_trigger_rx.stream();
568 let mut interval = interval(Duration::from_secs(republish_interval));
569 loop {
570 tokio::select! {
571 _ = interval.tick() => (),
572 _ = repub_trigger_rx.next() => (),
573 }
574 if let Err(e) = mp.run_republish_cycle().await {
575 warn!("Failed to republish pending messages: {}", e.to_string());
576 }
577 }
578 });
579 }
580
581 Ok(mp)
582 }
583}
584
585fn validate_static(msg: &SignedMessage) -> Result<(), Error> {
586 if to_vec(msg)?.len() > MAX_MESSAGE_SIZE {
587 return Err(Error::MessageTooBig);
588 }
589 let to = msg.message().to();
590 if to.protocol() == Protocol::Delegated {
591 EthAddress::from_filecoin_address(&to).context(format!(
592 "message recipient {to} is a delegated address but not a valid Eth Address"
593 ))?;
594 }
595 valid_for_block_inclusion(msg.message(), Gas::new(0), NEWEST_NETWORK_VERSION)?;
596 if msg.gas_fee_cap().atto() < &MINIMUM_BASE_FEE.into() {
597 return Err(Error::GasFeeCapTooLow);
598 }
599 Ok(())
600}
601
602fn validate_signature(
603 msg: &SignedMessage,
604 sig_val_cache: &SizeTrackingCache<CidWrapper, ()>,
605 eth_chain_id: u64,
606) -> Result<(), Error> {
607 let cid = msg.cid();
608 if sig_val_cache.get(&cid).is_some() {
609 return Ok(());
610 }
611 msg.verify(eth_chain_id)
612 .map_err(|e| Error::Other(e.to_string()))?;
613 sig_val_cache.insert(cid.into(), ());
614 Ok(())
615}
616
617fn validate_with_state(
619 msg: &SignedMessage,
620 chain_config: &ChainConfig,
621 cur_ts: &Tipset,
622 sender_actor: &ActorState,
623 expected_sequence: u64,
624 local: bool,
625) -> Result<bool, Error> {
626 if expected_sequence > msg.message().sequence {
627 return Err(Error::SequenceTooLow);
628 }
629
630 let nv_next = chain_config.network_version(cur_ts.epoch() + 1);
632 if msg.is_delegated() && !is_valid_eth_tx_for_sending(chain_config.eth_chain_id, nv_next, msg) {
633 return Err(Error::Other(
634 "Invalid Ethereum message for the current network version".to_owned(),
635 ));
636 }
637 if !is_valid_for_sending(nv_next, sender_actor) {
638 return Err(Error::Other(
639 "Sender actor is not a valid top-level sender".to_owned(),
640 ));
641 }
642
643 let nv_cur = chain_config.network_version(cur_ts.epoch());
644 let min_gas = price_list_by_network_version(nv_cur).on_chain_message(msg.chain_length()?);
645 valid_for_block_inclusion(msg.message(), min_gas.total(), NEWEST_NETWORK_VERSION)?;
646
647 let publish = check_base_fee_floor(msg, cur_ts, local)?;
648
649 let balance = TokenAmount::from(&sender_actor.balance);
650 let required = msg.required_funds();
651 if balance < required {
652 return Err(Error::NotEnoughFunds { balance, required });
653 }
654
655 Ok(publish)
656}
657
658pub(in crate::message_pool) fn check_base_fee_floor(
660 msg: &SignedMessage,
661 cur_ts: &Tipset,
662 local: bool,
663) -> Result<bool, Error> {
664 let base_fee = &cur_ts.block_headers().first().parent_base_fee;
665 let lb = get_base_fee_lower_bound(base_fee, BASE_FEE_LOWER_BOUND_FACTOR_CONSERVATIVE);
666 if msg.gas_fee_cap() >= lb {
667 return Ok(local);
668 }
669 if local {
670 warn!(
671 "local message will not be immediately published because GasFeeCap doesn't meet the lower bound for inclusion in the next 20 blocks (GasFeeCap: {}, baseFeeLowerBound: {})",
672 msg.gas_fee_cap(),
673 lb
674 );
675 return Ok(false);
676 }
677 Err(Error::SoftValidationFailure(format!(
678 "GasFeeCap doesn't meet base fee lower bound for inclusion in the next 20 blocks (GasFeeCap: {}, baseFeeLowerBound:{})",
679 msg.gas_fee_cap(),
680 lb
681 )))
682}
683
684#[cfg(test)]
685mod tests {
686 use crate::blocks::RawBlockHeader;
687 use crate::chain::ChainStore;
688 use crate::db::{DbImpl, MemoryDB};
689 use crate::message_pool::provider::Provider;
690 use crate::message_pool::test_provider::TestApi;
691 use crate::networks::ChainConfig;
692 use crate::shim::econ::TokenAmount;
693 use crate::shim::state_tree::{ActorState, StateTree, StateTreeVersion};
694 use crate::test_utils::dummy_ticket;
695 use crate::utils::db::CborStoreExt as _;
696
697 use super::*;
698 use crate::shim::message::Message as ShimMessage;
699
700 use tokio::task::JoinSet;
701
702 fn make_smsg(from: Address, seq: u64, premium: u64) -> SignedMessage {
703 SignedMessage::mock_bls_signed_message(ShimMessage {
704 from,
705 sequence: seq,
706 gas_premium: TokenAmount::from_atto(premium),
707 gas_limit: 1_000_000,
708 ..ShimMessage::default()
709 })
710 }
711
712 fn make_test_mpool(api: TestApi) -> (MessagePool<TestApi>, JoinSet<anyhow::Result<()>>) {
713 let (tx, _rx) = flume::bounded(50);
714 let mut services = JoinSet::new();
715 let mpool = MessagePool::new(
716 api,
717 tx,
718 Default::default(),
719 Default::default(),
720 &mut services,
721 )
722 .unwrap();
723 (mpool, services)
724 }
725
726 #[tokio::test]
729 async fn add_to_pool_unchecked_accepts_high_gas_limit() {
730 let api = TestApi::default();
731 let (mpool, _services) = make_test_mpool(api);
732 let cur_ts = mpool.current_tipset();
733 let message = ShimMessage {
734 gas_limit: 666_666_666,
735 ..ShimMessage::default()
736 };
737 let msg = SignedMessage::mock_bls_signed_message(message);
738 let res = mpool
739 .add_to_pool_unchecked(
740 &cur_ts,
741 msg,
742 TrustPolicy::Trusted,
743 StrictnessPolicy::Relaxed,
744 )
745 .await;
746 assert!(res.is_ok());
747 }
748
749 #[tokio::test]
750 async fn test_resolve_to_key_returns_non_id_unchanged() {
751 let api = TestApi::default();
752 let (mpool, _services) = make_test_mpool(api);
753 let ts = mpool.current_tipset();
754
755 let bls_addr = Address::new_bls(&[1u8; 48]).unwrap();
756 let result = mpool.resolve_to_key(&bls_addr, &ts).await.unwrap();
757 assert_eq!(result, bls_addr);
758 assert_eq!(
759 mpool.caches.key.len(),
760 0,
761 "cache should not be populated for non-ID addresses"
762 );
763 }
764
765 #[tokio::test]
766 async fn test_resolve_to_key_resolves_id_and_caches() {
767 let api = TestApi::default();
768 let id_addr = Address::new_id(100);
769 let key_addr = Address::new_bls(&[5u8; 48]).unwrap();
770 api.set_key_address_mapping(&id_addr, &key_addr);
771
772 let (mpool, _services) = make_test_mpool(api);
773 let ts = mpool.current_tipset();
774
775 let result = mpool.resolve_to_key(&id_addr, &ts).await.unwrap();
776 assert_eq!(result, key_addr);
777 assert_eq!(
778 mpool.caches.key.len(),
779 1,
780 "cache should have one entry after resolution"
781 );
782
783 let result2 = mpool.resolve_to_key(&id_addr, &ts).await.unwrap();
785 assert_eq!(result2, key_addr);
786 }
787
788 #[tokio::test]
789 async fn test_add_to_pool_unchecked_keys_pending_by_resolved_address() {
790 let api = TestApi::default();
791 let id_addr = Address::new_id(200);
792 let key_addr = Address::new_bls(&[7u8; 48]).unwrap();
793 api.set_key_address_mapping(&id_addr, &key_addr);
794 api.set_state_sequence(&key_addr, 0);
795
796 let (mpool, _services) = make_test_mpool(api);
797 let cur_ts = mpool.current_tipset();
798
799 let message = ShimMessage {
800 from: id_addr,
801 gas_limit: 1_000_000,
802 ..ShimMessage::default()
803 };
804 let msg = SignedMessage::mock_bls_signed_message(message);
805
806 mpool
807 .add_to_pool_unchecked(
808 &cur_ts,
809 msg,
810 TrustPolicy::Trusted,
811 StrictnessPolicy::Relaxed,
812 )
813 .await
814 .unwrap();
815
816 assert!(
817 mpool.pending.snapshot_for(&key_addr).is_some(),
818 "pending should be keyed by the resolved key address"
819 );
820 assert!(
821 mpool.pending.snapshot_for(&id_addr).is_none(),
822 "pending should NOT have an entry under the raw ID address"
823 );
824 }
825
826 #[tokio::test]
827 async fn test_get_sequence_works_with_both_address_forms() {
828 let api = TestApi::default();
829 let id_addr = Address::new_id(300);
830 let key_addr = Address::new_bls(&[9u8; 48]).unwrap();
831 api.set_key_address_mapping(&id_addr, &key_addr);
832 api.set_state_sequence(&key_addr, 0);
833
834 let (mpool, _services) = make_test_mpool(api);
835 let cur_ts = mpool.current_tipset();
836
837 for seq in 0..2 {
839 let message = ShimMessage {
840 from: id_addr,
841 sequence: seq,
842 gas_limit: 1_000_000,
843 ..ShimMessage::default()
844 };
845 let msg = SignedMessage::mock_bls_signed_message(message);
846 mpool
847 .add_to_pool_unchecked(
848 &cur_ts,
849 msg,
850 TrustPolicy::Trusted,
851 StrictnessPolicy::Relaxed,
852 )
853 .await
854 .unwrap();
855 }
856
857 let state_seq = mpool
858 .api
859 .get_actor_after(&id_addr, &cur_ts)
860 .unwrap()
861 .sequence;
862 let resolved_for_id = mpool.resolve_to_key(&id_addr, &cur_ts).await.unwrap();
863 let resolved_for_key = mpool.resolve_to_key(&key_addr, &cur_ts).await.unwrap();
864 assert_eq!(resolved_for_id, resolved_for_key);
865
866 let next_seq = mpool
867 .pending
868 .snapshot_for(&resolved_for_id)
869 .unwrap()
870 .next_sequence;
871 let expected = std::cmp::max(state_seq, next_seq);
872 assert_eq!(expected, 2, "should reflect both pending messages");
873 }
874
875 #[tokio::test]
876 async fn test_get_state_sequence_accounts_for_tipset_messages() {
877 use crate::message_pool::test_provider::mock_block;
878
879 let api = TestApi::default();
880 let sender = Address::new_bls(&[3u8; 48]).unwrap();
881 api.set_state_sequence(&sender, 5);
882
883 let block = mock_block(1, 1);
884 api.inner.lock().set_block_messages(
885 &block,
886 vec![make_smsg(sender, 5, 100), make_smsg(sender, 7, 100)],
887 );
888 let ts = Tipset::from(block);
889
890 let (mpool, _services) = make_test_mpool(api);
891
892 let nonce = mpool.get_state_sequence(&sender, &ts).await.unwrap();
893 assert_eq!(
894 nonce, 8,
895 "should account for non-consecutive tipset message at nonce 7"
896 );
897 }
898
899 #[tokio::test]
900 async fn test_get_state_sequence_ignores_other_addresses() {
901 use crate::message_pool::test_provider::mock_block;
902
903 let api = TestApi::default();
904 let addr_a = Address::new_bls(&[4u8; 48]).unwrap();
905 let addr_b = Address::new_bls(&[5u8; 48]).unwrap();
906 api.set_state_sequence(&addr_a, 0);
907 api.set_state_sequence(&addr_b, 0);
908
909 let block = mock_block(1, 1);
910 api.inner.lock().set_block_messages(
911 &block,
912 vec![
913 make_smsg(addr_b, 0, 100),
914 make_smsg(addr_b, 1, 100),
915 make_smsg(addr_b, 2, 100),
916 ],
917 );
918 let ts = Tipset::from(block);
919
920 let (mpool, _services) = make_test_mpool(api);
921
922 let nonce_a = mpool.get_state_sequence(&addr_a, &ts).await.unwrap();
923 assert_eq!(
924 nonce_a, 0,
925 "addr_a nonce should be unaffected by addr_b's messages"
926 );
927
928 let nonce_b = mpool.get_state_sequence(&addr_b, &ts).await.unwrap();
929 assert_eq!(
930 nonce_b, 3,
931 "addr_b nonce should reflect its tipset messages"
932 );
933 }
934
935 #[tokio::test]
936 async fn test_get_state_sequence_cache_hit() {
937 use crate::message_pool::test_provider::mock_block;
938
939 let api = TestApi::default();
940 let sender = Address::new_bls(&[6u8; 48]).unwrap();
941 api.set_state_sequence(&sender, 5);
942
943 let block = mock_block(1, 1);
944 api.inner
945 .lock()
946 .set_block_messages(&block, vec![make_smsg(sender, 5, 100)]);
947 let ts = Tipset::from(block);
948
949 let (mpool, _services) = make_test_mpool(api);
950
951 let nonce1 = mpool.get_state_sequence(&sender, &ts).await.unwrap();
952 assert_eq!(nonce1, 6);
953
954 mpool.api.set_state_sequence(&sender, 99);
956 let nonce2 = mpool.get_state_sequence(&sender, &ts).await.unwrap();
957 assert_eq!(
958 nonce2, 6,
959 "second call should return the cached value, not re-read state"
960 );
961 }
962
963 #[tokio::test]
964 async fn test_get_state_sequence_cache_miss_on_different_tipset() {
965 use crate::message_pool::test_provider::mock_block;
966
967 let api = TestApi::default();
968 let sender = Address::new_bls(&[7u8; 48]).unwrap();
969 api.set_state_sequence(&sender, 10);
970
971 let (mpool, _services) = make_test_mpool(api);
972
973 let block_a = mock_block(1, 1);
974 let ts_a = Tipset::from(&block_a);
975
976 let nonce_a = mpool.get_state_sequence(&sender, &ts_a).await.unwrap();
977 assert_eq!(nonce_a, 10);
978
979 mpool.api.set_state_sequence(&sender, 20);
981 let block_b = mock_block(2, 2);
982 let ts_b = Tipset::from(&block_b);
983
984 let nonce_b = mpool.get_state_sequence(&sender, &ts_b).await.unwrap();
985 assert_eq!(
986 nonce_b, 20,
987 "different tipset should miss the cache and read fresh state"
988 );
989 }
990
991 #[test]
992 fn resolve_to_key_uses_finality_lookback() {
993 let db: DbImpl = Arc::new(MemoryDB::default()).into();
994
995 let mut cfg = ChainConfig::default();
996 cfg.policy.chain_finality = 1;
997 let cfg = Arc::new(cfg);
998
999 let bls_a = Address::new_bls(&[8u8; 48]).unwrap();
1000 let bls_b = Address::new_bls(&[9u8; 48]).unwrap();
1001
1002 let mut st_a = StateTree::new(&db, StateTreeVersion::V5).unwrap();
1004 st_a.set_actor(
1005 &Address::new_id(300),
1006 ActorState::new_empty(Cid::default(), Some(bls_a)),
1007 )
1008 .unwrap();
1009 let root_a = st_a.flush().unwrap();
1010
1011 let mut st_b = StateTree::new(&db, StateTreeVersion::V5).unwrap();
1013 st_b.set_actor(
1014 &Address::new_id(400),
1015 ActorState::new_empty(Cid::default(), Some(bls_b)),
1016 )
1017 .unwrap();
1018 let root_b = st_b.flush().unwrap();
1019
1020 let genesis = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
1021 ticket: dummy_ticket(0),
1022 state_root: root_a,
1023 ..Default::default()
1024 }));
1025 db.put_cbor_default(genesis.block_headers().first())
1026 .unwrap();
1027
1028 let ts1 = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
1029 parents: genesis.key().clone(),
1030 ticket: dummy_ticket(1),
1031 epoch: 1,
1032 state_root: root_a,
1033 timestamp: 1,
1034 ..Default::default()
1035 }));
1036 db.put_cbor_default(ts1.block_headers().first()).unwrap();
1037
1038 let head = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
1039 parents: ts1.key().clone(),
1040 ticket: dummy_ticket(2),
1041 epoch: 2,
1042 state_root: root_b,
1043 timestamp: 2,
1044 ..Default::default()
1045 }));
1046 db.put_cbor_default(head.block_headers().first()).unwrap();
1047
1048 let cs = ChainStore::new(db, cfg, genesis.block_headers().first().clone()).unwrap();
1049
1050 let result = Provider::resolve_to_deterministic_address_at_finality(
1052 &cs,
1053 &Address::new_id(300),
1054 &head,
1055 )
1056 .unwrap();
1057 assert_eq!(result, bls_a);
1058
1059 Provider::resolve_to_deterministic_address_at_finality(&cs, &Address::new_id(400), &head)
1061 .expect_err("actor only in head state must not resolve via finality lookback");
1062 }
1063}