iota_transaction_checks/
deny.rs1use fastcrypto_zkp::bn254::zk_login::OIDCProvider;
6use iota_config::transaction_deny_config::TransactionDenyConfig;
7use iota_types::{
8 base_types::ObjectRef,
9 error::{IotaError, IotaResult, UserInputError},
10 signature::GenericSignature,
11 storage::BackingPackageStore,
12 transaction::{Command, InputObjectKind, TransactionData, TransactionDataAPI},
13};
14use tracing::instrument;
15macro_rules! deny_if_true {
16 ($cond:expr, $msg:expr) => {
17 if ($cond) {
18 return Err(IotaError::UserInput {
19 error: UserInputError::TransactionDenied {
20 error: $msg.to_string(),
21 },
22 });
23 }
24 };
25}
26
27#[instrument(level = "trace", skip_all, fields(tx_digest = ?tx_data.digest()))]
30pub fn check_transaction_for_signing(
31 tx_data: &TransactionData,
32 tx_signatures: &[GenericSignature],
33 input_object_kinds: &[InputObjectKind],
34 receiving_objects: &[ObjectRef],
35 filter_config: &TransactionDenyConfig,
36 package_store: &dyn BackingPackageStore,
37) -> IotaResult {
38 check_disabled_features(filter_config, tx_data, tx_signatures)?;
39
40 check_signers(filter_config, tx_data)?;
41
42 check_input_objects(filter_config, input_object_kinds)?;
43
44 check_package_dependencies(filter_config, tx_data, package_store)?;
45
46 check_receiving_objects(filter_config, receiving_objects)?;
47
48 Ok(())
49}
50
51#[instrument(level = "trace", skip_all)]
52fn check_receiving_objects(
53 filter_config: &TransactionDenyConfig,
54 receiving_objects: &[ObjectRef],
55) -> IotaResult {
56 deny_if_true!(
57 filter_config.receiving_objects_disabled() && !receiving_objects.is_empty(),
58 "Receiving objects is temporarily disabled".to_string()
59 );
60 for (id, _, _) in receiving_objects {
61 deny_if_true!(
62 filter_config.get_object_deny_set().contains(id),
63 format!("Access to object {:?} is temporarily disabled", id)
64 );
65 }
66 Ok(())
67}
68
69#[instrument(level = "trace", skip_all)]
70fn check_disabled_features(
71 filter_config: &TransactionDenyConfig,
72 tx_data: &TransactionData,
73 tx_signatures: &[GenericSignature],
74) -> IotaResult {
75 deny_if_true!(
76 filter_config.user_transaction_disabled(),
77 "Transaction signing is temporarily disabled"
78 );
79
80 tx_signatures.iter().try_for_each(|s| {
81 if let GenericSignature::ZkLoginAuthenticator(z) = s {
82 deny_if_true!(
83 filter_config.zklogin_sig_disabled(),
84 "zkLogin authenticator is temporarily disabled"
85 );
86 deny_if_true!(
87 filter_config.zklogin_disabled_providers().contains(
88 &OIDCProvider::from_iss(z.get_iss())
89 .map_err(|_| IotaError::UnexpectedMessage)?
90 .to_string()
91 ),
92 "zkLogin OAuth provider is temporarily disabled"
93 )
94 } else if let GenericSignature::MoveAuthenticator(_) = s {
95 deny_if_true!(
96 filter_config.move_authenticator_disabled(),
97 "MoveAuthenticator is temporarily disabled"
98 );
99 }
100 Ok(())
101 })?;
102
103 if !filter_config.package_publish_disabled() && !filter_config.package_upgrade_disabled() {
104 return Ok(());
105 }
106
107 for command in tx_data.kind().iter_commands() {
108 deny_if_true!(
109 filter_config.package_publish_disabled() && matches!(command, Command::Publish(..)),
110 "Package publish is temporarily disabled"
111 );
112 deny_if_true!(
113 filter_config.package_upgrade_disabled() && matches!(command, Command::Upgrade(..)),
114 "Package upgrade is temporarily disabled"
115 );
116 }
117 Ok(())
118}
119
120#[instrument(level = "trace", skip_all)]
121fn check_signers(filter_config: &TransactionDenyConfig, tx_data: &TransactionData) -> IotaResult {
122 let deny_map = filter_config.get_address_deny_set();
123 if deny_map.is_empty() {
124 return Ok(());
125 }
126 for signer in tx_data.signers() {
127 deny_if_true!(
128 deny_map.contains(&signer),
129 format!(
130 "Access to account address {:?} is temporarily disabled",
131 signer
132 )
133 );
134 }
135 Ok(())
136}
137
138#[instrument(level = "trace", skip_all)]
139fn check_input_objects(
140 filter_config: &TransactionDenyConfig,
141 input_object_kinds: &[InputObjectKind],
142) -> IotaResult {
143 let deny_map = filter_config.get_object_deny_set();
144 let shared_object_disabled = filter_config.shared_object_disabled();
145 if deny_map.is_empty() && !shared_object_disabled {
146 return Ok(());
148 }
149 for input_object_kind in input_object_kinds {
150 let id = input_object_kind.object_id();
151 deny_if_true!(
152 deny_map.contains(&id),
153 format!("Access to input object {:?} is temporarily disabled", id)
154 );
155 deny_if_true!(
156 shared_object_disabled && input_object_kind.is_shared_object(),
157 "Usage of shared object in transactions is temporarily disabled"
158 );
159 }
160 Ok(())
161}
162
163#[instrument(level = "trace", skip_all)]
164fn check_package_dependencies(
165 filter_config: &TransactionDenyConfig,
166 tx_data: &TransactionData,
167 package_store: &dyn BackingPackageStore,
168) -> IotaResult {
169 let deny_map = filter_config.get_package_deny_set();
170 if deny_map.is_empty() {
171 return Ok(());
172 }
173 let mut dependencies = vec![];
174 for command in tx_data.kind().iter_commands() {
175 match command {
176 Command::Publish(_, deps) => {
177 dependencies.extend(deps.iter().copied());
181 }
182 Command::Upgrade(_, deps, package_id, _) => {
183 dependencies.extend(deps.iter().copied());
184 dependencies.push(*package_id);
187 }
188 Command::MoveCall(call) => {
189 let package = package_store.get_package_object(&call.package)?.ok_or(
190 IotaError::UserInput {
191 error: UserInputError::ObjectNotFound {
192 object_id: call.package,
193 version: None,
194 },
195 },
196 )?;
197 dependencies.extend(
203 package
204 .move_package()
205 .linkage_table()
206 .values()
207 .map(|upgrade_info| upgrade_info.upgraded_id),
208 );
209 dependencies.push(package.move_package().id());
210 }
211 Command::TransferObjects(..)
212 | &Command::SplitCoins(..)
213 | &Command::MergeCoins(..)
214 | &Command::MakeMoveVec(..) => {}
215 }
216 }
217 for dep in dependencies {
218 deny_if_true!(
219 deny_map.contains(&dep),
220 format!("Access to package {:?} is temporarily disabled", dep)
221 );
222 }
223 Ok(())
224}