sui_config/
transaction_deny_config.rs

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