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

tari-project / tari / 16123384529

07 Jul 2025 05:11PM UTC coverage: 64.327% (-7.6%) from 71.89%
16123384529

push

github

web-flow
chore: new release v4.9.0-pre.0 (#7289)

Description
---
new release esmeralda

77151 of 119935 relevant lines covered (64.33%)

227108.34 hits per line

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

89.47
/base_layer/core/src/proof_of_work/difficulty.rs
1
// Copyright 2019. 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
use std::fmt;
24

25
use borsh::{BorshDeserialize, BorshSerialize};
26
use num_format::{Locale, ToFormattedString};
27
use primitive_types::U256;
28
use serde::{Deserialize, Serialize};
29
use tari_utilities::epoch_time::EpochTime;
30

31
use crate::proof_of_work::{error::DifficultyError, DifficultyAdjustmentError};
32

33
/// Minimum difficulty, enforced in diff retargeting
34
/// avoids getting stuck when trying to increase difficulty subject to dampening
35
pub const MIN_DIFFICULTY: u64 = 1;
36

37
/// The difficulty is defined as the maximum target divided by the block hash.
38
#[derive(
39
    Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Deserialize, Serialize, BorshSerialize, BorshDeserialize,
×
40
)]
41
pub struct Difficulty(u64);
42

43
impl Difficulty {
44
    /// A const constructor for Difficulty
45
    pub const fn from_u64(d: u64) -> Result<Self, DifficultyError> {
7,247✔
46
        if d < MIN_DIFFICULTY {
7,247✔
47
            return Err(DifficultyError::InvalidDifficulty);
1✔
48
        }
7,246✔
49
        Ok(Self(d))
7,246✔
50
    }
7,247✔
51

52
    /// Return the difficulty as a u64
53
    pub fn as_u64(self) -> u64 {
17,858✔
54
        self.0
17,858✔
55
    }
17,858✔
56

57
    /// Difficulty of MIN_DIFFICULTY
58
    pub const fn min() -> Difficulty {
5,630✔
59
        Difficulty(MIN_DIFFICULTY)
5,630✔
60
    }
5,630✔
61

62
    /// Maximum Difficulty
63
    pub const fn max() -> Difficulty {
6,106✔
64
        Difficulty(u64::MAX)
6,106✔
65
    }
6,106✔
66

67
    /// Helper function to provide the difficulty of the hash assuming the hash is big_endian
68
    pub fn big_endian_difficulty(hash: &[u8]) -> Result<Difficulty, DifficultyError> {
6,726✔
69
        let scalar = U256::from_big_endian(hash); // Big endian so the hash has leading zeroes
6,726✔
70
        Difficulty::u256_scalar_to_difficulty(scalar)
6,726✔
71
    }
6,726✔
72

73
    /// Helper function to provide the difficulty of the hash assuming the hash is little_endian
74
    pub fn little_endian_difficulty(hash: &[u8]) -> Result<Difficulty, DifficultyError> {
8✔
75
        let scalar = U256::from_little_endian(hash); // Little endian so the hash has trailing zeroes
8✔
76
        Difficulty::u256_scalar_to_difficulty(scalar)
8✔
77
    }
8✔
78

79
    fn u256_scalar_to_difficulty(scalar: U256) -> Result<Difficulty, DifficultyError> {
6,734✔
80
        if scalar == U256::zero() {
6,734✔
81
            return Err(DifficultyError::DivideByZero);
4✔
82
        }
6,730✔
83
        let result = U256::MAX / scalar;
6,730✔
84
        let result = result.min(u64::MAX.into());
6,730✔
85
        Difficulty::from_u64(result.low_u64())
6,730✔
86
    }
6,734✔
87

88
    pub fn checked_div_u64(&self, other: u64) -> Option<Difficulty> {
×
89
        match self.0.checked_div(other) {
×
90
            None => None,
×
91
            Some(n) => {
×
92
                if n < MIN_DIFFICULTY {
×
93
                    None
×
94
                } else {
95
                    Some(Difficulty(n))
×
96
                }
97
            },
98
        }
99
    }
×
100
}
101

102
/// These traits should not be implemented for `Difficulty`:
103
/// - `Add<Self> for Difficulty` "`+` must not be used, use `checked_add(value)` instead; to prevent overflow
104
/// - `Sub<Self> for Difficulty` `-` must not be used, use `checked_sub(value)` instead; to prevent underflow
105
/// - `Mul for Difficulty` `*` must not be used at all; difficulties should only be added to or subtracted from
106
/// - `Div for Difficulty` `/` must not be used at all; difficulties should only be added to or subtracted from
107
/// - `From<u64> for Difficulty` `Difficulty::from<u64>` must not be used, use `from_u64(value)` instead; to prevent
108
///   assignment `< MIN_DIFFICULTY`
109
impl Default for Difficulty {
110
    fn default() -> Self {
28✔
111
        Difficulty::min()
28✔
112
    }
28✔
113
}
114

115
impl From<Difficulty> for u64 {
116
    fn from(value: Difficulty) -> Self {
×
117
        value.0
×
118
    }
×
119
}
120

121
impl fmt::Display for Difficulty {
122
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1✔
123
        let formatted = self.0.to_formatted_string(&Locale::en);
1✔
124
        write!(f, "{}", formatted)
1✔
125
    }
1✔
126
}
127

128
/// General difficulty adjustment algorithm trait. The key method is `get_difficulty`, which returns the target
129
/// difficulty given a set of historical achieved difficulties; supplied through the `add` method.
130
pub trait DifficultyAdjustment {
131
    /// Adds the latest block timestamp (in seconds) and total accumulated difficulty. If the new data point violates
132
    /// some difficulty criteria, then `add` returns an error with the type of failure indicated
133
    fn add(
134
        &mut self,
135
        timestamp: EpochTime,
136
        accumulated_difficulty: Difficulty,
137
    ) -> Result<(), DifficultyAdjustmentError>;
138

139
    /// Return the calculated target difficulty for the next block.
140
    fn get_difficulty(&self) -> Option<Difficulty>;
141
}
142

143
#[cfg(test)]
144
mod test {
145
    use primitive_types::U256;
146

147
    use crate::proof_of_work::{difficulty::MIN_DIFFICULTY, Difficulty};
148

149
    #[test]
150
    fn test_format() {
1✔
151
        let d = Difficulty::from_u64(1_000_000).unwrap();
1✔
152
        assert_eq!("1,000,000", format!("{}", d));
1✔
153
    }
1✔
154

155
    #[test]
156
    fn difficulty_converts_correctly_at_its_limits() {
1✔
157
        for d in 0..=MIN_DIFFICULTY + 1 {
3✔
158
            if d < MIN_DIFFICULTY {
3✔
159
                assert!(Difficulty::from_u64(d).is_err());
1✔
160
            } else {
161
                assert!(Difficulty::from_u64(d).is_ok());
2✔
162
            }
163
        }
164
        assert_eq!(Difficulty::min().as_u64(), MIN_DIFFICULTY);
1✔
165
        assert_eq!(Difficulty::max().as_u64(), u64::MAX);
1✔
166
    }
1✔
167

168
    #[test]
169
    fn be_high_target() {
1✔
170
        let target: &[u8] = &[
1✔
171
            0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1✔
172
            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1✔
173
        ];
1✔
174
        let expected = Difficulty::min();
1✔
175
        assert_eq!(Difficulty::big_endian_difficulty(target).unwrap(), expected);
1✔
176
    }
1✔
177

178
    #[test]
179
    fn be_max_difficulty() {
1✔
180
        let target = U256::MAX / U256::from(u64::MAX);
1✔
181
        let mut bytes = [0u8; 32];
1✔
182
        target.to_big_endian(&mut bytes);
1✔
183
        assert_eq!(Difficulty::big_endian_difficulty(&bytes).unwrap(), Difficulty::max());
1✔
184
    }
1✔
185

186
    #[test]
187
    fn be_stop_overflow() {
1✔
188
        let target: u64 = 64;
1✔
189
        let expected = u64::MAX;
1✔
190
        assert_eq!(
1✔
191
            Difficulty::big_endian_difficulty(&target.to_be_bytes()).unwrap(),
1✔
192
            Difficulty::from_u64(expected).unwrap()
1✔
193
        );
1✔
194
    }
1✔
195

196
    #[test]
197
    fn le_high_target() {
1✔
198
        let target: &[u8] = &[
1✔
199
            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1✔
200
            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff,
1✔
201
        ];
1✔
202
        let expected = Difficulty::min();
1✔
203
        assert_eq!(Difficulty::little_endian_difficulty(target).unwrap(), expected);
1✔
204
    }
1✔
205

206
    #[test]
207
    fn le_max_difficulty() {
1✔
208
        let target = U256::MAX / U256::from(u64::MAX);
1✔
209
        let mut bytes = [0u8; 32];
1✔
210
        target.to_little_endian(&mut bytes);
1✔
211
        assert_eq!(Difficulty::little_endian_difficulty(&bytes).unwrap(), Difficulty::max());
1✔
212
    }
1✔
213

214
    #[test]
215
    fn le_stop_overflow() {
1✔
216
        let target: u64 = 64;
1✔
217
        let expected = u64::MAX;
1✔
218
        assert_eq!(
1✔
219
            Difficulty::little_endian_difficulty(&target.to_be_bytes()).unwrap(),
1✔
220
            Difficulty::from_u64(expected).unwrap()
1✔
221
        );
1✔
222
    }
1✔
223

224
    #[test]
225
    fn u256_scalar_to_difficulty_division_by_zero() {
1✔
226
        let bytes = [];
1✔
227
        assert!(Difficulty::little_endian_difficulty(&bytes).is_err());
1✔
228
        assert!(Difficulty::big_endian_difficulty(&bytes).is_err());
1✔
229
        let bytes = [0u8; 32];
1✔
230
        assert!(Difficulty::little_endian_difficulty(&bytes).is_err());
1✔
231
        assert!(Difficulty::big_endian_difficulty(&bytes).is_err());
1✔
232
    }
1✔
233
}
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