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