Skip to content

Commit a31dc85

Browse files
committed
Merge branch 'develop' of https://github.com/stacks-network/stacks-core into feat/signer-two-phase-commit-impl
2 parents c0e4b16 + 4850f72 commit a31dc85

File tree

6 files changed

+150
-22
lines changed

6 files changed

+150
-22
lines changed

.github/workflows/github-release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ jobs:
9090
cpu: armv7
9191
- arch: macos # excludes macos-armv7
9292
cpu: armv7
93+
- arch: macos # excludes macos-x64
94+
cpu: x86_64
9395
steps:
9496
- name: Build Binary (${{ matrix.arch }}_${{ matrix.cpu }})
9597
uses: stacks-network/actions/stacks-core/release/create-source-binary@main

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

stackslib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ mutants = "0.0.3"
9090
rlimit = "0.10.2"
9191
chrono = "0.4.19"
9292
tempfile = "3.3"
93+
proptest = { version = "1.6.0", default-features = false, features = ["std"] }
9394

9495
[features]
9596
default = []

stackslib/src/burnchains/burnchain.rs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,23 @@ impl BurnchainBlock {
450450
}
451451

452452
impl Burnchain {
453+
pub fn handle_thread_join<T>(
454+
handle: std::thread::JoinHandle<Result<T, burnchain_error>>,
455+
name: &str,
456+
) -> Result<T, burnchain_error> {
457+
match handle.join() {
458+
Ok(Ok(val)) => Ok(val),
459+
Ok(Err(e)) => {
460+
warn!("{} thread error: {:?}", name, e);
461+
Err(e)
462+
}
463+
Err(_) => {
464+
error!("{} thread panicked", name);
465+
Err(burnchain_error::ThreadChannelError)
466+
}
467+
}
468+
}
469+
453470
pub fn new(
454471
working_dir: &str,
455472
chain_name: &str,
@@ -1394,15 +1411,19 @@ impl Burnchain {
13941411
}
13951412

13961413
// join up
1397-
let _ = download_thread.join().unwrap();
1398-
let _ = parse_thread.join().unwrap();
1399-
let (block_snapshot, state_transition_opt) = match db_thread.join().unwrap() {
1400-
Ok(x) => x,
1401-
Err(e) => {
1402-
warn!("Failed to join burnchain download thread: {:?}", &e);
1403-
return Err(burnchain_error::TrySyncAgain);
1404-
}
1405-
};
1414+
let download_result = Self::handle_thread_join(download_thread, "burnchain-download");
1415+
let parse_result = Self::handle_thread_join(parse_thread, "burnchain-parse");
1416+
let db_result = Self::handle_thread_join(db_thread, "burnchain-db");
1417+
1418+
if let Err(e) = download_result {
1419+
warn!("Download thread failed: {:?}", e);
1420+
return Err(e);
1421+
}
1422+
if let Err(e) = parse_result {
1423+
warn!("Parse thread failed: {:?}", e);
1424+
return Err(e);
1425+
}
1426+
let (block_snapshot, state_transition_opt) = db_result?;
14061427

14071428
if block_snapshot.block_height < end_block {
14081429
warn!(
@@ -1783,20 +1804,21 @@ impl Burnchain {
17831804
}
17841805

17851806
// join up
1786-
let _ = download_thread.join().unwrap();
1787-
let _ = parse_thread.join().unwrap();
1788-
let block_header = match db_thread.join().unwrap() {
1789-
Ok(x) => x,
1790-
Err(e) => {
1791-
warn!("Failed to join burnchain download thread: {:?}", &e);
1792-
if let burnchain_error::CoordinatorClosed = e {
1793-
return Err(burnchain_error::CoordinatorClosed);
1794-
} else {
1795-
return Err(burnchain_error::TrySyncAgain);
1796-
}
1797-
}
1798-
};
1807+
let download_result = Self::handle_thread_join(download_thread, "download");
1808+
let parse_result = Self::handle_thread_join(parse_thread, "parse");
1809+
let db_result = Self::handle_thread_join(db_thread, "db");
1810+
1811+
if let Err(e) = download_result {
1812+
warn!("Download thread failed: {:?}", e);
1813+
return Err(e);
1814+
}
1815+
1816+
if let Err(e) = parse_result {
1817+
warn!("Parse thread failed: {:?}", e);
1818+
return Err(e);
1819+
}
17991820

1821+
let block_header = db_result?;
18001822
if block_header.block_height < end_block {
18011823
warn!(
18021824
"Try synchronizing the burn chain again: final snapshot {} < {}",

stackslib/src/burnchains/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
pub mod affirmation;
1818
pub mod burnchain;
1919
pub mod db;
20+
pub mod thread_join;
2021

2122
use std::collections::HashMap;
2223

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (C) 2025 Stacks Open Internet Foundation
2+
//
3+
// This program is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
16+
use std::time::Duration;
17+
use std::{panic, thread};
18+
19+
use proptest::prelude::*;
20+
21+
use crate::burnchains::bitcoin::Error as bitcoin_error;
22+
use crate::burnchains::{Burnchain, Error as burnchain_error};
23+
24+
// Run more cases than default with PROPTEST_CASES.
25+
// Example:
26+
// PROPTEST_CASES=500 cargo test -p stackslib -- \
27+
// burnchains::tests::thread_join
28+
29+
#[test]
30+
fn join_success() {
31+
proptest!(|(v in any::<u32>(), s in "[ -~]{1,20}")| {
32+
let h = thread::spawn(move || Ok(v));
33+
let r = Burnchain::handle_thread_join::<u32>(h, &s);
34+
prop_assert!(r.is_ok());
35+
prop_assert_eq!(r.unwrap(), v);
36+
});
37+
}
38+
39+
#[test]
40+
fn join_with_name() {
41+
proptest!(|(v in any::<u32>(), s in "[ -~]{1,20}")| {
42+
let h = thread::spawn(move || Ok(v));
43+
let r = Burnchain::handle_thread_join::<u32>(h, &s);
44+
prop_assert!(r.is_ok());
45+
prop_assert_eq!(r.unwrap(), v);
46+
});
47+
}
48+
49+
#[test]
50+
fn join_delay() {
51+
proptest!(|(d in 10u64..100, v in any::<u32>(), s in "[ -~]{1,20}")| {
52+
let h = thread::spawn(move || {
53+
thread::sleep(Duration::from_millis(d));
54+
Ok(v)
55+
});
56+
let r = Burnchain::handle_thread_join::<u32>(h, &s);
57+
prop_assert!(r.is_ok());
58+
prop_assert_eq!(r.unwrap(), v);
59+
});
60+
}
61+
62+
#[test]
63+
fn join_download_error() {
64+
let h = thread::spawn(move || {
65+
Err(burnchain_error::DownloadError(
66+
bitcoin_error::ConnectionError,
67+
))
68+
});
69+
let r = Burnchain::handle_thread_join::<u32>(h, "test");
70+
assert!(r.is_err());
71+
match r {
72+
Err(burnchain_error::DownloadError(_)) => {}
73+
_ => panic!("Expected DownloadError"),
74+
}
75+
}
76+
77+
#[test]
78+
fn join_parse_error() {
79+
let h = thread::spawn(move || Err(burnchain_error::ParseError));
80+
let r = Burnchain::handle_thread_join::<u32>(h, "test");
81+
assert!(r.is_err());
82+
match r {
83+
Err(burnchain_error::ParseError) => {}
84+
_ => panic!("Expected ParseError"),
85+
}
86+
}
87+
88+
#[test]
89+
fn join_panics() {
90+
let h = thread::spawn(|| {
91+
panic!("boom");
92+
#[allow(unreachable_code)]
93+
Ok(42)
94+
});
95+
let r = Burnchain::handle_thread_join::<u32>(h, "test");
96+
assert!(r.is_err());
97+
match r {
98+
Err(burnchain_error::ThreadChannelError) => {}
99+
_ => panic!("Expected ThreadChannelError"),
100+
}
101+
}

0 commit comments

Comments
 (0)