iota_core/authority/
transaction_deferral.rs1use iota_sdk_types::ObjectId;
6use iota_types::base_types::CommitRound;
7use serde::{Deserialize, Serialize};
8
9#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
10pub enum DeferralKey {
11 Randomness {
14 deferred_from_round: CommitRound,
15 },
16 ConsensusRound {
21 future_round: CommitRound,
22 deferred_from_round: CommitRound,
23 },
24}
25
26impl DeferralKey {
27 pub fn new_for_randomness(deferred_from_round: CommitRound) -> Self {
28 Self::Randomness {
29 deferred_from_round,
30 }
31 }
32
33 pub fn new_for_consensus_round(
34 future_round: CommitRound,
35 deferred_from_round: CommitRound,
36 ) -> Self {
37 Self::ConsensusRound {
38 future_round,
39 deferred_from_round,
40 }
41 }
42
43 pub fn full_range_for_randomness() -> (Self, Self) {
44 (
45 Self::Randomness {
46 deferred_from_round: 0,
47 },
48 Self::Randomness {
49 deferred_from_round: u64::MAX,
50 },
51 )
52 }
53
54 pub fn range_for_up_to_consensus_round(consensus_round: CommitRound) -> (Self, Self) {
57 (
58 Self::ConsensusRound {
59 future_round: 0,
60 deferred_from_round: 0,
61 },
62 Self::ConsensusRound {
63 future_round: consensus_round.checked_add(1).unwrap(),
64 deferred_from_round: 0,
65 },
66 )
67 }
68
69 pub fn deferred_from_round(&self) -> CommitRound {
70 match self {
71 Self::Randomness {
72 deferred_from_round,
73 } => *deferred_from_round,
74 Self::ConsensusRound {
75 deferred_from_round,
76 ..
77 } => *deferred_from_round,
78 }
79 }
80}
81
82#[derive(Debug)]
83pub enum DeferralReason {
84 RandomnessNotReady,
85
86 SharedObjectCongestion(Vec<ObjectId>),
88}
89
90pub fn transaction_deferral_within_limit(
91 deferral_key: &DeferralKey,
92 max_deferral_rounds_for_congestion_control: u64,
93) -> bool {
94 if let DeferralKey::ConsensusRound {
95 future_round,
96 deferred_from_round,
97 } = deferral_key
98 {
99 return (future_round - deferred_from_round) <= max_deferral_rounds_for_congestion_control;
100 }
101
102 true
105}
106
107#[cfg(test)]
108mod object_cost_tests {
109 use typed_store::{
110 DBMapUtils, Map,
111 rocks::{DBMap, MetricConf},
112 };
113
114 use super::*;
115
116 #[tokio::test]
117 async fn test_deferral_key_sort_order() {
118 use rand::prelude::*;
119
120 #[derive(DBMapUtils)]
121 struct TestDB {
122 deferred_certs: DBMap<DeferralKey, ()>,
123 }
124
125 let tmp_dir = iota_common::tempdir();
127
128 let db = TestDB::open_tables_read_write(
129 tmp_dir.path().to_owned(),
130 MetricConf::new("test_db"),
131 None,
132 None,
133 );
134
135 for _ in 0..10000 {
136 let future_round = rand::thread_rng().gen_range(0..u64::MAX);
137 let current_round = rand::thread_rng().gen_range(0..u64::MAX);
138
139 let key = DeferralKey::new_for_consensus_round(future_round, current_round);
140 db.deferred_certs.insert(&key, &()).unwrap();
141 }
142
143 let mut previous_future_round = 0;
144 for item in db.deferred_certs.safe_iter() {
145 match item.unwrap().0 {
146 DeferralKey::Randomness { .. } => (),
147 DeferralKey::ConsensusRound { future_round, .. } => {
148 assert!(previous_future_round <= future_round);
149 previous_future_round = future_round;
150 }
151 }
152 }
153 }
154
155 #[tokio::test]
158 async fn test_fetching_deferred_txs() {
159 use rand::prelude::*;
160
161 #[derive(DBMapUtils)]
162 struct TestDB {
163 deferred_certs: DBMap<DeferralKey, ()>,
164 }
165
166 let tmp_dir = iota_common::tempdir();
168
169 let db = TestDB::open_tables_read_write(
170 tmp_dir.path().to_owned(),
171 MetricConf::new("test_db"),
172 None,
173 None,
174 );
175
176 let min_future_round = 100;
178 let max_future_round = 300;
179 for _ in 0..10000 {
180 let future_round = rand::thread_rng().gen_range(min_future_round..=max_future_round);
181 let current_round = rand::thread_rng().gen_range(0..u64::MAX);
182
183 db.deferred_certs
184 .insert(
185 &DeferralKey::new_for_consensus_round(future_round, current_round),
186 &(),
187 )
188 .unwrap();
189 db.deferred_certs
192 .insert(&DeferralKey::new_for_randomness(current_round), &())
193 .unwrap();
194 }
195
196 let (min, max) = DeferralKey::range_for_up_to_consensus_round(200);
198 let mut previous_future_round = 0;
199 let mut result_count = 0;
200 for result in db
201 .deferred_certs
202 .safe_iter_with_bounds(Some(min), Some(max))
203 {
204 let (key, _) = result.unwrap();
205 match key {
206 DeferralKey::Randomness { .. } => {
207 panic!("Should not receive randomness deferral txn.")
208 }
209 DeferralKey::ConsensusRound { future_round, .. } => {
210 assert!(previous_future_round <= future_round);
211 previous_future_round = future_round;
212 assert!(future_round <= 200);
213 result_count += 1;
214 }
215 }
216 }
217 assert!(result_count > 0);
218 }
219}