1use crate::cache::Completed;
2use crate::error::TransactionErrorSource;
3use crate::{TransactionError, TxVerifyEnv};
4use ckb_chain_spec::consensus::Consensus;
5use ckb_dao::DaoCalculator;
6use ckb_dao_utils::DaoError;
7use ckb_error::Error;
8#[cfg(not(target_family = "wasm"))]
9use ckb_script::ChunkCommand;
10use ckb_script::{TransactionScriptsVerifier, TransactionState};
11use ckb_traits::{
12 CellDataProvider, EpochProvider, ExtensionProvider, HeaderFieldsProvider, HeaderProvider,
13};
14use ckb_types::{
15 core::{
16 cell::{CellMeta, ResolvedTransaction},
17 Capacity, Cycle, EpochNumberWithFraction, ScriptHashType, TransactionView, Version,
18 },
19 packed::{Byte32, CellOutput},
20 prelude::*,
21};
22use std::collections::HashSet;
23use std::sync::Arc;
24
25pub struct TimeRelativeTransactionVerifier<M> {
31 pub(crate) maturity: MaturityVerifier,
32 pub(crate) since: SinceVerifier<M>,
33}
34
35impl<DL: HeaderFieldsProvider> TimeRelativeTransactionVerifier<DL> {
36 pub fn new(
38 rtx: Arc<ResolvedTransaction>,
39 consensus: Arc<Consensus>,
40 data_loader: DL,
41 tx_env: Arc<TxVerifyEnv>,
42 ) -> Self {
43 TimeRelativeTransactionVerifier {
44 maturity: MaturityVerifier::new(
45 Arc::clone(&rtx),
46 tx_env.epoch(),
47 consensus.cellbase_maturity(),
48 ),
49 since: SinceVerifier::new(rtx, consensus, data_loader, tx_env),
50 }
51 }
52
53 pub fn verify(&self) -> Result<(), Error> {
55 self.maturity.verify()?;
56 self.since.verify()?;
57 Ok(())
58 }
59}
60
61pub struct NonContextualTransactionVerifier<'a> {
71 pub(crate) version: VersionVerifier<'a>,
72 pub(crate) size: SizeVerifier<'a>,
73 pub(crate) empty: EmptyVerifier<'a>,
74 pub(crate) duplicate_deps: DuplicateDepsVerifier<'a>,
75 pub(crate) outputs_data_verifier: OutputsDataVerifier<'a>,
76}
77
78impl<'a> NonContextualTransactionVerifier<'a> {
79 pub fn new(tx: &'a TransactionView, consensus: &'a Consensus) -> Self {
81 NonContextualTransactionVerifier {
82 version: VersionVerifier::new(tx, consensus.tx_version()),
83 size: SizeVerifier::new(tx, consensus.max_block_bytes()),
84 empty: EmptyVerifier::new(tx),
85 duplicate_deps: DuplicateDepsVerifier::new(tx),
86 outputs_data_verifier: OutputsDataVerifier::new(tx),
87 }
88 }
89
90 pub fn verify(&self) -> Result<(), Error> {
92 self.version.verify()?;
93 self.size.verify()?;
94 self.empty.verify()?;
95 self.duplicate_deps.verify()?;
96 self.outputs_data_verifier.verify()?;
97 Ok(())
98 }
99}
100
101pub struct ContextualTransactionVerifier<DL>
110where
111 DL: Send + Sync + Clone + CellDataProvider + HeaderProvider + ExtensionProvider + 'static,
112{
113 pub(crate) compatible: CompatibleVerifier,
114 pub(crate) time_relative: TimeRelativeTransactionVerifier<DL>,
115 pub(crate) capacity: CapacityVerifier,
116 pub(crate) script: ScriptVerifier<DL>,
117 pub(crate) fee_calculator: FeeCalculator<DL>,
118}
119
120impl<DL> ContextualTransactionVerifier<DL>
121where
122 DL: CellDataProvider
123 + HeaderProvider
124 + ExtensionProvider
125 + HeaderFieldsProvider
126 + EpochProvider
127 + Send
128 + Sync
129 + Clone
130 + 'static,
131{
132 pub fn new(
134 rtx: Arc<ResolvedTransaction>,
135 consensus: Arc<Consensus>,
136 data_loader: DL,
137 tx_env: Arc<TxVerifyEnv>,
138 ) -> Self {
139 ContextualTransactionVerifier {
140 compatible: CompatibleVerifier::new(
141 Arc::clone(&rtx),
142 Arc::clone(&consensus),
143 Arc::clone(&tx_env),
144 ),
145 time_relative: TimeRelativeTransactionVerifier::new(
146 Arc::clone(&rtx),
147 Arc::clone(&consensus),
148 data_loader.clone(),
149 Arc::clone(&tx_env),
150 ),
151 script: TransactionScriptsVerifier::new(
152 Arc::clone(&rtx),
153 data_loader.clone(),
154 Arc::clone(&consensus),
155 Arc::clone(&tx_env),
156 ),
157 capacity: CapacityVerifier::new(Arc::clone(&rtx), consensus.dao_type_hash()),
158 fee_calculator: FeeCalculator::new(rtx, consensus, data_loader),
159 }
160 }
161
162 pub fn verify(&self, max_cycles: Cycle, skip_script_verify: bool) -> Result<Completed, Error> {
166 self.compatible.verify()?;
167 self.time_relative.verify()?;
168 self.capacity.verify()?;
169 let cycles = if skip_script_verify {
170 0
171 } else {
172 self.script.verify(max_cycles)?
173 };
174 let fee = self.fee_calculator.transaction_fee()?;
175 Ok(Completed { cycles, fee })
176 }
177
178 #[cfg(not(target_family = "wasm"))]
181 pub async fn verify_with_pause(
182 &self,
183 max_cycles: Cycle,
184 command_rx: &mut tokio::sync::watch::Receiver<ChunkCommand>,
185 ) -> Result<Completed, Error> {
186 self.compatible.verify()?;
187 self.time_relative.verify()?;
188 self.capacity.verify()?;
189 let fee = self.fee_calculator.transaction_fee()?;
190 let cycles = self
191 .script
192 .resumable_verify_with_signal(max_cycles, command_rx)
193 .await?;
194 Ok(Completed { cycles, fee })
195 }
196
197 pub fn complete(
201 &self,
202 max_cycles: Cycle,
203 skip_script_verify: bool,
204 state: &TransactionState,
205 ) -> Result<Completed, Error> {
206 self.compatible.verify()?;
207 self.time_relative.verify()?;
208 self.capacity.verify()?;
209 let cycles = if skip_script_verify {
210 0
211 } else {
212 self.script.complete(state, max_cycles)?
213 };
214 let fee = self.fee_calculator.transaction_fee()?;
215 Ok(Completed { cycles, fee })
216 }
217}
218
219pub struct FeeCalculator<DL> {
253 transaction: Arc<ResolvedTransaction>,
254 consensus: Arc<Consensus>,
255 data_loader: DL,
256}
257
258impl<DL: CellDataProvider + HeaderProvider + ExtensionProvider + EpochProvider> FeeCalculator<DL> {
259 fn new(
260 transaction: Arc<ResolvedTransaction>,
261 consensus: Arc<Consensus>,
262 data_loader: DL,
263 ) -> Self {
264 Self {
265 transaction,
266 consensus,
267 data_loader,
268 }
269 }
270
271 fn transaction_fee(&self) -> Result<Capacity, DaoError> {
272 if self.transaction.is_cellbase() {
274 Ok(Capacity::zero())
275 } else {
276 DaoCalculator::new(self.consensus.as_ref(), &self.data_loader)
277 .transaction_fee(&self.transaction)
278 }
279 }
280}
281
282pub struct VersionVerifier<'a> {
283 transaction: &'a TransactionView,
284 tx_version: Version,
285}
286
287impl<'a> VersionVerifier<'a> {
288 pub fn new(transaction: &'a TransactionView, tx_version: Version) -> Self {
289 VersionVerifier {
290 transaction,
291 tx_version,
292 }
293 }
294
295 pub fn verify(&self) -> Result<(), Error> {
296 if self.transaction.version() != self.tx_version {
297 return Err((TransactionError::MismatchedVersion {
298 expected: self.tx_version,
299 actual: self.transaction.version(),
300 })
301 .into());
302 }
303 Ok(())
304 }
305}
306
307pub struct SizeVerifier<'a> {
308 transaction: &'a TransactionView,
309 block_bytes_limit: u64,
310}
311
312impl<'a> SizeVerifier<'a> {
313 pub fn new(transaction: &'a TransactionView, block_bytes_limit: u64) -> Self {
314 SizeVerifier {
315 transaction,
316 block_bytes_limit,
317 }
318 }
319
320 pub fn verify(&self) -> Result<(), Error> {
321 let size = self.transaction.data().serialized_size_in_block() as u64;
322 if size <= self.block_bytes_limit {
323 Ok(())
324 } else {
325 Err(TransactionError::ExceededMaximumBlockBytes {
326 actual: size,
327 limit: self.block_bytes_limit,
328 }
329 .into())
330 }
331 }
332}
333
334pub type ScriptVerifier<DL> = TransactionScriptsVerifier<DL>;
340
341pub struct EmptyVerifier<'a> {
342 transaction: &'a TransactionView,
343}
344
345impl<'a> EmptyVerifier<'a> {
346 pub fn new(transaction: &'a TransactionView) -> Self {
347 EmptyVerifier { transaction }
348 }
349
350 pub fn verify(&self) -> Result<(), Error> {
351 if self.transaction.inputs().is_empty() {
352 Err(TransactionError::Empty {
353 inner: TransactionErrorSource::Inputs,
354 }
355 .into())
356 } else if self.transaction.outputs().is_empty() && !self.transaction.is_cellbase() {
357 Err(TransactionError::Empty {
358 inner: TransactionErrorSource::Outputs,
359 }
360 .into())
361 } else {
362 Ok(())
363 }
364 }
365}
366
367pub struct MaturityVerifier {
371 transaction: Arc<ResolvedTransaction>,
372 epoch: EpochNumberWithFraction,
373 cellbase_maturity: EpochNumberWithFraction,
374}
375
376impl MaturityVerifier {
377 pub fn new(
378 transaction: Arc<ResolvedTransaction>,
379 epoch: EpochNumberWithFraction,
380 cellbase_maturity: EpochNumberWithFraction,
381 ) -> Self {
382 MaturityVerifier {
383 transaction,
384 epoch,
385 cellbase_maturity,
386 }
387 }
388
389 pub fn verify(&self) -> Result<(), Error> {
390 let cellbase_immature = |meta: &CellMeta| -> bool {
391 meta.transaction_info
392 .as_ref()
393 .map(|info| {
394 info.block_number > 0 && info.is_cellbase() && {
395 let threshold =
396 self.cellbase_maturity.to_rational() + info.block_epoch.to_rational();
397 let current = self.epoch.to_rational();
398 current < threshold
399 }
400 })
401 .unwrap_or(false)
402 };
403
404 if let Some(index) = self
405 .transaction
406 .resolved_inputs
407 .iter()
408 .position(cellbase_immature)
409 {
410 return Err(TransactionError::CellbaseImmaturity {
411 inner: TransactionErrorSource::Inputs,
412 index,
413 }
414 .into());
415 }
416
417 if let Some(index) = self
418 .transaction
419 .resolved_cell_deps
420 .iter()
421 .position(cellbase_immature)
422 {
423 return Err(TransactionError::CellbaseImmaturity {
424 inner: TransactionErrorSource::CellDeps,
425 index,
426 }
427 .into());
428 }
429
430 Ok(())
431 }
432}
433
434pub struct DuplicateDepsVerifier<'a> {
435 transaction: &'a TransactionView,
436}
437
438impl<'a> DuplicateDepsVerifier<'a> {
439 pub fn new(transaction: &'a TransactionView) -> Self {
440 DuplicateDepsVerifier { transaction }
441 }
442
443 pub fn verify(&self) -> Result<(), Error> {
444 let transaction = self.transaction;
445 let mut seen_cells = HashSet::with_capacity(self.transaction.cell_deps().len());
446 let mut seen_headers = HashSet::with_capacity(self.transaction.header_deps().len());
447
448 if let Some(dep) = transaction
449 .cell_deps_iter()
450 .find_map(|dep| seen_cells.replace(dep))
451 {
452 return Err(TransactionError::DuplicateCellDeps {
453 out_point: dep.out_point(),
454 }
455 .into());
456 }
457 if let Some(hash) = transaction
458 .header_deps_iter()
459 .find_map(|hash| seen_headers.replace(hash))
460 {
461 return Err(TransactionError::DuplicateHeaderDeps { hash }.into());
462 }
463 Ok(())
464 }
465}
466
467pub struct CapacityVerifier {
469 resolved_transaction: Arc<ResolvedTransaction>,
470 dao_type_hash: Byte32,
471}
472
473impl CapacityVerifier {
474 pub fn new(resolved_transaction: Arc<ResolvedTransaction>, dao_type_hash: Byte32) -> Self {
476 CapacityVerifier {
477 resolved_transaction,
478 dao_type_hash,
479 }
480 }
481
482 pub fn verify(&self) -> Result<(), Error> {
485 if !(self.resolved_transaction.is_cellbase() || self.valid_dao_withdraw_transaction()) {
490 let inputs_sum = self.resolved_transaction.inputs_capacity()?;
491 let outputs_sum = self.resolved_transaction.outputs_capacity()?;
492
493 if inputs_sum < outputs_sum {
494 return Err((TransactionError::OutputsSumOverflow {
495 inputs_sum,
496 outputs_sum,
497 })
498 .into());
499 }
500 }
501
502 for (index, (output, data)) in self
503 .resolved_transaction
504 .transaction
505 .outputs_with_data_iter()
506 .enumerate()
507 {
508 let data_occupied_capacity = Capacity::bytes(data.len())?;
509 if output.is_lack_of_capacity(data_occupied_capacity)? {
510 return Err((TransactionError::InsufficientCellCapacity {
511 index,
512 inner: TransactionErrorSource::Outputs,
513 capacity: output.capacity().unpack(),
514 occupied_capacity: output.occupied_capacity(data_occupied_capacity)?,
515 })
516 .into());
517 }
518 }
519
520 Ok(())
521 }
522
523 fn valid_dao_withdraw_transaction(&self) -> bool {
524 self.resolved_transaction
525 .resolved_inputs
526 .iter()
527 .any(|cell_meta| cell_uses_dao_type_script(&cell_meta.cell_output, &self.dao_type_hash))
528 }
529}
530
531fn cell_uses_dao_type_script(cell_output: &CellOutput, dao_type_hash: &Byte32) -> bool {
532 cell_output
533 .type_()
534 .to_opt()
535 .map(|t| {
536 Into::<u8>::into(t.hash_type()) == Into::<u8>::into(ScriptHashType::Type)
537 && &t.code_hash() == dao_type_hash
538 })
539 .unwrap_or(false)
540}
541
542const LOCK_TYPE_FLAG: u64 = 1 << 63;
543const METRIC_TYPE_FLAG_MASK: u64 = 0x6000_0000_0000_0000;
544const VALUE_MASK: u64 = 0x00ff_ffff_ffff_ffff;
545const REMAIN_FLAGS_BITS: u64 = 0x1f00_0000_0000_0000;
546
547pub enum SinceMetric {
549 BlockNumber(u64),
551 EpochNumberWithFraction(EpochNumberWithFraction),
553 Timestamp(u64),
555}
556
557#[derive(Copy, Clone, Debug)]
561pub struct Since(pub u64);
562
563impl Since {
564 pub fn is_absolute(self) -> bool {
566 self.0 & LOCK_TYPE_FLAG == 0
567 }
568
569 #[inline]
571 pub fn is_relative(self) -> bool {
572 !self.is_absolute()
573 }
574
575 pub fn flags_is_valid(self) -> bool {
577 (self.0 & REMAIN_FLAGS_BITS == 0)
578 && ((self.0 & METRIC_TYPE_FLAG_MASK) != METRIC_TYPE_FLAG_MASK)
579 }
580
581 pub fn extract_metric(self) -> Option<SinceMetric> {
583 let value = self.0 & VALUE_MASK;
584 match self.0 & METRIC_TYPE_FLAG_MASK {
585 0x0000_0000_0000_0000 => Some(SinceMetric::BlockNumber(value)),
587 0x2000_0000_0000_0000 => Some(SinceMetric::EpochNumberWithFraction(
589 EpochNumberWithFraction::from_full_value_unchecked(value),
590 )),
591 0x4000_0000_0000_0000 => Some(SinceMetric::Timestamp(value * 1000)),
593 _ => None,
594 }
595 }
596}
597
598pub struct SinceVerifier<DL> {
603 rtx: Arc<ResolvedTransaction>,
604 consensus: Arc<Consensus>,
605 data_loader: DL,
606 tx_env: Arc<TxVerifyEnv>,
607}
608
609impl<DL: HeaderFieldsProvider> SinceVerifier<DL> {
610 pub fn new(
611 rtx: Arc<ResolvedTransaction>,
612 consensus: Arc<Consensus>,
613 data_loader: DL,
614 tx_env: Arc<TxVerifyEnv>,
615 ) -> Self {
616 SinceVerifier {
617 rtx,
618 consensus,
619 data_loader,
620 tx_env,
621 }
622 }
623
624 fn parent_median_time(&self, block_hash: &Byte32) -> u64 {
625 let header_fields = self
626 .data_loader
627 .get_header_fields(block_hash)
628 .expect("parent block exist");
629 self.block_median_time(&header_fields.parent_hash)
630 }
631
632 fn block_median_time(&self, block_hash: &Byte32) -> u64 {
633 let median_block_count = self.consensus.median_time_block_count();
634 self.data_loader
635 .block_median_time(block_hash, median_block_count)
636 }
637
638 fn verify_absolute_lock(&self, index: usize, since: Since) -> Result<(), Error> {
639 if since.is_absolute() {
640 match since.extract_metric() {
641 Some(SinceMetric::BlockNumber(block_number)) => {
642 let proposal_window = self.consensus.tx_proposal_window();
643 if self.tx_env.block_number(proposal_window) < block_number {
644 return Err((TransactionError::Immature { index }).into());
645 }
646 }
647 Some(SinceMetric::EpochNumberWithFraction(epoch_number_with_fraction)) => {
648 if !epoch_number_with_fraction.is_well_formed_increment() {
649 return Err((TransactionError::InvalidSince { index }).into());
650 }
651 let a = self.tx_env.epoch().to_rational();
652 let b = epoch_number_with_fraction.normalize().to_rational();
653 if a < b {
654 return Err((TransactionError::Immature { index }).into());
655 }
656 }
657 Some(SinceMetric::Timestamp(timestamp)) => {
658 let parent_hash = self.tx_env.parent_hash();
659 let tip_timestamp = self.block_median_time(&parent_hash);
660 if tip_timestamp < timestamp {
661 return Err((TransactionError::Immature { index }).into());
662 }
663 }
664 None => {
665 return Err((TransactionError::InvalidSince { index }).into());
666 }
667 }
668 }
669 Ok(())
670 }
671
672 fn verify_relative_lock(
673 &self,
674 index: usize,
675 since: Since,
676 cell_meta: &CellMeta,
677 ) -> Result<(), Error> {
678 if since.is_relative() {
679 let info = match cell_meta.transaction_info {
680 Some(ref transaction_info) => Ok(transaction_info),
681 None => Err(TransactionError::Immature { index }),
682 }?;
683 match since.extract_metric() {
684 Some(SinceMetric::BlockNumber(block_number)) => {
685 let proposal_window = self.consensus.tx_proposal_window();
686 if self.tx_env.block_number(proposal_window) < info.block_number + block_number
687 {
688 return Err((TransactionError::Immature { index }).into());
689 }
690 }
691 Some(SinceMetric::EpochNumberWithFraction(epoch_number_with_fraction)) => {
692 if !epoch_number_with_fraction.is_well_formed_increment() {
693 return Err((TransactionError::InvalidSince { index }).into());
694 }
695 let a = self.tx_env.epoch().to_rational();
696 let b = info.block_epoch.to_rational()
697 + epoch_number_with_fraction.normalize().to_rational();
698 if a < b {
699 return Err((TransactionError::Immature { index }).into());
700 }
701 }
702 Some(SinceMetric::Timestamp(timestamp)) => {
703 let proposal_window = self.consensus.tx_proposal_window();
708 let parent_hash = self.tx_env.parent_hash();
709 let epoch_number = self.tx_env.epoch_number(proposal_window);
710 let hardfork_switch = self.consensus.hardfork_switch();
711 let base_timestamp = if hardfork_switch
712 .ckb2021
713 .is_block_ts_as_relative_since_start_enabled(epoch_number)
714 {
715 self.data_loader
716 .get_header_fields(&info.block_hash)
717 .expect("header exist")
718 .timestamp
719 } else {
720 self.parent_median_time(&info.block_hash)
721 };
722 let current_median_time = self.block_median_time(&parent_hash);
723 if current_median_time < base_timestamp + timestamp {
724 return Err((TransactionError::Immature { index }).into());
725 }
726 }
727 None => {
728 return Err((TransactionError::InvalidSince { index }).into());
729 }
730 }
731 }
732 Ok(())
733 }
734
735 pub fn verify(&self) -> Result<(), Error> {
736 for (index, (cell_meta, input)) in self
737 .rtx
738 .resolved_inputs
739 .iter()
740 .zip(self.rtx.transaction.inputs())
741 .enumerate()
742 {
743 let since: u64 = input.since().unpack();
745 if since == 0 {
746 continue;
747 }
748 let since = Since(since);
749 if !since.flags_is_valid() {
751 return Err((TransactionError::InvalidSince { index }).into());
752 }
753
754 self.verify_absolute_lock(index, since)?;
756 self.verify_relative_lock(index, since, cell_meta)?;
757 }
758 Ok(())
759 }
760}
761
762pub struct OutputsDataVerifier<'a> {
763 transaction: &'a TransactionView,
764}
765
766impl<'a> OutputsDataVerifier<'a> {
767 pub fn new(transaction: &'a TransactionView) -> Self {
768 Self { transaction }
769 }
770
771 pub fn verify(&self) -> Result<(), TransactionError> {
772 let outputs_len = self.transaction.outputs().len();
773 let outputs_data_len = self.transaction.outputs_data().len();
774
775 if outputs_len != outputs_data_len {
776 return Err(TransactionError::OutputsDataLengthMismatch {
777 outputs_len,
778 outputs_data_len,
779 });
780 }
781 Ok(())
782 }
783}
784
785pub struct CompatibleVerifier {
794 rtx: Arc<ResolvedTransaction>,
795 consensus: Arc<Consensus>,
796 tx_env: Arc<TxVerifyEnv>,
797}
798
799impl CompatibleVerifier {
800 pub fn new(
801 rtx: Arc<ResolvedTransaction>,
802 consensus: Arc<Consensus>,
803 tx_env: Arc<TxVerifyEnv>,
804 ) -> Self {
805 Self {
806 rtx,
807 consensus,
808 tx_env,
809 }
810 }
811
812 pub fn verify(&self) -> Result<(), Error> {
813 let proposal_window = self.consensus.tx_proposal_window();
814 let epoch_number = self.tx_env.epoch_number(proposal_window);
815 if !self
816 .consensus
817 .hardfork_switch()
818 .ckb2023
819 .is_vm_version_2_and_syscalls_3_enabled(epoch_number)
820 {
821 for ht in self
822 .rtx
823 .transaction
824 .outputs()
825 .into_iter()
826 .map(|output| output.lock().hash_type())
827 {
828 let hash_type: ScriptHashType = ht.try_into().map_err(|_| {
829 let val: u8 = ht.into();
830 TransactionError::Internal {
832 description: format!("unknown hash type {:02x}", val),
833 }
834 })?;
835 if hash_type == ScriptHashType::Data2 {
836 return Err(TransactionError::Compatible {
837 feature: "VM Version 2",
838 }
839 .into());
840 }
841 }
842 }
843 Ok(())
844 }
845}
846
847pub struct DaoScriptSizeVerifier<DL> {
850 resolved_transaction: Arc<ResolvedTransaction>,
851 consensus: Arc<Consensus>,
852 data_loader: DL,
853}
854
855impl<DL: CellDataProvider> DaoScriptSizeVerifier<DL> {
856 pub fn new(
858 resolved_transaction: Arc<ResolvedTransaction>,
859 consensus: Arc<Consensus>,
860 data_loader: DL,
861 ) -> Self {
862 DaoScriptSizeVerifier {
863 resolved_transaction,
864 consensus,
865 data_loader,
866 }
867 }
868
869 fn dao_type_hash(&self) -> Byte32 {
870 self.consensus.dao_type_hash()
871 }
872
873 pub fn verify(&self) -> Result<(), Error> {
876 let dao_type_hash = self.dao_type_hash();
877 for (i, (input_meta, cell_output)) in self
878 .resolved_transaction
879 .resolved_inputs
880 .iter()
881 .zip(self.resolved_transaction.transaction.outputs())
882 .enumerate()
883 {
884 if !(cell_uses_dao_type_script(&input_meta.cell_output, &dao_type_hash)
886 && cell_uses_dao_type_script(&cell_output, &dao_type_hash))
887 {
888 continue;
889 }
890
891 let input_data = match self.data_loader.load_cell_data(input_meta) {
893 Some(data) => data,
894 None => continue,
895 };
896
897 if input_data.into_iter().any(|b| b != 0) {
899 continue;
900 }
901
902 if let Some(info) = &input_meta.transaction_info {
905 if info.block_number
906 < self
907 .consensus
908 .starting_block_limiting_dao_withdrawing_lock()
909 {
910 continue;
911 }
912 }
913
914 if input_meta.cell_output.lock().total_size() != cell_output.lock().total_size() {
917 return Err((TransactionError::DaoLockSizeMismatch { index: i }).into());
918 }
919 }
920 Ok(())
921 }
922}