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

tari-project / tari / 19424872350

17 Nov 2025 09:33AM UTC coverage: 51.544% (+1.0%) from 50.497%
19424872350

push

github

SWvheerden
chore: new release v5.2.0-pre.4

59181 of 114817 relevant lines covered (51.54%)

8061.81 hits per line

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

85.07
/base_layer/transaction_components/src/transaction_components/transaction_kernel.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
    cmp::Ordering,
28
    fmt::{Display, Formatter},
29
};
30

31
use blake2::Blake2b;
32
use borsh::{BorshDeserialize, BorshSerialize};
33
use digest::consts::{U32, U64};
34
use serde::{Deserialize, Serialize};
35
use tari_common_types::types::{CompressedCommitment, CompressedPublicKey, CompressedSignature, FixedHash};
36
use tari_hashing::TransactionHashDomain;
37
use tari_utilities::{hex::Hex, message_format::MessageFormat};
38

39
use super::TransactionKernelVersion;
40
use crate::{
41
    consensus::DomainSeparatedConsensusHasher,
42
    transaction_components::{KernelFeatures, TransactionError},
43
    MicroMinotari,
44
};
45

46
/// The transaction kernel tracks the excess for a given transaction. For an explanation of what the excess is, and
47
/// why it is necessary, refer to the
48
/// [Mimblewimble TLU post](https://tlu.tarilabs.com/protocols/mimblewimble-1/sources/PITCHME.link.html?highlight=mimblewimble#mimblewimble).
49
/// The kernel also tracks other transaction metadata, such as the lock height for the transaction (i.e. the earliest
50
/// this transaction can be mined) and the transaction fee, in cleartext.
51
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize, Default)]
×
52
pub struct TransactionKernel {
53
    pub version: TransactionKernelVersion,
54
    /// Options for a kernel's structure or use
55
    pub features: KernelFeatures,
56
    /// Fee originally included in the transaction this proof is for.
57
    pub fee: MicroMinotari,
58
    /// This kernel is not valid earlier than lock_height blocks
59
    /// The max lock_height of all *inputs* to this transaction
60
    pub lock_height: u64,
61
    /// Remainder of the sum of all transaction commitments (minus an offset). If the transaction is well-formed,
62
    /// amounts plus fee will sum to zero, and the excess is hence a valid public key.
63
    pub excess: CompressedCommitment,
64
    /// An aggregated signature of the metadata in this kernel, signed by the individual excess values and the offset
65
    /// excess of the sender.
66
    pub excess_sig: CompressedSignature,
67
    /// This is an optional field that must be set if the transaction contains a burned output.
68
    pub burn_commitment: Option<CompressedCommitment>,
69
}
70

71
impl TransactionKernel {
72
    pub fn new(
406✔
73
        version: TransactionKernelVersion,
406✔
74
        features: KernelFeatures,
406✔
75
        fee: MicroMinotari,
406✔
76
        lock_height: u64,
406✔
77
        excess: CompressedCommitment,
406✔
78
        excess_sig: CompressedSignature,
406✔
79
        burn_commitment: Option<CompressedCommitment>,
406✔
80
    ) -> TransactionKernel {
406✔
81
        TransactionKernel {
406✔
82
            version,
406✔
83
            features,
406✔
84
            fee,
406✔
85
            lock_height,
406✔
86
            excess,
406✔
87
            excess_sig,
406✔
88
            burn_commitment,
406✔
89
        }
406✔
90
    }
406✔
91

92
    /// Produce a canonical hash for a transaction kernel.
93
    pub fn hash(&self) -> FixedHash {
4,936✔
94
        DomainSeparatedConsensusHasher::<TransactionHashDomain, Blake2b<U32>>::new("transaction_kernel")
4,936✔
95
            .chain(self)
4,936✔
96
            .finalize()
4,936✔
97
            .into()
4,936✔
98
    }
4,936✔
99

100
    pub fn new_current_version(
394✔
101
        features: KernelFeatures,
394✔
102
        fee: MicroMinotari,
394✔
103
        lock_height: u64,
394✔
104
        excess: CompressedCommitment,
394✔
105
        excess_sig: CompressedSignature,
394✔
106
        burn_commitment: Option<CompressedCommitment>,
394✔
107
    ) -> TransactionKernel {
394✔
108
        TransactionKernel::new(
394✔
109
            TransactionKernelVersion::get_current_version(),
394✔
110
            features,
394✔
111
            fee,
394✔
112
            lock_height,
394✔
113
            excess,
394✔
114
            excess_sig,
394✔
115
            burn_commitment,
394✔
116
        )
117
    }
394✔
118

119
    pub fn is_coinbase(&self) -> bool {
×
120
        self.features.is_coinbase()
×
121
    }
×
122

123
    /// Is this a burned output kernel?
124
    pub fn is_burned(&self) -> bool {
17✔
125
        self.features.is_burned()
17✔
126
    }
17✔
127

128
    pub fn verify_signature(&self) -> Result<(), TransactionError> {
591✔
129
        let excess = self.excess.to_compressed_key();
591✔
130
        let r = self.excess_sig.get_compressed_public_nonce();
591✔
131
        let c = TransactionKernel::build_kernel_signature_challenge(
591✔
132
            &self.version,
591✔
133
            r,
591✔
134
            &excess,
591✔
135
            self.fee,
591✔
136
            self.lock_height,
591✔
137
            &self.features,
591✔
138
            &self.burn_commitment,
591✔
139
        );
140

141
        if self
591✔
142
            .excess_sig
591✔
143
            .to_schnorr_signature()?
591✔
144
            .verify_raw_uniform(&excess.to_public_key()?, &c)
591✔
145
        {
146
            Ok(())
591✔
147
        } else {
148
            Err(TransactionError::InvalidSignatureError(
×
149
                "Verifying kernel signature".to_string(),
×
150
            ))
×
151
        }
152
    }
591✔
153

154
    /// This gets the burn commitment if it exists
155
    pub fn get_burn_commitment(&self) -> Result<&CompressedCommitment, TransactionError> {
1✔
156
        match self.burn_commitment {
1✔
157
            Some(ref burn_commitment) => Ok(burn_commitment),
1✔
158
            None => Err(TransactionError::InvalidKernel("Burn commitment not found".to_string())),
×
159
        }
160
    }
1✔
161

162
    /// Helper function to creates the kernel excess signature challenge.
163
    /// The challenge is defined as the hash of the following data:
164
    ///  Public nonce
165
    ///  Fee
166
    ///  Lock height
167
    ///  Features of the kernel
168
    ///  Burn commitment if present
169
    pub fn build_kernel_signature_challenge(
591✔
170
        version: &TransactionKernelVersion,
591✔
171
        sum_public_nonces: &CompressedPublicKey,
591✔
172
        total_excess: &CompressedPublicKey,
591✔
173
        fee: MicroMinotari,
591✔
174
        lock_height: u64,
591✔
175
        features: &KernelFeatures,
591✔
176
        burn_commitment: &Option<CompressedCommitment>,
591✔
177
    ) -> [u8; 64] {
591✔
178
        // We build the message separately to help with hardware wallet support. This reduces the amount of data that
179
        // needs to be transferred in order to sign the signature.
180
        let message =
591✔
181
            TransactionKernel::build_kernel_signature_message(version, fee, lock_height, features, burn_commitment);
591✔
182
        TransactionKernel::finalize_kernel_signature_challenge(version, sum_public_nonces, total_excess, &message)
591✔
183
    }
591✔
184

185
    /// Helper function to finalize the kernel excess signature challenge.
186
    pub fn finalize_kernel_signature_challenge(
1,896✔
187
        version: &TransactionKernelVersion,
1,896✔
188
        sum_public_nonces: &CompressedPublicKey,
1,896✔
189
        total_excess: &CompressedPublicKey,
1,896✔
190
        message: &[u8; 32],
1,896✔
191
    ) -> [u8; 64] {
1,896✔
192
        let common = DomainSeparatedConsensusHasher::<TransactionHashDomain, Blake2b<U64>>::new("kernel_signature")
1,896✔
193
            .chain(sum_public_nonces)
1,896✔
194
            .chain(total_excess)
1,896✔
195
            .chain(message);
1,896✔
196
        match version {
1,896✔
197
            TransactionKernelVersion::V0 => common.finalize().into(),
1,896✔
198
        }
199
    }
1,896✔
200

201
    /// Convenience function to create the entire kernel signature message for the challenge. This contains all data
202
    /// outside of the signing keys and nonces.
203
    pub fn build_kernel_signature_message(
985✔
204
        version: &TransactionKernelVersion,
985✔
205
        fee: MicroMinotari,
985✔
206
        lock_height: u64,
985✔
207
        features: &KernelFeatures,
985✔
208
        burn_commitment: &Option<CompressedCommitment>,
985✔
209
    ) -> [u8; 32] {
985✔
210
        let common = DomainSeparatedConsensusHasher::<TransactionHashDomain, Blake2b<U32>>::new("kernel_message")
985✔
211
            .chain(version)
985✔
212
            .chain(&fee)
985✔
213
            .chain(&lock_height)
985✔
214
            .chain(features)
985✔
215
            .chain(burn_commitment);
985✔
216
        match version {
985✔
217
            TransactionKernelVersion::V0 => common.finalize().into(),
985✔
218
        }
219
    }
985✔
220
}
221

222
impl Display for TransactionKernel {
223
    fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
×
224
        write!(
×
225
            fmt,
×
226
            "Fee: {}\nLock height: {}\nFeatures: {:?}\nExcess: {}\nExcess signature: {}\nCommitment: {}\n",
×
227
            self.fee,
228
            self.lock_height,
229
            self.features,
230
            self.excess.to_hex(),
×
231
            self.excess_sig
×
232
                .to_json()
×
233
                .unwrap_or_else(|_| "Failed to serialize signature".into()),
×
234
            match self.burn_commitment {
×
235
                Some(ref burn_commitment) => burn_commitment.to_hex(),
×
236
                None => "None".to_string(),
×
237
            }
238
        )
239
    }
×
240
}
241

242
impl PartialOrd for TransactionKernel {
243
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
4,614✔
244
        Some(self.cmp(other))
4,614✔
245
    }
4,614✔
246
}
247

248
impl Ord for TransactionKernel {
249
    fn cmp(&self, other: &Self) -> Ordering {
4,614✔
250
        self.excess_sig.cmp(&other.excess_sig)
4,614✔
251
    }
4,614✔
252
}
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