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

tari-project / tari / 16933396277

13 Aug 2025 09:35AM UTC coverage: 54.463% (+0.2%) from 54.254%
16933396277

push

github

web-flow
feat: add seed peer exclusion to the proactive dialer (#7396)

Description
---
Added seed peer exclusion to proactive dialing when selecting available
candidates from the peer_db.

Motivation and Context
---
Seed peers are known entities; they have been dialled during initial
seed_strap, and a well-connected network should try to learn about and
connect to other peers as well.

How Has This Been Tested?
---
System-level testing.

What process can a PR reviewer use to test or verify this change?
---
Code review.

<!-- Checklist -->
<!-- 1. Is the title of your PR in the form that would make nice release
notes? The title, excluding the conventional commit
tag, will be included exactly as is in the CHANGELOG, so please think
about it carefully. -->


Breaking Changes
---

- [x] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [ ] Other - Please specify

<!-- Does this include a breaking change? If so, include this line as a
footer -->
<!-- BREAKING CHANGE: Description what the user should do, e.g. delete a
database, resync the chain -->


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

* **Bug Fixes**
* Improved connection management by excluding seed peers from proactive
dialing candidates, enhancing network stability and reducing unnecessary
connection attempts and failed dials.

* **Documentation**
* Added a brief doc comment describing how to retrieve the list of seed
peers.

* **Tests**
* Expanded test coverage to validate discovery and syncing behavior when
seed peers are present and when filtering by external addresses.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

41 of 42 new or added lines in 3 files covered. (97.62%)

1673 existing lines in 28 files now uncovered.

76415 of 140305 relevant lines covered (54.46%)

194087.6 hits per line

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

86.84
/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> {
8,063✔
46
        if d < MIN_DIFFICULTY {
8,063✔
47
            return Err(DifficultyError::InvalidDifficulty);
1✔
48
        }
8,062✔
49
        Ok(Self(d))
8,062✔
50
    }
8,063✔
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 {
6,334✔
59
        Difficulty(MIN_DIFFICULTY)
6,334✔
60
    }
6,334✔
61

62
    /// Maximum Difficulty
63
    pub const fn max() -> Difficulty {
6,164✔
64
        Difficulty(u64::MAX)
6,164✔
65
    }
6,164✔
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> {
7,456✔
69
        let scalar = U256::from_big_endian(hash); // Big endian so the hash has leading zeroes
7,456✔
70
        Difficulty::u256_scalar_to_difficulty(scalar)
7,456✔
71
    }
7,456✔
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> {
7,464✔
80
        if scalar == U256::zero() {
7,464✔
81
            return Err(DifficultyError::DivideByZero);
4✔
82
        }
7,460✔
83
        let result = U256::MAX / scalar;
7,460✔
84
        let result = result.min(u64::MAX.into());
7,460✔
85
        Difficulty::from_u64(result.low_u64())
7,460✔
86
    }
7,464✔
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 {
UNCOV
110
    fn default() -> Self {
×
UNCOV
111
        Difficulty::min()
×
UNCOV
112
    }
×
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