1use std::{collections::BTreeMap, sync::Arc};
4
5use consensus_config::Epoch;
6use consensus_types::block::{
7 BlockRef, NUM_RESERVED_TRANSACTION_INDICES, PING_TRANSACTION_INDEX, Round, TransactionIndex,
8};
9use mysten_common::debug_fatal;
10use mysten_metrics::monitored_mpsc::{Receiver, Sender, channel};
11use parking_lot::Mutex;
12use tap::TapFallible;
13use thiserror::Error;
14use tokio::sync::oneshot;
15use tracing::{error, warn};
16
17use crate::{block::Transaction, context::Context};
18
19const MAX_PENDING_TRANSACTIONS: usize = 2_000;
21
22#[derive(Debug, Clone, Copy, Eq, PartialEq)]
26pub enum Priority {
27 Normal,
28 High,
29}
30
31const MAX_PENDING_PRIORITY_TRANSACTIONS: usize = 128;
34
35pub(crate) struct TransactionsGuard {
40 transactions: Vec<Transaction>,
43
44 included_in_block_ack: oneshot::Sender<(
47 BlockRef,
49 Vec<TransactionIndex>,
51 oneshot::Receiver<BlockStatus>,
53 )>,
54}
55
56pub(crate) struct TransactionConsumer {
60 tx_receiver: Receiver<TransactionsGuard>,
61 priority_tx_receiver: Receiver<TransactionsGuard>,
64 max_transactions_in_block_bytes: u64,
65 max_num_transactions_in_block: u64,
66 pending_transactions: Option<TransactionsGuard>,
67 block_status_subscribers: Arc<Mutex<BTreeMap<BlockRef, Vec<oneshot::Sender<BlockStatus>>>>>,
68}
69
70#[derive(Debug, Clone, Eq, PartialEq)]
71pub enum BlockStatus {
72 Sequenced(BlockRef),
75 GarbageCollected(BlockRef),
78}
79
80#[derive(Debug, Clone, Eq, PartialEq)]
81pub enum LimitReached {
82 MaxNumOfTransactions,
84 MaxBytes,
86 AllTransactionsIncluded,
88}
89
90impl TransactionConsumer {
91 pub(crate) fn new(
92 tx_receiver: Receiver<TransactionsGuard>,
93 priority_tx_receiver: Receiver<TransactionsGuard>,
94 context: Arc<Context>,
95 ) -> Self {
96 assert!(
101 context
102 .protocol_config
103 .max_num_transactions_in_block()
104 .saturating_sub(1)
105 < TransactionIndex::MAX.saturating_sub(NUM_RESERVED_TRANSACTION_INDICES) as u64,
106 "Unsupported max_num_transactions_in_block: {}",
107 context.protocol_config.max_num_transactions_in_block()
108 );
109
110 Self {
111 tx_receiver,
112 priority_tx_receiver,
113 max_transactions_in_block_bytes: context
114 .protocol_config
115 .max_transactions_in_block_bytes(),
116 max_num_transactions_in_block: context.protocol_config.max_num_transactions_in_block(),
117 pending_transactions: None,
118 block_status_subscribers: Arc::new(Mutex::new(BTreeMap::new())),
119 }
120 }
121
122 pub(crate) fn next(
127 &mut self,
128 ) -> (
129 Vec<Transaction>,
130 Box<dyn FnOnce(BlockRef) + Send>,
131 LimitReached,
132 ) {
133 let mut transactions = Vec::new();
134 let mut acks = Vec::new();
135 let mut total_bytes = 0;
136 let mut limit_reached = LimitReached::AllTransactionsIncluded;
137
138 let mut handle_txs = |t: TransactionsGuard| -> Option<TransactionsGuard> {
142 let transactions_num = t.transactions.len() as u64;
145 if transactions_num == 0 {
146 acks.push((t.included_in_block_ack, vec![PING_TRANSACTION_INDEX]));
147 return None;
148 }
149
150 let transactions_bytes =
152 t.transactions.iter().map(|t| t.data().len()).sum::<usize>() as u64;
153 if total_bytes + transactions_bytes > self.max_transactions_in_block_bytes {
154 limit_reached = LimitReached::MaxBytes;
155 return Some(t);
156 }
157 if transactions.len() as u64 + transactions_num > self.max_num_transactions_in_block {
158 limit_reached = LimitReached::MaxNumOfTransactions;
159 return Some(t);
160 }
161
162 total_bytes += transactions_bytes;
163
164 let start_idx = transactions.len() as TransactionIndex;
166 let indices: Vec<TransactionIndex> =
167 (start_idx..start_idx + t.transactions.len() as TransactionIndex).collect();
168
169 acks.push((t.included_in_block_ack, indices));
172 transactions.extend(t.transactions);
173 None
174 };
175
176 if let Some(t) = self.pending_transactions.take()
177 && let Some(pending_transactions) = handle_txs(t)
178 {
179 debug_fatal!(
180 "Previously pending transaction(s) should fit into an empty block! Dropping: {:?}",
181 pending_transactions.transactions
182 );
183 }
184
185 for receiver in [&mut self.priority_tx_receiver, &mut self.tx_receiver] {
189 while self.pending_transactions.is_none() {
190 if let Ok(t) = receiver.try_recv() {
191 self.pending_transactions = handle_txs(t);
192 } else {
193 break;
194 }
195 }
196 }
197
198 let block_status_subscribers = self.block_status_subscribers.clone();
199 (
200 transactions,
201 Box::new(move |block_ref: BlockRef| {
202 let mut block_status_subscribers = block_status_subscribers.lock();
203
204 for (ack, tx_indices) in acks {
205 let (status_tx, status_rx) = oneshot::channel();
206
207 block_status_subscribers
208 .entry(block_ref)
209 .or_default()
210 .push(status_tx);
211
212 let _ = ack.send((block_ref, tx_indices, status_rx));
213 }
214 }),
215 limit_reached,
216 )
217 }
218
219 pub(crate) fn notify_own_blocks_status(
223 &self,
224 committed_blocks: Vec<BlockRef>,
225 gc_round: Round,
226 ) {
227 let mut block_status_subscribers = self.block_status_subscribers.lock();
229 for block_ref in committed_blocks {
230 if let Some(subscribers) = block_status_subscribers.remove(&block_ref) {
231 subscribers.into_iter().for_each(|s| {
232 let _ = s.send(BlockStatus::Sequenced(block_ref));
233 });
234 }
235 }
236
237 while let Some((block_ref, subscribers)) = block_status_subscribers.pop_first() {
239 if block_ref.round <= gc_round {
240 subscribers.into_iter().for_each(|s| {
241 let _ = s.send(BlockStatus::GarbageCollected(block_ref));
242 });
243 } else {
244 block_status_subscribers.insert(block_ref, subscribers);
245 break;
246 }
247 }
248 }
249
250 #[cfg(test)]
251 pub(crate) fn subscribe_for_block_status_testing(
252 &self,
253 block_ref: BlockRef,
254 ) -> oneshot::Receiver<BlockStatus> {
255 let (tx, rx) = oneshot::channel();
256 let mut block_status_subscribers = self.block_status_subscribers.lock();
257 block_status_subscribers
258 .entry(block_ref)
259 .or_default()
260 .push(tx);
261 rx
262 }
263
264 #[cfg(test)]
265 fn is_empty(&mut self) -> bool {
266 if self.pending_transactions.is_some() {
267 return false;
268 }
269 for receiver in [&mut self.priority_tx_receiver, &mut self.tx_receiver] {
270 if let Ok(t) = receiver.try_recv() {
271 self.pending_transactions = Some(t);
272 return false;
273 }
274 }
275 true
276 }
277}
278
279#[derive(Clone)]
280pub struct TransactionClient {
281 context: Arc<Context>,
282 sender: Sender<TransactionsGuard>,
283 priority_sender: Sender<TransactionsGuard>,
285 max_transaction_size: u64,
286 max_transactions_in_block_bytes: u64,
287 max_transactions_in_block_count: u64,
288}
289
290#[derive(Debug, Error)]
291pub enum ClientError {
292 #[error("Failed to submit transaction, consensus is shutting down: {0}")]
293 ConsensusShuttingDown(String),
294
295 #[error("Transaction size ({0}B) is over limit ({1}B)")]
296 OversizedTransaction(u64, u64),
297
298 #[error("Transaction bundle size ({0}B) is over limit ({1}B)")]
299 OversizedTransactionBundleBytes(u64, u64),
300
301 #[error("Transaction bundle count ({0}) is over limit ({1})")]
302 OversizedTransactionBundleCount(u64, u64),
303}
304
305impl TransactionClient {
306 pub(crate) fn new(
308 context: Arc<Context>,
309 ) -> (
310 Self,
311 Receiver<TransactionsGuard>,
312 Receiver<TransactionsGuard>,
313 ) {
314 Self::new_with_max_pending_transactions(
315 context,
316 MAX_PENDING_TRANSACTIONS,
317 MAX_PENDING_PRIORITY_TRANSACTIONS,
318 )
319 }
320
321 fn new_with_max_pending_transactions(
323 context: Arc<Context>,
324 max_pending_transactions: usize,
325 max_pending_priority_transactions: usize,
326 ) -> (
327 Self,
328 Receiver<TransactionsGuard>,
329 Receiver<TransactionsGuard>,
330 ) {
331 let (sender, receiver) = channel("consensus_input", max_pending_transactions);
332 let (priority_sender, priority_receiver) = channel(
333 "consensus_input_priority",
334 max_pending_priority_transactions,
335 );
336 (
337 Self {
338 sender,
339 priority_sender,
340 max_transaction_size: context.protocol_config.max_transaction_size_bytes(),
341
342 max_transactions_in_block_bytes: context
343 .protocol_config
344 .max_transactions_in_block_bytes(),
345 max_transactions_in_block_count: context
346 .protocol_config
347 .max_num_transactions_in_block(),
348 context: context.clone(),
349 },
350 receiver,
351 priority_receiver,
352 )
353 }
354
355 pub fn epoch(&self) -> Epoch {
357 self.context.committee.epoch()
358 }
359
360 pub async fn submit(
367 &self,
368 transactions: Vec<Vec<u8>>,
369 priority: Priority,
370 ) -> Result<
371 (
372 BlockRef,
373 Vec<TransactionIndex>,
374 oneshot::Receiver<BlockStatus>,
375 ),
376 ClientError,
377 > {
378 let included_in_block = self.submit_no_wait(transactions, priority).await?;
379 included_in_block
380 .await
381 .tap_err(|e| warn!("Transaction acknowledge failed with {:?}", e))
382 .map_err(|e| ClientError::ConsensusShuttingDown(e.to_string()))
383 }
384
385 pub(crate) async fn submit_no_wait(
395 &self,
396 transactions: Vec<Vec<u8>>,
397 priority: Priority,
398 ) -> Result<
399 oneshot::Receiver<(
400 BlockRef,
401 Vec<TransactionIndex>,
402 oneshot::Receiver<BlockStatus>,
403 )>,
404 ClientError,
405 > {
406 let (included_in_block_ack_send, included_in_block_ack_receive) = oneshot::channel();
407
408 let mut bundle_size = 0;
409
410 if transactions.len() as u64 > self.max_transactions_in_block_count {
411 return Err(ClientError::OversizedTransactionBundleCount(
412 transactions.len() as u64,
413 self.max_transactions_in_block_count,
414 ));
415 }
416
417 for transaction in &transactions {
418 if transaction.len() as u64 > self.max_transaction_size {
419 return Err(ClientError::OversizedTransaction(
420 transaction.len() as u64,
421 self.max_transaction_size,
422 ));
423 }
424 bundle_size += transaction.len() as u64;
425
426 if bundle_size > self.max_transactions_in_block_bytes {
427 return Err(ClientError::OversizedTransactionBundleBytes(
428 bundle_size,
429 self.max_transactions_in_block_bytes,
430 ));
431 }
432 }
433
434 let t = TransactionsGuard {
435 transactions: transactions.into_iter().map(Transaction::new).collect(),
436 included_in_block_ack: included_in_block_ack_send,
437 };
438 let sender = match priority {
439 Priority::High => &self.priority_sender,
440 Priority::Normal => &self.sender,
441 };
442 if priority == Priority::High && sender.capacity() == 0 {
445 self.context
446 .metrics
447 .node_metrics
448 .priority_submission_backpressure
449 .inc();
450 }
451 sender
452 .send(t)
453 .await
454 .tap_err(|e| error!("Submit transactions failed with {:?}", e))
455 .map_err(|e| ClientError::ConsensusShuttingDown(e.to_string()))?;
456 Ok(included_in_block_ack_receive)
457 }
458}
459
460pub trait TransactionPool: Send + Sync + 'static {
464 fn take(
471 &self,
472 max_count: usize,
473 max_bytes: usize,
474 ) -> (
475 Vec<Transaction>,
476 Box<dyn FnOnce(BlockRef) + Send>,
477 LimitReached,
478 );
479
480 fn notify_committed(&self, own_committed_blocks: Vec<BlockRef>, gc_round: Round);
484}
485
486pub(crate) struct TransactionConsumerPool {
489 consumer: Mutex<TransactionConsumer>,
490}
491
492impl TransactionConsumerPool {
493 pub(crate) fn new(consumer: TransactionConsumer) -> Self {
494 Self {
495 consumer: Mutex::new(consumer),
496 }
497 }
498
499 #[cfg(test)]
500 pub(crate) fn subscribe_for_block_status_testing(
501 &self,
502 block_ref: BlockRef,
503 ) -> oneshot::Receiver<BlockStatus> {
504 self.consumer
505 .lock()
506 .subscribe_for_block_status_testing(block_ref)
507 }
508}
509
510impl TransactionPool for TransactionConsumerPool {
511 fn take(
513 &self,
514 _max_count: usize,
515 _max_bytes: usize,
516 ) -> (
517 Vec<Transaction>,
518 Box<dyn FnOnce(BlockRef) + Send>,
519 LimitReached,
520 ) {
521 self.consumer.lock().next()
522 }
523
524 fn notify_committed(&self, own_committed_blocks: Vec<BlockRef>, gc_round: Round) {
525 self.consumer
526 .lock()
527 .notify_own_blocks_status(own_committed_blocks, gc_round);
528 }
529}
530
531pub trait TransactionVerifier: Send + Sync + 'static {
534 fn verify_batch(&self, batch: &[&[u8]]) -> Result<(), ValidationError>;
537
538 fn verify_and_vote_batch(
547 &self,
548 block_ref: &BlockRef,
549 batch: &[&[u8]],
550 ) -> Result<Vec<TransactionIndex>, ValidationError>;
551}
552
553#[derive(Debug, Error)]
554pub enum ValidationError {
555 #[error("Invalid transaction: {0}")]
556 InvalidTransaction(String),
557}
558
559#[cfg(any(test, msim))]
561pub struct NoopTransactionVerifier;
562
563#[cfg(any(test, msim))]
564impl TransactionVerifier for NoopTransactionVerifier {
565 fn verify_batch(&self, _batch: &[&[u8]]) -> Result<(), ValidationError> {
566 Ok(())
567 }
568
569 fn verify_and_vote_batch(
570 &self,
571 _block_ref: &BlockRef,
572 _batch: &[&[u8]],
573 ) -> Result<Vec<TransactionIndex>, ValidationError> {
574 Ok(vec![])
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use std::{sync::Arc, time::Duration};
581
582 use consensus_config::AuthorityIndex;
583 use consensus_types::block::{
584 BlockDigest, BlockRef, NUM_RESERVED_TRANSACTION_INDICES, PING_TRANSACTION_INDEX,
585 TransactionIndex,
586 };
587 use futures::{StreamExt, stream::FuturesUnordered};
588 use tokio::time::timeout;
589
590 use crate::transaction::NoopTransactionVerifier;
591 use crate::{
592 block_verifier::SignedBlockVerifier,
593 context::Context,
594 transaction::{
595 BlockStatus, LimitReached, MAX_PENDING_PRIORITY_TRANSACTIONS, Priority,
596 TransactionClient, TransactionConsumer,
597 },
598 };
599
600 #[tokio::test(flavor = "current_thread", start_paused = true)]
601 async fn basic_submit_and_consume() {
602 let (mut context, _) = Context::new_for_test(4);
603 context
604 .protocol_config
605 .set_max_transaction_size_bytes_for_testing(2_000); context
607 .protocol_config
608 .set_max_transactions_in_block_bytes_for_testing(2_000);
609 let context = Arc::new(context);
610 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
611 let mut consumer =
612 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
613
614 let mut included_in_block_waiters = FuturesUnordered::new();
616 for i in 0..3 {
617 let transaction =
618 bcs::to_bytes(&format!("transaction {i}")).expect("Serialization should not fail.");
619 let w = client
620 .submit_no_wait(vec![transaction], Priority::Normal)
621 .await
622 .expect("Shouldn't submit successfully transaction");
623 included_in_block_waiters.push(w);
624 }
625
626 let (transactions, ack_transactions, _limit_reached) = consumer.next();
628 assert_eq!(transactions.len(), 3);
629
630 for (i, t) in transactions.iter().enumerate() {
631 let t: String = bcs::from_bytes(t.data()).unwrap();
632 assert_eq!(format!("transaction {i}").to_string(), t);
633 }
634
635 assert!(
636 timeout(Duration::from_secs(1), included_in_block_waiters.next())
637 .await
638 .is_err(),
639 "We should expect to timeout as none of the transactions have been acknowledged yet"
640 );
641
642 ack_transactions(BlockRef::MIN);
644
645 while let Some(result) = included_in_block_waiters.next().await {
647 assert!(result.is_ok());
648 }
649
650 assert!(consumer.is_empty());
652 }
653
654 #[tokio::test(flavor = "current_thread", start_paused = true)]
655 async fn high_priority_transactions_included_first() {
656 let (mut context, _) = Context::new_for_test(4);
657 context
658 .protocol_config
659 .set_max_transaction_size_bytes_for_testing(2_000);
660 context
661 .protocol_config
662 .set_max_transactions_in_block_bytes_for_testing(2_000);
663 let context = Arc::new(context);
664 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
665 let mut consumer =
666 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
667
668 for i in 0..3 {
670 let t = bcs::to_bytes(&format!("normal {i}")).unwrap();
671 client
672 .submit_no_wait(vec![t], Priority::Normal)
673 .await
674 .unwrap();
675 }
676 let high = bcs::to_bytes(&"high".to_string()).unwrap();
677 client
678 .submit_no_wait(vec![high], Priority::High)
679 .await
680 .unwrap();
681
682 let (transactions, _ack, _limit) = consumer.next();
684 let decoded: Vec<String> = transactions
685 .iter()
686 .map(|t| bcs::from_bytes(t.data()).unwrap())
687 .collect();
688 assert_eq!(decoded, vec!["high", "normal 0", "normal 1", "normal 2"]);
689 }
690
691 #[tokio::test(flavor = "current_thread", start_paused = true)]
692 async fn high_priority_bypasses_full_normal_lane() {
693 let (mut context, _) = Context::new_for_test(4);
694 context
695 .protocol_config
696 .set_max_transaction_size_bytes_for_testing(2_000);
697 context
698 .protocol_config
699 .set_max_transactions_in_block_bytes_for_testing(2_000);
700 let context = Arc::new(context);
701 let (client, _tx_receiver, _priority_tx_receiver) =
704 TransactionClient::new_with_max_pending_transactions(context.clone(), 1, 1);
705
706 let n0 = bcs::to_bytes(&"n0".to_string()).unwrap();
708 client
709 .submit_no_wait(vec![n0], Priority::Normal)
710 .await
711 .unwrap();
712
713 let n1 = bcs::to_bytes(&"n1".to_string()).unwrap();
715 assert!(
716 timeout(
717 Duration::from_millis(100),
718 client.submit_no_wait(vec![n1], Priority::Normal)
719 )
720 .await
721 .is_err(),
722 "normal lane is full, so this submission must block"
723 );
724
725 let h = bcs::to_bytes(&"h".to_string()).unwrap();
727 timeout(
728 Duration::from_millis(100),
729 client.submit_no_wait(vec![h], Priority::High),
730 )
731 .await
732 .expect("high-priority submission must not block on a full normal lane")
733 .expect("high-priority submission should succeed");
734 }
735
736 #[tokio::test(flavor = "current_thread", start_paused = true)]
737 async fn priority_overflow_held_and_drained_before_normal() {
738 let (mut context, _) = Context::new_for_test(4);
739 context
740 .protocol_config
741 .set_max_transaction_size_bytes_for_testing(2_000);
742 context
743 .protocol_config
744 .set_max_transactions_in_block_bytes_for_testing(2_000);
745 context
747 .protocol_config
748 .set_max_num_transactions_in_block_for_testing(2);
749 let context = Arc::new(context);
750 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
751 let mut consumer =
752 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
753
754 for i in 0..3 {
755 let t = bcs::to_bytes(&format!("p{i}")).unwrap();
756 client
757 .submit_no_wait(vec![t], Priority::High)
758 .await
759 .unwrap();
760 }
761 let n = bcs::to_bytes(&"n0".to_string()).unwrap();
762 client
763 .submit_no_wait(vec![n], Priority::Normal)
764 .await
765 .unwrap();
766
767 let (txs, ack, limit) = consumer.next();
769 let got: Vec<String> = txs
770 .iter()
771 .map(|t| bcs::from_bytes(t.data()).unwrap())
772 .collect();
773 assert_eq!(got, vec!["p0", "p1"]);
774 assert_eq!(limit, LimitReached::MaxNumOfTransactions);
775 ack(BlockRef::MIN);
776
777 let (txs, ack, _) = consumer.next();
779 let got: Vec<String> = txs
780 .iter()
781 .map(|t| bcs::from_bytes(t.data()).unwrap())
782 .collect();
783 assert_eq!(got, vec!["p2", "n0"]);
784 ack(BlockRef::MIN);
785 assert!(consumer.is_empty());
786 }
787
788 #[tokio::test(flavor = "current_thread", start_paused = true)]
789 async fn priority_overflow_by_bytes_held_and_re_offered() {
790 let (mut context, _) = Context::new_for_test(4);
791 context
792 .protocol_config
793 .set_max_transaction_size_bytes_for_testing(2_000);
794 context
796 .protocol_config
797 .set_max_transactions_in_block_bytes_for_testing(15);
798 let context = Arc::new(context);
799 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
800 let mut consumer =
801 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
802
803 for p in ["AAAAAAAAAA", "BBBBBBBBBB"] {
804 let t = bcs::to_bytes(&p.to_string()).unwrap();
805 assert_eq!(t.len(), 11);
806 client
807 .submit_no_wait(vec![t], Priority::High)
808 .await
809 .unwrap();
810 }
811
812 let (txs, ack, limit) = consumer.next();
814 let got: Vec<String> = txs
815 .iter()
816 .map(|t| bcs::from_bytes(t.data()).unwrap())
817 .collect();
818 assert_eq!(got, vec!["AAAAAAAAAA"]);
819 assert_eq!(limit, LimitReached::MaxBytes);
820 ack(BlockRef::MIN);
821
822 let (txs, ack, _) = consumer.next();
824 let got: Vec<String> = txs
825 .iter()
826 .map(|t| bcs::from_bytes(t.data()).unwrap())
827 .collect();
828 assert_eq!(got, vec!["BBBBBBBBBB"]);
829 ack(BlockRef::MIN);
830 assert!(consumer.is_empty());
831 }
832
833 #[tokio::test(flavor = "current_thread", start_paused = true)]
834 async fn interleaved_submissions_ordered_priority_first_then_fifo() {
835 let (mut context, _) = Context::new_for_test(4);
836 context
837 .protocol_config
838 .set_max_transaction_size_bytes_for_testing(2_000);
839 context
840 .protocol_config
841 .set_max_transactions_in_block_bytes_for_testing(2_000);
842 let context = Arc::new(context);
843 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
844 let mut consumer =
845 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
846
847 for (payload, priority) in [
849 ("n0", Priority::Normal),
850 ("p0", Priority::High),
851 ("n1", Priority::Normal),
852 ("p1", Priority::High),
853 ] {
854 let t = bcs::to_bytes(&payload.to_string()).unwrap();
855 client.submit_no_wait(vec![t], priority).await.unwrap();
856 }
857
858 let (txs, _ack, _) = consumer.next();
860 let got: Vec<String> = txs
861 .iter()
862 .map(|t| bcs::from_bytes(t.data()).unwrap())
863 .collect();
864 assert_eq!(got, vec!["p0", "p1", "n0", "n1"]);
865 }
866
867 #[tokio::test(flavor = "current_thread", start_paused = true)]
868 async fn block_status_update() {
869 let (mut context, _) = Context::new_for_test(4);
870 context
871 .protocol_config
872 .set_max_transaction_size_bytes_for_testing(2_000); context
874 .protocol_config
875 .set_max_transactions_in_block_bytes_for_testing(2_000);
876 context.protocol_config.set_gc_depth_for_testing(10);
877 let context = Arc::new(context);
878 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
879 let mut consumer =
880 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
881
882 let mut included_in_block_waiters = FuturesUnordered::new();
884 for i in 1..=10 {
885 let transaction =
886 bcs::to_bytes(&format!("transaction {i}")).expect("Serialization should not fail.");
887 let w = client
888 .submit_no_wait(vec![transaction], Priority::Normal)
889 .await
890 .expect("Shouldn't submit successfully transaction");
891 included_in_block_waiters.push(w);
892
893 if i % 2 == 0 {
895 let (transactions, ack_transactions, _limit_reached) = consumer.next();
896 assert_eq!(transactions.len(), 2);
897 ack_transactions(BlockRef::new(
898 i,
899 AuthorityIndex::new_for_test(0),
900 BlockDigest::MIN,
901 ));
902 }
903 }
904
905 let mut transaction_count = 0;
906 let mut block_status_waiters = Vec::new();
908 while let Some(result) = included_in_block_waiters.next().await {
909 let (block_ref, tx_indices, block_status_waiter) =
910 result.expect("Block inclusion waiter shouldn't fail");
911 assert_eq!(tx_indices.len(), 1);
913 assert_eq!(tx_indices[0], transaction_count % 2);
917 transaction_count += 1;
918
919 block_status_waiters.push((block_ref, block_status_waiter));
920 }
921
922 let gc_round = 5;
924 consumer.notify_own_blocks_status(
925 vec![
926 BlockRef::new(6, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
927 BlockRef::new(8, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
928 BlockRef::new(10, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
929 ],
930 gc_round,
931 );
932
933 for (block_ref, waiter) in block_status_waiters {
935 let block_status = waiter.await.expect("Block status waiter shouldn't fail");
936
937 if block_ref.round <= gc_round {
938 assert!(matches!(block_status, BlockStatus::GarbageCollected(_)))
939 } else {
940 assert!(matches!(block_status, BlockStatus::Sequenced(_)));
941 }
942 }
943
944 assert!(consumer.block_status_subscribers.lock().is_empty());
946 }
947
948 #[tokio::test]
949 async fn submit_over_max_fetch_size_and_consume() {
950 let (mut context, _) = Context::new_for_test(4);
951 context
952 .protocol_config
953 .set_max_transaction_size_bytes_for_testing(100);
954 context
955 .protocol_config
956 .set_max_transactions_in_block_bytes_for_testing(100);
957 let context = Arc::new(context);
958 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
959 let mut consumer =
960 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
961
962 for i in 0..10 {
964 let transaction =
965 bcs::to_bytes(&format!("transaction {i}")).expect("Serialization should not fail.");
966 let _w = client
967 .submit_no_wait(vec![transaction], Priority::Normal)
968 .await
969 .expect("Shouldn't submit successfully transaction");
970 }
971
972 let mut all_transactions = Vec::new();
974 let (transactions, _ack_transactions, _limit_reached) = consumer.next();
975 assert_eq!(transactions.len(), 7);
976
977 let total_size: u64 = transactions.iter().map(|t| t.data().len() as u64).sum();
979 assert!(
980 total_size <= context.protocol_config.max_transactions_in_block_bytes(),
981 "Should have fetched transactions up to {}",
982 context.protocol_config.max_transactions_in_block_bytes()
983 );
984 all_transactions.extend(transactions);
985
986 let (transactions, _ack_transactions, _limit_reached) = consumer.next();
988 assert_eq!(transactions.len(), 3);
989
990 let total_size: u64 = transactions.iter().map(|t| t.data().len() as u64).sum();
992 assert!(
993 total_size <= context.protocol_config.max_transactions_in_block_bytes(),
994 "Should have fetched transactions up to {}",
995 context.protocol_config.max_transactions_in_block_bytes()
996 );
997 all_transactions.extend(transactions);
998
999 assert!(consumer.is_empty());
1001
1002 for (i, t) in all_transactions.iter().enumerate() {
1003 let t: String = bcs::from_bytes(t.data()).unwrap();
1004 assert_eq!(format!("transaction {i}").to_string(), t);
1005 }
1006 }
1007
1008 #[tokio::test]
1009 async fn submit_large_batch_and_ack() {
1010 let (mut context, _) = Context::new_for_test(4);
1011 context
1012 .protocol_config
1013 .set_max_transaction_size_bytes_for_testing(15);
1014 context
1015 .protocol_config
1016 .set_max_transactions_in_block_bytes_for_testing(200);
1017 let context = Arc::new(context);
1018 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
1019 let mut consumer =
1020 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
1021 let mut all_receivers = Vec::new();
1022 for i in 0..10 {
1024 let transaction =
1025 bcs::to_bytes(&format!("transaction {i}")).expect("Serialization should not fail.");
1026 let w = client
1027 .submit_no_wait(vec![transaction], Priority::Normal)
1028 .await
1029 .expect("Should submit successfully transaction");
1030 all_receivers.push(w);
1031 }
1032
1033 {
1035 let transactions: Vec<_> = (10..15)
1036 .map(|i| {
1037 bcs::to_bytes(&format!("transaction {i}"))
1038 .expect("Serialization should not fail.")
1039 })
1040 .collect();
1041 let w = client
1042 .submit_no_wait(transactions, Priority::Normal)
1043 .await
1044 .expect("Should submit successfully transaction");
1045 all_receivers.push(w);
1046 }
1047
1048 {
1050 let i = 15;
1051 let transaction =
1052 bcs::to_bytes(&format!("transaction {i}")).expect("Serialization should not fail.");
1053 let w = client
1054 .submit_no_wait(vec![transaction], Priority::Normal)
1055 .await
1056 .expect("Shouldn't submit successfully transaction");
1057 all_receivers.push(w);
1058 }
1059
1060 {
1062 let transactions: Vec<_> = (16..32)
1063 .map(|i| {
1064 bcs::to_bytes(&format!("transaction {i}"))
1065 .expect("Serialization should not fail.")
1066 })
1067 .collect();
1068 let result = client
1069 .submit_no_wait(transactions, Priority::Normal)
1070 .await
1071 .unwrap_err();
1072 assert_eq!(
1073 result.to_string(),
1074 "Transaction bundle size (210B) is over limit (200B)"
1075 );
1076 }
1077
1078 let mut all_acks: Vec<Box<dyn FnOnce(BlockRef)>> = Vec::new();
1081 let mut batch_index = 0;
1082 while !consumer.is_empty() {
1083 let (transactions, ack_transactions, _limit_reached) = consumer.next();
1084
1085 assert!(
1086 transactions.len() as u64
1087 <= context.protocol_config.max_num_transactions_in_block(),
1088 "Should have fetched transactions up to {}",
1089 context.protocol_config.max_num_transactions_in_block()
1090 );
1091
1092 let total_size: u64 = transactions.iter().map(|t| t.data().len() as u64).sum();
1093 assert!(
1094 total_size <= context.protocol_config.max_transactions_in_block_bytes(),
1095 "Should have fetched transactions up to {}",
1096 context.protocol_config.max_transactions_in_block_bytes()
1097 );
1098
1099 if batch_index == 0 {
1101 assert_eq!(transactions.len(), 10);
1102 for (i, transaction) in transactions.iter().enumerate() {
1103 let t: String = bcs::from_bytes(transaction.data()).unwrap();
1104 assert_eq!(format!("transaction {}", i).to_string(), t);
1105 }
1106 } else if batch_index == 1 {
1108 assert_eq!(transactions.len(), 6);
1109 for (i, transaction) in transactions.iter().enumerate() {
1110 let t: String = bcs::from_bytes(transaction.data()).unwrap();
1111 assert_eq!(format!("transaction {}", i + 10).to_string(), t);
1112 }
1113 } else {
1114 panic!("Unexpected batch index");
1115 }
1116
1117 batch_index += 1;
1118
1119 all_acks.push(ack_transactions);
1120 }
1121
1122 for ack in all_acks {
1124 ack(BlockRef::MIN);
1125 }
1126
1127 for w in all_receivers {
1129 let r = w.await;
1130 assert!(r.is_ok());
1131 }
1132 }
1133
1134 #[tokio::test]
1135 async fn test_submit_over_max_block_size_and_validate_block_size() {
1136 {
1138 let (mut context, _) = Context::new_for_test(4);
1139 context
1140 .protocol_config
1141 .set_max_transaction_size_bytes_for_testing(100);
1142 context
1143 .protocol_config
1144 .set_max_num_transactions_in_block_for_testing(10);
1145 context
1146 .protocol_config
1147 .set_max_transactions_in_block_bytes_for_testing(300);
1148 let context = Arc::new(context);
1149 let (client, tx_receiver, priority_tx_receiver) =
1150 TransactionClient::new(context.clone());
1151 let mut consumer =
1152 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
1153 let mut all_receivers = Vec::new();
1154
1155 let max_num_transactions_in_block =
1157 context.protocol_config.max_num_transactions_in_block();
1158 for i in 0..2 * max_num_transactions_in_block {
1159 let transaction = bcs::to_bytes(&format!("transaction {i}"))
1160 .expect("Serialization should not fail.");
1161 let w = client
1162 .submit_no_wait(vec![transaction], Priority::Normal)
1163 .await
1164 .expect("Should submit successfully transaction");
1165 all_receivers.push(w);
1166 }
1167
1168 let (transactions, _ack_transactions, limit) = consumer.next();
1170 assert_eq!(limit, LimitReached::MaxNumOfTransactions);
1171 assert_eq!(transactions.len() as u64, max_num_transactions_in_block);
1172
1173 let block_verifier =
1175 SignedBlockVerifier::new(context.clone(), Arc::new(NoopTransactionVerifier {}));
1176
1177 let batch: Vec<_> = transactions.iter().map(|t| t.data()).collect();
1178 assert!(
1179 block_verifier.check_transactions(&batch).is_ok(),
1180 "Number of transactions limit verification failed"
1181 );
1182 }
1183
1184 {
1186 let (mut context, _) = Context::new_for_test(4);
1187 context
1188 .protocol_config
1189 .set_max_transaction_size_bytes_for_testing(100);
1190 context
1191 .protocol_config
1192 .set_max_num_transactions_in_block_for_testing(1_000);
1193 context
1194 .protocol_config
1195 .set_max_transactions_in_block_bytes_for_testing(300);
1196 let context = Arc::new(context);
1197 let (client, tx_receiver, priority_tx_receiver) =
1198 TransactionClient::new(context.clone());
1199 let mut consumer =
1200 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
1201 let mut all_receivers = Vec::new();
1202
1203 let max_transactions_in_block_bytes =
1204 context.protocol_config.max_transactions_in_block_bytes();
1205 let mut total_size = 0;
1206 loop {
1207 let transaction = bcs::to_bytes(&"transaction".to_string())
1208 .expect("Serialization should not fail.");
1209 total_size += transaction.len() as u64;
1210 let w = client
1211 .submit_no_wait(vec![transaction], Priority::Normal)
1212 .await
1213 .expect("Should submit successfully transaction");
1214 all_receivers.push(w);
1215
1216 if total_size >= 2 * max_transactions_in_block_bytes {
1218 break;
1219 }
1220 }
1221
1222 let (transactions, _ack_transactions, limit) = consumer.next();
1224 let batch: Vec<_> = transactions.iter().map(|t| t.data()).collect();
1225 let size = batch.iter().map(|t| t.len() as u64).sum::<u64>();
1226
1227 assert_eq!(limit, LimitReached::MaxBytes);
1228 assert!(
1229 batch.len() < context.protocol_config.max_num_transactions_in_block() as usize,
1230 "Should have submitted less than the max number of transactions in a block"
1231 );
1232 assert!(size <= max_transactions_in_block_bytes);
1233
1234 let block_verifier =
1236 SignedBlockVerifier::new(context.clone(), Arc::new(NoopTransactionVerifier {}));
1237
1238 assert!(
1239 block_verifier.check_transactions(&batch).is_ok(),
1240 "Total size of transactions limit verification failed"
1241 );
1242 }
1243 }
1244
1245 #[tokio::test]
1247 async fn submit_with_no_transactions() {
1248 let (mut context, _) = Context::new_for_test(4);
1249 context
1250 .protocol_config
1251 .set_max_transaction_size_bytes_for_testing(15);
1252 context
1253 .protocol_config
1254 .set_max_transactions_in_block_bytes_for_testing(200);
1255 let context = Arc::new(context);
1256 let (client, tx_receiver, priority_tx_receiver) = TransactionClient::new(context.clone());
1257 let mut consumer =
1258 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
1259
1260 let w_no_transactions = client
1261 .submit_no_wait(vec![], Priority::Normal)
1262 .await
1263 .expect("Should submit successfully empty array of transactions");
1264
1265 let transaction =
1266 bcs::to_bytes(&"transaction".to_string()).expect("Serialization should not fail.");
1267 let w_with_transactions = client
1268 .submit_no_wait(vec![transaction], Priority::Normal)
1269 .await
1270 .expect("Should submit successfully transaction");
1271
1272 let (transactions, ack_transactions, _limit_reached) = consumer.next();
1273 assert_eq!(transactions.len(), 1);
1274
1275 ack_transactions(BlockRef::MIN);
1277
1278 {
1279 let r = w_no_transactions.await;
1280 let (block_ref, indices, _status) = r.unwrap();
1281 assert_eq!(block_ref, BlockRef::MIN);
1282 assert_eq!(indices, vec![PING_TRANSACTION_INDEX]);
1283 }
1284
1285 {
1286 let r = w_with_transactions.await;
1287 let (block_ref, indices, _status) = r.unwrap();
1288 assert_eq!(block_ref, BlockRef::MIN);
1289 assert_eq!(indices, vec![0]);
1290 }
1291 }
1292
1293 #[tokio::test]
1294 async fn ping_transaction_index_never_reached() {
1295 static MAX_NUM_TRANSACTIONS_IN_BLOCK: u64 =
1297 (TransactionIndex::MAX - NUM_RESERVED_TRANSACTION_INDICES) as u64;
1298
1299 static MAX_PENDING_TRANSACTIONS: usize = 2 * MAX_NUM_TRANSACTIONS_IN_BLOCK as usize;
1301
1302 let (mut context, _) = Context::new_for_test(4);
1303 context
1304 .protocol_config
1305 .set_max_transaction_size_bytes_for_testing(200_000);
1306 context
1307 .protocol_config
1308 .set_max_transactions_in_block_bytes_for_testing(1_000_000);
1309 context
1310 .protocol_config
1311 .set_max_num_transactions_in_block_for_testing(MAX_NUM_TRANSACTIONS_IN_BLOCK);
1312 let context = Arc::new(context);
1313 let (client, tx_receiver, priority_tx_receiver) =
1314 TransactionClient::new_with_max_pending_transactions(
1315 context.clone(),
1316 MAX_PENDING_TRANSACTIONS,
1317 MAX_PENDING_PRIORITY_TRANSACTIONS,
1318 );
1319 let mut consumer =
1320 TransactionConsumer::new(tx_receiver, priority_tx_receiver, context.clone());
1321
1322 for i in 0..MAX_NUM_TRANSACTIONS_IN_BLOCK + 10 {
1324 println!("Submitting transaction {i}");
1325 let transaction =
1326 bcs::to_bytes(&format!("t {i}")).expect("Serialization should not fail.");
1327 let _w = client
1328 .submit_no_wait(vec![transaction], Priority::Normal)
1329 .await
1330 .expect("Shouldn't submit successfully transaction");
1331 }
1332
1333 let (transactions, _ack_transactions, _limit_reached) = consumer.next();
1335 assert_eq!(transactions.len() as u64, MAX_NUM_TRANSACTIONS_IN_BLOCK);
1336
1337 let t: String = bcs::from_bytes(transactions.last().unwrap().data()).unwrap();
1338 assert_eq!(
1339 t,
1340 format!(
1341 "t {}",
1342 PING_TRANSACTION_INDEX - NUM_RESERVED_TRANSACTION_INDICES - 1
1343 )
1344 );
1345 }
1346}