Skip to content

Commit 9ad8852

Browse files
committed
Merge branch 'develop' into fix/5159
2 parents ca9c516 + 39216e5 commit 9ad8852

File tree

25 files changed

+1522
-453
lines changed

25 files changed

+1522
-453
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ jobs:
4545
- tests::neon_integrations::liquid_ustx_integration
4646
- tests::neon_integrations::microblock_fork_poison_integration_test
4747
- tests::neon_integrations::microblock_integration_test
48-
- tests::neon_integrations::microblock_large_tx_integration_test_FLAKY
48+
# Disable this flaky test. Microblocks are no longer supported anyways.
49+
# - tests::neon_integrations::microblock_large_tx_integration_test_FLAKY
4950
- tests::neon_integrations::microblock_limit_hit_integration_test
5051
- tests::neon_integrations::miner_submit_twice
5152
- tests::neon_integrations::mining_events_integration_test
@@ -86,15 +87,14 @@ jobs:
8687
- tests::nakamoto_integrations::nakamoto_attempt_time
8788
- tests::signer::v0::block_proposal_rejection
8889
- tests::signer::v0::miner_gather_signatures
89-
- tests::signer::v0::mine_2_nakamoto_reward_cycles
9090
- tests::signer::v0::end_of_tenure
9191
- tests::signer::v0::forked_tenure_okay
9292
- tests::signer::v0::forked_tenure_invalid
9393
- tests::signer::v0::empty_sortition
9494
- tests::signer::v0::bitcoind_forking_test
9595
- tests::signer::v0::multiple_miners
9696
- tests::signer::v0::mock_sign_epoch_25
97-
- tests::signer::v0::signer_set_rollover
97+
- tests::signer::v0::multiple_miners_mock_sign_epoch_25
9898
- tests::signer::v0::miner_forking
9999
- tests::signer::v0::reloads_signer_set_in
100100
- tests::signer::v0::signers_broadcast_signed_blocks
@@ -105,6 +105,10 @@ jobs:
105105
- tests::signer::v0::locally_rejected_blocks_overriden_by_global_acceptance
106106
- tests::signer::v0::reorg_locally_accepted_blocks_across_tenures_succeeds
107107
- tests::signer::v0::miner_recovers_when_broadcast_block_delay_across_tenures_occurs
108+
- tests::signer::v0::multiple_miners_with_nakamoto_blocks
109+
- tests::signer::v0::partial_tenure_fork
110+
- tests::signer::v0::mine_2_nakamoto_reward_cycles
111+
- tests::signer::v0::signer_set_rollover
108112
- tests::nakamoto_integrations::stack_stx_burn_op_integration_test
109113
- tests::nakamoto_integrations::check_block_heights
110114
- tests::nakamoto_integrations::clarity_burn_state

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,28 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1717
- `get-tenure-info?` added
1818
- `get-block-info?` removed
1919

20+
## [2.5.0.0.7]
21+
22+
### Added
23+
24+
- Add warn logs for block validate rejections (#5079)
25+
- Neon mock miner replay (#5060)
26+
27+
### Changed
28+
29+
- Revert BurnchainHeaderHash serialization change (#5094)
30+
- boot_to_epoch_3 in SignerTest should wait for a new commit (#5087)
31+
- Fix block proposal rejection test (#5084)
32+
- Mock signing revamp (#5070)
33+
- Multi miner fixes jude (#5040)
34+
- Remove spurious deadlock condition whenever the sortition DB is opened
35+
36+
## [2.5.0.0.6]
37+
38+
### Changed
39+
40+
- If there is a getchunk/putchunk that fails due to a stale (or future) version NACK, the StackerDB sync state machine should immediately retry sync (#5066)
41+
2042
## [2.5.0.0.5]
2143

2244
### Added

clarity/src/vm/database/sqlite.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use rusqlite::{
2121
};
2222
use stacks_common::types::chainstate::{BlockHeaderHash, StacksBlockId};
2323
use stacks_common::types::sqlite::NO_PARAMS;
24-
use stacks_common::util::db_common::tx_busy_handler;
24+
use stacks_common::util::db::tx_busy_handler;
2525
use stacks_common::util::hash::Sha512Trunc256Sum;
2626

2727
use super::clarity_store::{make_contract_hash_key, ContractCommitment};

docs/rpc/openapi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ paths:
568568
example:
569569
$ref: ./api/core-node/post-block-proposal-req.example.json
570570

571-
/v2/stacker_set/{cycle_number}:
571+
/v3/stacker_set/{cycle_number}:
572572
get:
573573
summary: Fetch the stacker and signer set information for a given cycle.
574574
tags:

stacks-common/src/util/db.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2+
// Copyright (C) 2020 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::backtrace::Backtrace;
18+
use std::sync::{LazyLock, Mutex};
19+
use std::thread;
20+
use std::time::{Duration, Instant};
21+
22+
use hashbrown::HashMap;
23+
use rand::{thread_rng, Rng};
24+
use rusqlite::Connection;
25+
26+
use crate::util::sleep_ms;
27+
28+
/// Keep track of DB locks, for deadlock debugging
29+
/// - **key:** `rusqlite::Connection` debug print
30+
/// - **value:** Lock holder (thread name + timestamp)
31+
///
32+
/// This uses a `Mutex` inside of `LazyLock` because:
33+
/// - Using `Mutex` alone, it can't be statically initialized because `HashMap::new()` isn't `const`
34+
/// - Using `LazyLock` alone doesn't allow interior mutability
35+
static LOCK_TABLE: LazyLock<Mutex<HashMap<String, String>>> =
36+
LazyLock::new(|| Mutex::new(HashMap::new()));
37+
/// Generate timestanps for use in `LOCK_TABLE`
38+
/// `Instant` is preferable to `SystemTime` because it uses `CLOCK_MONOTONIC` and is not affected by NTP adjustments
39+
static LOCK_TABLE_TIMER: LazyLock<Instant> = LazyLock::new(Instant::now);
40+
41+
/// Call when using an operation which locks a database
42+
/// Updates `LOCK_TABLE`
43+
pub fn update_lock_table(conn: &Connection) {
44+
let timestamp = LOCK_TABLE_TIMER.elapsed().as_millis();
45+
// The debug format for `Connection` includes the path
46+
let k = format!("{conn:?}");
47+
let v = format!("{:?}@{timestamp}", thread::current().name());
48+
LOCK_TABLE.lock().unwrap().insert(k, v);
49+
}
50+
51+
/// Called by `rusqlite` if we are waiting too long on a database lock
52+
/// If called too many times, will assume a deadlock and panic
53+
pub fn tx_busy_handler(run_count: i32) -> bool {
54+
const TIMEOUT: Duration = Duration::from_secs(300);
55+
const AVG_SLEEP_TIME_MS: u64 = 100;
56+
57+
// First, check if this is taking unreasonably long. If so, it's probably a deadlock
58+
let run_count = run_count.unsigned_abs();
59+
let approx_time_elapsed =
60+
Duration::from_millis(AVG_SLEEP_TIME_MS.saturating_mul(u64::from(run_count)));
61+
if approx_time_elapsed > TIMEOUT {
62+
error!("Deadlock detected. Waited {} seconds (estimated) for database lock. Giving up", approx_time_elapsed.as_secs();
63+
"run_count" => run_count,
64+
"backtrace" => ?Backtrace::capture()
65+
);
66+
for (k, v) in LOCK_TABLE.lock().unwrap().iter() {
67+
error!("Database '{k}' last locked by {v}");
68+
}
69+
panic!("Deadlock in thread {:?}", thread::current().name());
70+
}
71+
72+
let mut sleep_time_ms = 2u64.saturating_pow(run_count);
73+
74+
sleep_time_ms = sleep_time_ms.saturating_add(thread_rng().gen_range(0..sleep_time_ms));
75+
76+
if sleep_time_ms > AVG_SLEEP_TIME_MS {
77+
let jitter = 10;
78+
sleep_time_ms =
79+
thread_rng().gen_range((AVG_SLEEP_TIME_MS - jitter)..(AVG_SLEEP_TIME_MS + jitter));
80+
}
81+
82+
let msg = format!("Database is locked; sleeping {sleep_time_ms}ms and trying again");
83+
if run_count > 10 && run_count % 10 == 0 {
84+
warn!("{msg}";
85+
"run_count" => run_count,
86+
"backtrace" => ?Backtrace::capture()
87+
);
88+
} else {
89+
debug!("{msg}");
90+
}
91+
92+
sleep_ms(sleep_time_ms);
93+
true
94+
}

stacks-common/src/util/mod.rs

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod log;
1919
#[macro_use]
2020
pub mod macros;
2121
pub mod chunked_encoding;
22+
pub mod db;
2223
pub mod hash;
2324
pub mod pair;
2425
pub mod pipe;
@@ -85,32 +86,6 @@ impl error::Error for HexError {
8586
}
8687
}
8788

88-
pub mod db_common {
89-
use std::{thread, time};
90-
91-
use rand::{thread_rng, Rng};
92-
93-
pub fn tx_busy_handler(run_count: i32) -> bool {
94-
let mut sleep_count = 10;
95-
if run_count > 0 {
96-
sleep_count = 2u64.saturating_pow(run_count as u32);
97-
}
98-
sleep_count = sleep_count.saturating_add(thread_rng().gen::<u64>() % sleep_count);
99-
100-
if sleep_count > 5000 {
101-
sleep_count = 5000;
102-
}
103-
104-
debug!(
105-
"Database is locked; sleeping {}ms and trying again",
106-
&sleep_count
107-
);
108-
109-
thread::sleep(time::Duration::from_millis(sleep_count));
110-
true
111-
}
112-
}
113-
11489
/// Write any `serde_json` object directly to a file
11590
pub fn serialize_json_to_file<J, P>(json: &J, path: P) -> Result<(), std::io::Error>
11691
where

stacks-signer/CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,43 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
77

88
## [Unreleased]
99

10+
### Added
11+
12+
### Changed
13+
14+
## [2.5.0.0.5.3]
15+
16+
### Added
17+
18+
### Changed
19+
20+
- Update node endpoints to match stacks-core release 2.5.0.0.7
21+
- `/v2/block_proposal` -> `/v3/block_proposal`
22+
- `/v2/stacker_set` -> `/v3/stacker_set`
23+
24+
## [2.5.0.0.5.2]
25+
26+
### Added
27+
28+
### Changed
29+
30+
- Reuse BlockResponse slot for MockSignature message type (#5103)
31+
32+
## [2.5.0.0.5.2-rc1]
33+
34+
### Added
35+
36+
- Signer set handoff integration test (#5037)
37+
- Add mock signing (#5020)
38+
- Add versioning info set at build-time (#5016)
39+
40+
### Changed
41+
42+
- Fix out of sync `RPCPeerInfo` with stacks-node (#5033, #5014, #4999)
43+
- Logging Improvements (#5025)
44+
- Timeout empty sortition (#5003)
45+
- Enum for version specific data (#4981)
46+
1047
## [2.5.0.0.5.1]
1148

1249
### Added

stacks-signer/src/client/stacks_client.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,13 +564,13 @@ impl StacksClient {
564564
warn!("Failed to parse the GetStackers error response: {e}");
565565
backoff::Error::permanent(e.into())
566566
})?;
567-
if &error_data.err_type == GetStackersErrors::NOT_AVAILABLE_ERR_TYPE {
568-
return Err(backoff::Error::transient(ClientError::NoSortitionOnChain));
567+
if error_data.err_type == GetStackersErrors::NOT_AVAILABLE_ERR_TYPE {
568+
Err(backoff::Error::transient(ClientError::NoSortitionOnChain))
569569
} else {
570570
warn!("Got error response ({status}): {}", error_data.err_msg);
571-
return Err(backoff::Error::permanent(ClientError::RequestFailure(
571+
Err(backoff::Error::permanent(ClientError::RequestFailure(
572572
status,
573-
)));
573+
)))
574574
}
575575
};
576576
let stackers_response =

stacks-signer/src/runloop.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,9 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
420420
"reward_cycle_before_refresh" => reward_cycle_before_refresh,
421421
"current_reward_cycle" => current_reward_cycle,
422422
"configured_for_current" => Self::is_configured_for_cycle(&self.stacks_signers, current_reward_cycle),
423+
"registered_for_current" => Self::is_registered_for_cycle(&self.stacks_signers, current_reward_cycle),
423424
"configured_for_next" => Self::is_configured_for_cycle(&self.stacks_signers, next_reward_cycle),
425+
"registered_for_next" => Self::is_registered_for_cycle(&self.stacks_signers, next_reward_cycle),
424426
"is_in_next_prepare_phase" => is_in_next_prepare_phase,
425427
);
426428

@@ -430,10 +432,10 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
430432
if !Self::is_configured_for_cycle(&self.stacks_signers, current_reward_cycle) {
431433
self.refresh_signer_config(current_reward_cycle);
432434
}
433-
if is_in_next_prepare_phase {
434-
if !Self::is_configured_for_cycle(&self.stacks_signers, next_reward_cycle) {
435-
self.refresh_signer_config(next_reward_cycle);
436-
}
435+
if is_in_next_prepare_phase
436+
&& !Self::is_configured_for_cycle(&self.stacks_signers, next_reward_cycle)
437+
{
438+
self.refresh_signer_config(next_reward_cycle);
437439
}
438440

439441
self.cleanup_stale_signers(current_reward_cycle);
@@ -455,6 +457,17 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
455457
signer.reward_cycle() == reward_cycle
456458
}
457459

460+
fn is_registered_for_cycle(
461+
stacks_signers: &HashMap<u64, ConfiguredSigner<Signer, T>>,
462+
reward_cycle: u64,
463+
) -> bool {
464+
let Some(signer) = stacks_signers.get(&(reward_cycle % 2)) else {
465+
return false;
466+
};
467+
signer.reward_cycle() == reward_cycle
468+
&& matches!(signer, ConfiguredSigner::RegisteredSigner(_))
469+
}
470+
458471
fn cleanup_stale_signers(&mut self, current_reward_cycle: u64) {
459472
let mut to_delete = Vec::new();
460473
for (idx, signer) in &mut self.stacks_signers {

0 commit comments

Comments
 (0)