Skip to content

Commit 5f9c466

Browse files
authored
Merge branch 'develop' into feat/clarity-wasm-develop
2 parents f6fff25 + 05280cd commit 5f9c466

File tree

16 files changed

+894
-122
lines changed

16 files changed

+894
-122
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ jobs:
9393
- tests::signer::v0::bitcoind_forking_test
9494
- tests::signer::v0::multiple_miners
9595
- tests::signer::v0::mock_sign_epoch_25
96+
- tests::signer::v0::signer_set_rollover
9697
- tests::signer::v0::miner_forking
9798
- tests::nakamoto_integrations::stack_stx_burn_op_integration_test
9899
- tests::nakamoto_integrations::check_block_heights
@@ -101,6 +102,7 @@ jobs:
101102
- tests::nakamoto_integrations::check_block_info
102103
- tests::nakamoto_integrations::check_block_info_rewards
103104
- tests::nakamoto_integrations::continue_tenure_extend
105+
- tests::nakamoto_integrations::mock_mining
104106
- tests::nakamoto_integrations::multiple_miners
105107
# Do not run this one until we figure out why it fails in CI
106108
# - tests::neon_integrations::bitcoin_reorg_flap

.vscode/extensions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"recommendations": [
33
"rust-lang.rust-analyzer",
44
"vadimcn.vscode-lldb",
5-
"serayuzgur.crates",
5+
"fill-labs.dependi",
66
"editorconfig.editorconfig",
77
]
88
}

libsigner/src/runloop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ impl<
262262

263263
// start receiving events and doing stuff with them
264264
let runloop_thread = thread::Builder::new()
265-
.name("signer_runloop".to_string())
265+
.name(format!("signer_runloop:{}", bind_addr.port()))
266266
.stack_size(THREAD_STACK_SIZE)
267267
.spawn(move || {
268268
signer_loop.main_loop(event_recv, command_receiver, result_sender, stop_signaler)

stacks-signer/src/chainstate.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,12 @@ impl SortitionsView {
303303
let last_in_tenure = signer_db
304304
.get_last_signed_block_in_tenure(&block.header.consensus_hash)
305305
.map_err(|e| ClientError::InvalidResponse(e.to_string()))?;
306-
if last_in_tenure.is_some() {
306+
if let Some(last_in_tenure) = last_in_tenure {
307307
warn!(
308308
"Miner block proposal contains a tenure change, but we've already signed a block in this tenure. Considering proposal invalid.";
309309
"proposed_block_consensus_hash" => %block.header.consensus_hash,
310310
"proposed_block_signer_sighash" => %block.header.signer_signature_hash(),
311+
"last_in_tenure_signer_sighash" => %last_in_tenure.block.header.signer_signature_hash(),
311312
);
312313
return Ok(false);
313314
}

stacks-signer/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ pub struct SpawnedSigner<S: Signer<T> + Send, T: SignerEventTrait> {
100100
pub cmd_send: Sender<RunLoopCommand>,
101101
/// The result receiver for interacting with the running signer
102102
pub res_recv: Receiver<Vec<SignerResult>>,
103+
/// The spawned signer's config
104+
pub config: GlobalConfig,
103105
/// Phantom data for the signer type
104106
_phantom: std::marker::PhantomData<S>,
105107
}
@@ -136,7 +138,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SpawnedSigner
136138
{
137139
crate::monitoring::start_serving_monitoring_metrics(config.clone()).ok();
138140
}
139-
let runloop = RunLoop::new(config);
141+
let runloop = RunLoop::new(config.clone());
140142
let mut signer: RunLoopSigner<S, T> =
141143
libsigner::Signer::new(runloop, ev, cmd_recv, res_send);
142144
let running_signer = signer.spawn(endpoint).expect("Failed to spawn signer");
@@ -145,6 +147,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SpawnedSigner
145147
cmd_send,
146148
res_recv,
147149
_phantom: std::marker::PhantomData,
150+
config,
148151
}
149152
}
150153
}

stackslib/src/chainstate/nakamoto/coordinator/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ pub fn load_nakamoto_reward_set<U: RewardSetProvider>(
546546
"burnchain_height" => %anchor_block_sn.block_height);
547547

548548
let reward_set = provider.get_reward_set_nakamoto(
549-
prepare_end_height.saturating_sub(1),
549+
prepare_end_height,
550550
chain_state,
551551
burnchain,
552552
sort_db,

stackslib/src/chainstate/nakamoto/coordinator/tests.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use crate::chainstate::nakamoto::coordinator::load_nakamoto_reward_set;
4444
use crate::chainstate::nakamoto::miner::NakamotoBlockBuilder;
4545
use crate::chainstate::nakamoto::signer_set::NakamotoSigners;
4646
use crate::chainstate::nakamoto::test_signers::TestSigners;
47+
use crate::chainstate::nakamoto::test_stall::*;
4748
use crate::chainstate::nakamoto::tests::get_account;
4849
use crate::chainstate::nakamoto::tests::node::TestStacker;
4950
use crate::chainstate::nakamoto::{
@@ -2453,3 +2454,89 @@ pub fn simple_nakamoto_coordinator_10_extended_tenures_10_sortitions() -> TestPe
24532454
fn test_nakamoto_coordinator_10_tenures_and_extensions_10_blocks() {
24542455
simple_nakamoto_coordinator_10_extended_tenures_10_sortitions();
24552456
}
2457+
2458+
#[test]
2459+
fn process_next_nakamoto_block_deadlock() {
2460+
let private_key = StacksPrivateKey::from_seed(&[2]);
2461+
let addr = StacksAddress::p2pkh(false, &StacksPublicKey::from_private(&private_key));
2462+
2463+
let num_stackers: u32 = 4;
2464+
let mut signing_key_seed = num_stackers.to_be_bytes().to_vec();
2465+
signing_key_seed.extend_from_slice(&[1, 1, 1, 1]);
2466+
let signing_key = StacksPrivateKey::from_seed(signing_key_seed.as_slice());
2467+
let test_stackers = (0..num_stackers)
2468+
.map(|index| TestStacker {
2469+
signer_private_key: signing_key.clone(),
2470+
stacker_private_key: StacksPrivateKey::from_seed(&index.to_be_bytes()),
2471+
amount: u64::MAX as u128 - 10000,
2472+
pox_addr: Some(PoxAddress::Standard(
2473+
StacksAddress::new(
2474+
C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
2475+
Hash160::from_data(&index.to_be_bytes()),
2476+
),
2477+
Some(AddressHashMode::SerializeP2PKH),
2478+
)),
2479+
max_amount: None,
2480+
})
2481+
.collect::<Vec<_>>();
2482+
let test_signers = TestSigners::new(vec![signing_key]);
2483+
let mut pox_constants = TestPeerConfig::default().burnchain.pox_constants;
2484+
pox_constants.reward_cycle_length = 10;
2485+
pox_constants.v2_unlock_height = 21;
2486+
pox_constants.pox_3_activation_height = 26;
2487+
pox_constants.v3_unlock_height = 27;
2488+
pox_constants.pox_4_activation_height = 28;
2489+
2490+
let mut boot_plan = NakamotoBootPlan::new(function_name!())
2491+
.with_test_stackers(test_stackers.clone())
2492+
.with_test_signers(test_signers.clone())
2493+
.with_private_key(private_key);
2494+
boot_plan.pox_constants = pox_constants;
2495+
2496+
info!("Creating peer");
2497+
2498+
let mut peer = boot_plan.boot_into_nakamoto_peer(vec![], None);
2499+
let mut sortition_db = peer.sortdb().reopen().unwrap();
2500+
let (chainstate, _) = &mut peer
2501+
.stacks_node
2502+
.as_mut()
2503+
.unwrap()
2504+
.chainstate
2505+
.reopen()
2506+
.unwrap();
2507+
2508+
enable_process_block_stall();
2509+
2510+
let miner_thread = std::thread::spawn(move || {
2511+
info!(" ------------------------------- MINING TENURE");
2512+
let (block, burn_height, ..) =
2513+
peer.single_block_tenure(&private_key, |_| {}, |_| {}, |_| true);
2514+
info!(" ------------------------------- TENURE MINED");
2515+
});
2516+
2517+
// Wait a bit, to ensure the miner has reached the stall
2518+
std::thread::sleep(std::time::Duration::from_secs(10));
2519+
2520+
// Lock the sortdb
2521+
info!(" ------------------------------- TRYING TO LOCK THE SORTDB");
2522+
let sort_tx = sortition_db.tx_begin().unwrap();
2523+
info!(" ------------------------------- SORTDB LOCKED");
2524+
2525+
// Un-stall the block processing
2526+
disable_process_block_stall();
2527+
2528+
// Wait a bit, to ensure the tenure will have grabbed any locks it needs
2529+
std::thread::sleep(std::time::Duration::from_secs(10));
2530+
2531+
// Lock the chainstate db
2532+
info!(" ------------------------------- TRYING TO LOCK THE CHAINSTATE");
2533+
let chainstate_tx = chainstate.chainstate_tx_begin().unwrap();
2534+
2535+
info!(" ------------------------------- SORTDB AND CHAINSTATE LOCKED");
2536+
drop(chainstate_tx);
2537+
drop(sort_tx);
2538+
info!(" ------------------------------- MAIN THREAD FINISHED");
2539+
2540+
// Wait for the blocker and miner threads to finish
2541+
miner_thread.join().unwrap();
2542+
}

stackslib/src/chainstate/nakamoto/mod.rs

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,31 @@ lazy_static! {
270270
];
271271
}
272272

273+
#[cfg(test)]
274+
mod test_stall {
275+
pub static TEST_PROCESS_BLOCK_STALL: std::sync::Mutex<Option<bool>> =
276+
std::sync::Mutex::new(None);
277+
278+
pub fn stall_block_processing() {
279+
if *TEST_PROCESS_BLOCK_STALL.lock().unwrap() == Some(true) {
280+
// Do an extra check just so we don't log EVERY time.
281+
warn!("Block processing is stalled due to testing directive.");
282+
while *TEST_PROCESS_BLOCK_STALL.lock().unwrap() == Some(true) {
283+
std::thread::sleep(std::time::Duration::from_millis(10));
284+
}
285+
info!("Block processing is no longer stalled due to testing directive.");
286+
}
287+
}
288+
289+
pub fn enable_process_block_stall() {
290+
TEST_PROCESS_BLOCK_STALL.lock().unwrap().replace(true);
291+
}
292+
293+
pub fn disable_process_block_stall() {
294+
TEST_PROCESS_BLOCK_STALL.lock().unwrap().replace(false);
295+
}
296+
}
297+
273298
/// Trait for common MARF getters between StacksDBConn and StacksDBTx
274299
pub trait StacksDBIndexed {
275300
fn get(&mut self, tip: &StacksBlockId, key: &str) -> Result<Option<String>, DBError>;
@@ -1722,6 +1747,9 @@ impl NakamotoChainState {
17221747
canonical_sortition_tip: &SortitionId,
17231748
dispatcher_opt: Option<&'a T>,
17241749
) -> Result<Option<StacksEpochReceipt>, ChainstateError> {
1750+
#[cfg(test)]
1751+
test_stall::stall_block_processing();
1752+
17251753
let nakamoto_blocks_db = stacks_chain_state.nakamoto_blocks_db();
17261754
let Some((next_ready_block, block_size)) =
17271755
nakamoto_blocks_db.next_ready_nakamoto_block(stacks_chain_state.db())?
@@ -1992,22 +2020,14 @@ impl NakamotoChainState {
19922020
next_ready_block.header.consensus_hash
19932021
);
19942022

1995-
// set stacks block accepted
1996-
let mut sort_tx = sort_db.tx_handle_begin(canonical_sortition_tip)?;
1997-
sort_tx.set_stacks_block_accepted(
1998-
&next_ready_block.header.consensus_hash,
1999-
&next_ready_block.header.block_hash(),
2000-
next_ready_block.header.chain_length,
2001-
)?;
2002-
20032023
// this will panic if the Clarity commit fails.
20042024
clarity_commit.commit();
20052025
chainstate_tx.commit()
2006-
.unwrap_or_else(|e| {
2007-
error!("Failed to commit chainstate transaction after committing Clarity block. The chainstate database is now corrupted.";
2008-
"error" => ?e);
2009-
panic!()
2010-
});
2026+
.unwrap_or_else(|e| {
2027+
error!("Failed to commit chainstate transaction after committing Clarity block. The chainstate database is now corrupted.";
2028+
"error" => ?e);
2029+
panic!()
2030+
});
20112031

20122032
// as a separate transaction, mark this block as processed.
20132033
// This is done separately so that the staging blocks DB, which receives writes
@@ -2019,6 +2039,22 @@ impl NakamotoChainState {
20192039

20202040
let signer_bitvec = (&next_ready_block).header.pox_treatment.clone();
20212041

2042+
// set stacks block accepted
2043+
let mut sort_tx = sort_db.tx_handle_begin(canonical_sortition_tip)?;
2044+
sort_tx.set_stacks_block_accepted(
2045+
&next_ready_block.header.consensus_hash,
2046+
&next_ready_block.header.block_hash(),
2047+
next_ready_block.header.chain_length,
2048+
)?;
2049+
2050+
sort_tx
2051+
.commit()
2052+
.unwrap_or_else(|e| {
2053+
error!("Failed to commit sortition db transaction after committing chainstate and clarity block. The chainstate database is now corrupted.";
2054+
"error" => ?e);
2055+
panic!()
2056+
});
2057+
20222058
// announce the block, if we're connected to an event dispatcher
20232059
if let Some(dispatcher) = dispatcher_opt {
20242060
let block_event = (
@@ -2045,14 +2081,6 @@ impl NakamotoChainState {
20452081
);
20462082
}
20472083

2048-
sort_tx
2049-
.commit()
2050-
.unwrap_or_else(|e| {
2051-
error!("Failed to commit sortition db transaction after committing chainstate and clarity block. The chainstate database is now corrupted.";
2052-
"error" => ?e);
2053-
panic!()
2054-
});
2055-
20562084
Ok(Some(receipt))
20572085
}
20582086

testnet/stacks-node/src/event_dispatcher.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use std::thread::sleep;
66
use std::time::Duration;
77

88
use async_h1::client;
9+
use async_std::future::timeout;
910
use async_std::net::TcpStream;
11+
use async_std::task;
1012
use clarity::vm::analysis::contract_interface_builder::build_contract_interface;
1113
use clarity::vm::costs::ExecutionCost;
1214
use clarity::vm::events::{FTEventType, NFTEventType, STXEventType};
@@ -138,6 +140,7 @@ pub struct MinedNakamotoBlockEvent {
138140
pub signer_signature_hash: Sha512Trunc256Sum,
139141
pub tx_events: Vec<TransactionEvent>,
140142
pub signer_bitvec: String,
143+
pub signer_signature: Vec<MessageSignature>,
141144
}
142145

143146
impl InnerStackerDBChannel {
@@ -318,27 +321,33 @@ impl EventObserver {
318321
};
319322

320323
let backoff = Duration::from_millis((1.0 * 1_000.0) as u64);
324+
let connection_timeout = Duration::from_secs(5);
321325

322326
loop {
323327
let body = body.clone();
324328
let mut req = Request::new(Method::Post, url.clone());
325329
req.append_header("Content-Type", "application/json");
326330
req.set_body(body);
327331

328-
let response = async_std::task::block_on(async {
329-
let stream = match TcpStream::connect(self.endpoint.clone()).await {
330-
Ok(stream) => stream,
331-
Err(err) => {
332-
warn!("Event dispatcher: connection failed - {:?}", err);
333-
return None;
334-
}
335-
};
332+
let response = task::block_on(async {
333+
let stream =
334+
match timeout(connection_timeout, TcpStream::connect(&self.endpoint)).await {
335+
Ok(Ok(stream)) => stream,
336+
Ok(Err(err)) => {
337+
warn!("Event dispatcher: connection failed - {:?}", err);
338+
return None;
339+
}
340+
Err(_) => {
341+
error!("Event dispatcher: connection attempt timed out");
342+
return None;
343+
}
344+
};
336345

337346
match client::connect(stream, req).await {
338347
Ok(response) => Some(response),
339348
Err(err) => {
340349
warn!("Event dispatcher: rpc invocation failed - {:?}", err);
341-
return None;
350+
None
342351
}
343352
}
344353
});
@@ -1261,6 +1270,7 @@ impl EventDispatcher {
12611270
tx_events,
12621271
miner_signature: block.header.miner_signature.clone(),
12631272
signer_signature_hash: block.header.signer_signature_hash(),
1273+
signer_signature: block.header.signer_signature.clone(),
12641274
signer_bitvec,
12651275
})
12661276
.unwrap();

0 commit comments

Comments
 (0)