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 {kind} is not supported in dev-inspect"
169 ))
170 .into());
171 }
172 let mut used_objects: HashSet<IotaAddress> = HashSet::new();
173 for input_object in input_objects.iter() {
174 let Some(object) = input_object.as_object() else {
175 continue;
177 };
178
179 if !object.is_immutable() {
180 fp_ensure!(
181 used_objects.insert(object.id().into()),
182 UserInputError::MutableObjectUsedMoreThanOnce {
183 object_id: object.id()
184 }
185 .into()
186 );
187 }
188 }
189
190 Ok(input_objects.into_checked())
191 }
192
193 fn check_transaction_input_inner(
195 protocol_config: &ProtocolConfig,
196 reference_gas_price: u64,
197 transaction: &TransactionData,
198 input_objects: &InputObjects,
199 gas_override: &[ObjectRef],
201 ) -> IotaResult<IotaGasStatus> {
202 let gas = if gas_override.is_empty() {
204 transaction.gas()
205 } else {
206 gas_override
207 };
208
209 let gas_status = get_gas_status(
210 input_objects,
211 gas,
212 protocol_config,
213 reference_gas_price,
214 transaction,
215 )?;
216 check_objects(transaction, input_objects)?;
217
218 Ok(gas_status)
219 }
220
221 fn check_receiving_objects(
222 input_objects: &InputObjects,
223 receiving_objects: &ReceivingObjects,
224 ) -> Result<(), IotaError> {
225 let mut objects_in_txn: HashSet<_> = input_objects
226 .object_kinds()
227 .map(|x| x.object_id())
228 .collect();
229
230 for ReceivingObjectReadResult {
239 object_ref: (object_id, version, object_digest),
240 object,
241 } in receiving_objects.iter()
242 {
243 fp_ensure!(
244 *version < SequenceNumber::MAX,
245 UserInputError::InvalidSequenceNumber.into()
246 );
247
248 let Some(object) = object.as_object() else {
249 continue;
251 };
252
253 if !(object.owner.is_address_owned()
254 && object.version() == *version
255 && object.digest() == *object_digest)
256 {
257 fp_ensure!(
259 object.version() == *version,
260 UserInputError::ObjectVersionUnavailableForConsumption {
261 provided_obj_ref: (*object_id, *version, *object_digest),
262 current_version: object.version(),
263 }
264 .into()
265 );
266
267 fp_ensure!(
269 !object.is_package(),
270 UserInputError::MovePackageAsObject {
271 object_id: *object_id
272 }
273 .into()
274 );
275
276 let expected_digest = object.digest();
278 fp_ensure!(
279 expected_digest == *object_digest,
280 UserInputError::InvalidObjectDigest {
281 object_id: *object_id,
282 expected_digest
283 }
284 .into()
285 );
286
287 match object.owner {
288 Owner::AddressOwner(_) => {
289 debug_assert!(
290 false,
291 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
292 (*object_id, *version, *object_id),
293 object
294 );
295 error!(
296 "Receiving object {:?} is invalid but we expect it should be valid. {:?}",
297 (*object_id, *version, *object_id),
298 object
299 );
300 fp_bail!(
303 UserInputError::ObjectNotFound {
304 object_id: *object_id,
305 version: Some(*version),
306 }
307 .into()
308 )
309 }
310 Owner::ObjectOwner(owner) => {
311 fp_bail!(
312 UserInputError::InvalidChildObjectArgument {
313 child_id: object.id(),
314 parent_id: owner.into(),
315 }
316 .into()
317 )
318 }
319 Owner::Shared { .. } => fp_bail!(UserInputError::NotSharedObject.into()),
320 Owner::Immutable => fp_bail!(
321 UserInputError::MutableParameterExpected {
322 object_id: *object_id
323 }
324 .into()
325 ),
326 };
327 }
328
329 fp_ensure!(
330 !objects_in_txn.contains(object_id),
331 UserInputError::DuplicateObjectRefInput.into()
332 );
333
334 objects_in_txn.insert(*object_id);
335 }
336 Ok(())
337 }
338
339 #[instrument(level = "trace", skip_all)]
342 fn check_gas(
343 objects: &InputObjects,
344 protocol_config: &ProtocolConfig,
345 reference_gas_price: u64,
346 gas: &[ObjectRef],
347 gas_budget: u64,
348 gas_price: u64,
349 tx_kind: &TransactionKind,
350 ) -> IotaResult<IotaGasStatus> {
351 if tx_kind.is_system_tx() {
352 Ok(IotaGasStatus::new_unmetered())
353 } else {
354 let gas_status =
355 IotaGasStatus::new(gas_budget, gas_price, reference_gas_price, protocol_config)?;
356
357 let objects: BTreeMap<_, _> = objects.iter().map(|o| (o.id(), o)).collect();
360 let mut gas_objects = vec![];
361 for obj_ref in gas {
362 let obj = objects.get(&obj_ref.0);
363 let obj = *obj.ok_or(UserInputError::ObjectNotFound {
364 object_id: obj_ref.0,
365 version: Some(obj_ref.1),
366 })?;
367 gas_objects.push(obj);
368 }
369 gas_status.check_gas_balance(&gas_objects, gas_budget)?;
370 Ok(gas_status)
371 }
372 }
373
374 #[instrument(level = "trace", skip_all)]
377 fn check_objects(transaction: &TransactionData, objects: &InputObjects) -> UserInputResult<()> {
378 let mut used_objects: HashSet<IotaAddress> = HashSet::new();
380 for object in objects.iter() {
381 if object.is_mutable() {
382 fp_ensure!(
383 used_objects.insert(object.id().into()),
384 UserInputError::MutableObjectUsedMoreThanOnce {
385 object_id: object.id()
386 }
387 );
388 }
389 }
390
391 if !transaction.is_genesis_tx() && objects.is_empty() {
392 return Err(UserInputError::ObjectInputArityViolation);
393 }
394
395 let gas_coins: HashSet<ObjectID> =
396 HashSet::from_iter(transaction.gas().iter().map(|obj_ref| obj_ref.0));
397 for object in objects.iter() {
398 let input_object_kind = object.input_object_kind;
399
400 match &object.object {
401 ObjectReadResultKind::Object(object) => {
402 let owner_address = if gas_coins.contains(&object.id()) {
404 transaction.gas_owner()
405 } else {
406 transaction.sender()
407 };
408 let system_transaction = transaction.is_system_tx();
411 check_one_object(
412 &owner_address,
413 input_object_kind,
414 object,
415 system_transaction,
416 )?;
417 }
418 ObjectReadResultKind::DeletedSharedObject(_, _) => (),
420 ObjectReadResultKind::CancelledTransactionSharedObject(_) => (),
423 }
424 }
425
426 Ok(())
427 }
428
429 fn check_one_object(
431 owner: &IotaAddress,
432 object_kind: InputObjectKind,
433 object: &Object,
434 system_transaction: bool,
435 ) -> UserInputResult {
436 match object_kind {
437 InputObjectKind::MovePackage(package_id) => {
438 fp_ensure!(
439 object.data.try_as_package().is_some(),
440 UserInputError::MoveObjectAsPackage {
441 object_id: package_id
442 }
443 );
444 }
445 InputObjectKind::ImmOrOwnedMoveObject((object_id, sequence_number, object_digest)) => {
446 fp_ensure!(
447 !object.is_package(),
448 UserInputError::MovePackageAsObject { object_id }
449 );
450 fp_ensure!(
451 sequence_number < SequenceNumber::MAX,
452 UserInputError::InvalidSequenceNumber
453 );
454
455 assert_eq!(
457 object.version(),
458 sequence_number,
459 "The fetched object version {} does not match the requested version {}, object id: {}",
460 object.version(),
461 sequence_number,
462 object.id(),
463 );
464
465 let expected_digest = object.digest();
467 fp_ensure!(
468 expected_digest == object_digest,
469 UserInputError::InvalidObjectDigest {
470 object_id,
471 expected_digest
472 }
473 );
474
475 match object.owner {
476 Owner::Immutable => {
477 }
479 Owner::AddressOwner(actual_owner) => {
480 fp_ensure!(
482 owner == &actual_owner,
483 UserInputError::IncorrectUserSignature {
484 error: format!(
485 "Object {object_id:?} is owned by account address {actual_owner:?}, but given owner/signer address is {owner:?}"
486 ),
487 }
488 );
489 }
490 Owner::ObjectOwner(owner) => {
491 return Err(UserInputError::InvalidChildObjectArgument {
492 child_id: object.id(),
493 parent_id: owner.into(),
494 });
495 }
496 Owner::Shared { .. } => {
497 return Err(UserInputError::NotSharedObject);
500 }
501 };
502 }
503 InputObjectKind::SharedMoveObject {
504 id: IOTA_CLOCK_OBJECT_ID,
505 initial_shared_version: IOTA_CLOCK_OBJECT_SHARED_VERSION,
506 mutable: true,
507 } => {
508 if system_transaction {
511 return Ok(());
512 } else {
513 return Err(UserInputError::ImmutableParameterExpected {
514 object_id: IOTA_CLOCK_OBJECT_ID,
515 });
516 }
517 }
518 InputObjectKind::SharedMoveObject {
519 id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
520 ..
521 } => {
522 if system_transaction {
523 return Ok(());
524 } else {
525 return Err(UserInputError::InaccessibleSystemObject {
526 object_id: IOTA_AUTHENTICATOR_STATE_OBJECT_ID,
527 });
528 }
529 }
530 InputObjectKind::SharedMoveObject {
531 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
532 mutable: true,
533 ..
534 } => {
535 if system_transaction {
538 return Ok(());
539 } else {
540 return Err(UserInputError::ImmutableParameterExpected {
541 object_id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
542 });
543 }
544 }
545 InputObjectKind::SharedMoveObject {
546 initial_shared_version: input_initial_shared_version,
547 ..
548 } => {
549 fp_ensure!(
550 object.version() < SequenceNumber::MAX,
551 UserInputError::InvalidSequenceNumber
552 );
553
554 match object.owner {
555 Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
556 return Err(UserInputError::NotSharedObject);
558 }
559 Owner::Shared {
560 initial_shared_version: actual_initial_shared_version,
561 } => {
562 fp_ensure!(
563 input_initial_shared_version == actual_initial_shared_version,
564 UserInputError::SharedObjectStartingVersionMismatch
565 )
566 }
567 }
568 }
569 };
570 Ok(())
571 }
572
573 #[instrument(level = "trace", skip_all)]
575 pub fn check_non_system_packages_to_be_published(
576 transaction: &TransactionData,
577 protocol_config: &ProtocolConfig,
578 metrics: &Arc<BytecodeVerifierMetrics>,
579 verifier_signing_config: &VerifierSigningConfig,
580 ) -> UserInputResult<()> {
581 if transaction.is_system_tx() {
583 return Ok(());
584 }
585
586 let TransactionKind::ProgrammableTransaction(pt) = transaction.kind() else {
587 return Ok(());
588 };
589
590 let signing_limits = Some(verifier_signing_config.limits_for_signing());
593 let mut verifier = iota_execution::verifier(protocol_config, signing_limits, metrics);
594 let mut meter = verifier.meter(verifier_signing_config.meter_config_for_signing());
595
596 let shared_meter_verifier_timer = metrics
598 .verifier_runtime_per_ptb_success_latency
599 .start_timer();
600
601 let verifier_status = pt
602 .non_system_packages_to_be_published()
603 .try_for_each(|module_bytes| {
604 verifier.meter_module_bytes(protocol_config, module_bytes, meter.as_mut())
605 })
606 .map_err(|e| UserInputError::PackageVerificationTimedout { err: e.to_string() });
607
608 match verifier_status {
609 Ok(_) => {
610 shared_meter_verifier_timer.stop_and_record();
612 }
613 Err(err) => {
614 metrics
617 .verifier_runtime_per_ptb_timeout_latency
618 .observe(shared_meter_verifier_timer.stop_and_discard());
619 return Err(err);
620 }
621 };
622
623 Ok(())
624 }
625}