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