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    /// In-memory maps for faster lookup of various lists.
58    #[serde(skip)]
59    object_deny_set: OnceCell<HashSet<ObjectID>>,
60
61    #[serde(skip)]
62    package_deny_set: OnceCell<HashSet<ObjectID>>,
63
64    #[serde(skip)]
65    address_deny_set: OnceCell<HashSet<SuiAddress>>,
66
67    /// Whether receiving objects transferred to other objects is allowed
68    #[serde(default)]
69    receiving_objects_disabled: bool,
70
71    /// Whether zklogin transaction is disabled
72    #[serde(default)]
73    zklogin_sig_disabled: bool,
74
75    /// A list of disabled OAuth providers for zkLogin
76    #[serde(default)]
77    zklogin_disabled_providers: HashSet<String>,
78
79    /// Dynamic transaction checks to run on transactions.
80    /// Program is loaded at deserialization time to ensure that any syntactic issues are caught
81    /// immediately.
82    #[serde(
83        default,
84        skip_serializing_if = "Option::is_none",
85        serialize_with = "crate::dynamic_transaction_signing_checks::serialize_dynamic_transaction_checks",
86        deserialize_with = "crate::dynamic_transaction_signing_checks::deserialize_dynamic_transaction_checks"
87    )]
88    dynamic_transaction_checks: Option<DynamicCheckRunnerContext>,
89    // TODO: We could consider add a deny list for types that we want to disable public transfer.
90    // TODO: We could also consider disable more types of commands, such as transfer, split and etc.
91}
92
93impl TransactionDenyConfig {
94    pub fn get_object_deny_set(&self) -> &HashSet<ObjectID> {
95        self.object_deny_set
96            .get_or_init(|| self.object_deny_list.iter().cloned().collect())
97    }
98
99    pub fn get_package_deny_set(&self) -> &HashSet<ObjectID> {
100        self.package_deny_set
101            .get_or_init(|| self.package_deny_list.iter().cloned().collect())
102    }
103
104    pub fn get_address_deny_set(&self) -> &HashSet<SuiAddress> {
105        self.address_deny_set
106            .get_or_init(|| self.address_deny_list.iter().cloned().collect())
107    }
108
109    pub fn package_publish_disabled(&self) -> bool {
110        self.package_publish_disabled
111    }
112
113    pub fn package_upgrade_disabled(&self) -> bool {
114        self.package_upgrade_disabled
115    }
116
117    pub fn shared_object_disabled(&self) -> bool {
118        self.shared_object_disabled
119    }
120
121    pub fn user_transaction_disabled(&self) -> bool {
122        self.user_transaction_disabled
123    }
124
125    pub fn receiving_objects_disabled(&self) -> bool {
126        self.receiving_objects_disabled
127    }
128
129    pub fn zklogin_sig_disabled(&self) -> bool {
130        self.zklogin_sig_disabled
131    }
132
133    pub fn zklogin_disabled_providers(&self) -> &HashSet<String> {
134        &self.zklogin_disabled_providers
135    }
136
137    pub fn dynamic_transaction_checks(&self) -> &Option<DynamicCheckRunnerContext> {
138        &self.dynamic_transaction_checks
139    }
140}
141
142#[derive(Default)]
143pub struct TransactionDenyConfigBuilder {
144    config: TransactionDenyConfig,
145}
146
147impl TransactionDenyConfigBuilder {
148    pub fn new() -> Self {
149        Self::default()
150    }
151
152    pub fn build(self) -> TransactionDenyConfig {
153        self.config
154    }
155
156    pub fn disable_user_transaction(mut self) -> Self {
157        self.config.user_transaction_disabled = true;
158        self
159    }
160
161    pub fn disable_shared_object_transaction(mut self) -> Self {
162        self.config.shared_object_disabled = true;
163        self
164    }
165
166    pub fn disable_package_publish(mut self) -> Self {
167        self.config.package_publish_disabled = true;
168        self
169    }
170
171    pub fn disable_package_upgrade(mut self) -> Self {
172        self.config.package_upgrade_disabled = true;
173        self
174    }
175
176    pub fn disable_receiving_objects(mut self) -> Self {
177        self.config.receiving_objects_disabled = true;
178        self
179    }
180
181    pub fn add_denied_object(mut self, id: ObjectID) -> Self {
182        self.config.object_deny_list.push(id);
183        self
184    }
185
186    pub fn add_denied_address(mut self, address: SuiAddress) -> Self {
187        self.config.address_deny_list.push(address);
188        self
189    }
190
191    pub fn add_denied_package(mut self, id: ObjectID) -> Self {
192        self.config.package_deny_list.push(id);
193        self
194    }
195
196    pub fn disable_zklogin_sig(mut self) -> Self {
197        self.config.zklogin_sig_disabled = true;
198        self
199    }
200
201    pub fn add_zklogin_disabled_provider(mut self, provider: String) -> Self {
202        self.config.zklogin_disabled_providers.insert(provider);
203        self
204    }
205
206    pub fn add_dynamic_transaction_checks(
207        mut self,
208        checks: String,
209    ) -> Result<Self, DynamicCheckRunnerError> {
210        self.config.dynamic_transaction_checks = Some(DynamicCheckRunnerContext::new(checks)?);
211        Ok(self)
212    }
213}