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

tari-project / tari / 14842392104

05 May 2025 05:29PM UTC coverage: 73.211% (-0.007%) from 73.218%
14842392104

push

github

SWvheerden
reduce logs

2 of 2 new or added lines in 1 file covered. (100.0%)

460 existing lines in 19 files now uncovered.

81547 of 111387 relevant lines covered (73.21%)

272481.62 hits per line

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

80.28
/base_layer/core/src/blocks/block.rs
1
// Copyright 2018 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
// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License,
24
// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0.
25

26
use std::{
27
    fmt,
28
    fmt::{Display, Formatter},
29
};
30

31
use borsh::{BorshDeserialize, BorshSerialize};
32
use log::*;
33
use serde::{Deserialize, Serialize};
34
use tari_common_types::types::{FixedHash, PrivateKey};
35
use thiserror::Error;
36

37
use crate::{
38
    blocks::BlockHeader,
39
    consensus::ConsensusConstants,
40
    proof_of_work::ProofOfWork,
41
    transactions::{
42
        aggregated_body::AggregateBody,
43
        tari_amount::MicroMinotari,
44
        transaction_components::{
45
            KernelFeatures,
46
            OutputType,
47
            Transaction,
48
            TransactionError,
49
            TransactionInput,
50
            TransactionKernel,
51
            TransactionOutput,
52
        },
53
        CryptoFactories,
54
    },
55
};
56

57
#[derive(Clone, Debug, Error)]
58
pub enum BlockValidationError {
59
    #[error("A transaction in the block failed to validate: `{0}`")]
60
    TransactionError(#[from] TransactionError),
61
    #[error("Mismatched {kind} MMR roots")]
62
    MismatchedMmrRoots { kind: &'static str },
63
    #[error("MMR size for {mmr_tree} does not match. Expected: {expected}, received: {actual}")]
64
    MismatchedMmrSize {
65
        mmr_tree: String,
66
        expected: u64,
67
        actual: u64,
68
    },
69
}
70

71
/// A Minotari block. Blocks are linked together into a blockchain.
72
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
×
73
pub struct Block {
74
    /// The BlockHeader contains all the metadata for the block, including proof of work, a link to the previous block
75
    /// and the transaction kernels.
76
    pub header: BlockHeader,
77
    /// The components of the block or transaction. The same struct can be used for either, since in Mimblewimble,
78
    /// blocks consist of inputs, outputs and kernels, rather than transactions.
79
    pub body: AggregateBody,
80
}
81

82
impl Block {
83
    pub fn new(header: BlockHeader, body: AggregateBody) -> Self {
92✔
84
        Self { header, body }
92✔
85
    }
92✔
86

UNCOV
87
    pub fn version(&self) -> u16 {
×
UNCOV
88
        self.header.version
×
UNCOV
89
    }
×
90

91
    /// This function will calculate the total fees contained in a block
92
    pub fn calculate_fees(&self) -> MicroMinotari {
×
93
        self.body.kernels().iter().fold(0.into(), |sum, x| sum + x.fee)
×
94
    }
×
95

96
    /// Run through the outputs of the block and check that
97
    /// 1. There is exactly ONE coinbase output
98
    /// 2. The output's maturity is correctly set
99
    /// 3. The amount is correct.
100
    pub fn check_coinbase_output(
78✔
101
        &self,
78✔
102
        reward: MicroMinotari,
78✔
103
        consensus_constants: &ConsensusConstants,
78✔
104
        factories: &CryptoFactories,
78✔
105
    ) -> Result<(), BlockValidationError> {
78✔
106
        self.body.check_coinbase_output(
78✔
107
            reward,
78✔
108
            consensus_constants.coinbase_min_maturity(),
78✔
109
            factories,
78✔
110
            self.header.height,
78✔
111
            consensus_constants.max_block_coinbase_count(),
78✔
112
        )?;
78✔
113
        Ok(())
72✔
114
    }
78✔
115

116
    /// Destroys the block and returns the pieces of the block: header, inputs, outputs and kernels
117
    pub fn dissolve(
8✔
118
        self,
8✔
119
    ) -> (
8✔
120
        BlockHeader,
8✔
121
        Vec<TransactionInput>,
8✔
122
        Vec<TransactionOutput>,
8✔
123
        Vec<TransactionKernel>,
8✔
124
    ) {
8✔
125
        let (inputs, outputs, kernels) = self.body.dissolve();
8✔
126
        (self.header, inputs, outputs, kernels)
8✔
127
    }
8✔
128

129
    /// Destroys the block and returns the pieces of the block: header, body
130
    pub fn into_header_body(self) -> (BlockHeader, AggregateBody) {
×
131
        (self.header, self.body)
×
132
    }
×
133

134
    /// Return a cloned version of this block with the TransactionInputs in their compact form
135
    pub fn to_compact(&self) -> Self {
×
136
        Self {
×
137
            header: self.header.clone(),
×
138
            body: self.body.to_compact(),
×
139
        }
×
140
    }
×
141

142
    /// The block hash is just the header hash, since the inputs, outputs and range proofs are captured by their
143
    /// respective MMR roots in the header itself.
144
    pub fn hash(&self) -> FixedHash {
4,183✔
145
        self.header.hash()
4,183✔
146
    }
4,183✔
147
}
148

149
impl Display for Block {
150
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
×
151
        writeln!(f, "----------------- Block -----------------")?;
×
152
        writeln!(f, "--- Header ---")?;
×
153
        writeln!(f, "Hash: {}", self.header.hash())?;
×
154
        writeln!(f, "{}", self.header)?;
×
155
        writeln!(f, "---  Body  ---")?;
×
156
        writeln!(f, "{}", self.body)
×
157
    }
×
158
}
159

160
pub struct BlockBuilder {
161
    header: BlockHeader,
162
    inputs: Vec<TransactionInput>,
163
    outputs: Vec<TransactionOutput>,
164
    kernels: Vec<TransactionKernel>,
165
}
166

167
impl BlockBuilder {
168
    pub fn new(blockchain_version: u16) -> BlockBuilder {
756✔
169
        BlockBuilder {
756✔
170
            header: BlockHeader::new(blockchain_version),
756✔
171
            inputs: Vec::new(),
756✔
172
            outputs: Vec::new(),
756✔
173
            kernels: Vec::new(),
756✔
174
        }
756✔
175
    }
756✔
176

177
    /// This function adds a header to the block
178
    pub fn with_header(mut self, header: BlockHeader) -> Self {
752✔
179
        self.header = header;
752✔
180
        self
752✔
181
    }
752✔
182

183
    /// This function adds the provided transaction inputs to the block
184
    pub fn add_inputs(mut self, mut inputs: Vec<TransactionInput>) -> Self {
591✔
185
        self.inputs.append(&mut inputs);
591✔
186
        self
591✔
187
    }
591✔
188

189
    /// This function adds the provided transaction outputs to the block WITHOUT updating output_mmr_size in the header
190
    pub fn add_outputs(mut self, mut outputs: Vec<TransactionOutput>) -> Self {
612✔
191
        self.outputs.append(&mut outputs);
612✔
192
        self
612✔
193
    }
612✔
194

195
    /// This function adds the provided transaction kernels to the block WITHOUT updating kernel_mmr_size in the header
196
    pub fn add_kernels(mut self, mut kernels: Vec<TransactionKernel>) -> Self {
612✔
197
        self.kernels.append(&mut kernels);
612✔
198
        self
612✔
199
    }
612✔
200

201
    /// This functions adds the provided transactions to the block, modifying the header MMR counts and offsets
202
    pub fn with_transactions(mut self, txs: Vec<Transaction>) -> Self {
492✔
203
        for tx in txs {
889✔
204
            self = self.add_transaction(tx)
397✔
205
        }
206
        self
492✔
207
    }
492✔
208

209
    /// This functions adds the provided transaction to the block, modifying the header MMR counts and offsets
210
    pub fn add_transaction(mut self, tx: Transaction) -> Self {
397✔
211
        let (inputs, outputs, kernels) = tx.body.dissolve();
397✔
212
        self = self.add_inputs(inputs);
397✔
213
        self.header.output_smt_size += outputs.len() as u64;
397✔
214
        self = self.add_outputs(outputs);
397✔
215
        self.header.kernel_mmr_size += kernels.len() as u64;
397✔
216
        self = self.add_kernels(kernels);
397✔
217
        self.header.total_kernel_offset = self.header.total_kernel_offset + tx.offset;
397✔
218
        self.header.total_script_offset = self.header.total_script_offset + tx.script_offset;
397✔
219
        self
397✔
220
    }
397✔
221

222
    /// This will add the given coinbase UTXO to the block
223
    pub fn with_coinbase_utxo(mut self, coinbase_utxo: TransactionOutput, coinbase_kernel: TransactionKernel) -> Self {
327✔
224
        self.kernels.push(coinbase_kernel);
327✔
225
        self.outputs.push(coinbase_utxo);
327✔
226
        self
327✔
227
    }
327✔
228

229
    /// Add the provided ProofOfWork metadata to the block
230
    pub fn with_pow(mut self, pow: ProofOfWork) -> Self {
×
231
        self.header.pow = pow;
×
232
        self
×
233
    }
×
234

235
    /// This will finish construction of the block and create the block
236
    pub fn build(self) -> Block {
752✔
237
        let mut block = Block {
752✔
238
            header: self.header,
752✔
239
            body: AggregateBody::new(self.inputs, self.outputs, self.kernels),
752✔
240
        };
752✔
241
        block.body.sort();
752✔
242
        block
752✔
243
    }
752✔
244
}
245

246
//---------------------------------- NewBlock --------------------------------------------//
247
pub struct NewBlock {
248
    /// The block header.
249
    pub header: BlockHeader,
250
    /// Coinbase kernel of the block
251
    pub coinbase_kernels: Vec<TransactionKernel>,
252
    /// Coinbase output of the block
253
    pub coinbase_outputs: Vec<TransactionOutput>,
254
    /// The scalar `s` component of the kernel excess signatures of the transactions contained in the block.
255
    pub kernel_excess_sigs: Vec<PrivateKey>,
256
}
257

258
impl From<&Block> for NewBlock {
259
    fn from(block: &Block) -> Self {
38✔
260
        let coinbase_kernels = block
38✔
261
            .body
38✔
262
            .kernels()
38✔
263
            .clone()
38✔
264
            .into_iter()
38✔
265
            .filter(|k| k.features.contains(KernelFeatures::COINBASE_KERNEL))
48✔
266
            .collect();
38✔
267
        let coinbase_outputs = block
38✔
268
            .body
38✔
269
            .outputs()
38✔
270
            .clone()
38✔
271
            .into_iter()
38✔
272
            .filter(|o| o.features.output_type == OutputType::Coinbase)
75✔
273
            .collect();
38✔
274

38✔
275
        Self {
38✔
276
            header: block.header.clone(),
38✔
277
            coinbase_kernels,
38✔
278
            coinbase_outputs,
38✔
279
            kernel_excess_sigs: block
38✔
280
                .body
38✔
281
                .kernels()
38✔
282
                .iter()
38✔
283
                .filter(|k| !k.features.contains(KernelFeatures::COINBASE_KERNEL))
48✔
284
                .map(|kernel| kernel.excess_sig.get_signature().clone())
38✔
285
                .collect(),
38✔
286
        }
38✔
287
    }
38✔
288
}
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

© 2026 Coveralls, Inc