iota_config/
transaction_deny_config.rs1use std::collections::HashSet;
6
7use iota_sdk_types::{Address, DenyRuleSet, ObjectId};
8use iota_types::deny_rule_governance::DenyRuleConfig;
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, DenyRuleSet, ObjectId};
252
253 use super::{DenyRuleConfig, TransactionDenyConfig, TransactionDenyConfigBuilder};
254
255 #[test]
256 fn trait_impl_reflects_config() {
257 let addr = Address::new([1u8; 32]);
258 let obj = ObjectId::new([2u8; 32]);
259 let pkg = ObjectId::new([3u8; 32]);
260 let config = TransactionDenyConfigBuilder::new()
261 .add_denied_address(addr)
262 .add_denied_object(obj)
263 .add_denied_package(pkg)
264 .disable_user_transaction()
265 .disable_shared_object_transaction()
266 .disable_move_authenticator()
267 .build();
268
269 let deny: &dyn DenyRuleConfig = &config;
271 assert!(deny.is_address_denied(&addr));
272 assert!(!deny.is_address_denied(&Address::new([9u8; 32])));
273 assert!(deny.is_object_denied(&obj));
274 assert!(!deny.is_object_denied(&ObjectId::new([9u8; 32])));
275 assert!(deny.is_package_denied(&pkg));
276 assert!(deny.has_denied_addresses());
277 assert!(deny.has_denied_objects());
278 assert!(deny.has_denied_packages());
279 assert!(deny.user_transaction_disabled());
280 assert!(deny.shared_object_disabled());
281 assert!(deny.move_authenticator_disabled());
282 assert!(!deny.package_publish_disabled());
283 assert!(!deny.package_upgrade_disabled());
284 assert!(!deny.receiving_objects_disabled());
285
286 let empty: &dyn DenyRuleConfig = &TransactionDenyConfig::default();
287 assert!(!empty.has_denied_addresses());
288 assert!(!empty.has_denied_objects());
289 assert!(!empty.has_denied_packages());
290 }
291
292 #[test]
293 fn to_deny_rule_set_round_trips_lists_and_switches() {
294 let addr = Address::new([1u8; 32]);
295 let obj = ObjectId::new([2u8; 32]);
296 let pkg = ObjectId::new([3u8; 32]);
297 let rules = TransactionDenyConfigBuilder::new()
298 .add_denied_address(addr)
299 .add_denied_object(obj)
300 .add_denied_package(pkg)
301 .build()
302 .to_deny_rule_set();
303 assert_eq!(rules.denied_addresses, [addr].into());
304 assert_eq!(rules.denied_objects, [obj].into());
305 assert_eq!(rules.denied_packages, [pkg].into());
306
307 type Setter = fn(TransactionDenyConfigBuilder) -> TransactionDenyConfigBuilder;
310 type Getter = fn(&DenyRuleSet) -> bool;
311 let switches: [(&str, Setter, Getter); 6] = [
312 (
313 "package_publish_disabled",
314 TransactionDenyConfigBuilder::disable_package_publish,
315 |r| r.package_publish_disabled,
316 ),
317 (
318 "package_upgrade_disabled",
319 TransactionDenyConfigBuilder::disable_package_upgrade,
320 |r| r.package_upgrade_disabled,
321 ),
322 (
323 "shared_object_disabled",
324 TransactionDenyConfigBuilder::disable_shared_object_transaction,
325 |r| r.shared_object_disabled,
326 ),
327 (
328 "user_transaction_disabled",
329 TransactionDenyConfigBuilder::disable_user_transaction,
330 |r| r.user_transaction_disabled,
331 ),
332 (
333 "receiving_objects_disabled",
334 TransactionDenyConfigBuilder::disable_receiving_objects,
335 |r| r.receiving_objects_disabled,
336 ),
337 (
338 "move_authenticator_disabled",
339 TransactionDenyConfigBuilder::disable_move_authenticator,
340 |r| r.move_authenticator_disabled,
341 ),
342 ];
343 for (hot, (hot_name, set, _)) in switches.iter().enumerate() {
344 let rules = set(TransactionDenyConfigBuilder::new())
345 .build()
346 .to_deny_rule_set();
347 for (i, (name, _, get)) in switches.iter().enumerate() {
348 assert_eq!(get(&rules), i == hot, "set {hot_name}, checked {name}");
349 }
350 }
351
352 assert_eq!(
355 TransactionDenyConfig::default().to_deny_rule_set(),
356 DenyRuleSet::default()
357 );
358 }
359}