1use std::{collections::HashSet, sync::Arc};
2
3use super::BlockBodyProcessor;
4use crate::errors::{BlockProcessResult, RuleError};
5use kaspa_consensus_core::{block::Block, merkle::calc_hash_merkle_root, tx::TransactionOutpoint};
6
7impl BlockBodyProcessor {
8 pub fn validate_body_in_isolation(self: &Arc<Self>, block: &Block) -> BlockProcessResult<u64> {
9 let storage_mass_activated = block.header.daa_score > self.storage_mass_activation_daa_score;
10
11 Self::check_has_transactions(block)?;
12 Self::check_hash_merkle_root(block, storage_mass_activated)?;
13 Self::check_only_one_coinbase(block)?;
14 self.check_transactions_in_isolation(block)?;
15 let mass = self.check_block_mass(block, storage_mass_activated)?;
16 self.check_duplicate_transactions(block)?;
17 self.check_block_double_spends(block)?;
18 self.check_no_chained_transactions(block)?;
19 Ok(mass)
20 }
21
22 fn check_has_transactions(block: &Block) -> BlockProcessResult<()> {
23 if block.transactions.is_empty() {
26 return Err(RuleError::NoTransactions);
27 }
28 Ok(())
29 }
30
31 fn check_hash_merkle_root(block: &Block, storage_mass_activated: bool) -> BlockProcessResult<()> {
32 let calculated = calc_hash_merkle_root(block.transactions.iter(), storage_mass_activated);
33 if calculated != block.header.hash_merkle_root {
34 return Err(RuleError::BadMerkleRoot(block.header.hash_merkle_root, calculated));
35 }
36 Ok(())
37 }
38
39 fn check_only_one_coinbase(block: &Block) -> BlockProcessResult<()> {
40 if !block.transactions[0].is_coinbase() {
41 return Err(RuleError::FirstTxNotCoinbase);
42 }
43
44 if let Some(i) = block.transactions[1..].iter().position(|tx| tx.is_coinbase()) {
45 return Err(RuleError::MultipleCoinbases(i));
46 }
47
48 Ok(())
49 }
50
51 fn check_transactions_in_isolation(self: &Arc<Self>, block: &Block) -> BlockProcessResult<()> {
52 for tx in block.transactions.iter() {
53 if let Err(e) = self.transaction_validator.validate_tx_in_isolation(tx) {
54 return Err(RuleError::TxInIsolationValidationFailed(tx.id(), e));
55 }
56 }
57 Ok(())
58 }
59
60 fn check_block_mass(self: &Arc<Self>, block: &Block, storage_mass_activated: bool) -> BlockProcessResult<u64> {
61 let mut total_mass: u64 = 0;
62 if storage_mass_activated {
63 for tx in block.transactions.iter() {
64 let calculated_tx_compute_mass = self.mass_calculator.calc_tx_compute_mass(tx);
66 let committed_contextual_mass = tx.mass();
67 if committed_contextual_mass < calculated_tx_compute_mass {
70 return Err(RuleError::MassFieldTooLow(tx.id(), committed_contextual_mass, calculated_tx_compute_mass));
71 }
72 total_mass = total_mass.saturating_add(committed_contextual_mass);
74 if total_mass > self.max_block_mass {
75 return Err(RuleError::ExceedsMassLimit(self.max_block_mass));
76 }
77 }
78 } else {
79 for tx in block.transactions.iter() {
80 let calculated_tx_mass = self.mass_calculator.calc_tx_compute_mass(tx);
81 total_mass = total_mass.saturating_add(calculated_tx_mass);
82 if total_mass > self.max_block_mass {
83 return Err(RuleError::ExceedsMassLimit(self.max_block_mass));
84 }
85 }
86 }
87 Ok(total_mass)
88 }
89
90 fn check_block_double_spends(self: &Arc<Self>, block: &Block) -> BlockProcessResult<()> {
91 let mut existing = HashSet::new();
92 for input in block.transactions.iter().flat_map(|tx| &tx.inputs) {
93 if !existing.insert(input.previous_outpoint) {
94 return Err(RuleError::DoubleSpendInSameBlock(input.previous_outpoint));
95 }
96 }
97 Ok(())
98 }
99
100 fn check_no_chained_transactions(self: &Arc<Self>, block: &Block) -> BlockProcessResult<()> {
101 let mut block_created_outpoints = HashSet::new();
102 for tx in block.transactions.iter() {
103 for index in 0..tx.outputs.len() {
104 block_created_outpoints.insert(TransactionOutpoint { transaction_id: tx.id(), index: index as u32 });
105 }
106 }
107
108 for input in block.transactions.iter().flat_map(|tx| &tx.inputs) {
109 if block_created_outpoints.contains(&input.previous_outpoint) {
110 return Err(RuleError::ChainedTransaction(input.previous_outpoint));
111 }
112 }
113 Ok(())
114 }
115
116 fn check_duplicate_transactions(self: &Arc<Self>, block: &Block) -> BlockProcessResult<()> {
117 let mut ids = HashSet::new();
118 for tx in block.transactions.iter() {
119 if !ids.insert(tx.id()) {
120 return Err(RuleError::DuplicateTransactions(tx.id()));
121 }
122 }
123
124 Ok(())
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use crate::{
131 config::{Config, ConfigBuilder},
132 consensus::test_consensus::TestConsensus,
133 errors::RuleError,
134 params::MAINNET_PARAMS,
135 };
136 use kaspa_consensus_core::{
137 api::{BlockValidationFutures, ConsensusApi},
138 block::MutableBlock,
139 header::Header,
140 merkle::calc_hash_merkle_root as calc_hash_merkle_root_with_options,
141 subnets::{SUBNETWORK_ID_COINBASE, SUBNETWORK_ID_NATIVE},
142 tx::{scriptvec, ScriptPublicKey, Transaction, TransactionId, TransactionInput, TransactionOutpoint, TransactionOutput},
143 };
144 use kaspa_core::assert_match;
145 use kaspa_hashes::Hash;
146
147 fn calc_hash_merkle_root<'a>(txs: impl ExactSizeIterator<Item = &'a Transaction>) -> Hash {
148 calc_hash_merkle_root_with_options(txs, false)
149 }
150
151 #[test]
152 fn validate_body_in_isolation_test() {
153 let consensus = TestConsensus::new(&Config::new(MAINNET_PARAMS));
154 let wait_handles = consensus.init();
155
156 let body_processor = consensus.block_body_processor();
157 let example_block = MutableBlock::new(
158 Header::new_finalized(
159 0,
160 vec![vec![
161 Hash::from_slice(&[
162 0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95, 0xd9, 0xc6, 0x41, 0xf3, 0xb8, 0xee, 0xc2, 0xf3, 0x46, 0x11,
163 0x89, 0x6b, 0x82, 0x1a, 0x68, 0x3b, 0x7a, 0x4e, 0xde, 0xfe, 0x2c, 0x00, 0x00, 0x00,
164 ]),
165 Hash::from_slice(&[
166 0x4b, 0xb0, 0x75, 0x35, 0xdf, 0xd5, 0x8e, 0x0b, 0x3c, 0xd6, 0x4f, 0xd7, 0x15, 0x52, 0x80, 0x87, 0x2a, 0x04,
167 0x71, 0xbc, 0xf8, 0x30, 0x95, 0x52, 0x6a, 0xce, 0x0e, 0x38, 0xc6, 0x00, 0x00, 0x00,
168 ]),
169 ]],
170 Hash::from_slice(&[
171 0x46, 0xec, 0xf4, 0x5b, 0xe3, 0xba, 0xca, 0x34, 0x9d, 0xfe, 0x8a, 0x78, 0xde, 0xaf, 0x05, 0x3b, 0x0a, 0xa6, 0xd5,
172 0x38, 0x97, 0x4d, 0xa5, 0x0f, 0xd6, 0xef, 0xb4, 0xd2, 0x66, 0xbc, 0x8d, 0x21,
173 ]),
174 Default::default(),
175 Default::default(),
176 0x17305aa654a,
177 0x207fffff,
178 1,
179 0,
180 0.into(),
181 9,
182 Default::default(),
183 ),
184 vec![
185 Transaction::new(
186 0,
187 vec![],
188 vec![TransactionOutput {
189 value: 0x12a05f200,
190 script_public_key: ScriptPublicKey::new(
191 0,
192 scriptvec!(
193 0xa9, 0x14, 0xda, 0x17, 0x45, 0xe9, 0xb5, 0x49, 0xbd, 0x0b, 0xfa, 0x1a, 0x56, 0x99, 0x71, 0xc7, 0x7e,
194 0xba, 0x30, 0xcd, 0x5a, 0x4b, 0x87
195 ),
196 ),
197 }],
198 0,
199 SUBNETWORK_ID_COINBASE,
200 0,
201 vec![9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
202 ),
203 Transaction::new(
204 0,
205 vec![
206 TransactionInput {
207 previous_outpoint: TransactionOutpoint {
208 transaction_id: TransactionId::from_slice(&[
209 0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95, 0xd9, 0xc6, 0x41, 0xf3, 0xb8, 0xee, 0xc2, 0xf3,
210 0x46, 0x11, 0x89, 0x6b, 0x82, 0x1a, 0x68, 0x3b, 0x7a, 0x4e, 0xde, 0xfe, 0x2c, 0x00, 0x00, 0x00,
211 ]),
212 index: 0xffffffff,
213 },
214 signature_script: vec![],
215 sequence: u64::MAX,
216 sig_op_count: 0,
217 },
218 TransactionInput {
219 previous_outpoint: TransactionOutpoint {
220 transaction_id: TransactionId::from_slice(&[
221 0x4b, 0xb0, 0x75, 0x35, 0xdf, 0xd5, 0x8e, 0x0b, 0x3c, 0xd6, 0x4f, 0xd7, 0x15, 0x52, 0x80, 0x87,
222 0x2a, 0x04, 0x71, 0xbc, 0xf8, 0x30, 0x95, 0x52, 0x6a, 0xce, 0x0e, 0x38, 0xc6, 0x00, 0x00, 0x00,
223 ]),
224 index: 0xffffffff,
225 },
226 signature_script: vec![],
227 sequence: u64::MAX,
228 sig_op_count: 0,
229 },
230 ],
231 vec![],
232 0,
233 SUBNETWORK_ID_NATIVE,
234 0,
235 vec![],
236 ),
237 Transaction::new(
238 0,
239 vec![TransactionInput {
240 previous_outpoint: TransactionOutpoint {
241 transaction_id: TransactionId::from_slice(&[
242 0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60, 0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac, 0xc4,
243 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07, 0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87,
244 ]),
245 index: 0,
246 },
247 signature_script: vec![
248 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3, 0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6, 0x3a,
250 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94, 0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58, 0xe4, 0xeb,
251 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00, 0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62, 0x81, 0x9f, 0x15,
252 0xd3, 0x3e, 0xe7, 0x05, 0x5c, 0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60, 0x28, 0xd9, 0xcd, 0xb1,
253 0xc3, 0xaf, 0x77, 0x48, 0x01, 0x41, 0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d, 0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38, 0x3a,
256 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25, 0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e, 0xe8, 0xe8,
257 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8, 0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd, 0x1f, 0x63, 0x3f,
258 0x25, 0xf8, 0x7c, 0x16, 0x1b, 0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3, 0xd3, ],
260 sequence: u64::MAX,
261 sig_op_count: 0,
262 }],
263 vec![
264 TransactionOutput {
265 value: 0x2123e300,
266 script_public_key: ScriptPublicKey::new(
267 0,
268 scriptvec!(
269 0x76, 0xa9, 0x14, 0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60, 0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e,
273 0xf7, 0xf5, 0x8b, 0x32, 0x88, 0xac ),
276 ),
277 },
278 TransactionOutput {
279 value: 0x108e20f00,
280 script_public_key: ScriptPublicKey::new(
281 0,
282 scriptvec!(
283 0x76, 0xa9, 0x14, 0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f, 0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b,
287 0x52, 0xde, 0x3d, 0x7c, 0x88, 0xac ),
290 ),
291 },
292 ],
293 0,
294 SUBNETWORK_ID_NATIVE,
295 0,
296 vec![],
297 ),
298 Transaction::new(
299 0,
300 vec![TransactionInput {
301 previous_outpoint: TransactionOutpoint {
302 transaction_id: TransactionId::from_slice(&[
303 0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d, 0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27, 0x86,
304 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65, 0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf,
305 ]),
306 index: 1,
307 },
308 signature_script: vec![
309 0x47, 0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf, 0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5, 0xeb,
311 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34, 0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31, 0xe0, 0xba,
312 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee, 0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f, 0x5b, 0x4a, 0xd7,
313 0xd7, 0xbc, 0x3e, 0x62, 0x8c, 0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e, 0xae, 0xb8, 0x1e, 0x26,
314 0xb4, 0xfe, 0x01, 0x41, 0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78, 0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5, 0xb2,
316 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39, 0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21, 0xf0, 0xba,
317 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee, 0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3, 0xa7, 0xb9, 0x0d,
318 0xa4, 0x63, 0x1e, 0xe3, 0x95, 0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85, 0x0f, ],
320 sequence: u64::MAX,
321 sig_op_count: 0,
322 }],
323 vec![
324 TransactionOutput {
325 value: 0xf4240,
326 script_public_key: ScriptPublicKey::new(
327 0,
328 scriptvec!(
329 0x76, 0xa9, 0x14, 0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04, 0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d,
333 0xad, 0xbe, 0x7e, 0x10, 0x88, 0xac ),
336 ),
337 },
338 TransactionOutput {
339 value: 0x11d260c0,
340 script_public_key: ScriptPublicKey::new(
341 0,
342 scriptvec!(
343 0x76, 0xa9, 0x14, 0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1, 0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab,
347 0xb3, 0x40, 0x9c, 0xd9, 0x88, 0xac ),
350 ),
351 },
352 ],
353 0,
354 SUBNETWORK_ID_NATIVE,
355 0,
356 vec![],
357 ),
358 Transaction::new(
359 0,
360 vec![TransactionInput {
361 previous_outpoint: TransactionOutpoint {
362 transaction_id: TransactionId::from_slice(&[
363 0x0b, 0x60, 0x72, 0xb3, 0x86, 0xd4, 0xa7, 0x73, 0x23, 0x52, 0x37, 0xf6, 0x4c, 0x11, 0x26, 0xac, 0x3b,
364 0x24, 0x0c, 0x84, 0xb9, 0x17, 0xa3, 0x90, 0x9b, 0xa1, 0xc4, 0x3d, 0xed, 0x5f, 0x51, 0xf4,
365 ]),
366 index: 0,
367 },
368 signature_script: vec![
369 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xbb, 0x1a, 0xd2, 0x6d, 0xf9, 0x30, 0xa5, 0x1c, 0xce, 0x11, 0x0c, 0xf4,
371 0x4f, 0x7a, 0x48, 0xc3, 0xc5, 0x61, 0xfd, 0x97, 0x75, 0x00, 0xb1, 0xae, 0x5d, 0x6b, 0x6f, 0xd1, 0x3d,
372 0x0b, 0x3f, 0x4a, 0x02, 0x21, 0x00, 0xc5, 0xb4, 0x29, 0x51, 0xac, 0xed, 0xff, 0x14, 0xab, 0xba, 0x27,
373 0x36, 0xfd, 0x57, 0x4b, 0xdb, 0x46, 0x5f, 0x3e, 0x6f, 0x8d, 0xa1, 0x2e, 0x2c, 0x53, 0x03, 0x95, 0x4a,
374 0xca, 0x7f, 0x78, 0xf3, 0x01, 0x41, 0x04, 0xa7, 0x13, 0x5b, 0xfe, 0x82, 0x4c, 0x97, 0xec, 0xc0, 0x1e, 0xc7, 0xd7, 0xe3, 0x36, 0x18, 0x5c,
377 0x81, 0xe2, 0xaa, 0x2c, 0x41, 0xab, 0x17, 0x54, 0x07, 0xc0, 0x94, 0x84, 0xce, 0x96, 0x94, 0xb4, 0x49,
378 0x53, 0xfc, 0xb7, 0x51, 0x20, 0x65, 0x64, 0xa9, 0xc2, 0x4d, 0xd0, 0x94, 0xd4, 0x2f, 0xdb, 0xfd, 0xd5,
379 0xaa, 0xd3, 0xe0, 0x63, 0xce, 0x6a, 0xf4, 0xcf, 0xaa, 0xea, 0x4e, 0xa1, 0x4f, 0xbb, ],
381 sequence: u64::MAX,
382 sig_op_count: 0,
383 }],
384 vec![TransactionOutput {
385 value: 0xf4240,
386 script_public_key: ScriptPublicKey::new(
387 0,
388 scriptvec!(
389 0x76, 0xa9, 0x14, 0x39, 0xaa, 0x3d, 0x56, 0x9e, 0x06, 0xa1, 0xd7, 0x92, 0x6d, 0xc4, 0xbe, 0x11, 0x93, 0xc9, 0x9b, 0xf2,
393 0xeb, 0x9e, 0xe0, 0x88, 0xac ),
396 ),
397 }],
398 0,
399 SUBNETWORK_ID_NATIVE,
400 0,
401 vec![],
402 ),
403 ],
404 );
405
406 body_processor.validate_body_in_isolation(&example_block.clone().to_immutable()).unwrap();
407
408 let mut block = example_block.clone();
409 let txs = &mut block.transactions;
410 txs[1].version += 1;
411 assert_match!(body_processor.validate_body_in_isolation(&block.to_immutable()), Err(RuleError::BadMerkleRoot(_, _)));
412
413 let mut block = example_block.clone();
414 let txs = &mut block.transactions;
415 txs[1].inputs[0].sig_op_count = 255;
416 txs[1].inputs[1].sig_op_count = 255;
417 block.header.hash_merkle_root = calc_hash_merkle_root(txs.iter());
418 assert_match!(body_processor.validate_body_in_isolation(&block.to_immutable()), Err(RuleError::ExceedsMassLimit(_)));
419
420 let mut block = example_block.clone();
421 let txs = &mut block.transactions;
422 txs.push(txs[1].clone());
423 block.header.hash_merkle_root = calc_hash_merkle_root(txs.iter());
424 assert_match!(body_processor.validate_body_in_isolation(&block.to_immutable()), Err(RuleError::DuplicateTransactions(_)));
425
426 let mut block = example_block.clone();
427 let txs = &mut block.transactions;
428 txs[1].subnetwork_id = SUBNETWORK_ID_COINBASE;
429 block.header.hash_merkle_root = calc_hash_merkle_root(txs.iter());
430 assert_match!(body_processor.validate_body_in_isolation(&block.to_immutable()), Err(RuleError::MultipleCoinbases(_)));
431
432 let mut block = example_block.clone();
433 let txs = &mut block.transactions;
434 txs[2].inputs[0].previous_outpoint = txs[1].inputs[0].previous_outpoint;
435 block.header.hash_merkle_root = calc_hash_merkle_root(txs.iter());
436 assert_match!(body_processor.validate_body_in_isolation(&block.to_immutable()), Err(RuleError::DoubleSpendInSameBlock(_)));
437
438 let mut block = example_block.clone();
439 let txs = &mut block.transactions;
440 txs[0].subnetwork_id = SUBNETWORK_ID_NATIVE;
441 block.header.hash_merkle_root = calc_hash_merkle_root(txs.iter());
442 assert_match!(body_processor.validate_body_in_isolation(&block.to_immutable()), Err(RuleError::FirstTxNotCoinbase));
443
444 let mut block = example_block.clone();
445 let txs = &mut block.transactions;
446 txs[1].inputs = vec![];
447 block.header.hash_merkle_root = calc_hash_merkle_root(txs.iter());
448 assert_match!(
449 body_processor.validate_body_in_isolation(&block.to_immutable()),
450 Err(RuleError::TxInIsolationValidationFailed(_, _))
451 );
452
453 let mut block = example_block;
454 let txs = &mut block.transactions;
455 txs[3].inputs[0].previous_outpoint = TransactionOutpoint { transaction_id: txs[2].id(), index: 0 };
456 block.header.hash_merkle_root = calc_hash_merkle_root(txs.iter());
457 assert_match!(body_processor.validate_body_in_isolation(&block.to_immutable()), Err(RuleError::ChainedTransaction(_)));
458
459 consensus.shutdown(wait_handles);
460 }
461
462 #[tokio::test]
463 async fn merkle_root_missing_parents_known_invalid_test() {
464 let config = ConfigBuilder::new(MAINNET_PARAMS).skip_proof_of_work().build();
465 let consensus = TestConsensus::new(&config);
466 let wait_handles = consensus.init();
467
468 let mut block = consensus.build_block_with_parents_and_transactions(1.into(), vec![config.genesis.hash], vec![]);
469 block.transactions[0].version += 1;
470
471 let BlockValidationFutures { block_task, virtual_state_task } =
472 consensus.validate_and_insert_block(block.clone().to_immutable());
473
474 assert_match!(block_task.await, Err(RuleError::BadMerkleRoot(_, _)));
475 assert_match!(virtual_state_task.await, Err(RuleError::BadMerkleRoot(_, _)));
477
478 assert_match!(
480 consensus.validate_and_insert_block(block.to_immutable()).virtual_state_task.await,
481 Err(RuleError::BadMerkleRoot(_, _))
482 );
483
484 let mut block = consensus.build_block_with_parents_and_transactions(1.into(), vec![config.genesis.hash], vec![]);
485 block.header.parents_by_level[0][0] = 0.into();
486
487 assert_match!(
488 consensus.validate_and_insert_block(block.clone().to_immutable()).virtual_state_task.await,
489 Err(RuleError::MissingParents(_))
490 );
491
492 assert_match!(
494 consensus.validate_and_insert_block(block.to_immutable()).virtual_state_task.await,
495 Err(RuleError::MissingParents(_))
496 );
497
498 consensus.shutdown(wait_handles);
499 }
500}