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

stacks-network / stacks-core / 26289146850-1

22 May 2026 12:58PM UTC coverage: 85.575% (-0.09%) from 85.663%
26289146850-1

Pull #7229

github

1a81d1
web-flow
Merge 734f21a91 into fa58f050a
Pull Request #7229: chore: drop apt-get from build_binaries.sh reqs

188802 of 220627 relevant lines covered (85.58%)

18864603.79 hits per line

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

88.35
/stacks-node/src/syncctl.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020-2024 Stacks Open Internet Foundation
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
18
use std::sync::Arc;
19

20
use stacks::burnchains::{Burnchain, Error as burnchain_error};
21
use stacks_common::util::{get_epoch_time_secs, sleep_ms};
22

23
use crate::burnchains::BurnchainTip;
24
use crate::Config;
25

26
#[derive(Clone)]
27
pub struct PoxSyncWatchdogComms {
28
    /// how many passes in the p2p state machine have taken place since startup?
29
    p2p_state_passes: Arc<AtomicU64>,
30
    /// how many times have we done an inv sync?
31
    inv_sync_passes: Arc<AtomicU64>,
32
    /// how many times have we done a download pass?
33
    download_passes: Arc<AtomicU64>,
34
    /// What's our last IBD status?
35
    last_ibd: Arc<AtomicBool>,
36
    /// Should keep running?
37
    should_keep_running: Arc<AtomicBool>,
38
}
39

40
impl PoxSyncWatchdogComms {
41
    pub fn new(should_keep_running: Arc<AtomicBool>) -> PoxSyncWatchdogComms {
524✔
42
        PoxSyncWatchdogComms {
524✔
43
            p2p_state_passes: Arc::new(AtomicU64::new(0)),
524✔
44
            inv_sync_passes: Arc::new(AtomicU64::new(0)),
524✔
45
            download_passes: Arc::new(AtomicU64::new(0)),
524✔
46
            last_ibd: Arc::new(AtomicBool::new(true)),
524✔
47
            should_keep_running,
524✔
48
        }
524✔
49
    }
524✔
50

51
    pub fn get_p2p_state_passes(&self) -> u64 {
×
52
        self.p2p_state_passes.load(Ordering::SeqCst)
×
53
    }
×
54

55
    pub fn get_inv_sync_passes(&self) -> u64 {
×
56
        self.inv_sync_passes.load(Ordering::SeqCst)
×
57
    }
×
58

59
    pub fn get_download_passes(&self) -> u64 {
×
60
        self.download_passes.load(Ordering::SeqCst)
×
61
    }
×
62

63
    pub fn get_ibd(&self) -> bool {
2,172,452✔
64
        self.last_ibd.load(Ordering::SeqCst)
2,172,452✔
65
    }
2,172,452✔
66

67
    fn interruptable_sleep(&self, secs: u64) -> Result<(), burnchain_error> {
12,147✔
68
        let deadline = secs + get_epoch_time_secs();
12,147✔
69
        while get_epoch_time_secs() < deadline {
24,210✔
70
            sleep_ms(1000);
12,147✔
71
            if !self.should_keep_running() {
12,147✔
72
                return Err(burnchain_error::CoordinatorClosed);
84✔
73
            }
12,063✔
74
        }
75
        Ok(())
12,063✔
76
    }
12,147✔
77

78
    pub fn should_keep_running(&self) -> bool {
12,146✔
79
        self.should_keep_running.load(Ordering::SeqCst)
12,146✔
80
    }
12,146✔
81

82
    pub fn notify_p2p_state_pass(&mut self) {
959,339✔
83
        self.p2p_state_passes.fetch_add(1, Ordering::SeqCst);
959,339✔
84
    }
959,339✔
85

86
    pub fn notify_inv_sync_pass(&mut self) {
533,025✔
87
        self.inv_sync_passes.fetch_add(1, Ordering::SeqCst);
533,025✔
88
    }
533,025✔
89

90
    pub fn notify_download_pass(&mut self) {
179,646✔
91
        self.download_passes.fetch_add(1, Ordering::SeqCst);
179,646✔
92
    }
179,646✔
93

94
    pub fn set_ibd(&mut self, value: bool) {
437,229✔
95
        self.last_ibd.store(value, Ordering::SeqCst);
437,229✔
96
    }
437,229✔
97
}
98

99
/// Monitor the state of the Stacks blockchain as the peer network and relay threads download and
100
/// proces Stacks blocks.  Don't allow the node to process the next PoX reward cycle's sortitions
101
/// unless it's reasonably sure that it has processed all Stacks blocks for this reward cycle.
102
/// This struct monitors the Stacks chainstate to make this determination.
103
pub struct PoxSyncWatchdog {
104
    /// time between burnchain syncs in steady state
105
    steady_state_burnchain_sync_interval: u64,
106
    /// handle to relayer thread that informs the watchdog when the P2P state-machine does stuff
107
    relayer_comms: PoxSyncWatchdogComms,
108
    /// should this sync watchdog always download? used in integration tests.
109
    unconditionally_download: bool,
110
}
111

112
impl PoxSyncWatchdog {
113
    pub fn new(
277✔
114
        config: &Config,
277✔
115
        watchdog_comms: PoxSyncWatchdogComms,
277✔
116
    ) -> Result<PoxSyncWatchdog, String> {
277✔
117
        let burnchain_poll_time = config.burnchain.poll_time_secs;
277✔
118
        let unconditionally_download = config.node.pox_sync_sample_secs == 0;
277✔
119

120
        Ok(PoxSyncWatchdog {
277✔
121
            unconditionally_download,
277✔
122
            steady_state_burnchain_sync_interval: burnchain_poll_time,
277✔
123
            relayer_comms: watchdog_comms,
277✔
124
        })
277✔
125
    }
277✔
126

127
    pub fn make_comms_handle(&self) -> PoxSyncWatchdogComms {
×
128
        self.relayer_comms.clone()
×
129
    }
×
130

131
    /// Are we in the initial burnchain block download? i.e. is the burn tip snapshot far enough away
132
    /// from the burnchain height that we should be eagerly downloading snapshots?
133
    fn infer_initial_burnchain_block_download(
416,522✔
134
        burnchain: &Burnchain,
416,522✔
135
        last_processed_height: u64,
416,522✔
136
        burnchain_height: u64,
416,522✔
137
    ) -> bool {
416,522✔
138
        let ibd =
416,522✔
139
            last_processed_height + (burnchain.stable_confirmations as u64) < burnchain_height;
416,522✔
140
        if ibd {
416,522✔
141
            debug!(
3,365✔
142
                "PoX watchdog: {last_processed_height} + {} < {burnchain_height}, so initial block download",
143
                burnchain.stable_confirmations
144
            );
145
        } else {
146
            debug!(
413,157✔
147
                "PoX watchdog: {last_processed_height} + {} >= {burnchain_height}, so steady-state",
148
                burnchain.stable_confirmations
149
            );
150
        }
151
        ibd
416,522✔
152
    }
416,522✔
153

154
    /// Wait until the next PoX anchor block arrives.
155
    /// We know for a fact that they all exist for Epochs 2.5 and earlier, in both mainnet and
156
    /// testnet.
157
    /// Return (still-in-ibd?, maximum-burnchain-sync-height) on success.
158
    pub fn pox_sync_wait(
416,522✔
159
        &mut self,
416,522✔
160
        burnchain: &Burnchain,
416,522✔
161
        burnchain_tip: &BurnchainTip, // this is the highest burnchain snapshot we've sync'ed to
416,522✔
162
        burnchain_height: u64,        // this is the absolute burnchain block height
416,522✔
163
    ) -> Result<(bool, u64), burnchain_error> {
416,522✔
164
        let burnchain_rc = burnchain
416,522✔
165
            .block_height_to_reward_cycle(burnchain_height)
416,522✔
166
            .expect("FATAL: burnchain height is before system start");
416,522✔
167

168
        let sortition_rc = burnchain
416,522✔
169
            .block_height_to_reward_cycle(burnchain_tip.block_snapshot.block_height)
416,522✔
170
            .expect("FATAL: sortition height is before system start");
416,522✔
171

172
        let ibbd = PoxSyncWatchdog::infer_initial_burnchain_block_download(
416,522✔
173
            burnchain,
416,522✔
174
            burnchain_tip.block_snapshot.block_height,
416,522✔
175
            burnchain_height,
416,522✔
176
        );
177

178
        let max_sync_height = if sortition_rc < burnchain_rc {
416,522✔
179
            burnchain
3,590✔
180
                .reward_cycle_to_block_height(sortition_rc + 1)
3,590✔
181
                .min(burnchain_height)
3,590✔
182
        } else {
183
            burnchain_tip
412,932✔
184
                .block_snapshot
412,932✔
185
                .block_height
412,932✔
186
                .max(burnchain_height)
412,932✔
187
        };
188

189
        self.relayer_comms.set_ibd(ibbd);
416,522✔
190
        if !self.unconditionally_download {
416,522✔
191
            self.relayer_comms
12,147✔
192
                .interruptable_sleep(self.steady_state_burnchain_sync_interval)?;
12,147✔
193
        }
404,375✔
194

195
        Ok((ibbd, max_sync_height))
416,439✔
196
    }
416,522✔
197
}
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