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

tari-project / tari / 18097567115

29 Sep 2025 12:50PM UTC coverage: 58.554% (-2.3%) from 60.88%
18097567115

push

github

web-flow
chore(ci): switch rust toolchain to stable (#7524)

Description
switch rust toolchain to stable

Motivation and Context
use stable rust toolchain


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

## Summary by CodeRabbit

* **Chores**
* Standardized Rust toolchain on stable across CI workflows for more
predictable builds.
* Streamlined setup by removing unnecessary components and aligning
toolchain configuration with environment variables.
  * Enabled an environment flag to improve rustup behavior during CI.
* Improved coverage workflow consistency with dynamic toolchain
selection.

* **Tests**
* Removed nightly-only requirements, simplifying test commands and
improving compatibility.
* Expanded CI triggers to include ci-* branches for better pre-merge
validation.
* Maintained existing job logic while improving reliability and
maintainability.

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

66336 of 113291 relevant lines covered (58.55%)

551641.45 hits per line

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

46.02
/base_layer/sidechain/src/commit_proof.rs
1
// Copyright 2024 The Tari Project
2
// SPDX-License-Identifier: BSD-3-Clause
3

4
use std::fmt::Display;
5

6
use borsh::{BorshDeserialize, BorshSerialize};
7
use serde::{Deserialize, Serialize};
8
use tari_common_types::{
9
    epoch::VnEpoch,
10
    types::{CompressedPublicKey, FixedHash, PrivateKey, UncompressedPublicKey},
11
};
12
use tari_crypto::signatures::CompressedSchnorrSignature;
13
use tari_hashing::{layer2, ValidatorNodeHashDomain};
14
use tari_jellyfish::{LeafKey, SparseMerkleProofExt, TreeHash};
15
use tari_utilities::ByteArray;
16

17
use super::error::SidechainProofValidationError;
18
use crate::{
19
    command::{Command, ToCommand},
20
    serde::hex_or_bytes,
21
    shard_group::ShardGroup,
22
    validations::check_proof_elements,
23
};
24

25
pub type ValidatorBlockSignature =
26
    CompressedSchnorrSignature<UncompressedPublicKey, PrivateKey, ValidatorNodeHashDomain>;
27
pub type CheckVnFunc<'a> = dyn Fn(&CompressedPublicKey) -> Result<bool, SidechainProofValidationError> + 'a;
28

29
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
×
30
pub enum CommandCommitProof<C> {
31
    V1(CommandCommitProofV1<C>),
32
}
33

34
impl<C: ToCommand> CommandCommitProof<C> {
35
    pub fn new(command: C, commit_proof: SidechainBlockCommitProof, inclusion_proof: SparseMerkleProofExt) -> Self {
×
36
        Self::V1(CommandCommitProofV1 {
×
37
            command,
×
38
            commit_proof,
×
39
            inclusion_proof,
×
40
        })
×
41
    }
×
42

43
    pub fn command(&self) -> &C {
×
44
        match self {
×
45
            CommandCommitProof::V1(v1) => &v1.command,
×
46
        }
47
    }
×
48

49
    pub fn header(&self) -> &SidechainBlockHeader {
×
50
        match self {
×
51
            CommandCommitProof::V1(v1) => &v1.commit_proof.header,
×
52
        }
53
    }
×
54

55
    pub fn epoch(&self) -> VnEpoch {
×
56
        match self {
×
57
            CommandCommitProof::V1(v1) => VnEpoch(v1.commit_proof.header().epoch),
×
58
        }
59
    }
×
60

61
    pub fn shard_group(&self) -> ShardGroup {
×
62
        match self {
×
63
            CommandCommitProof::V1(v1) => v1.commit_proof.header().shard_group,
×
64
        }
65
    }
×
66

67
    pub fn validate_committed(
2✔
68
        &self,
2✔
69
        quorum_threshold: usize,
2✔
70
        check_vn: &CheckVnFunc<'_>,
2✔
71
    ) -> Result<(), SidechainProofValidationError> {
2✔
72
        #[allow(clippy::single_match)]
73
        match self {
2✔
74
            CommandCommitProof::V1(v1) => v1.validate_committed(quorum_threshold, check_vn),
2✔
75
        }
76
    }
2✔
77
}
78

79
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
×
80
pub struct CommandCommitProofV1<C> {
81
    pub command: C,
82
    pub commit_proof: SidechainBlockCommitProof,
83
    pub inclusion_proof: SparseMerkleProofExt,
84
}
85

86
impl<C: ToCommand> CommandCommitProofV1<C> {
87
    pub fn command(&self) -> &C {
×
88
        &self.command
×
89
    }
×
90

91
    pub fn commit_proof(&self) -> &SidechainBlockCommitProof {
×
92
        &self.commit_proof
×
93
    }
×
94

95
    pub fn inclusion_proof(&self) -> &SparseMerkleProofExt {
×
96
        &self.inclusion_proof
×
97
    }
×
98

99
    fn validate_inclusion_proof(&self, command: &Command) -> Result<(), SidechainProofValidationError> {
2✔
100
        let command_hash = TreeHash::new(command.hash().into_array());
2✔
101
        // Command JMT uses an identity mapping between hashes and keys.
102
        let key = LeafKey::new(command_hash);
2✔
103
        let root_hash = TreeHash::new(self.commit_proof.header.command_merkle_root.into_array());
2✔
104
        self.inclusion_proof.verify_inclusion(&root_hash, &key, &command_hash)?;
2✔
105
        Ok(())
2✔
106
    }
2✔
107

108
    pub fn validate_committed(
2✔
109
        &self,
2✔
110
        quorum_threshold: usize,
2✔
111
        check_vn: &CheckVnFunc<'_>,
2✔
112
    ) -> Result<(), SidechainProofValidationError> {
2✔
113
        let command = self.command.to_command();
2✔
114
        self.validate_inclusion_proof(&command)?;
2✔
115
        self.commit_proof.validate_committed(quorum_threshold, check_vn)
2✔
116
    }
2✔
117
}
118

119
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
×
120
pub struct SidechainBlockCommitProof {
121
    pub header: SidechainBlockHeader,
122
    pub proof_elements: Vec<CommitProofElement>,
123
}
124

125
impl SidechainBlockCommitProof {
126
    pub fn validate_committed(
3✔
127
        &self,
3✔
128
        quorum_threshold: usize,
3✔
129
        check_vn: &CheckVnFunc<'_>,
3✔
130
    ) -> Result<(), SidechainProofValidationError> {
3✔
131
        check_proof_elements(
3✔
132
            &self.header,
3✔
133
            &self.proof_elements,
3✔
134
            check_vn,
3✔
135
            QuorumDecision::Accept,
3✔
136
            quorum_threshold,
3✔
137
        )?;
1✔
138

139
        Ok(())
2✔
140
    }
3✔
141

142
    pub fn proof_elements(&self) -> &[CommitProofElement] {
×
143
        &self.proof_elements
×
144
    }
×
145

146
    pub fn header(&self) -> &SidechainBlockHeader {
×
147
        &self.header
×
148
    }
×
149

150
    pub fn last_qc(&self) -> Option<&QuorumCertificate> {
×
151
        self.proof_elements
×
152
            .iter()
×
153
            .filter_map(|elem| match elem {
×
154
                CommitProofElement::QuorumCertificate(qc) => Some(qc),
×
155
                _ => None,
×
156
            })
×
157
            .next_back()
×
158
    }
×
159
}
160

161
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
×
162
pub enum CommitProofElement {
163
    QuorumCertificate(QuorumCertificate),
164
    ChainLinks(Vec<ChainLink>),
165
}
166

167
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
×
168
pub struct ChainLink {
169
    #[serde(with = "hex_or_bytes")]
170
    pub header_hash: FixedHash,
171
    #[serde(with = "hex_or_bytes")]
172
    pub parent_id: FixedHash,
173
}
174

175
impl ChainLink {
176
    pub fn calc_block_id(&self) -> FixedHash {
2✔
177
        layer2::block_hasher()
2✔
178
            .chain(&self.parent_id)
2✔
179
            .chain(&self.header_hash)
2✔
180
            .finalize()
2✔
181
            .into()
2✔
182
    }
2✔
183
}
184

185
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
×
186
pub struct SidechainBlockHeader {
187
    pub network: u8,
188
    #[serde(with = "hex_or_bytes")]
189
    pub parent_id: FixedHash,
190
    #[serde(with = "hex_or_bytes")]
191
    pub justify_id: FixedHash,
192
    pub height: u64,
193
    pub epoch: u64,
194
    pub shard_group: ShardGroup,
195
    pub proposed_by: CompressedPublicKey,
196
    #[serde(with = "hex_or_bytes")]
197
    pub state_merkle_root: FixedHash,
198
    #[serde(with = "hex_or_bytes")]
199
    pub command_merkle_root: FixedHash,
200
    /// Signature of block by the proposer.
201
    pub signature: ValidatorBlockSignature,
202
    #[serde(with = "hex_or_bytes")]
203
    pub metadata_hash: FixedHash,
204
}
205

206
impl SidechainBlockHeader {
207
    pub fn calculate_hash(&self) -> FixedHash {
2✔
208
        let fields = BlockHeaderHashFields::V1(BlockHeaderHashFieldsV1 {
2✔
209
            network: self.network,
2✔
210
            justify_id: &self.justify_id,
2✔
211
            height: self.height,
2✔
212
            epoch: self.epoch,
2✔
213
            shard_group: self.shard_group,
2✔
214
            proposed_by: self.proposed_by.as_bytes(),
2✔
215
            state_merkle_root: &self.state_merkle_root,
2✔
216
            command_merkle_root: &self.command_merkle_root,
2✔
217
            metadata_hash: &self.metadata_hash,
2✔
218
        });
2✔
219

220
        layer2::block_hasher().chain(&fields).finalize().into()
2✔
221
    }
2✔
222

223
    pub fn calculate_block_id(&self) -> FixedHash {
2✔
224
        let header_hash = self.calculate_hash();
2✔
225
        layer2::block_hasher()
2✔
226
            .chain(&self.parent_id)
2✔
227
            .chain(&header_hash)
2✔
228
            .finalize()
2✔
229
            .into()
2✔
230
    }
2✔
231

232
    pub fn signature(&self) -> &ValidatorBlockSignature {
×
233
        &self.signature
×
234
    }
×
235
}
236

237
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
×
238
pub struct QuorumCertificate {
239
    #[serde(with = "hex_or_bytes")]
240
    pub header_hash: FixedHash,
241
    #[serde(with = "hex_or_bytes")]
242
    pub parent_id: FixedHash,
243
    pub signatures: Vec<ValidatorQcSignature>,
244
    pub decision: QuorumDecision,
245
}
246

247
impl QuorumCertificate {
248
    pub fn calculate_justified_block(&self) -> FixedHash {
20✔
249
        layer2::block_hasher()
20✔
250
            .chain(&self.parent_id)
20✔
251
            .chain(&self.header_hash)
20✔
252
            .finalize()
20✔
253
            .into()
20✔
254
    }
20✔
255
}
256

257
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
×
258
pub enum QuorumDecision {
259
    Accept,
260
    Reject,
261
}
262

263
impl QuorumDecision {
264
    pub fn is_accept(&self) -> bool {
×
265
        matches!(self, QuorumDecision::Accept)
×
266
    }
×
267

268
    pub fn is_reject(&self) -> bool {
×
269
        matches!(self, QuorumDecision::Reject)
×
270
    }
×
271
}
272

273
impl QuorumDecision {
274
    pub fn as_u8(&self) -> u8 {
×
275
        match self {
×
276
            QuorumDecision::Accept => 0,
×
277
            QuorumDecision::Reject => 1,
×
278
        }
279
    }
×
280

281
    pub fn from_u8(v: u8) -> Option<Self> {
×
282
        match v {
×
283
            0 => Some(QuorumDecision::Accept),
×
284
            1 => Some(QuorumDecision::Reject),
×
285
            _ => None,
×
286
        }
287
    }
×
288
}
289

290
impl TryFrom<u8> for QuorumDecision {
291
    type Error = InvalidQuorumDecisionByteError;
292

293
    fn try_from(value: u8) -> Result<Self, Self::Error> {
×
294
        Self::from_u8(value).ok_or(InvalidQuorumDecisionByteError(value))
×
295
    }
×
296
}
297

298
impl Display for QuorumDecision {
299
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
300
        match self {
×
301
            QuorumDecision::Accept => write!(f, "Accept"),
×
302
            QuorumDecision::Reject => write!(f, "Reject"),
×
303
        }
304
    }
×
305
}
306

307
#[derive(Debug, thiserror::Error)]
308
#[error("Invalid quorum decision byte: {0}")]
309
pub struct InvalidQuorumDecisionByteError(u8);
310

311
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
×
312
pub struct ValidatorQcSignature {
313
    pub public_key: CompressedPublicKey,
314
    pub signature: ValidatorBlockSignature,
315
}
316

317
impl ValidatorQcSignature {
318
    #[must_use]
319
    pub fn verify(&self, block_id: &FixedHash, decision: QuorumDecision) -> bool {
32✔
320
        let Ok(public_key) = self.public_key.to_public_key() else {
32✔
321
            return false;
×
322
        };
323

324
        let Ok(signature) = self.signature.to_schnorr_signature() else {
32✔
325
            return false;
×
326
        };
327

328
        let fields = ProposalCertificateSignatureFields { block_id, decision };
32✔
329

330
        let message = layer2::proposal_vote_signature_hasher().chain(&fields).finalize();
32✔
331
        signature.verify(&public_key, message)
32✔
332
    }
32✔
333

334
    pub fn public_key(&self) -> &CompressedPublicKey {
×
335
        &self.public_key
×
336
    }
×
337

338
    pub fn signature(&self) -> &ValidatorBlockSignature {
×
339
        &self.signature
×
340
    }
×
341
}
342

343
#[derive(Debug, BorshSerialize)]
×
344
pub struct ProposalCertificateSignatureFields<'a> {
345
    pub block_id: &'a FixedHash,
346
    pub decision: QuorumDecision,
347
}
348

349
#[derive(Debug, BorshSerialize)]
×
350
pub enum BlockHeaderHashFields<'a> {
351
    V1(BlockHeaderHashFieldsV1<'a>),
352
}
353

354
#[derive(Debug, BorshSerialize)]
×
355
pub struct BlockHeaderHashFieldsV1<'a> {
356
    pub network: u8,
357
    pub justify_id: &'a FixedHash,
358
    pub height: u64,
359
    pub epoch: u64,
360
    pub shard_group: ShardGroup,
361
    // NOTE this is borsh encoded as variable length bytes - technically should always be 32
362
    pub proposed_by: &'a [u8],
363
    pub state_merkle_root: &'a FixedHash,
364
    pub command_merkle_root: &'a FixedHash,
365
    pub metadata_hash: &'a FixedHash,
366
}
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