ckb_verification/
block_verifier.rs1use crate::{
2 BlockErrorKind, CellbaseError, transaction_verifier::NonContextualTransactionVerifier,
3};
4use ckb_chain_spec::consensus::Consensus;
5use ckb_constant::consensus::ENABLED_SCRIPT_HASH_TYPE;
6use ckb_error::Error;
7use ckb_types::{
8 core::{BlockView, ScriptHashType},
9 packed::{CellInput, CellbaseWitness},
10 prelude::*,
11};
12use ckb_verification_traits::Verifier;
13use std::collections::HashSet;
14
15#[derive(Clone)]
25pub struct BlockVerifier<'a> {
26 consensus: &'a Consensus,
27}
28
29impl<'a> BlockVerifier<'a> {
30 pub fn new(consensus: &'a Consensus) -> Self {
32 BlockVerifier { consensus }
33 }
34}
35
36impl<'a> Verifier for BlockVerifier<'a> {
37 type Target = BlockView;
38
39 fn verify(&self, target: &BlockView) -> Result<(), Error> {
40 let max_block_proposals_limit = self.consensus.max_block_proposals_limit();
41 let max_block_bytes = self.consensus.max_block_bytes();
42 BlockProposalsLimitVerifier::new(max_block_proposals_limit).verify(target)?;
43 BlockBytesVerifier::new(max_block_bytes).verify(target)?;
44 CellbaseVerifier::new().verify(target)?;
45 DuplicateVerifier::new().verify(target)?;
46 MerkleRootVerifier::new().verify(target)
47 }
48}
49
50#[derive(Clone)]
58pub struct CellbaseVerifier {}
59
60impl CellbaseVerifier {
61 pub fn new() -> Self {
63 CellbaseVerifier {}
64 }
65
66 pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
67 if block.is_genesis() {
68 return Ok(());
69 }
70
71 let cellbase_len = block
72 .transactions()
73 .iter()
74 .filter(|tx| tx.is_cellbase())
75 .count();
76
77 if cellbase_len != 1 {
79 return Err((CellbaseError::InvalidQuantity).into());
80 }
81
82 let cellbase_transaction = &block.transactions()[0];
83
84 if !cellbase_transaction.is_cellbase() {
85 return Err((CellbaseError::InvalidPosition).into());
86 }
87
88 if cellbase_transaction.outputs().len() > 1
90 || cellbase_transaction.outputs_data().len() > 1
91 || cellbase_transaction.outputs().len() != cellbase_transaction.outputs_data().len()
92 {
93 return Err((CellbaseError::InvalidOutputQuantity).into());
94 }
95
96 if !cellbase_transaction
98 .outputs_data()
99 .get(0)
100 .map(|data| data.is_empty())
101 .unwrap_or(true)
102 {
103 return Err((CellbaseError::InvalidOutputData).into());
104 }
105
106 if cellbase_transaction
107 .witnesses()
108 .get(0)
109 .and_then(|witness| {
110 CellbaseWitness::from_slice(&witness.raw_data())
111 .ok()
112 .and_then(|cellbase_witness| {
113 ScriptHashType::try_from(cellbase_witness.lock().hash_type())
114 .ok()
115 .and_then(|hash_type| {
116 let val: u8 = hash_type.into();
117 ENABLED_SCRIPT_HASH_TYPE.contains(&val).then_some(())
118 })
119 })
120 })
121 .is_none()
122 {
123 return Err((CellbaseError::InvalidWitness).into());
124 }
125
126 if cellbase_transaction
128 .outputs()
129 .into_iter()
130 .any(|output| output.type_().is_some())
131 {
132 return Err((CellbaseError::InvalidTypeScript).into());
133 }
134
135 for output in cellbase_transaction.outputs() {
136 if let Ok(hash_type) = TryInto::<ScriptHashType>::try_into(output.lock().hash_type()) {
137 let val: u8 = hash_type.into();
138 if !ENABLED_SCRIPT_HASH_TYPE.contains(&val) {
139 return Err((CellbaseError::InvalidOutputLock).into());
140 }
141 } else {
142 return Err((CellbaseError::InvalidOutputLock).into());
143 }
144 }
145
146 let cellbase_input = &cellbase_transaction
147 .inputs()
148 .get(0)
149 .expect("cellbase should have input");
150 if cellbase_input != &CellInput::new_cellbase_input(block.header().number()) {
151 return Err((CellbaseError::InvalidInput).into());
152 }
153
154 Ok(())
155 }
156}
157
158#[derive(Clone)]
163pub struct DuplicateVerifier {}
164
165impl DuplicateVerifier {
166 pub fn new() -> Self {
167 DuplicateVerifier {}
168 }
169
170 pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
171 let mut seen = HashSet::with_capacity(block.transactions().len());
172 if !block.transactions().iter().all(|tx| seen.insert(tx.hash())) {
173 return Err((BlockErrorKind::CommitTransactionDuplicate).into());
174 }
175
176 let mut seen = HashSet::with_capacity(block.data().proposals().len());
177 if !block
178 .data()
179 .proposals()
180 .into_iter()
181 .all(|id| seen.insert(id))
182 {
183 return Err((BlockErrorKind::ProposalTransactionDuplicate).into());
184 }
185 Ok(())
186 }
187}
188
189#[derive(Clone, Default)]
193pub struct MerkleRootVerifier {}
194
195impl MerkleRootVerifier {
196 pub fn new() -> Self {
197 MerkleRootVerifier::default()
198 }
199
200 pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
201 if block.transactions_root() != block.calc_transactions_root() {
202 return Err(BlockErrorKind::TransactionsRoot.into());
203 }
204
205 if block.proposals_hash() != block.calc_proposals_hash() {
206 return Err(BlockErrorKind::ProposalTransactionsHash.into());
207 }
208
209 Ok(())
210 }
211}
212
213#[derive(Clone)]
217pub struct BlockProposalsLimitVerifier {
218 block_proposals_limit: u64,
219}
220
221impl BlockProposalsLimitVerifier {
222 pub fn new(block_proposals_limit: u64) -> Self {
223 BlockProposalsLimitVerifier {
224 block_proposals_limit,
225 }
226 }
227
228 pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
229 let proposals_len = block.data().proposals().len() as u64;
230 if proposals_len <= self.block_proposals_limit {
231 Ok(())
232 } else {
233 Err(BlockErrorKind::ExceededMaximumProposalsLimit.into())
234 }
235 }
236}
237
238#[derive(Clone)]
242pub struct BlockBytesVerifier {
243 block_bytes_limit: u64,
244}
245
246impl BlockBytesVerifier {
247 pub fn new(block_bytes_limit: u64) -> Self {
248 BlockBytesVerifier { block_bytes_limit }
249 }
250
251 pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
252 if block.is_genesis() {
254 return Ok(());
255 }
256 let block_bytes = block.data().serialized_size_without_uncle_proposals() as u64;
257 if block_bytes <= self.block_bytes_limit {
258 Ok(())
259 } else {
260 Err(BlockErrorKind::ExceededMaximumBlockBytes.into())
261 }
262 }
263}
264
265pub struct NonContextualBlockTxsVerifier<'a> {
270 consensus: &'a Consensus,
271}
272
273impl<'a> NonContextualBlockTxsVerifier<'a> {
274 pub fn new(consensus: &'a Consensus) -> Self {
276 NonContextualBlockTxsVerifier { consensus }
277 }
278
279 pub fn verify(&self, block: &BlockView) -> Result<Vec<()>, Error> {
281 block
282 .transactions()
283 .iter()
284 .map(|tx| NonContextualTransactionVerifier::new(tx, self.consensus).verify())
285 .collect()
286 }
287}