1use crate::cache::Completed;
2use crate::error::TransactionErrorSource;
3use crate::{TransactionError, TxVerifyEnv};
4use ckb_chain_spec::consensus::Consensus;
5use ckb_constant::consensus::ENABLED_SCRIPT_HASH_TYPE;
6use ckb_dao::DaoCalculator;
7use ckb_dao_utils::DaoError;
8use ckb_error::Error;
9#[cfg(not(target_family = "wasm"))]
10use ckb_script::ChunkCommand;
11use ckb_script::{ScriptError, TransactionScriptsVerifier};
12use ckb_traits::{
13 CellDataProvider, EpochProvider, ExtensionProvider, HeaderFieldsProvider, HeaderProvider,
14};
15use ckb_types::{
16 core::{
17 Capacity, Cycle, EpochNumberWithFraction, ScriptHashType, TransactionView, Version,
18 cell::{CellMeta, ResolvedTransaction},
19 },
20 packed::{Byte32, CellOutput},
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> {
72 pub(crate) version: VersionVerifier<'a>,
73 pub(crate) size: SizeVerifier<'a>,
74 pub(crate) empty: EmptyVerifier<'a>,
75 pub(crate) duplicate_deps: DuplicateDepsVerifier<'a>,
76 pub(crate) outputs_data_verifier: OutputsDataVerifier<'a>,
77 pub(crate) script_hash_type: ScriptHashTypeVerifier<'a>,
78}
79
80impl<'a> NonContextualTransactionVerifier<'a> {
81 pub fn new(tx: &'a TransactionView, consensus: &'a Consensus) -> Self {
83 NonContextualTransactionVerifier {
84 version: VersionVerifier::new(tx, consensus.tx_version()),
85 size: SizeVerifier::new(tx, consensus.max_block_bytes()),
86 empty: EmptyVerifier::new(tx),
87 duplicate_deps: DuplicateDepsVerifier::new(tx),
88 outputs_data_verifier: OutputsDataVerifier::new(tx),
89 script_hash_type: ScriptHashTypeVerifier::new(tx),
90 }
91 }
92
93 pub fn verify(&self) -> Result<(), Error> {
95 self.version.verify()?;
96 self.size.verify()?;
97 self.empty.verify()?;
98 self.duplicate_deps.verify()?;
99 self.outputs_data_verifier.verify()?;
100 self.script_hash_type.verify()?;
101 Ok(())
102 }
103}
104
105struct CachedScriptVerifier<DL>
107where
108 DL: Send + Sync + Clone + CellDataProvider + HeaderProvider + ExtensionProvider + 'static,
109{
110 inner: ScriptVerifier<DL>,
111 cached_cycles: Option<Cycle>,
112}
113
114impl<DL> CachedScriptVerifier<DL>
115where
116 DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
117{
118 fn new(
119 rtx: Arc<ResolvedTransaction>,
120 data_loader: DL,
121 consensus: Arc<Consensus>,
122 tx_env: Arc<TxVerifyEnv>,
123 cached_cycles: Option<Cycle>,
124 ) -> Self {
125 Self {
126 inner: TransactionScriptsVerifier::new(rtx, data_loader, consensus, tx_env),
127 cached_cycles,
128 }
129 }
130
131 fn verify(&self, max_cycles: Cycle) -> Result<Cycle, Error> {
132 match self.cached_cycles {
133 Some(cycles) if cycles <= max_cycles => Ok(cycles),
134 Some(_) => Err(ScriptError::ExceededMaximumCycles(max_cycles)
135 .unknown_source()
136 .into()),
137 None => self.inner.verify(max_cycles),
138 }
139 }
140
141 #[cfg(not(target_family = "wasm"))]
142 async fn resumable_verify_with_signal(
143 &self,
144 max_cycles: Cycle,
145 command_rx: &mut tokio::sync::watch::Receiver<ChunkCommand>,
146 ) -> Result<Cycle, Error> {
147 match self.cached_cycles {
148 Some(cycles) if cycles <= max_cycles => Ok(cycles),
149 Some(_) => Err(ScriptError::ExceededMaximumCycles(max_cycles)
150 .unknown_source()
151 .into()),
152 None => {
153 self.inner
154 .resumable_verify_with_signal(max_cycles, command_rx)
155 .await
156 }
157 }
158 }
159}
160
161pub struct ContextualTransactionVerifier<DL>
169where
170 DL: Send + Sync + Clone + CellDataProvider + HeaderProvider + ExtensionProvider + 'static,
171{
172 pub(crate) time_relative: TimeRelativeTransactionVerifier<DL>,
173 pub(crate) capacity: CapacityVerifier,
174 script: CachedScriptVerifier<DL>,
175 pub(crate) fee_calculator: FeeCalculator<DL>,
176}
177
178impl<DL> ContextualTransactionVerifier<DL>
179where
180 DL: CellDataProvider
181 + HeaderProvider
182 + ExtensionProvider
183 + HeaderFieldsProvider
184 + EpochProvider
185 + Send
186 + Sync
187 + Clone
188 + 'static,
189{
190 pub fn new(
192 rtx: Arc<ResolvedTransaction>,
193 consensus: Arc<Consensus>,
194 data_loader: DL,
195 tx_env: Arc<TxVerifyEnv>,
196 ) -> Self {
197 Self::new_with_cached_script_cycles(rtx, consensus, data_loader, tx_env, None)
198 }
199
200 pub fn new_with_cached_script_cycles(
202 rtx: Arc<ResolvedTransaction>,
203 consensus: Arc<Consensus>,
204 data_loader: DL,
205 tx_env: Arc<TxVerifyEnv>,
206 cached_script_cycles: Option<Cycle>,
207 ) -> Self {
208 ContextualTransactionVerifier {
209 time_relative: TimeRelativeTransactionVerifier::new(
210 Arc::clone(&rtx),
211 Arc::clone(&consensus),
212 data_loader.clone(),
213 Arc::clone(&tx_env),
214 ),
215 script: CachedScriptVerifier::new(
216 Arc::clone(&rtx),
217 data_loader.clone(),
218 Arc::clone(&consensus),
219 Arc::clone(&tx_env),
220 cached_script_cycles,
221 ),
222 capacity: CapacityVerifier::new(Arc::clone(&rtx), consensus.dao_type_hash()),
223 fee_calculator: FeeCalculator::new(rtx, consensus, data_loader),
224 }
225 }
226
227 pub fn verify(&self, max_cycles: Cycle, skip_script_verify: bool) -> Result<Completed, Error> {
231 self.time_relative.verify()?;
232 self.capacity.verify()?;
233 let cycles = if skip_script_verify {
234 0
235 } else {
236 self.script.verify(max_cycles)?
237 };
238 let fee = self.fee_calculator.transaction_fee()?;
239 Ok(Completed { cycles, fee })
240 }
241
242 #[cfg(not(target_family = "wasm"))]
245 pub async fn verify_with_pause(
246 &self,
247 max_cycles: Cycle,
248 command_rx: &mut tokio::sync::watch::Receiver<ChunkCommand>,
249 ) -> Result<Completed, Error> {
250 self.time_relative.verify()?;
251 self.capacity.verify()?;
252 let fee = self.fee_calculator.transaction_fee()?;
253 let cycles = self
254 .script
255 .resumable_verify_with_signal(max_cycles, command_rx)
256 .await?;
257 Ok(Completed { cycles, fee })
258 }
259}
260
261pub struct FeeCalculator<DL> {
295 transaction: Arc<ResolvedTransaction>,
296 consensus: Arc<Consensus>,
297 data_loader: DL,
298}
299
300impl<DL: CellDataProvider + HeaderProvider + ExtensionProvider + EpochProvider> FeeCalculator<DL> {
301 fn new(
302 transaction: Arc<ResolvedTransaction>,
303 consensus: Arc<Consensus>,
304 data_loader: DL,
305 ) -> Self {
306 Self {
307 transaction,
308 consensus,
309 data_loader,
310 }
311 }
312
313 fn transaction_fee(&self) -> Result<Capacity, DaoError> {
314 if self.transaction.is_cellbase() {
316 Ok(Capacity::zero())
317 } else {
318 DaoCalculator::new(self.consensus.as_ref(), &self.data_loader)
319 .transaction_fee(&self.transaction)
320 }
321 }
322}
323
324pub struct VersionVerifier<'a> {
325 transaction: &'a TransactionView,
326 tx_version: Version,
327}
328
329impl<'a> VersionVerifier<'a> {
330 pub fn new(transaction: &'a TransactionView, tx_version: Version) -> Self {
331 VersionVerifier {
332 transaction,
333 tx_version,
334 }
335 }
336
337 pub fn verify(&self) -> Result<(), Error> {
338 if self.transaction.version() != self.tx_version {
339 return Err((TransactionError::MismatchedVersion {
340 expected: self.tx_version,
341 actual: self.transaction.version(),
342 })
343 .into());
344 }
345 Ok(())
346 }
347}
348
349pub struct SizeVerifier<'a> {
350 transaction: &'a TransactionView,
351 block_bytes_limit: u64,
352}
353
354impl<'a> SizeVerifier<'a> {
355 pub fn new(transaction: &'a TransactionView, block_bytes_limit: u64) -> Self {
356 SizeVerifier {
357 transaction,
358 block_bytes_limit,
359 }
360 }
361
362 pub fn verify(&self) -> Result<(), Error> {
363 let size = self.transaction.data().serialized_size_in_block() as u64;
364 if size <= self.block_bytes_limit {
365 Ok(())
366 } else {
367 Err(TransactionError::ExceededMaximumBlockBytes {
368 actual: size,
369 limit: self.block_bytes_limit,
370 }
371 .into())
372 }
373 }
374}
375
376pub type ScriptVerifier<DL> = TransactionScriptsVerifier<DL>;
382
383pub struct EmptyVerifier<'a> {
384 transaction: &'a TransactionView,
385}
386
387impl<'a> EmptyVerifier<'a> {
388 pub fn new(transaction: &'a TransactionView) -> Self {
389 EmptyVerifier { transaction }
390 }
391
392 pub fn verify(&self) -> Result<(), Error> {
393 if self.transaction.inputs().is_empty() {
394 Err(TransactionError::Empty {
395 inner: TransactionErrorSource::Inputs,
396 }
397 .into())
398 } else if self.transaction.outputs().is_empty() && !self.transaction.is_cellbase() {
399 Err(TransactionError::Empty {
400 inner: TransactionErrorSource::Outputs,
401 }
402 .into())
403 } else {
404 Ok(())
405 }
406 }
407}
408
409pub struct MaturityVerifier {
413 transaction: Arc<ResolvedTransaction>,
414 epoch: EpochNumberWithFraction,
415 cellbase_maturity: EpochNumberWithFraction,
416}
417
418impl MaturityVerifier {
419 pub fn new(
420 transaction: Arc<ResolvedTransaction>,
421 epoch: EpochNumberWithFraction,
422 cellbase_maturity: EpochNumberWithFraction,
423 ) -> Self {
424 MaturityVerifier {
425 transaction,
426 epoch,
427 cellbase_maturity,
428 }
429 }
430
431 pub fn verify(&self) -> Result<(), Error> {
432 let cellbase_immature = |meta: &CellMeta| -> bool {
433 meta.transaction_info
434 .as_ref()
435 .map(|info| {
436 info.block_number > 0 && info.is_cellbase() && {
437 let threshold =
438 self.cellbase_maturity.to_rational() + info.block_epoch.to_rational();
439 let current = self.epoch.to_rational();
440 current < threshold
441 }
442 })
443 .unwrap_or(false)
444 };
445
446 if let Some(index) = self
447 .transaction
448 .resolved_inputs
449 .iter()
450 .position(cellbase_immature)
451 {
452 return Err(TransactionError::CellbaseImmaturity {
453 inner: TransactionErrorSource::Inputs,
454 index,
455 }
456 .into());
457 }
458
459 if let Some(index) = self
460 .transaction
461 .resolved_cell_deps
462 .iter()
463 .position(cellbase_immature)
464 {
465 return Err(TransactionError::CellbaseImmaturity {
466 inner: TransactionErrorSource::CellDeps,
467 index,
468 }
469 .into());
470 }
471
472 Ok(())
473 }
474}
475
476pub struct DuplicateDepsVerifier<'a> {
477 transaction: &'a TransactionView,
478}
479
480impl<'a> DuplicateDepsVerifier<'a> {
481 pub fn new(transaction: &'a TransactionView) -> Self {
482 DuplicateDepsVerifier { transaction }
483 }
484
485 pub fn verify(&self) -> Result<(), Error> {
486 let transaction = self.transaction;
487 let mut seen_cells = HashSet::with_capacity(self.transaction.cell_deps().len());
488 let mut seen_headers = HashSet::with_capacity(self.transaction.header_deps().len());
489
490 if let Some(dep) = transaction
491 .cell_deps_iter()
492 .find_map(|dep| seen_cells.replace(dep))
493 {
494 return Err(TransactionError::DuplicateCellDeps {
495 out_point: dep.out_point(),
496 }
497 .into());
498 }
499 if let Some(hash) = transaction
500 .header_deps_iter()
501 .find_map(|hash| seen_headers.replace(hash))
502 {
503 return Err(TransactionError::DuplicateHeaderDeps { hash }.into());
504 }
505 Ok(())
506 }
507}
508
509pub struct CapacityVerifier {
511 resolved_transaction: Arc<ResolvedTransaction>,
512 dao_type_hash: Byte32,
513}
514
515impl CapacityVerifier {
516 pub fn new(resolved_transaction: Arc<ResolvedTransaction>, dao_type_hash: Byte32) -> Self {
518 CapacityVerifier {
519 resolved_transaction,
520 dao_type_hash,
521 }
522 }
523
524 pub fn verify(&self) -> Result<(), Error> {
527 if !(self.resolved_transaction.is_cellbase() || self.valid_dao_withdraw_transaction()) {
532 let inputs_sum = self.resolved_transaction.inputs_capacity()?;
533 let outputs_sum = self.resolved_transaction.outputs_capacity()?;
534
535 if inputs_sum < outputs_sum {
536 return Err((TransactionError::OutputsSumOverflow {
537 inputs_sum,
538 outputs_sum,
539 })
540 .into());
541 }
542 }
543
544 for (index, (output, data)) in self
545 .resolved_transaction
546 .transaction
547 .outputs_with_data_iter()
548 .enumerate()
549 {
550 let data_occupied_capacity = Capacity::bytes(data.len())?;
551 if output.is_lack_of_capacity(data_occupied_capacity)? {
552 return Err((TransactionError::InsufficientCellCapacity {
553 index,
554 inner: TransactionErrorSource::Outputs,
555 capacity: output.capacity().into(),
556 occupied_capacity: output.occupied_capacity(data_occupied_capacity)?,
557 })
558 .into());
559 }
560 }
561
562 Ok(())
563 }
564
565 fn valid_dao_withdraw_transaction(&self) -> bool {
566 self.resolved_transaction
567 .resolved_inputs
568 .iter()
569 .any(|cell_meta| cell_uses_dao_type_script(&cell_meta.cell_output, &self.dao_type_hash))
570 }
571}
572
573fn cell_uses_dao_type_script(cell_output: &CellOutput, dao_type_hash: &Byte32) -> bool {
574 cell_output
575 .type_()
576 .to_opt()
577 .map(|t| {
578 Into::<u8>::into(t.hash_type()) == Into::<u8>::into(ScriptHashType::Type)
579 && &t.code_hash() == dao_type_hash
580 })
581 .unwrap_or(false)
582}
583
584const LOCK_TYPE_FLAG: u64 = 1 << 63;
585const METRIC_TYPE_FLAG_MASK: u64 = 0x6000_0000_0000_0000;
586const VALUE_MASK: u64 = 0x00ff_ffff_ffff_ffff;
587const REMAIN_FLAGS_BITS: u64 = 0x1f00_0000_0000_0000;
588
589pub enum SinceMetric {
591 BlockNumber(u64),
593 EpochNumberWithFraction(EpochNumberWithFraction),
595 Timestamp(u64),
597}
598
599#[derive(Copy, Clone, Debug)]
603pub struct Since(pub u64);
604
605impl Since {
606 pub fn is_absolute(self) -> bool {
608 self.0 & LOCK_TYPE_FLAG == 0
609 }
610
611 #[inline]
613 pub fn is_relative(self) -> bool {
614 !self.is_absolute()
615 }
616
617 pub fn flags_is_valid(self) -> bool {
619 (self.0 & REMAIN_FLAGS_BITS == 0)
620 && ((self.0 & METRIC_TYPE_FLAG_MASK) != METRIC_TYPE_FLAG_MASK)
621 }
622
623 pub fn extract_metric(self) -> Option<SinceMetric> {
625 let value = self.0 & VALUE_MASK;
626 match self.0 & METRIC_TYPE_FLAG_MASK {
627 0x0000_0000_0000_0000 => Some(SinceMetric::BlockNumber(value)),
629 0x2000_0000_0000_0000 => Some(SinceMetric::EpochNumberWithFraction(
631 EpochNumberWithFraction::from_full_value_unchecked(value),
632 )),
633 0x4000_0000_0000_0000 => value.checked_mul(1000).map(SinceMetric::Timestamp),
635 _ => None,
636 }
637 }
638}
639
640pub struct SinceVerifier<DL> {
645 rtx: Arc<ResolvedTransaction>,
646 consensus: Arc<Consensus>,
647 data_loader: DL,
648 tx_env: Arc<TxVerifyEnv>,
649}
650
651impl<DL: HeaderFieldsProvider> SinceVerifier<DL> {
652 pub fn new(
653 rtx: Arc<ResolvedTransaction>,
654 consensus: Arc<Consensus>,
655 data_loader: DL,
656 tx_env: Arc<TxVerifyEnv>,
657 ) -> Self {
658 SinceVerifier {
659 rtx,
660 consensus,
661 data_loader,
662 tx_env,
663 }
664 }
665
666 fn parent_median_time(&self, block_hash: &Byte32) -> u64 {
667 let header_fields = self
668 .data_loader
669 .get_header_fields(block_hash)
670 .expect("parent block exist");
671 self.block_median_time(&header_fields.parent_hash)
672 }
673
674 fn block_median_time(&self, block_hash: &Byte32) -> u64 {
675 let median_block_count = self.consensus.median_time_block_count();
676 self.data_loader
677 .block_median_time(block_hash, median_block_count)
678 }
679
680 fn verify_absolute_lock(&self, index: usize, since: Since) -> Result<(), Error> {
681 if since.is_absolute() {
682 match since.extract_metric() {
683 Some(SinceMetric::BlockNumber(block_number)) => {
684 let proposal_window = self.consensus.tx_proposal_window();
685 if self.tx_env.block_number(proposal_window) < block_number {
686 return Err((TransactionError::Immature { index }).into());
687 }
688 }
689 Some(SinceMetric::EpochNumberWithFraction(epoch_number_with_fraction)) => {
690 if !epoch_number_with_fraction.is_well_formed_increment() {
691 return Err((TransactionError::InvalidSince { index }).into());
692 }
693 let a = self.tx_env.epoch().to_rational();
694 let b = epoch_number_with_fraction.normalize().to_rational();
695 if a < b {
696 return Err((TransactionError::Immature { index }).into());
697 }
698 }
699 Some(SinceMetric::Timestamp(timestamp)) => {
700 let parent_hash = self.tx_env.parent_hash();
701 let tip_timestamp = self.block_median_time(&parent_hash);
702 if tip_timestamp < timestamp {
703 return Err((TransactionError::Immature { index }).into());
704 }
705 }
706 None => {
707 return Err((TransactionError::InvalidSince { index }).into());
708 }
709 }
710 }
711 Ok(())
712 }
713
714 fn verify_relative_lock(
715 &self,
716 index: usize,
717 since: Since,
718 cell_meta: &CellMeta,
719 ) -> Result<(), Error> {
720 if since.is_relative() {
721 let info = match cell_meta.transaction_info {
722 Some(ref transaction_info) => Ok(transaction_info),
723 None => Err(TransactionError::Immature { index }),
724 }?;
725 match since.extract_metric() {
726 Some(SinceMetric::BlockNumber(block_number)) => {
727 let proposal_window = self.consensus.tx_proposal_window();
728 let required_block_number = info
729 .block_number
730 .checked_add(block_number)
731 .ok_or(TransactionError::InvalidSince { index })?;
732 if self.tx_env.block_number(proposal_window) < required_block_number {
733 return Err((TransactionError::Immature { index }).into());
734 }
735 }
736 Some(SinceMetric::EpochNumberWithFraction(epoch_number_with_fraction)) => {
737 if !epoch_number_with_fraction.is_well_formed_increment() {
738 return Err((TransactionError::InvalidSince { index }).into());
739 }
740 let a = self.tx_env.epoch().to_rational();
741 let b = info.block_epoch.to_rational()
742 + epoch_number_with_fraction.normalize().to_rational();
743 if a < b {
744 return Err((TransactionError::Immature { index }).into());
745 }
746 }
747 Some(SinceMetric::Timestamp(timestamp)) => {
748 let proposal_window = self.consensus.tx_proposal_window();
753 let parent_hash = self.tx_env.parent_hash();
754 let epoch_number = self.tx_env.epoch_number(proposal_window);
755 let hardfork_switch = self.consensus.hardfork_switch();
756 let base_timestamp = if hardfork_switch
757 .ckb2021
758 .is_block_ts_as_relative_since_start_enabled(epoch_number)
759 {
760 self.data_loader
761 .get_header_fields(&info.block_hash)
762 .expect("header exist")
763 .timestamp
764 } else {
765 self.parent_median_time(&info.block_hash)
766 };
767 let current_median_time = self.block_median_time(&parent_hash);
768 let required_timestamp = base_timestamp
769 .checked_add(timestamp)
770 .ok_or(TransactionError::InvalidSince { index })?;
771 if current_median_time < required_timestamp {
772 return Err((TransactionError::Immature { index }).into());
773 }
774 }
775 None => {
776 return Err((TransactionError::InvalidSince { index }).into());
777 }
778 }
779 }
780 Ok(())
781 }
782
783 pub fn verify(&self) -> Result<(), Error> {
784 for (index, (cell_meta, input)) in self
785 .rtx
786 .resolved_inputs
787 .iter()
788 .zip(self.rtx.transaction.inputs())
789 .enumerate()
790 {
791 let since: u64 = input.since().into();
793 if since == 0 {
794 continue;
795 }
796 let since = Since(since);
797 if !since.flags_is_valid() {
799 return Err((TransactionError::InvalidSince { index }).into());
800 }
801
802 self.verify_absolute_lock(index, since)?;
804 self.verify_relative_lock(index, since, cell_meta)?;
805 }
806 Ok(())
807 }
808}
809
810pub struct OutputsDataVerifier<'a> {
811 transaction: &'a TransactionView,
812}
813
814impl<'a> OutputsDataVerifier<'a> {
815 pub fn new(transaction: &'a TransactionView) -> Self {
816 Self { transaction }
817 }
818
819 pub fn verify(&self) -> Result<(), TransactionError> {
820 let outputs_len = self.transaction.outputs().len();
821 let outputs_data_len = self.transaction.outputs_data().len();
822
823 if outputs_len != outputs_data_len {
824 return Err(TransactionError::OutputsDataLengthMismatch {
825 outputs_len,
826 outputs_data_len,
827 });
828 }
829 Ok(())
830 }
831}
832
833pub struct ScriptHashTypeVerifier<'a> {
836 transaction: &'a TransactionView,
837}
838
839impl<'a> ScriptHashTypeVerifier<'a> {
840 pub fn new(transaction: &'a TransactionView) -> Self {
841 Self { transaction }
842 }
843
844 pub fn verify(&self) -> Result<(), Error> {
845 for output in self.transaction.outputs() {
846 if let Ok(hash_type) = TryInto::<ScriptHashType>::try_into(output.lock().hash_type()) {
847 let val: u8 = hash_type.into();
848 if !ENABLED_SCRIPT_HASH_TYPE.contains(&val) {
849 return Err(
850 TransactionError::ScriptHashTypeNotPermitted { hash_type: val }.into(),
851 );
852 }
853 } else {
854 return Err((TransactionError::InvalidScriptHashType {
855 hash_type: output.lock().hash_type(),
856 })
857 .into());
858 }
859 }
860
861 Ok(())
862 }
863}
864
865pub struct DaoScriptSizeVerifier<DL> {
868 resolved_transaction: Arc<ResolvedTransaction>,
869 consensus: Arc<Consensus>,
870 data_loader: DL,
871}
872
873impl<DL: CellDataProvider> DaoScriptSizeVerifier<DL> {
874 pub fn new(
876 resolved_transaction: Arc<ResolvedTransaction>,
877 consensus: Arc<Consensus>,
878 data_loader: DL,
879 ) -> Self {
880 DaoScriptSizeVerifier {
881 resolved_transaction,
882 consensus,
883 data_loader,
884 }
885 }
886
887 fn dao_type_hash(&self) -> Byte32 {
888 self.consensus.dao_type_hash()
889 }
890
891 pub fn verify(&self) -> Result<(), Error> {
894 let dao_type_hash = self.dao_type_hash();
895
896 for (i, (input_meta, cell_output)) in self
897 .resolved_transaction
898 .resolved_inputs
899 .iter()
900 .zip(self.resolved_transaction.transaction.outputs())
901 .enumerate()
902 {
903 if !(cell_uses_dao_type_script(&input_meta.cell_output, &dao_type_hash)
905 && cell_uses_dao_type_script(&cell_output, &dao_type_hash))
906 {
907 continue;
908 }
909
910 let input_data = match self.data_loader.load_cell_data(input_meta) {
912 Some(data) => data,
913 None => continue,
914 };
915
916 if input_data.into_iter().any(|b| b != 0) {
918 continue;
919 }
920
921 if let Some(info) = &input_meta.transaction_info
924 && info.block_number
925 < self
926 .consensus
927 .starting_block_limiting_dao_withdrawing_lock()
928 {
929 continue;
930 }
931
932 if input_meta.cell_output.lock().total_size() != cell_output.lock().total_size() {
935 return Err((TransactionError::DaoLockSizeMismatch { index: i }).into());
936 }
937 }
938 self.verify_output_data(&dao_type_hash)?;
939 Ok(())
940 }
941
942 fn verify_output_data(&self, dao_type_hash: &Byte32) -> Result<(), Error> {
943 let transaction = &self.resolved_transaction.transaction;
944 let outputs = transaction.outputs();
945 let outputs_data = transaction.outputs_data();
946
947 for (index, output) in outputs.into_iter().enumerate() {
948 if !cell_uses_dao_type_script(&output, dao_type_hash) {
949 continue;
950 }
951
952 let Some(output_data) = outputs_data.get(index).map(|data| data.raw_data()) else {
953 continue;
954 };
955
956 if output_data.iter().all(|b| *b == 0) {
957 continue;
958 }
959
960 if !self.same_index_input_is_dao_deposit(index, dao_type_hash) {
961 return Err((TransactionError::DaoOutputDataMismatch { index }).into());
962 }
963 }
964
965 Ok(())
966 }
967
968 fn same_index_input_is_dao_deposit(&self, index: usize, dao_type_hash: &Byte32) -> bool {
969 let Some(input_meta) = self.resolved_transaction.resolved_inputs.get(index) else {
970 return false;
971 };
972
973 if !cell_uses_dao_type_script(&input_meta.cell_output, dao_type_hash) {
974 return false;
975 }
976
977 self.data_loader
978 .load_cell_data(input_meta)
979 .map(|input_data| input_data.iter().all(|b| *b == 0))
980 .unwrap_or(false)
981 }
982}