1#![allow(clippy::indexing_slicing)]
4use std::{
5 cmp::Ordering,
6 mem,
7 ops::{Index, IndexMut},
8};
9
10use crate::message::{MessageRead as _, SignedMessage};
11use crate::networks::ChainConfig;
12use crate::shim::{
13 address::Address,
14 econ::TokenAmount,
15 gas::{Gas, price_list_by_network_version},
16};
17use crate::{
18 blocks::{BLOCK_MESSAGE_LIMIT, Tipset},
19 shim::crypto::SignatureType,
20};
21use ahash::HashMap;
22use num_traits::Zero;
23use slotmap::{SlotMap, new_key_type};
24use tracing::warn;
25
26use super::errors::Error;
27use crate::message_pool::{
28 provider::Provider,
29 utils::{get_gas_perf, get_gas_reward},
30};
31
32new_key_type! {
33 pub struct NodeKey;
34}
35
36pub(in crate::message_pool) struct Chains {
45 pub map: SlotMap<NodeKey, MsgChainNode>,
46 pub key_vec: Vec<NodeKey>,
47}
48
49impl Chains {
50 pub(in crate::message_pool) fn sort_effective(&mut self) {
52 let mut chains = mem::take(&mut self.key_vec);
53 chains.sort_by(|a, b| {
54 let a = self.map.get(*a).unwrap();
55 let b = self.map.get(*b).unwrap();
56 a.cmp_effective(b)
57 });
58 let _ = mem::replace(&mut self.key_vec, chains);
59 }
60
61 pub(in crate::message_pool) fn sort_range_effective(
63 &mut self,
64 range: std::ops::RangeFrom<usize>,
65 ) {
66 let mut chains = mem::take(&mut self.key_vec);
67 chains[range].sort_by(|a, b| {
68 self.map
69 .get(*a)
70 .unwrap()
71 .cmp_effective(self.map.get(*b).unwrap())
72 });
73 let _ = mem::replace(&mut self.key_vec, chains);
74 }
75
76 pub(in crate::message_pool) fn get_mut_with_prev_eff(
80 &mut self,
81 k: NodeKey,
82 ) -> (Option<&mut MsgChainNode>, Option<(f64, u64)>) {
83 let node = self.map.get(k);
84 let prev = if let Some(node) = node {
85 if let Some(prev_key) = node.prev {
86 let prev_node = self.map.get(prev_key).unwrap();
87 Some((prev_node.eff_perf, prev_node.gas_limit))
88 } else {
89 None
90 }
91 } else {
92 None
93 };
94
95 let node = self.map.get_mut(k);
96 (node, prev)
97 }
98
99 pub(in crate::message_pool) fn get(&self, k: NodeKey) -> Option<&MsgChainNode> {
101 self.map.get(k)
102 }
103}
104
105impl Chains {
106 pub(in crate::message_pool) fn new() -> Self {
107 Self {
108 map: SlotMap::with_key(),
109 key_vec: vec![],
110 }
111 }
112
113 pub(in crate::message_pool) fn push_with(
116 &mut self,
117 cur_chain: MsgChainNode,
118 node_vec: &mut Vec<NodeKey>,
119 ) {
120 let key = self.map.insert(cur_chain);
121 node_vec.push(key);
122 }
123
124 pub(in crate::message_pool) fn sort(&mut self, rev: bool) {
127 let mut chains = mem::take(&mut self.key_vec);
129 chains.sort_by(|a, b| {
130 let a = self.map.get(*a).unwrap();
131 let b = self.map.get(*b).unwrap();
132 if rev { b.compare(a) } else { a.compare(b) }
133 });
134 let _ = mem::replace(&mut self.key_vec, chains);
135 }
136
137 pub(in crate::message_pool) fn get_mut(&mut self, k: NodeKey) -> Option<&mut MsgChainNode> {
139 self.map.get_mut(k)
140 }
141
142 pub(in crate::message_pool) fn get_mut_at(&mut self, i: usize) -> Option<&mut MsgChainNode> {
144 let key = self.key_vec.get(i)?;
145 self.get_mut(*key)
146 }
147
148 pub(in crate::message_pool) fn get_from(&self, i: usize, vec: &[NodeKey]) -> &MsgChainNode {
150 #[allow(clippy::indexing_slicing)]
151 self.map.get(vec[i]).unwrap()
152 }
153
154 pub(in crate::message_pool) fn get_mut_from(
156 &mut self,
157 i: usize,
158 vec: &[NodeKey],
159 ) -> &mut MsgChainNode {
160 #[allow(clippy::indexing_slicing)]
161 self.map.get_mut(vec[i]).unwrap()
162 }
163
164 pub(in crate::message_pool) fn get_key_at(&self, i: usize) -> Option<NodeKey> {
166 self.key_vec.get(i).copied()
167 }
168
169 pub(in crate::message_pool) fn get_at(&self, i: usize) -> Option<&MsgChainNode> {
171 self.map.get(self.get_key_at(i)?)
172 }
173
174 pub(in crate::message_pool) fn len(&self) -> usize {
176 self.map.len()
177 }
178
179 pub(in crate::message_pool) fn is_empty(&self) -> bool {
182 self.map.is_empty()
183 }
184
185 #[tracing::instrument(skip_all, level = "debug")]
187 pub(in crate::message_pool) fn trim_msgs_at(
188 &mut self,
189 idx: usize,
190 gas_limit: u64,
191 msg_limit: usize,
192 base_fee: &TokenAmount,
193 ) {
194 let prev = match idx {
195 0 => None,
196 _ => self
197 .get_at(idx - 1)
198 .map(|prev| (prev.eff_perf, prev.gas_limit)),
199 };
200 let chain_node = self.get_mut_at(idx).unwrap();
201 let mut i = chain_node.msgs.len() as i64 - 1;
202
203 while i >= 0
204 && (chain_node.gas_limit > gas_limit
205 || chain_node.gas_perf < 0.0
206 || i >= msg_limit as i64)
207 {
208 #[allow(clippy::indexing_slicing)]
209 let msg = &chain_node.msgs[i as usize];
210 let gas_reward = get_gas_reward(msg, base_fee);
211 chain_node.gas_reward -= gas_reward;
212 chain_node.gas_limit = chain_node.gas_limit.saturating_sub(msg.gas_limit());
213 if chain_node.gas_limit > 0 {
214 chain_node.gas_perf = get_gas_perf(&chain_node.gas_reward, chain_node.gas_limit);
215 if chain_node.bp != 0.0 {
216 chain_node.set_eff_perf(prev);
217 }
218 } else {
219 chain_node.gas_perf = 0.0;
220 chain_node.eff_perf = 0.0;
221 }
222 i -= 1;
223 }
224
225 if i < 0 {
226 chain_node.msgs.clear();
227 chain_node.valid = false;
228 } else {
229 chain_node.msgs.truncate(i as usize + 1);
230 }
231
232 let next = chain_node.next;
233 if next.is_some() {
234 self.invalidate(next);
235 }
236 }
237
238 pub(in crate::message_pool) fn bubble_down_after_trim(&mut self, from: usize) {
239 let mut j = from;
240 while j < self.key_vec.len().saturating_sub(1) {
241 #[allow(clippy::indexing_slicing)]
242 if self[j].compare(&self[j + 1]) == Ordering::Less {
243 break;
244 }
245 self.key_vec.swap(j, j + 1);
246 j += 1;
247 }
248 }
249
250 pub(in crate::message_pool) fn invalidate(&mut self, mut key: Option<NodeKey>) {
251 let mut next_keys = vec![];
252
253 while let Some(nk) = key {
254 let chain_node = self.map.get(nk).unwrap();
255 next_keys.push(nk);
256 key = chain_node.next;
257 }
258
259 for k in next_keys.iter().rev() {
260 if let Some(node) = self.map.get_mut(*k) {
261 node.valid = false;
262 node.msgs.clear();
263 node.next = None;
264 }
265 }
266 }
267
268 pub(in crate::message_pool) fn drop_invalid(&mut self, key_vec: &mut Vec<NodeKey>) {
270 let mut valid_keys = vec![];
271 for k in key_vec.iter() {
272 if self.map.get(*k).map(|n| n.valid).unwrap() {
273 valid_keys.push(*k);
274 } else {
275 self.map.remove(*k);
276 }
277 }
278
279 *key_vec = valid_keys;
280 }
281}
282
283impl Index<usize> for Chains {
284 type Output = MsgChainNode;
285 fn index(&self, i: usize) -> &Self::Output {
286 self.get_at(i).unwrap()
287 }
288}
289
290impl IndexMut<usize> for Chains {
291 fn index_mut(&mut self, i: usize) -> &mut Self::Output {
292 #[allow(clippy::indexing_slicing)]
293 self.map.get_mut(self.key_vec[i]).unwrap()
294 }
295}
296
297#[derive(Clone, Debug)]
299pub struct MsgChainNode {
300 pub msgs: Vec<SignedMessage>,
301 pub gas_reward: TokenAmount,
302 pub gas_limit: u64,
303 pub gas_perf: f64,
304 pub eff_perf: f64,
305 pub bp: f64,
306 pub parent_offset: f64,
307 pub valid: bool,
308 pub merged: bool,
309 pub next: Option<NodeKey>,
310 pub prev: Option<NodeKey>,
311 pub sig_type: Option<SignatureType>,
312}
313
314impl MsgChainNode {
315 pub fn compare(&self, other: &Self) -> Ordering {
316 if approx_cmp(self.gas_perf, other.gas_perf) == Ordering::Greater
317 || approx_cmp(self.gas_perf, other.gas_perf) == Ordering::Equal
318 && self.gas_reward.cmp(&other.gas_reward) == Ordering::Greater
319 {
320 return Ordering::Greater;
321 }
322
323 Ordering::Less
324 }
325
326 pub fn set_eff_perf(&mut self, prev: Option<(f64, u64)>) {
327 let mut eff_perf = self.gas_perf * self.bp;
328 if let Some(prev) = prev
329 && eff_perf > 0.0
330 {
331 let prev_eff_perf = prev.0;
332 let prev_gas_limit = prev.1;
333 let eff_perf_with_parent = (eff_perf * self.gas_limit as f64
334 + prev_eff_perf * prev_gas_limit as f64)
335 / (self.gas_limit + prev_gas_limit) as f64;
336 self.parent_offset = eff_perf - eff_perf_with_parent;
337 eff_perf = eff_perf_with_parent;
338 }
339 self.eff_perf = eff_perf;
340 }
341}
342
343impl MsgChainNode {
344 pub(in crate::message_pool) fn cmp_effective(&self, other: &Self) -> Ordering {
345 match (self.merged, other.merged) {
350 (true, false) => return Ordering::Greater,
351 (false, true) => return Ordering::Less,
352 _ => {}
353 }
354
355 if self.gas_perf >= 0.0 && other.gas_perf < 0.0
356 || self.eff_perf > other.eff_perf
357 || (approx_cmp(self.eff_perf, other.eff_perf) == Ordering::Equal
358 && self.gas_perf > other.gas_perf)
359 || (approx_cmp(self.eff_perf, other.eff_perf) == Ordering::Equal
360 && approx_cmp(self.gas_perf, other.gas_perf) == Ordering::Equal
361 && self.gas_reward > other.gas_reward)
362 {
363 return Ordering::Greater;
364 }
365
366 Ordering::Less
367 }
368
369 pub fn set_null_effective_perf(&mut self) {
370 if self.gas_perf < 0.0 {
371 self.eff_perf = self.gas_perf;
372 } else {
373 self.eff_perf = 0.0;
374 }
375 }
376}
377
378impl std::default::Default for MsgChainNode {
379 fn default() -> Self {
380 Self {
381 msgs: vec![],
382 gas_reward: TokenAmount::zero(),
383 gas_limit: 0,
384 gas_perf: 0.0,
385 eff_perf: 0.0,
386 bp: 0.0,
387 parent_offset: 0.0,
388 valid: true,
389 merged: false,
390 next: None,
391 prev: None,
392 sig_type: None,
393 }
394 }
395}
396
397pub(in crate::message_pool) fn create_message_chains<T>(
398 api: &T,
399 actor: &Address,
400 mset: &HashMap<u64, SignedMessage>,
401 base_fee: &TokenAmount,
402 ts: &Tipset,
403 chains: &mut Chains,
404 chain_config: &ChainConfig,
405) -> Result<(), Error>
406where
407 T: Provider,
408{
409 let mut msgs: Vec<SignedMessage> = mset.values().cloned().collect();
411 msgs.sort_by_key(|v| v.sequence());
412
413 let Ok(actor_state) = api.get_actor_after(actor, ts) else {
422 tracing::warn!("failed to load actor state, not building chain for {actor}");
423 return Ok(());
424 };
425 let mut cur_seq = actor_state.sequence;
426 let mut balance: TokenAmount = TokenAmount::from(&actor_state.balance);
427
428 let mut gas_limit = 0;
429 let mut skip = 0;
430 let mut i = 0;
431 let mut rewards = Vec::with_capacity(msgs.len());
432
433 while let Some(m) = msgs.get(i) {
434 if m.sequence() < cur_seq {
435 warn!(
436 "encountered message from actor {} with nonce {} less than the current nonce {}",
437 actor,
438 m.sequence(),
439 cur_seq
440 );
441 skip += 1;
442 i += 1;
443 continue;
444 }
445
446 if m.sequence() != cur_seq {
447 break;
448 }
449 cur_seq += 1;
450
451 let network_version = chain_config.network_version(ts.epoch());
452
453 let min_gas = price_list_by_network_version(network_version)
454 .on_chain_message(m.chain_length()?)
455 .total();
456
457 if Gas::new(m.gas_limit()) < min_gas {
458 break;
459 }
460 gas_limit += m.gas_limit();
461 if gas_limit > crate::shim::econ::BLOCK_GAS_LIMIT {
462 break;
463 }
464
465 let required = m.required_funds();
466 if balance < required {
467 break;
468 }
469
470 balance -= required;
471 let value = m.value();
472 balance -= value;
473
474 let gas_reward = get_gas_reward(m, base_fee);
475 rewards.push(gas_reward);
476 i += 1;
477 }
478
479 let mut msgs = if i > skip {
481 #[allow(clippy::indexing_slicing)]
482 msgs[skip..i].to_vec()
483 } else {
484 return Ok(());
485 };
486
487 if msgs.len() > BLOCK_MESSAGE_LIMIT {
489 warn!(
490 "dropping {} messages from {actor} as they exceed the block message limit of {BLOCK_MESSAGE_LIMIT}",
491 msgs.len() - BLOCK_MESSAGE_LIMIT,
492 );
493 msgs.truncate(BLOCK_MESSAGE_LIMIT);
494 };
495
496 let mut cur_chain = MsgChainNode::default();
497 let mut node_vec = vec![];
498
499 let new_chain = |m: SignedMessage, reward: &TokenAmount| -> MsgChainNode {
500 let gl = m.gas_limit();
501 let sig_type = Some(m.signature().sig_type);
502 MsgChainNode {
503 msgs: vec![m],
504 gas_reward: reward.clone(),
505 gas_limit: gl,
506 gas_perf: get_gas_perf(reward, gl),
507 eff_perf: 0.0,
508 bp: 0.0,
509 parent_offset: 0.0,
510 valid: true,
511 merged: false,
512 prev: None,
513 next: None,
514 sig_type,
515 }
516 };
517
518 for (i, (m, reward)) in msgs.into_iter().zip(rewards.iter()).enumerate() {
521 if i == 0 {
522 cur_chain = new_chain(m, reward);
523 continue;
524 }
525
526 let gas_reward = cur_chain.gas_reward.clone() + reward;
527 let gas_limit = cur_chain.gas_limit + m.gas_limit();
528 let gas_perf = get_gas_perf(&gas_reward, gas_limit);
529
530 if gas_perf < cur_chain.gas_perf {
533 chains.push_with(cur_chain, &mut node_vec);
534 cur_chain = new_chain(m, reward);
535 } else {
536 cur_chain.msgs.push(m);
537 cur_chain.gas_reward = gas_reward;
538 cur_chain.gas_limit = gas_limit;
539 cur_chain.gas_perf = gas_perf;
540 }
541 }
542
543 chains.push_with(cur_chain, &mut node_vec);
544
545 loop {
547 let mut merged = 0;
548 for i in (1..node_vec.len()).rev() {
549 if chains.get_from(i, &node_vec).gas_perf >= chains.get_from(i - 1, &node_vec).gas_perf
550 {
551 let chain_i_msg = chains.get_from(i, &node_vec).msgs.clone();
553 chains
554 .get_mut_from(i - 1, &node_vec)
555 .msgs
556 .extend(chain_i_msg);
557
558 let chain_i_gas_reward = chains.get_from(i, &node_vec).gas_reward.clone();
560 chains.get_mut_from(i - 1, &node_vec).gas_reward += chain_i_gas_reward;
561
562 let chain_i_gas_limit = chains.get_from(i, &node_vec).gas_limit;
564 chains.get_mut_from(i - 1, &node_vec).gas_limit += chain_i_gas_limit;
565
566 let chain_i_gas_perf = get_gas_perf(
568 &chains.get_from(i - 1, &node_vec).gas_reward,
569 chains.get_from(i - 1, &node_vec).gas_limit,
570 );
571 chains.get_mut_from(i - 1, &node_vec).gas_perf = chain_i_gas_perf;
572 chains.get_mut_from(i, &node_vec).valid = false;
574 merged += 1;
575 }
576 }
577
578 if merged == 0 {
579 break;
580 }
581
582 chains.drop_invalid(&mut node_vec);
583 }
584
585 if node_vec.len() > 1 {
586 for (&k1, &k2) in node_vec.iter().zip(node_vec.iter().skip(1)) {
587 let n1 = chains
589 .get_mut(k1)
590 .ok_or_else(|| Error::Other(format!("{k1:?} should present in `chains`")))?;
591 n1.next = Some(k2);
592 let n2 = chains
596 .get_mut(k2)
597 .ok_or_else(|| Error::Other(format!("{k2:?} should present in `chains`")))?;
598 n2.prev = Some(k1);
599 }
601 }
602
603 chains.key_vec.extend(node_vec);
605
606 Ok(())
607}
608
609fn approx_cmp(a: f64, b: f64) -> Ordering {
610 if (a - b).abs() <= (a * f64::EPSILON).abs() {
611 Ordering::Equal
612 } else {
613 a.partial_cmp(&b).unwrap()
614 }
615}