• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tari-project / tari / 17297453740

28 Aug 2025 01:33PM UTC coverage: 61.046% (+0.9%) from 60.14%
17297453740

push

github

web-flow
chore(ci): add a wasm build step in ci (#7448)

Description
Add a wasm build step in ci

Motivation and Context
Test the wasm builds

How Has This Been Tested?
Builds in local fork


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Added CI workflow to build WebAssembly targets with optimized caching
on both hosted and self-hosted runners, improving build consistency and
speed.
* **Tests**
* Expanded automated checks to include WebAssembly build verification
for multiple modules, increasing coverage and early detection of build
issues.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

72582 of 118897 relevant lines covered (61.05%)

301536.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.61
/base_layer/core/src/validation/block_body/test.rs
1
//  Copyright 2021, The Tari Project
2
//
3
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4
//  following conditions are met:
5
//
6
//  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7
//  disclaimer.
8
//
9
//  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10
//  following disclaimer in the documentation and/or other materials provided with the distribution.
11
//
12
//  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13
//  products derived from this software without specific prior written permission.
14
//
15
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16
//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21
//  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22

23
#![allow(clippy::indexing_slicing)]
24
use std::sync::Arc;
25

26
use tari_common::configuration::Network;
27
use tari_common_types::{key_branches::TransactionKeyManagerBranch, tari_address::TariAddress};
28
use tari_node_components::blocks::BlockValidationError;
29
use tari_script::{push_pubkey_script, script};
30
use tari_test_utils::unpack_enum;
31
use tari_transaction_components::{
32
    aggregated_body::AggregateBody,
33
    consensus::ConsensusConstantsBuilder,
34
    crypto_factories::CryptoFactories,
35
    key_manager::TariKeyId,
36
    tari_amount::{uT, T},
37
    tari_proof_of_work::Difficulty,
38
    test_helpers::schema_to_transaction,
39
    transaction_components::{
40
        encrypted_data::STATIC_ENCRYPTED_DATA_SIZE_TOTAL,
41
        EncryptedData,
42
        MemoField,
43
        RangeProofType,
44
        TransactionError,
45
    },
46
    txn_schema,
47
    validation::AggregatedBodyValidationError,
48
    CoinbaseBuilder,
49
};
50
use tokio::time::Instant;
51

52
use super::BlockBodyFullValidator;
53
use crate::{
54
    block_spec,
55
    consensus::BaseNodeConsensusManager,
56
    test_helpers::{blockchain::TestBlockchain, BlockSpec},
57
    validation::{BlockBodyValidator, ValidationError},
58
};
59
async fn setup_with_rules(
14✔
60
    rules: BaseNodeConsensusManager,
14✔
61
    check_rangeproof: bool,
14✔
62
) -> (TestBlockchain, BlockBodyFullValidator) {
14✔
63
    let blockchain = TestBlockchain::create(rules.clone()).await;
14✔
64
    let validator = BlockBodyFullValidator::new(rules, check_rangeproof);
14✔
65
    (blockchain, validator)
14✔
66
}
14✔
67

68
async fn setup(check_rangeproof: bool) -> (TestBlockchain, BlockBodyFullValidator) {
11✔
69
    let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
11✔
70
        .add_consensus_constants(
11✔
71
            ConsensusConstantsBuilder::new(Network::LocalNet)
11✔
72
                .with_coinbase_lockheight(0)
11✔
73
                .with_max_block_transaction_weight(127_795)
11✔
74
                .build(),
11✔
75
        )
11✔
76
        .build()
11✔
77
        .unwrap();
11✔
78
    setup_with_rules(rules, check_rangeproof).await
11✔
79
}
11✔
80

81
#[tokio::test]
82
async fn it_passes_if_large_output_block_is_valid() {
1✔
83
    // we use this test to benchmark a block with multiple outputs
1✔
84
    let (mut blockchain, validator) = setup(false).await;
1✔
85
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
86
    let mut outs = Vec::new();
1✔
87
    // create 498 outputs, so we have a block with 500 outputs, 498 + change + coinbase
1✔
88
    for _ in 0..498 {
499✔
89
        outs.push(9000 * uT);
498✔
90
    }
498✔
91

1✔
92
    let schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: outs);
1✔
93
    let (txs, _outputs) = schema_to_transaction(&[schema1], &blockchain.km).await;
1✔
94

1✔
95
    let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
96
    let (chain_block, _coinbase_b) = blockchain
1✔
97
        .create_next_tip(block_spec!("B",parent: "A", transactions: txs))
1✔
98
        .await;
1✔
99
    let (mut block, mmr_roots) = blockchain
1✔
100
        .db()
1✔
101
        .calculate_mmr_roots(chain_block.block().clone())
1✔
102
        .unwrap();
1✔
103
    block.header.input_mr = mmr_roots.input_mr;
1✔
104
    block.header.block_output_mr = mmr_roots.block_output_mr;
1✔
105
    block.header.output_mr = mmr_roots.output_mr;
1✔
106
    block.header.output_smt_size = mmr_roots.output_smt_size;
1✔
107
    block.header.kernel_mr = mmr_roots.kernel_mr;
1✔
108
    block.header.kernel_mmr_size = mmr_roots.kernel_mmr_size;
1✔
109
    block.header.validator_node_mr = mmr_roots.validator_node_mr;
1✔
110
    block.header.validator_node_size = mmr_roots.validator_node_size;
1✔
111

1✔
112
    let txn = blockchain.db().db_read_access().unwrap();
1✔
113
    let start = Instant::now();
1✔
114
    assert!(validator.validate_body(&*txn, &block).is_ok());
1✔
115
    let finished = start.elapsed();
1✔
116
    // this here here for benchmarking purposes.
×
117
    // we can extrapolate full block validation by multiplying the time by 4.6, this we get from the max_weight /weight
×
118
    // of the block
×
119
    println!("finished validating in: {}", finished.as_millis());
×
120
}
1✔
121

122
#[tokio::test]
123
async fn it_validates_when_a_coinbase_is_spent() {
1✔
124
    // we use this test to benchmark a block with multiple outputs
1✔
125
    let (mut blockchain, validator) = setup(false).await;
1✔
126
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
127

1✔
128
    let schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![9000 * uT]);
1✔
129
    let (txs, _outputs) = schema_to_transaction(&[schema1], &blockchain.km).await;
1✔
130

1✔
131
    let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
132
    let (chain_block, _coinbase_b) = blockchain
1✔
133
        .create_next_tip(block_spec!("B",parent: "A", transactions: txs))
1✔
134
        .await;
1✔
135
    let (mut block, mmr_roots) = blockchain
1✔
136
        .db()
1✔
137
        .calculate_mmr_roots(chain_block.block().clone())
1✔
138
        .unwrap();
1✔
139
    block.header.input_mr = mmr_roots.input_mr;
1✔
140
    block.header.output_mr = mmr_roots.output_mr;
1✔
141
    block.header.block_output_mr = mmr_roots.block_output_mr;
1✔
142
    block.header.output_smt_size = mmr_roots.output_smt_size;
1✔
143
    block.header.kernel_mr = mmr_roots.kernel_mr;
1✔
144
    block.header.kernel_mmr_size = mmr_roots.kernel_mmr_size;
1✔
145
    block.header.validator_node_mr = mmr_roots.validator_node_mr;
1✔
146
    block.header.validator_node_size = mmr_roots.validator_node_size;
1✔
147

1✔
148
    let txn = blockchain.db().db_read_access().unwrap();
1✔
149
    assert!(validator.validate_body(&*txn, &block).is_ok());
1✔
150
}
1✔
151

152
#[tokio::test]
153
async fn it_passes_if_large_block_is_valid() {
1✔
154
    // we use this test to benchmark a block with multiple inputs and outputs
1✔
155
    let (mut blockchain, validator) = setup(false).await;
1✔
156
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
157
    let schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T]);
1✔
158
    let (txs, outputs) = schema_to_transaction(&[schema1], &blockchain.km).await;
1✔
159

1✔
160
    let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
161
    let (_block, _coinbase_b) = blockchain
1✔
162
        .append(block_spec!("B", parent: "A", transactions: txs))
1✔
163
        .await
1✔
164
        .unwrap();
1✔
165

1✔
166
    let mut schemas = Vec::new();
1✔
167
    for output in outputs {
2,147,483,647✔
168
        let new_schema = txn_schema!(from: vec![output.clone()], to: vec![1 * T, 1 * T, 1 * T, 1 * T]);
2,147,483,647✔
169
        schemas.push(new_schema);
2,147,483,647✔
170
    }
2,147,483,647✔
171
    let (txs, _) = schema_to_transaction(&schemas, &blockchain.km).await;
1✔
172

1✔
173
    let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
174
    let (chain_block, _coinbase_c) = blockchain
1✔
175
        .create_next_tip(block_spec!("C",parent: "B", transactions: txs))
×
176
        .await;
×
177
    let (mut block, mmr_roots) = blockchain
1✔
178
        .db()
×
179
        .calculate_mmr_roots(chain_block.block().clone())
×
180
        .unwrap();
×
181
    block.header.input_mr = mmr_roots.input_mr;
×
182
    block.header.output_mr = mmr_roots.output_mr;
×
183
    block.header.block_output_mr = mmr_roots.block_output_mr;
×
184
    block.header.output_smt_size = mmr_roots.output_smt_size;
×
185
    block.header.kernel_mr = mmr_roots.kernel_mr;
×
186
    block.header.kernel_mmr_size = mmr_roots.kernel_mmr_size;
×
187
    block.header.validator_node_mr = mmr_roots.validator_node_mr;
×
188
    block.header.validator_node_size = mmr_roots.validator_node_size;
×
189

×
190
    let txn = blockchain.db().db_read_access().unwrap();
×
191
    let start = Instant::now();
×
192
    validator.validate_body(&*txn, &block).unwrap();
×
193
    // assert!(validator.validate_body(&*txn, &block).is_ok());
×
194
    let finished = start.elapsed();
×
195
    // this here here for benchmarking purposes.
×
196
    // we can extrapolate full block validation by multiplying the time by 32.9, this we get from the max_weight /weight
×
197
    // of the block
×
198
    println!("finished validating in: {}", finished.as_millis());
×
199
}
1✔
200

201
#[tokio::test]
202
async fn it_passes_if_block_is_valid() {
1✔
203
    let (blockchain, validator) = setup(true).await;
1✔
204

1✔
205
    let (chain_block, _) = blockchain.create_next_tip(BlockSpec::default()).await;
1✔
206

1✔
207
    let (mut block, mmr_roots) = blockchain
1✔
208
        .db()
1✔
209
        .calculate_mmr_roots(chain_block.block().clone())
1✔
210
        .unwrap();
1✔
211
    block.header.input_mr = mmr_roots.input_mr;
1✔
212
    block.header.output_mr = mmr_roots.output_mr;
1✔
213
    block.header.block_output_mr = mmr_roots.block_output_mr;
1✔
214
    block.header.output_smt_size = mmr_roots.output_smt_size;
1✔
215
    block.header.kernel_mr = mmr_roots.kernel_mr;
1✔
216
    block.header.kernel_mmr_size = mmr_roots.kernel_mmr_size;
1✔
217
    block.header.validator_node_mr = mmr_roots.validator_node_mr;
1✔
218
    block.header.validator_node_size = mmr_roots.validator_node_size;
1✔
219

1✔
220
    let txn = blockchain.db().db_read_access().unwrap();
1✔
221
    assert!(validator.validate_body(&*txn, &block).is_ok());
1✔
222
}
1✔
223

224
#[tokio::test]
225
async fn it_checks_the_coinbase_reward() {
1✔
226
    let (blockchain, validator) = setup(true).await;
1✔
227

1✔
228
    let (block, _) = blockchain
1✔
229
        .create_chained_block(block_spec!("A", parent: "GB", reward: 10 * T, ))
1✔
230
        .await;
1✔
231
    let txn = blockchain.db().db_read_access().unwrap();
1✔
232
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
233
    println!("err {err:?}");
1✔
234
    assert!(matches!(
1✔
235
        err,
1✔
236
        ValidationError::BlockError(BlockValidationError::TransactionError(
1✔
237
            TransactionError::InvalidCoinbase
1✔
238
        ))
1✔
239
    ));
1✔
240
}
1✔
241

242
#[tokio::test]
243
async fn it_allows_multiple_coinbases() {
1✔
244
    let (blockchain, validator) = setup(true).await;
1✔
245

1✔
246
    let (mut block, coinbase) = blockchain.create_unmined_block(block_spec!("A1", parent: "GB")).await;
1✔
247
    let commitment_mask_key = TariKeyId::Managed {
1✔
248
        branch: TransactionKeyManagerBranch::CommitmentMask.get_branch_key(),
1✔
249
        index: 42,
1✔
250
    };
1✔
251
    let wallet_payment_address = TariAddress::default();
1✔
252
    let (_, coinbase_output) = CoinbaseBuilder::new(blockchain.km.clone())
1✔
253
        .with_block_height(1)
1✔
254
        .with_fees(0.into())
1✔
255
        .with_commitment_mask_id(commitment_mask_key.clone())
1✔
256
        .with_encryption_key_id(TariKeyId::default())
1✔
257
        .with_sender_offset_key_id(TariKeyId::default())
1✔
258
        .with_script_key_id(TariKeyId::default())
1✔
259
        .with_script(push_pubkey_script(wallet_payment_address.public_spend_key()))
1✔
260
        .with_range_proof_type(RangeProofType::RevealedValue)
1✔
261
        .build_with_reward(
1✔
262
            blockchain.rules().consensus_constants(1),
1✔
263
            coinbase.value,
1✔
264
            MemoField::new_empty(),
1✔
265
        )
1✔
266
        .await
1✔
267
        .unwrap();
1✔
268

1✔
269
    block
1✔
270
        .body
1✔
271
        .add_output(coinbase_output.to_transaction_output(&blockchain.km).await.unwrap());
1✔
272
    block.body.sort();
1✔
273

1✔
274
    let (block, _) = blockchain
1✔
275
        .create_unmined_block(block_spec!("A2", parent: "GB", skip_coinbase: true,))
1✔
276
        .await;
1✔
277
    let block = blockchain.mine_block("GB", block, Difficulty::min());
1✔
278
    let txn = blockchain.db().db_read_access().unwrap();
1✔
279
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
280
    assert!(matches!(
1✔
281
        err,
1✔
282
        ValidationError::BlockError(BlockValidationError::TransactionError(TransactionError::NoCoinbase))
1✔
283
    ));
1✔
284
}
1✔
285

286
#[tokio::test]
287
async fn it_checks_duplicate_kernel() {
1✔
288
    let (mut blockchain, validator) = setup(true).await;
1✔
289

1✔
290
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
291
    let (txs, _) = schema_to_transaction(
1✔
292
        &[txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T])],
1✔
293
        &blockchain.km,
1✔
294
    )
1✔
295
    .await;
1✔
296

1✔
297
    blockchain
1✔
298
        .add_next_tip(block_spec!("1", transactions: txs.iter().map(|t| (**t).clone()).collect()))
1✔
299
        .await
1✔
300
        .unwrap();
1✔
301
    let (block, _) = blockchain
1✔
302
        .create_next_tip(
1✔
303
            BlockSpec::new()
1✔
304
                .with_transactions(txs.iter().map(|t| (**t).clone()).collect())
1✔
305
                .finish(),
1✔
306
        )
1✔
307
        .await;
1✔
308
    let txn = blockchain.db().db_read_access().unwrap();
1✔
309
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
310
    assert!(matches!(err, ValidationError::DuplicateKernelError(_)));
1✔
311
}
1✔
312

313
#[tokio::test]
314
async fn it_checks_double_spends() {
1✔
315
    let (mut blockchain, validator) = setup(true).await;
1✔
316

1✔
317
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
318
    let (txs, _) = schema_to_transaction(
1✔
319
        &[txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T])],
1✔
320
        &blockchain.km,
1✔
321
    )
1✔
322
    .await;
1✔
323

1✔
324
    blockchain
1✔
325
        .add_next_tip(block_spec!("1", transactions: txs.iter().map(|t| (**t).clone()).collect()))
1✔
326
        .await
1✔
327
        .unwrap();
1✔
328
    // lets create a new transction from the same input
1✔
329
    let (txs2, _) = schema_to_transaction(
1✔
330
        &[txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T])],
1✔
331
        &blockchain.km,
1✔
332
    )
1✔
333
    .await;
1✔
334
    let (block, _) = blockchain
1✔
335
        .create_next_tip(
1✔
336
            BlockSpec::new()
1✔
337
                .with_transactions(txs2.iter().map(|t| (**t).clone()).collect())
1✔
338
                .finish(),
1✔
339
        )
1✔
340
        .await;
1✔
341
    let txn = blockchain.db().db_read_access().unwrap();
1✔
342
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
343
    assert!(matches!(err, ValidationError::ContainsSTxO));
1✔
344
}
1✔
345

346
#[tokio::test]
347
async fn it_checks_input_maturity() {
1✔
348
    let (mut blockchain, validator) = setup(true).await;
1✔
349

1✔
350
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
351
    let mut schema = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T]);
1✔
352
    schema.from[0].features.maturity = 100;
1✔
353
    let (txs, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
354

1✔
355
    let (block, _) = blockchain
1✔
356
        .create_next_tip(
1✔
357
            BlockSpec::new()
1✔
358
                .with_transactions(txs.iter().map(|t| (**t).clone()).collect())
1✔
359
                .finish(),
1✔
360
        )
1✔
361
        .await;
1✔
362
    let txn = blockchain.db().db_read_access().unwrap();
1✔
363
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
364
    assert!(matches!(
1✔
365
        err,
1✔
366
        ValidationError::TransactionError(TransactionError::InputMaturity)
1✔
367
    ));
1✔
368
    unpack_enum!(ValidationError::TransactionError(TransactionError::InputMaturity) = err);
1✔
369
}
1✔
370

371
#[tokio::test]
372
async fn it_checks_txo_sort_order() {
1✔
373
    let (mut blockchain, validator) = setup(true).await;
1✔
374

1✔
375
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
376

1✔
377
    let schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]);
1✔
378
    let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await;
1✔
379
    let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
380

1✔
381
    let (mut block, _) = blockchain
1✔
382
        .create_unmined_block(block_spec!("B->A", transactions: txs))
1✔
383
        .await;
1✔
384
    let outputs = block.body.outputs().iter().rev().cloned().collect::<Vec<_>>();
1✔
385
    let inputs = block.body.inputs().clone();
1✔
386
    let kernels = block.body.kernels().clone();
1✔
387
    block.body = AggregateBody::new_sorted_unchecked(inputs, outputs, kernels);
1✔
388
    let block = blockchain.mine_block("A", block, Difficulty::min());
1✔
389

1✔
390
    let txn = blockchain.db().db_read_access().unwrap();
1✔
391
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
392
    assert!(matches!(
1✔
393
        err,
1✔
394
        ValidationError::AggregatedBodyValidationError(AggregatedBodyValidationError::UnsortedOrDuplicateOutput)
1✔
395
    ));
1✔
396
}
1✔
397

398
#[tokio::test]
399
async fn it_limits_the_script_byte_size() {
1✔
400
    let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
401
        .add_consensus_constants(
1✔
402
            ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
403
                .with_coinbase_lockheight(0)
1✔
404
                .with_max_script_byte_size(2)
1✔
405
                .build(),
1✔
406
        )
1✔
407
        .build()
1✔
408
        .unwrap();
1✔
409
    let (mut blockchain, validator) = setup_with_rules(rules, true).await;
1✔
410

1✔
411
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
412

1✔
413
    let mut schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]);
1✔
414
    schema1.script = script!(Nop Nop Nop).unwrap();
1✔
415
    let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await;
1✔
416
    let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
417
    let (block, _) = blockchain.create_next_tip(block_spec!("B", transactions: txs)).await;
1✔
418

1✔
419
    let txn = blockchain.db().db_read_access().unwrap();
1✔
420
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
421
    assert!(matches!(
1✔
422
        err,
1✔
423
        ValidationError::AggregatedBodyValidationError(AggregatedBodyValidationError::TariScriptExceedsMaxSize { .. })
1✔
424
    ));
1✔
425
}
1✔
426

427
#[tokio::test]
428
async fn it_limits_the_encrypted_data_byte_size() {
1✔
429
    let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
430
        .add_consensus_constants(
1✔
431
            ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
432
                .with_coinbase_lockheight(0)
1✔
433
                .build(),
1✔
434
        )
1✔
435
        .build()
1✔
436
        .unwrap();
1✔
437
    let (mut blockchain, validator) = setup_with_rules(rules, true).await;
1✔
438

1✔
439
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
440

1✔
441
    let mut schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]);
1✔
442
    schema1.script = script!(Nop Nop Nop).unwrap();
1✔
443
    let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await;
1✔
444
    let mut txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
445
    let mut outputs = txs[0].body.outputs().clone();
1✔
446
    outputs[0].encrypted_data = EncryptedData::from_bytes(&vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL + 250]).unwrap();
1✔
447
    txs[0].body = AggregateBody::new(txs[0].body.inputs().clone(), outputs, txs[0].body.kernels().clone());
1✔
448
    let (block, _) = blockchain.create_next_tip(block_spec!("B", transactions: txs)).await;
1✔
449

1✔
450
    let txn = blockchain.db().db_read_access().unwrap();
1✔
451
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
452
    assert!(matches!(
1✔
453
        err,
1✔
454
        ValidationError::AggregatedBodyValidationError(
1✔
455
            AggregatedBodyValidationError::EncryptedDataExceedsMaxSize { .. }
1✔
456
        )
1✔
457
    ));
1✔
458
}
1✔
459

460
#[tokio::test]
461
async fn it_rejects_invalid_input_metadata() {
1✔
462
    let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
463
        .add_consensus_constants(
1✔
464
            ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
465
                .with_coinbase_lockheight(0)
1✔
466
                .build(),
1✔
467
        )
1✔
468
        .build()
1✔
469
        .unwrap();
1✔
470
    let (mut blockchain, validator) = setup_with_rules(rules.clone(), true).await;
1✔
471

1✔
472
    let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
473

1✔
474
    let mut schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]);
1✔
475
    schema1.from[0].sender_offset_public_key = Default::default();
1✔
476
    let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await;
1✔
477
    let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
478
    let (block, _) = blockchain.create_next_tip(block_spec!("B", transactions: txs)).await;
1✔
479

1✔
480
    let txn = blockchain.db().db_read_access().unwrap();
1✔
481
    let err = validator.validate_body(&*txn, block.block()).unwrap_err();
1✔
482
    assert!(matches!(err, ValidationError::UnknownInputs(_)));
1✔
483
}
1✔
484

485
#[tokio::test]
486
async fn it_rejects_zero_conf_double_spends() {
1✔
487
    let (mut blockchain, validator) = setup(true).await;
1✔
488
    let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap();
1✔
489

1✔
490
    let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]);
1✔
491
    let (initial_tx, outputs) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
492

1✔
493
    let schema = txn_schema!(from: vec![outputs[0].clone()], to: vec![200 * T]);
1✔
494
    let (first_spend, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
495

1✔
496
    let schema = txn_schema!(from: vec![outputs[0].clone()], to: vec![150 * T]);
1✔
497
    let (double_spend, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
498

1✔
499
    let transactions = initial_tx
1✔
500
        .into_iter()
1✔
501
        .chain(first_spend)
1✔
502
        .chain(double_spend)
1✔
503
        .map(|b| Arc::try_unwrap(b).unwrap())
3✔
504
        .collect::<Vec<_>>();
1✔
505

1✔
506
    let (unmined, _) = blockchain
1✔
507
        .create_unmined_block(block_spec!("2", parent: "1", transactions: transactions))
1✔
508
        .await;
1✔
509
    let txn = blockchain.db().db_read_access().unwrap();
1✔
510
    let err = validator.validate_body(&*txn, &unmined).unwrap_err();
1✔
511
    assert!(matches!(
1✔
512
        err,
1✔
513
        ValidationError::AggregatedBodyValidationError(AggregatedBodyValidationError::UnsortedOrDuplicateInput)
1✔
514
    ));
1✔
515
}
1✔
516

517
mod body_only {
518
    use super::*;
519

520
    #[tokio::test]
521
    async fn it_rejects_invalid_input_metadata() {
1✔
522
        let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
523
            .add_consensus_constants(
1✔
524
                ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
525
                    .with_coinbase_lockheight(0)
1✔
526
                    .build(),
1✔
527
            )
1✔
528
            .build()
1✔
529
            .unwrap();
1✔
530
        let mut blockchain = TestBlockchain::create(rules.clone()).await;
1✔
531
        let validator = BlockBodyFullValidator::new(rules, true);
1✔
532

1✔
533
        let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap();
1✔
534

1✔
535
        let mut schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]);
1✔
536
        schema1.from[0].sender_offset_public_key = Default::default();
1✔
537
        let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await;
1✔
538
        let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
1✔
539
        let (block, _) = blockchain
1✔
540
            .create_next_tip(BlockSpec::new().with_transactions(txs).finish())
1✔
541
            .await;
1✔
542

1✔
543
        let metadata = blockchain.db().get_chain_metadata().unwrap();
1✔
544

1✔
545
        let db = blockchain.db().db_read_access().unwrap();
1✔
546
        let err = validator.validate(&*db, block.block(), Some(&metadata)).unwrap_err();
1✔
547
        assert!(matches!(err, ValidationError::UnknownInputs(_)));
1✔
548
    }
1✔
549
}
550

551
mod orphan_validator {
552
    use tari_transaction_components::{transaction_components::OutputType, txn_schema};
553

554
    use super::*;
555
    use crate::validation::block_body::BlockBodyInternalConsistencyValidator;
556

557
    #[tokio::test]
558
    async fn it_rejects_zero_conf_double_spends() {
1✔
559
        let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
560
            .add_consensus_constants(
1✔
561
                ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
562
                    .with_coinbase_lockheight(0)
1✔
563
                    .build(),
1✔
564
            )
1✔
565
            .build()
1✔
566
            .unwrap();
1✔
567
        let mut blockchain = TestBlockchain::create(rules.clone()).await;
1✔
568
        let validator = BlockBodyInternalConsistencyValidator::new(rules, false, CryptoFactories::default());
1✔
569
        let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap();
1✔
570

1✔
571
        let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]);
1✔
572
        let (initial_tx, outputs) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
573

1✔
574
        let schema = txn_schema!(from: vec![outputs[0].clone()], to: vec![200 * T]);
1✔
575
        let (first_spend, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
576

1✔
577
        let schema = txn_schema!(from: vec![outputs[0].clone()], to: vec![150 * T]);
1✔
578
        let (double_spend, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
579

1✔
580
        let transactions = initial_tx
1✔
581
            .into_iter()
1✔
582
            .chain(first_spend)
1✔
583
            .chain(double_spend)
1✔
584
            .map(|b| Arc::try_unwrap(b).unwrap())
3✔
585
            .collect::<Vec<_>>();
1✔
586

1✔
587
        let (unmined, _) = blockchain
1✔
588
            .create_unmined_block(block_spec!("2", parent: "1", transactions: transactions))
1✔
589
            .await;
1✔
590
        let err = validator.validate(&unmined).unwrap_err();
1✔
591
        assert!(matches!(
1✔
592
            err,
1✔
593
            ValidationError::AggregatedBodyValidationError(AggregatedBodyValidationError::UnsortedOrDuplicateInput)
1✔
594
        ));
1✔
595
    }
1✔
596

597
    #[tokio::test]
598
    async fn it_rejects_unpermitted_output_types() {
1✔
599
        let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
600
            .add_consensus_constants(
1✔
601
                ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
602
                    .with_permitted_output_types(&[OutputType::Coinbase])
1✔
603
                    .with_coinbase_lockheight(0)
1✔
604
                    .build(),
1✔
605
            )
1✔
606
            .build()
1✔
607
            .unwrap();
1✔
608
        let mut blockchain = TestBlockchain::create(rules.clone()).await;
1✔
609
        let validator = BlockBodyInternalConsistencyValidator::new(rules, false, CryptoFactories::default());
1✔
610
        let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap();
1✔
611

1✔
612
        let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]);
1✔
613
        let (tx, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
614

1✔
615
        let transactions = tx.into_iter().map(|b| Arc::try_unwrap(b).unwrap()).collect::<Vec<_>>();
1✔
616

1✔
617
        let (unmined, _) = blockchain
1✔
618
            .create_unmined_block(block_spec!("2", parent: "1", transactions: transactions))
1✔
619
            .await;
1✔
620
        let err = validator.validate(&unmined).unwrap_err();
1✔
621
        unpack_enum!(ValidationError::AggregatedBodyValidationError(err) = err);
1✔
622
        unpack_enum!(AggregatedBodyValidationError::OutputTypeNotPermitted { output_type } = err);
1✔
623
        assert_eq!(output_type, OutputType::Standard);
1✔
624
    }
1✔
625

626
    #[tokio::test]
627
    async fn it_rejects_unpermitted_range_proof_types() {
1✔
628
        let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
629
            .add_consensus_constants(
1✔
630
                ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
631
                    .with_permitted_range_proof_types(&[
1✔
632
                        (OutputType::Standard, &[RangeProofType::RevealedValue]),
1✔
633
                        (OutputType::Coinbase, &[RangeProofType::RevealedValue]),
1✔
634
                        (OutputType::Burn, &[RangeProofType::RevealedValue]),
1✔
635
                        (OutputType::ValidatorNodeRegistration, &[RangeProofType::RevealedValue]),
1✔
636
                        (OutputType::CodeTemplateRegistration, &[RangeProofType::RevealedValue]),
1✔
637
                    ])
1✔
638
                    .with_coinbase_lockheight(0)
1✔
639
                    .build(),
1✔
640
            )
1✔
641
            .build()
1✔
642
            .unwrap();
1✔
643
        let mut blockchain = TestBlockchain::create(rules.clone()).await;
1✔
644
        let validator = BlockBodyInternalConsistencyValidator::new(rules, false, CryptoFactories::default());
1✔
645
        let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap();
1✔
646

1✔
647
        let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]);
1✔
648
        let (tx, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
649

1✔
650
        let transactions = tx.into_iter().map(|b| Arc::try_unwrap(b).unwrap()).collect::<Vec<_>>();
1✔
651

1✔
652
        let (unmined, _) = blockchain
1✔
653
            .create_unmined_block(block_spec!("2", parent: "1", transactions: transactions))
1✔
654
            .await;
1✔
655
        let err = validator.validate(&unmined).unwrap_err();
1✔
656
        unpack_enum!(ValidationError::AggregatedBodyValidationError(err) = err);
1✔
657
        unpack_enum!(AggregatedBodyValidationError::RangeProofTypeNotPermitted { range_proof_type } = err);
1✔
658
        assert_eq!(range_proof_type, RangeProofType::BulletProofPlus);
1✔
659
    }
1✔
660

661
    #[tokio::test]
662
    async fn it_accepts_permitted_range_proof_types() {
1✔
663
        let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
664
            .add_consensus_constants(
1✔
665
                ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
666
                    .with_permitted_range_proof_types(&[
1✔
667
                        (OutputType::Standard, &[RangeProofType::BulletProofPlus]),
1✔
668
                        (OutputType::Coinbase, &[RangeProofType::BulletProofPlus]),
1✔
669
                        (OutputType::Burn, &[RangeProofType::BulletProofPlus]),
1✔
670
                        (OutputType::ValidatorNodeRegistration, &[
1✔
671
                            RangeProofType::BulletProofPlus,
1✔
672
                        ]),
1✔
673
                        (OutputType::CodeTemplateRegistration, &[RangeProofType::BulletProofPlus]),
1✔
674
                    ])
1✔
675
                    .with_coinbase_lockheight(0)
1✔
676
                    .build(),
1✔
677
            )
1✔
678
            .build()
1✔
679
            .unwrap();
1✔
680
        let mut blockchain = TestBlockchain::create(rules.clone()).await;
1✔
681
        let validator = BlockBodyInternalConsistencyValidator::new(rules, false, CryptoFactories::default());
1✔
682
        let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap();
1✔
683

1✔
684
        let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]);
1✔
685
        let (tx, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
686

1✔
687
        let transactions = tx.into_iter().map(|b| Arc::try_unwrap(b).unwrap()).collect::<Vec<_>>();
1✔
688

1✔
689
        let (unmined, _) = blockchain
1✔
690
            .create_unmined_block(block_spec!("2", parent: "1", transactions: transactions))
1✔
691
            .await;
1✔
692
        assert!(validator.validate(&unmined).is_ok());
1✔
693
    }
1✔
694

695
    #[tokio::test]
696
    async fn it_rejects_when_output_types_are_not_matched() {
1✔
697
        let rules = BaseNodeConsensusManager::builder(Network::LocalNet)
1✔
698
            .add_consensus_constants(
1✔
699
                ConsensusConstantsBuilder::new(Network::LocalNet)
1✔
700
                    .with_permitted_range_proof_types(&[(OutputType::CodeTemplateRegistration, &[
1✔
701
                        RangeProofType::BulletProofPlus,
1✔
702
                    ])])
1✔
703
                    .with_coinbase_lockheight(0)
1✔
704
                    .build(),
1✔
705
            )
1✔
706
            .build()
1✔
707
            .unwrap();
1✔
708
        let mut blockchain = TestBlockchain::create(rules.clone()).await;
1✔
709
        let validator = BlockBodyInternalConsistencyValidator::new(rules, false, CryptoFactories::default());
1✔
710
        let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap();
1✔
711

1✔
712
        let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]);
1✔
713
        let (tx, _) = schema_to_transaction(&[schema], &blockchain.km).await;
1✔
714

1✔
715
        let transactions = tx.into_iter().map(|b| Arc::try_unwrap(b).unwrap()).collect::<Vec<_>>();
1✔
716

1✔
717
        let (unmined, _) = blockchain
1✔
718
            .create_unmined_block(block_spec!("2", parent: "1", transactions: transactions))
1✔
719
            .await;
1✔
720
        let err = validator.validate(&unmined).unwrap_err();
1✔
721
        unpack_enum!(ValidationError::AggregatedBodyValidationError(err) = err);
1✔
722
        unpack_enum!(AggregatedBodyValidationError::OutputTypeNotMatchedToRangeProofType { output_type } = err);
1✔
723
        assert!(output_type == OutputType::Standard || output_type == OutputType::Coinbase);
1✔
724
    }
1✔
725
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc