1pub mod deny;
6
7pub use checked::*;
8
9#[iota_macros::with_checked_arithmetic]
10mod checked {
11 use std::{
12 collections::{BTreeMap, HashSet},
13 sync::Arc,
14 };
15
16 use iota_config::verifier_signing_config::VerifierSigningConfig;
17 use iota_protocol_config::ProtocolConfig;
18 use iota_types::{
19 IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_OBJECT_SHARED_VERSION,
20 IOTA_RANDOMNESS_STATE_OBJECT_ID,
21 base_types::{IotaAddress, ObjectID, ObjectRef, SequenceNumber},
22 error::{IotaError, IotaResult, UserInputError, UserInputResult},
23 executable_transaction::VerifiedExecutableTransaction,
24 fp_bail, fp_ensure,
25 gas::IotaGasStatus,
26 metrics::BytecodeVerifierMetrics,
27 object::{Object, Owner},
28 transaction::{
29 CheckedInputObjects, InputObjectKind, InputObjects, ObjectReadResult,
30 ObjectReadResultKind, ReceivingObjectReadResult, ReceivingObjects, TransactionData,
31 TransactionDataAPI, TransactionKind,
32 },
33 };
34 use tracing::{error, instrument};
35
36 trait IntoChecked {
37 fn into_checked(self) -> CheckedInputObjects;
38 }
39
40 impl IntoChecked for InputObjects {
41 fn into_checked(self) -> CheckedInputObjects {
42 CheckedInputObjects::new_with_checked_transaction_inputs(self)
43 }
44 }
45
46 pub fn get_gas_status(
51 objects: &InputObjects,
52 gas: &[ObjectRef],
53 protocol_config: &ProtocolConfig,
54 reference_gas_price: u64,
55 transaction: &TransactionData,
56 ) -> IotaResult<IotaGasStatus> {
57 check_gas(
58 objects,
59 protocol_config,
60 reference_gas_price,
61 gas,
62 transaction.gas_budget(),
63 transaction.gas_price(),
64 transaction.kind(),
65 )
66 }
67
68 #[instrument(level = "trace", skip_all)]
69 pub fn check_transaction_input(
70 protocol_config: &ProtocolConfig,
71 reference_gas_price: u64,
72 transaction: &TransactionData,
73 input_objects: InputObjects,
74 receiving_objects: &ReceivingObjects,
75 metrics: &Arc<BytecodeVerifierMetrics>,
76 verifier_signing_config: &VerifierSigningConfig,
77 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
78 let gas_status = check_transaction_input_inner(
79 protocol_config,
80 reference_gas_price,
81 transaction,
82 &input_objects,
83 &[],
84 )?;
85 check_receiving_objects(&input_objects, receiving_objects)?;
86 check_non_system_packages_to_be_published(
88 transaction,
89 protocol_config,
90 metrics,
91 verifier_signing_config,
92 )?;
93
94 Ok((gas_status, input_objects.into_checked()))
95 }
96
97 pub fn check_transaction_input_with_given_gas(
98 protocol_config: &ProtocolConfig,
99 reference_gas_price: u64,
100 transaction: &TransactionData,
101 mut input_objects: InputObjects,
102 receiving_objects: ReceivingObjects,
103 gas_object: Object,
104 metrics: &Arc<BytecodeVerifierMetrics>,
105 verifier_signing_config: &VerifierSigningConfig,
106 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
107 let gas_object_ref = gas_object.compute_object_reference();
108 input_objects.push(ObjectReadResult::new_from_gas_object(&gas_object));
109
110 let gas_status = check_transaction_input_inner(
111 protocol_config,
112 reference_gas_price,
113 transaction,
114 &input_objects,
115 &[gas_object_ref],
116 )?;
117 check_receiving_objects(&input_objects, &receiving_objects)?;
118 check_non_system_packages_to_be_published(
120 transaction,
121 protocol_config,
122 metrics,
123 verifier_signing_config,
124 )?;
125
126 Ok((gas_status, input_objects.into_checked()))
127 }
128
129 #[instrument(level = "trace", skip_all)]
135 pub fn check_certificate_input(
136 cert: &VerifiedExecutableTransaction,
137 input_objects: InputObjects,
138 protocol_config: &ProtocolConfig,
139 reference_gas_price: u64,
140 ) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
141 let transaction = cert.data().transaction_data();
142 let gas_status = check_transaction_input_inner(
143 protocol_config,
144 reference_gas_price,
145 transaction,
146 &input_objects,
147 &[],
148 )?;
149 Ok((gas_status, input_objects.into_checked()))
154 }
155
156 pub fn check_dev_inspect_input(
159 config: &ProtocolConfig,
160 kind: &TransactionKind,
161 input_objects: InputObjects,
162 _receiving_objects: ReceivingObjects,
164 ) -> IotaResult<CheckedInputObjects> {
165 kind.validity_check(config)?;
166 if kind.is_system_tx() {
167 return Err(UserInputError::Unsupported(format!(
168 "Transaction kind {} is not supported in dev-inspect",
169 kind
170 ))
171 .into());
172 }
173 let mut used_objects: HashSet<IotaAddress> = HashSet::new();
174 for input_object in input_objects.iter() {
175 let Some(object) = input_object.as_object() else {
176 continue;
178 };
179
180 if !object.is_immutable() {
181 fp_ensure!(
182 used_objects.insert(object.id().into()),
183 UserInputError::MutableObjectUsedMoreThanOnce {
184 object_id: object.id()
185 }
186 .into()
187 );
188 }
189 }
190
191 Ok(input_objects.into_checked())
192 }
193
194 fn check_transaction_input_inner(
196 protocol_config: &ProtocolConfig,
197 reference_gas_price: u64,
198 transaction: &TransactionData,
199 input_objects: &InputObjects,
200 gas_override: &[ObjectRef],
202 ) -> IotaResult<IotaGasStatus> {
203 let gas = if gas_override.is_empty() {
205 transaction.gas()
206 } else {
207 gas_override
208 };
209
210 let gas_status = get_gas_status(
211 input_objects,
212 gas,
213 protocol_config,
214 reference_gas_price,
215 transaction,
216 )?;
217 check_objects(transaction, input_objects)?;
218
219 Ok(gas_status)
220 }
221
222 fn check_receiving_objects(
223 input_objects: &InputObjects,
224 receiving_objects: &ReceivingObjects,
225 ) -> Result<(), IotaError> {
226 let mut objects_in_txn: HashSet<_> = input_objects
227 .object_kinds()
228 .map(|x| x.object_id())
229 .collect();
230
231 for ReceivingObjectReadResult {
240 object_ref: (object_id, version, object_digest),
241 object,
242 } in receiving_objects.iter()
243 {
244 fp_ensure!(
245 *version < SequenceNumber::MAX,
246 UserInputError::InvalidSequenceNumber.into()
247 );
248
249 let Some(object) = object.as_object() else {
250 continue;
252 };
253
254 if !(object.owner.is_address_owned()
255 && object.version() == *version
256 && object.digest() == *object_digest)
257 {
258 fp_ensure!(
260 object.version() == *version,
261 UserInputError::ObjectVersionUnavailableForConsumption {
262 provided_obj_ref: (*object_id, *version, *object_digest),
263 current_version: object.version(),
264 }
265 .into()
266 );
267
268 fp_ensure!(
270 !object.is_package(),
271 UserInputError::MovePackageAsObject {
272 object_id: *object_id
273 }
274 .into()
275 );
276
277 let expected_digest = object.digest();
279 fp_ensure!(
280 expected_digest == *object_digest,
281 UserInputError::InvalidObjectDigest {
282 object_id: *object_id,
283 expected_digest
284 }
285 .into()
286 );
287
288 match object.owner {
289 Owner::AddressOwner(_) => {
290 debug_assert!(
291 false,
292 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
293 (*object_id, *version, *object_id),
294 object
295 );
296 error!(
297 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
298 (*object_id, *version, *object_id),
299 object
300 );
301 fp_bail!(
304 UserInputError::ObjectNotFound {
305 object_id: *object_id,
306 version: Some(*version),
307 }
308 .into()
309 )
310 }
311 Owner::ObjectOwner(owner) => {
312 fp_bail!(
313 UserInputError::InvalidChildObjectArgument {
314 child_id: object.id(),
315 parent_id: owner.into(),
316 }
317 .into()
318 )
319 }
320 Owner::Shared { .. } => fp_bail!(UserInputError::NotSharedObject.into()),
321 Owner::Immutable => fp_bail!(
322 UserInputError::MutableParameterExpected {
323 object_id: *object_id
324 }
325 .into()
326 ),
327 };
328 }
329
330 fp_ensure!(
331 !objects_in_txn.contains(object_id),
332 UserInputError::DuplicateObjectRefInput.into()
333 );
334
335 objects_in_txn.insert(*object_id);
336 }
337 Ok(())
338 }
339
340 #[instrument(level = "trace", skip_all)]
343 fn check_gas(
344 objects: &InputObjects,
345 protocol_config: &ProtocolConfig,
346 reference_gas_price: u64,
347 gas: &[ObjectRef],
348 gas_budget: u64,
349 gas_price: u64,
350 tx_kind: &TransactionKind,
351 ) -> IotaResult<IotaGasStatus> {
352 if tx_kind.is_system_tx() {
353 Ok(IotaGasStatus::new_unmetered())
354 } else {
355 let gas_status =
356 IotaGasStatus::new(gas_budget, gas_price, reference_gas_price, protocol_config)?;
357
358 let objects: BTreeMap<_, _> = objects.iter().map(|o| (o.id(), o)).collect();
361 let mut gas_objects = vec![];
362 for obj_ref in gas {
363 let obj = objects.get(&obj_ref.0);
364 let obj = *obj.ok_or(UserInputError::ObjectNotFound {
365 object_id: obj_ref.0,
366 version: Some(obj_ref.1),
367 })?;
368 gas_objects.push(obj);
369 }
370 gas_status.check_gas_balance(&gas_objects, gas_budget)?;
371 Ok(gas_status)
372 }
373 }
374
375 #[instrument(level = "trace", skip_all)]
378 fn check_objects(transaction: &TransactionData, objects: &InputObjects) -> UserInputResult<()> {
379 let mut used_objects: HashSet<IotaAddress> = HashSet::new();
381 for object in objects.iter() {
382 if object.is_mutable() {
383 fp_ensure!(
384 used_objects.insert(object.id().into()),
385 UserInputError::MutableObjectUsedMoreThanOnce {
386 object_id: object.id()
387 }
388 );
389 }
390 }
391
392 if !transaction.is_genesis_tx() && objects.is_empty() {
393 return Err(UserInputError::ObjectInputArityViolation);
394 }
395
396 let gas_coins: HashSet<ObjectID> =
397 HashSet::from_iter(transaction.gas().iter().map(|obj_ref| obj_ref.0));
398 for object in objects.iter() {
399 let input_object_kind = object.input_object_kind;
400
401 match &object.object {
402 ObjectReadResultKind::Object(object) => {
403 let owner_address = if gas_coins.contains(&object.id()) {
405 transaction.gas_owner()
406 } else {
407 transaction.sender()
408 };
409 let system_transaction = transaction.is_system_tx();
412 check_one_object(
413 &owner_address,
414 input_object_kind,
415 object,
416 system_transaction,
417 )?;
418 }
419 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
421 ObjectReadResultKind::CancelledTransactionSharedObject(_) => (),
424 }
425 }
426
427 Ok(())
428 }
429
430 fn check_one_object(
432 owner: &IotaAddress,
433 object_kind: InputObjectKind,
434 object: &Object,
435 system_transaction: bool,
436 ) -> UserInputResult {
437 match object_kind {
438 InputObjectKind::MovePackage(package_id) => {
439 fp_ensure!(
440 object.data.try_as_package().is_some(),
441 UserInputError::MoveObjectAsPackage {
442 object_id: package_id
443 }
444 );
445 }
446 InputObjectKind::ImmOrOwnedMoveObject((object_id, sequence_number, object_digest)) => {
447 fp_ensure!(
448 !object.is_package(),
449 UserInputError::MovePackageAsObject { object_id }
450 );
451 fp_ensure!(
452 sequence_number < SequenceNumber::MAX,
453 UserInputError::InvalidSequenceNumber
454 );
455
456 assert_eq!(
458 object.version(),
459 sequence_number,
460 "The fetched object version {} does not match the requested version {}, object id: {}",
461 object.version(),
462 sequence_number,
463 object.id(),
464 );
465
466 let expected_digest = object.digest();
468 fp_ensure!(
469 expected_digest == object_digest,
470 UserInputError::InvalidObjectDigest {
471 object_id,
472 expected_digest
473 }
474 );
475
476 match object.owner {
477 Owner::Immutable => {
478 }
480 Owner::AddressOwner(actual_owner) => {
481 fp_ensure!(
483 owner == &actual_owner,
484 UserInputError::IncorrectUserSignature {
485 error: format!(
486 "Object {:?} is owned by account address {:?}, but given owner/signer address is {:?}",
487 object_id, actual_owner, owner
488 ),
489 }
490 );
491 }
492 Owner::ObjectOwner(owner) => {
493 return Err(UserInputError::InvalidChildObjectArgument {
494 child_id: object.id(),
495 parent_id: owner.into(),
496 });
497 }
498 Owner::Shared { .. } => {
499 return Err(UserInputError::NotSharedObject);
502 }
503 };
504 }
505 InputObjectKind::SharedMoveObject {
506 id: IOTA_CLOCK_OBJECT_ID,
507 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
508 mutable: true,
509 } => {
510 if system_transaction {
513 return Ok(());
514 } else {
515 return Err(UserInputError::ImmutableParameterExpected {
516 object_id: IOTA_CLOCK_OBJECT_ID,
517 });
518 }
519 }
520 InputObjectKind::SharedMoveObject {
521 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
522 ..
523 } => {
524 if system_transaction {
525 return Ok(());
526 } else {
527 return Err(UserInputError::InaccessibleSystemObject {
528 object_id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
529 });
530 }
531 }
532 InputObjectKind::SharedMoveObject {
533 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
534 mutable: true,
535 ..
536 } => {
537 if system_transaction {
540 return Ok(());
541 } else {
542 return Err(UserInputError::ImmutableParameterExpected {
543 object_id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
544 });
545 }
546 }
547 InputObjectKind::SharedMoveObject {
548 initial_shared_version: input_initial_shared_version,
549 ..
550 } => {
551 fp_ensure!(
552 object.version() < SequenceNumber::MAX,
553 UserInputError::InvalidSequenceNumber
554 );
555
556 match object.owner {
557 Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
558 return Err(UserInputError::NotSharedObject);
560 }
561 Owner::Shared {
562 initial_shared_version: actual_initial_shared_version,
563 } => {
564 fp_ensure!(
565 input_initial_shared_version == actual_initial_shared_version,
566 UserInputError::SharedObjectStartingVersionMismatch
567 )
568 }
569 }
570 }
571 };
572 Ok(())
573 }
574
575 #[instrument(level = "trace", skip_all)]
577 pub fn check_non_system_packages_to_be_published(
578 transaction: &TransactionData,
579 protocol_config: &ProtocolConfig,
580 metrics: &Arc<BytecodeVerifierMetrics>,
581 verifier_signing_config: &VerifierSigningConfig,
582 ) -> UserInputResult<()> {
583 if transaction.is_system_tx() {
585 return Ok(());
586 }
587
588 let TransactionKind::ProgrammableTransaction(pt) = transaction.kind() else {
589 return Ok(());
590 };
591
592 let signing_limits = Some(verifier_signing_config.limits_for_signing());
595 let mut verifier = iota_execution::verifier(protocol_config, signing_limits, metrics);
596 let mut meter = verifier.meter(verifier_signing_config.meter_config_for_signing());
597
598 let shared_meter_verifier_timer = metrics
600 .verifier_runtime_per_ptb_success_latency
601 .start_timer();
602
603 let verifier_status = pt
604 .non_system_packages_to_be_published()
605 .try_for_each(|module_bytes| {
606 verifier.meter_module_bytes(protocol_config, module_bytes, meter.as_mut())
607 })
608 .map_err(|e| UserInputError::PackageVerificationTimedout { err: e.to_string() });
609
610 match verifier_status {
611 Ok(_) => {
612 shared_meter_verifier_timer.stop_and_record();
614 }
615 Err(err) => {
616 metrics
619 .verifier_runtime_per_ptb_timeout_latency
620 .observe(shared_meter_verifier_timer.stop_and_discard());
621 return Err(err);
622 }
623 };
624
625 Ok(())
626 }
627}