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 }
95 Ok(())
96 })?;
97
98 if !filter_config.package_publish_disabled() && !filter_config.package_upgrade_disabled() {
99 return Ok(());
100 }
101
102 for command in tx_data.kind().iter_commands() {
103 deny_if_true!(
104 filter_config.package_publish_disabled() && matches!(command, Command::Publish(..)),
105 "Package publish is temporarily disabled"
106 );
107 deny_if_true!(
108 filter_config.package_upgrade_disabled() && matches!(command, Command::Upgrade(..)),
109 "Package upgrade is temporarily disabled"
110 );
111 }
112 Ok(())
113}
114
115#[instrument(level = "trace", skip_all)]
116fn check_signers(filter_config: &TransactionDenyConfig, tx_data: &TransactionData) -> IotaResult {
117 let deny_map = filter_config.get_address_deny_set();
118 if deny_map.is_empty() {
119 return Ok(());
120 }
121 for signer in tx_data.signers() {
122 deny_if_true!(
123 deny_map.contains(&signer),
124 format!(
125 "Access to account address {:?} is temporarily disabled",
126 signer
127 )
128 );
129 }
130 Ok(())
131}
132
133#[instrument(level = "trace", skip_all)]
134fn check_input_objects(
135 filter_config: &TransactionDenyConfig,
136 input_object_kinds: &[InputObjectKind],
137) -> IotaResult {
138 let deny_map = filter_config.get_object_deny_set();
139 let shared_object_disabled = filter_config.shared_object_disabled();
140 if deny_map.is_empty() && !shared_object_disabled {
141 return Ok(());
143 }
144 for input_object_kind in input_object_kinds {
145 let id = input_object_kind.object_id();
146 deny_if_true!(
147 deny_map.contains(&id),
148 format!("Access to input object {:?} is temporarily disabled", id)
149 );
150 deny_if_true!(
151 shared_object_disabled && input_object_kind.is_shared_object(),
152 "Usage of shared object in transactions is temporarily disabled"
153 );
154 }
155 Ok(())
156}
157
158#[instrument(level = "trace", skip_all)]
159fn check_package_dependencies(
160 filter_config: &TransactionDenyConfig,
161 tx_data: &TransactionData,
162 package_store: &dyn BackingPackageStore,
163) -> IotaResult {
164 let deny_map = filter_config.get_package_deny_set();
165 if deny_map.is_empty() {
166 return Ok(());
167 }
168 let mut dependencies = vec![];
169 for command in tx_data.kind().iter_commands() {
170 match command {
171 Command::Publish(_, deps) => {
172 dependencies.extend(deps.iter().copied());
176 }
177 Command::Upgrade(_, deps, package_id, _) => {
178 dependencies.extend(deps.iter().copied());
179 dependencies.push(*package_id);
182 }
183 Command::MoveCall(call) => {
184 let package = package_store.get_package_object(&call.package)?.ok_or(
185 IotaError::UserInput {
186 error: UserInputError::ObjectNotFound {
187 object_id: call.package,
188 version: None,
189 },
190 },
191 )?;
192 dependencies.extend(
198 package
199 .move_package()
200 .linkage_table()
201 .values()
202 .map(|upgrade_info| upgrade_info.upgraded_id),
203 );
204 dependencies.push(package.move_package().id());
205 }
206 Command::TransferObjects(..)
207 | &Command::SplitCoins(..)
208 | &Command::MergeCoins(..)
209 | &Command::MakeMoveVec(..) => {}
210 }
211 }
212 for dep in dependencies {
213 deny_if_true!(
214 deny_map.contains(&dep),
215 format!("Access to package {:?} is temporarily disabled", dep)
216 );
217 }
218 Ok(())
219}