iota_config/
transaction_deny_config.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::HashSet;
6
7use iota_types::base_types::{IotaAddress, ObjectID};
8use once_cell::sync::OnceCell;
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, Default, Deserialize, Serialize)]
12#[serde(rename_all = "kebab-case")]
13pub struct TransactionDenyConfig {
14    /// A list of object IDs that are not allowed to be accessed/used in
15    /// transactions. Note that since this is checked during transaction
16    /// signing, only root object ids are supported here (i.e. no
17    /// child-objects). Similarly this does not apply to wrapped objects as
18    /// they are not directly accessible.
19    #[serde(default, skip_serializing_if = "Vec::is_empty")]
20    object_deny_list: Vec<ObjectID>,
21
22    /// A list of package object IDs that are not allowed to be called into in
23    /// transactions, either directly or indirectly through transitive
24    /// dependencies. Note that this does not apply to type arguments.
25    /// Also since we only compare the deny list against the upgraded package ID
26    /// of each dependency in the used package, when a package ID is denied,
27    /// newer versions of that package are still allowed. If we want to deny
28    /// the entire upgrade family of a package, we need to explicitly
29    /// specify all the package IDs in the deny list. TODO: We could
30    /// consider making this more flexible, e.g. whether to check in type args,
31    /// whether to block entire upgrade family, whether to allow upgrade and
32    /// etc.
33    #[serde(default, skip_serializing_if = "Vec::is_empty")]
34    package_deny_list: Vec<ObjectID>,
35
36    /// A list of iota addresses that are not allowed to be used as the sender
37    /// or sponsor.
38    #[serde(default, skip_serializing_if = "Vec::is_empty")]
39    address_deny_list: Vec<IotaAddress>,
40
41    /// Whether publishing new packages is disabled.
42    #[serde(default)]
43    package_publish_disabled: bool,
44
45    /// Whether upgrading existing packages is disabled.
46    #[serde(default)]
47    package_upgrade_disabled: bool,
48
49    /// Whether usage of shared objects is disabled.
50    #[serde(default)]
51    shared_object_disabled: bool,
52
53    /// Whether user transactions are disabled (i.e. only system transactions
54    /// are allowed). This is essentially a kill switch for transactions
55    /// processing to a degree.
56    #[serde(default)]
57    user_transaction_disabled: bool,
58
59    /// In-memory maps for faster lookup of various lists.
60    #[serde(skip)]
61    object_deny_set: OnceCell<HashSet<ObjectID>>,
62
63    #[serde(skip)]
64    package_deny_set: OnceCell<HashSet<ObjectID>>,
65
66    #[serde(skip)]
67    address_deny_set: OnceCell<HashSet<IotaAddress>>,
68
69    /// Whether receiving objects transferred to other objects is allowed
70    #[serde(default)]
71    receiving_objects_disabled: bool,
72
73    /// Whether zklogin transaction is disabled
74    #[serde(default)]
75    zklogin_sig_disabled: bool,
76
77    /// A list of disabled OAuth providers for zkLogin
78    #[serde(default)]
79    zklogin_disabled_providers: HashSet<String>,
80
81    /// Whether `MoveAuthenticator` is disabled
82    #[serde(default)]
83    move_authenticator_disabled: bool,
84    // TODO: We could consider add a deny list for types that we want to disable public transfer.
85    // TODO: We could also consider disable more types of commands, such as transfer, split and
86    // etc.
87}
88
89impl TransactionDenyConfig {
90    pub fn get_object_deny_set(&self) -> &HashSet<ObjectID> {
91        self.object_deny_set
92            .get_or_init(|| self.object_deny_list.iter().cloned().collect())
93    }
94
95    pub fn get_package_deny_set(&self) -> &HashSet<ObjectID> {
96        self.package_deny_set
97            .get_or_init(|| self.package_deny_list.iter().cloned().collect())
98    }
99
100    pub fn get_address_deny_set(&self) -> &HashSet<IotaAddress> {
101        self.address_deny_set
102            .get_or_init(|| self.address_deny_list.iter().cloned().collect())
103    }
104
105    pub fn package_publish_disabled(&self) -> bool {
106        self.package_publish_disabled
107    }
108
109    pub fn package_upgrade_disabled(&self) -> bool {
110        self.package_upgrade_disabled
111    }
112
113    pub fn shared_object_disabled(&self) -> bool {
114        self.shared_object_disabled
115    }
116
117    pub fn user_transaction_disabled(&self) -> bool {
118        self.user_transaction_disabled
119    }
120
121    pub fn receiving_objects_disabled(&self) -> bool {
122        self.receiving_objects_disabled
123    }
124
125    pub fn zklogin_sig_disabled(&self) -> bool {
126        self.zklogin_sig_disabled
127    }
128
129    pub fn zklogin_disabled_providers(&self) -> &HashSet<String> {
130        &self.zklogin_disabled_providers
131    }
132
133    pub fn move_authenticator_disabled(&self) -> bool {
134        self.move_authenticator_disabled
135    }
136}
137
138#[derive(Default)]
139pub struct TransactionDenyConfigBuilder {
140    config: TransactionDenyConfig,
141}
142
143impl TransactionDenyConfigBuilder {
144    pub fn new() -> Self {
145        Self::default()
146    }
147
148    pub fn build(self) -> TransactionDenyConfig {
149        self.config
150    }
151
152    pub fn disable_user_transaction(mut self) -> Self {
153        self.config.user_transaction_disabled = true;
154        self
155    }
156
157    pub fn disable_shared_object_transaction(mut self) -> Self {
158        self.config.shared_object_disabled = true;
159        self
160    }
161
162    pub fn disable_package_publish(mut self) -> Self {
163        self.config.package_publish_disabled = true;
164        self
165    }
166
167    pub fn disable_package_upgrade(mut self) -> Self {
168        self.config.package_upgrade_disabled = true;
169        self
170    }
171
172    pub fn disable_receiving_objects(mut self) -> Self {
173        self.config.receiving_objects_disabled = true;
174        self
175    }
176
177    pub fn add_denied_object(mut self, id: ObjectID) -> Self {
178        self.config.object_deny_list.push(id);
179        self
180    }
181
182    pub fn add_denied_address(mut self, address: IotaAddress) -> Self {
183        self.config.address_deny_list.push(address);
184        self
185    }
186
187    pub fn add_denied_package(mut self, id: ObjectID) -> Self {
188        self.config.package_deny_list.push(id);
189        self
190    }
191
192    pub fn disable_zklogin_sig(mut self) -> Self {
193        self.config.zklogin_sig_disabled = true;
194        self
195    }
196
197    pub fn add_zklogin_disabled_provider(mut self, provider: String) -> Self {
198        self.config.zklogin_disabled_providers.insert(provider);
199        self
200    }
201
202    pub fn disable_move_authenticator(mut self) -> Self {
203        self.config.move_authenticator_disabled = true;
204        self
205    }
206}