iota_config/
transaction_deny_config.rs1use std::collections::HashSet;
6
7use iota_sdk_types::{Address, ObjectId};
8use iota_types::deny_rule_governance::{DenyRuleConfig, DenyRuleSet};
9use once_cell::sync::OnceCell;
10use serde::{Deserialize, Serialize};
11
12#[derive(Clone, Debug, Default, Deserialize, Serialize)]
13#[serde(rename_all = "kebab-case")]
14pub struct TransactionDenyConfig {
15 #[serde(default, skip_serializing_if = "Vec::is_empty")]
21 object_deny_list: Vec<ObjectId>,
22
23 #[serde(default, skip_serializing_if = "Vec::is_empty")]
35 package_deny_list: Vec<ObjectId>,
36
37 #[serde(default, skip_serializing_if = "Vec::is_empty")]
40 address_deny_list: Vec<Address>,
41
42 #[serde(default)]
44 package_publish_disabled: bool,
45
46 #[serde(default)]
48 package_upgrade_disabled: bool,
49
50 #[serde(default)]
52 shared_object_disabled: bool,
53
54 #[serde(default)]
58 user_transaction_disabled: bool,
59
60 #[serde(skip)]
62 object_deny_set: OnceCell<HashSet<ObjectId>>,
63
64 #[serde(skip)]
65 package_deny_set: OnceCell<HashSet<ObjectId>>,
66
67 #[serde(skip)]
68 address_deny_set: OnceCell<HashSet<Address>>,
69
70 #[serde(default)]
72 receiving_objects_disabled: bool,
73
74 #[serde(default)]
76 move_authenticator_disabled: bool,
77 }
81
82impl TransactionDenyConfig {
83 pub fn get_object_deny_set(&self) -> &HashSet<ObjectId> {
84 self.object_deny_set
85 .get_or_init(|| self.object_deny_list.iter().cloned().collect())
86 }
87
88 pub fn get_package_deny_set(&self) -> &HashSet<ObjectId> {
89 self.package_deny_set
90 .get_or_init(|| self.package_deny_list.iter().cloned().collect())
91 }
92
93 pub fn get_address_deny_set(&self) -> &HashSet<Address> {
94 self.address_deny_set
95 .get_or_init(|| self.address_deny_list.iter().cloned().collect())
96 }
97
98 pub fn package_publish_disabled(&self) -> bool {
99 self.package_publish_disabled
100 }
101
102 pub fn package_upgrade_disabled(&self) -> bool {
103 self.package_upgrade_disabled
104 }
105
106 pub fn shared_object_disabled(&self) -> bool {
107 self.shared_object_disabled
108 }
109
110 pub fn user_transaction_disabled(&self) -> bool {
111 self.user_transaction_disabled
112 }
113
114 pub fn receiving_objects_disabled(&self) -> bool {
115 self.receiving_objects_disabled
116 }
117
118 pub fn move_authenticator_disabled(&self) -> bool {
119 self.move_authenticator_disabled
120 }
121
122 pub fn to_deny_rule_set(&self) -> DenyRuleSet {
125 DenyRuleSet {
126 denied_addresses: self.address_deny_list.iter().copied().collect(),
127 denied_objects: self.object_deny_list.iter().copied().collect(),
128 denied_packages: self.package_deny_list.iter().copied().collect(),
129 package_publish_disabled: self.package_publish_disabled,
130 package_upgrade_disabled: self.package_upgrade_disabled,
131 shared_object_disabled: self.shared_object_disabled,
132 user_transaction_disabled: self.user_transaction_disabled,
133 receiving_objects_disabled: self.receiving_objects_disabled,
134 move_authenticator_disabled: self.move_authenticator_disabled,
135 }
136 }
137}
138
139#[derive(Default)]
140pub struct TransactionDenyConfigBuilder {
141 config: TransactionDenyConfig,
142}
143
144impl TransactionDenyConfigBuilder {
145 pub fn new() -> Self {
146 Self::default()
147 }
148
149 pub fn build(self) -> TransactionDenyConfig {
150 self.config
151 }
152
153 pub fn disable_user_transaction(mut self) -> Self {
154 self.config.user_transaction_disabled = true;
155 self
156 }
157
158 pub fn disable_shared_object_transaction(mut self) -> Self {
159 self.config.shared_object_disabled = true;
160 self
161 }
162
163 pub fn disable_package_publish(mut self) -> Self {
164 self.config.package_publish_disabled = true;
165 self
166 }
167
168 pub fn disable_package_upgrade(mut self) -> Self {
169 self.config.package_upgrade_disabled = true;
170 self
171 }
172
173 pub fn disable_receiving_objects(mut self) -> Self {
174 self.config.receiving_objects_disabled = true;
175 self
176 }
177
178 pub fn add_denied_object(mut self, id: ObjectId) -> Self {
179 self.config.object_deny_list.push(id);
180 self
181 }
182
183 pub fn add_denied_address(mut self, address: Address) -> Self {
184 self.config.address_deny_list.push(address);
185 self
186 }
187
188 pub fn add_denied_package(mut self, id: ObjectId) -> Self {
189 self.config.package_deny_list.push(id);
190 self
191 }
192
193 pub fn disable_move_authenticator(mut self) -> Self {
194 self.config.move_authenticator_disabled = true;
195 self
196 }
197}
198
199impl DenyRuleConfig for TransactionDenyConfig {
200 fn is_address_denied(&self, address: &Address) -> bool {
201 self.get_address_deny_set().contains(address)
202 }
203
204 fn is_object_denied(&self, id: &ObjectId) -> bool {
205 self.get_object_deny_set().contains(id)
206 }
207
208 fn is_package_denied(&self, id: &ObjectId) -> bool {
209 self.get_package_deny_set().contains(id)
210 }
211
212 fn has_denied_addresses(&self) -> bool {
213 !self.address_deny_list.is_empty()
214 }
215
216 fn has_denied_objects(&self) -> bool {
217 !self.object_deny_list.is_empty()
218 }
219
220 fn has_denied_packages(&self) -> bool {
221 !self.package_deny_list.is_empty()
222 }
223
224 fn package_publish_disabled(&self) -> bool {
225 self.package_publish_disabled
226 }
227
228 fn package_upgrade_disabled(&self) -> bool {
229 self.package_upgrade_disabled
230 }
231
232 fn shared_object_disabled(&self) -> bool {
233 self.shared_object_disabled
234 }
235
236 fn user_transaction_disabled(&self) -> bool {
237 self.user_transaction_disabled
238 }
239
240 fn receiving_objects_disabled(&self) -> bool {
241 self.receiving_objects_disabled
242 }
243
244 fn move_authenticator_disabled(&self) -> bool {
245 self.move_authenticator_disabled
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use iota_sdk_types::{Address, ObjectId};
252 use iota_types::deny_rule_governance::DenyRuleSet;
253
254 use super::{DenyRuleConfig, TransactionDenyConfig, TransactionDenyConfigBuilder};
255
256 #[test]
257 fn trait_impl_reflects_config() {
258 let addr = Address::new([1u8; 32]);
259 let obj = ObjectId::new([2u8; 32]);
260 let pkg = ObjectId::new([3u8; 32]);
261 let config = TransactionDenyConfigBuilder::new()
262 .add_denied_address(addr)
263 .add_denied_object(obj)
264 .add_denied_package(pkg)
265 .disable_user_transaction()
266 .disable_shared_object_transaction()
267 .disable_move_authenticator()
268 .build();
269
270 let deny: &dyn DenyRuleConfig = &config;
272 assert!(deny.is_address_denied(&addr));
273 assert!(!deny.is_address_denied(&Address::new([9u8; 32])));
274 assert!(deny.is_object_denied(&obj));
275 assert!(!deny.is_object_denied(&ObjectId::new([9u8; 32])));
276 assert!(deny.is_package_denied(&pkg));
277 assert!(deny.has_denied_addresses());
278 assert!(deny.has_denied_objects());
279 assert!(deny.has_denied_packages());
280 assert!(deny.user_transaction_disabled());
281 assert!(deny.shared_object_disabled());
282 assert!(deny.move_authenticator_disabled());
283 assert!(!deny.package_publish_disabled());
284 assert!(!deny.package_upgrade_disabled());
285 assert!(!deny.receiving_objects_disabled());
286
287 let empty: &dyn DenyRuleConfig = &TransactionDenyConfig::default();
288 assert!(!empty.has_denied_addresses());
289 assert!(!empty.has_denied_objects());
290 assert!(!empty.has_denied_packages());
291 }
292
293 #[test]
294 fn to_deny_rule_set_round_trips_lists_and_switches() {
295 let addr = Address::new([1u8; 32]);
296 let obj = ObjectId::new([2u8; 32]);
297 let pkg = ObjectId::new([3u8; 32]);
298 let rules = TransactionDenyConfigBuilder::new()
299 .add_denied_address(addr)
300 .add_denied_object(obj)
301 .add_denied_package(pkg)
302 .build()
303 .to_deny_rule_set();
304 assert_eq!(rules.denied_addresses, [addr].into());
305 assert_eq!(rules.denied_objects, [obj].into());
306 assert_eq!(rules.denied_packages, [pkg].into());
307
308 type Setter = fn(TransactionDenyConfigBuilder) -> TransactionDenyConfigBuilder;
311 type Getter = fn(&DenyRuleSet) -> bool;
312 let switches: [(&str, Setter, Getter); 6] = [
313 (
314 "package_publish_disabled",
315 TransactionDenyConfigBuilder::disable_package_publish,
316 |r| r.package_publish_disabled,
317 ),
318 (
319 "package_upgrade_disabled",
320 TransactionDenyConfigBuilder::disable_package_upgrade,
321 |r| r.package_upgrade_disabled,
322 ),
323 (
324 "shared_object_disabled",
325 TransactionDenyConfigBuilder::disable_shared_object_transaction,
326 |r| r.shared_object_disabled,
327 ),
328 (
329 "user_transaction_disabled",
330 TransactionDenyConfigBuilder::disable_user_transaction,
331 |r| r.user_transaction_disabled,
332 ),
333 (
334 "receiving_objects_disabled",
335 TransactionDenyConfigBuilder::disable_receiving_objects,
336 |r| r.receiving_objects_disabled,
337 ),
338 (
339 "move_authenticator_disabled",
340 TransactionDenyConfigBuilder::disable_move_authenticator,
341 |r| r.move_authenticator_disabled,
342 ),
343 ];
344 for (hot, (hot_name, set, _)) in switches.iter().enumerate() {
345 let rules = set(TransactionDenyConfigBuilder::new())
346 .build()
347 .to_deny_rule_set();
348 for (i, (name, _, get)) in switches.iter().enumerate() {
349 assert_eq!(get(&rules), i == hot, "set {hot_name}, checked {name}");
350 }
351 }
352
353 assert_eq!(
356 TransactionDenyConfig::default().to_deny_rule_set(),
357 DenyRuleSet::default()
358 );
359 }
360}