Skip to content

Commit 54c6bee

Browse files
committed
Merge branch 'chore/move-signer-logs-to-info' of github.com:stacks-network/stacks-core into chore/move-signer-logs-to-info
2 parents d37f2bc + efd3424 commit 54c6bee

File tree

22 files changed

+1279
-345
lines changed

22 files changed

+1279
-345
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,15 @@ jobs:
8989
- tests::signer::v0::end_of_tenure
9090
- tests::signer::v0::forked_tenure_okay
9191
- tests::signer::v0::forked_tenure_invalid
92+
- tests::signer::v0::bitcoind_forking_test
9293
- tests::nakamoto_integrations::stack_stx_burn_op_integration_test
9394
- tests::nakamoto_integrations::check_block_heights
9495
- tests::nakamoto_integrations::clarity_burn_state
9596
- tests::nakamoto_integrations::check_block_times
9697
- tests::nakamoto_integrations::check_block_info
9798
- tests::nakamoto_integrations::check_block_info_rewards
9899
- tests::nakamoto_integrations::continue_tenure_extend
100+
- tests::nakamoto_integrations::multiple_miners
99101
# Do not run this one until we figure out why it fails in CI
100102
# - tests::neon_integrations::bitcoin_reorg_flap
101103
# - tests::neon_integrations::bitcoin_reorg_flap_with_follower

libsigner/src/events.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,6 @@ fn process_stackerdb_event<T: SignerEventTrait>(
393393
local_addr: Option<SocketAddr>,
394394
mut request: HttpRequest,
395395
) -> Result<SignerEvent<T>, EventError> {
396-
debug!("Got stackerdb_chunks event");
397396
let mut body = String::new();
398397
if let Err(e) = request.as_reader().read_to_string(&mut body) {
399398
error!("Failed to read body: {:?}", &e);
@@ -404,6 +403,7 @@ fn process_stackerdb_event<T: SignerEventTrait>(
404403
)));
405404
}
406405

406+
debug!("Got stackerdb_chunks event"; "chunks_event_body" => %body);
407407
let event: StackerDBChunksEvent = serde_json::from_slice(body.as_bytes())
408408
.map_err(|e| EventError::Deserialize(format!("Could not decode body to JSON: {:?}", &e)))?;
409409

stacks-common/src/deps_common/httparse/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,6 @@ mod tests {
12801280
);
12811281
}
12821282

1283-
#[cfg(feature = "std")]
12841283
#[test]
12851284
fn test_std_error() {
12861285
use std::error::Error as StdError;

stackslib/src/chainstate/nakamoto/mod.rs

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,38 @@ impl MaturedMinerPaymentSchedules {
455455
}
456456
}
457457

458+
/// Struct containing information about the miners assigned in the
459+
/// .miners stackerdb config
460+
pub struct MinersDBInformation {
461+
signer_0_sortition: ConsensusHash,
462+
signer_1_sortition: ConsensusHash,
463+
latest_winner: u16,
464+
}
465+
466+
impl MinersDBInformation {
467+
/// What index in the `.miners` stackerdb is the miner who won
468+
/// `sortition`?
469+
pub fn get_signer_index(&self, sortition: &ConsensusHash) -> Option<u16> {
470+
if sortition == &self.signer_0_sortition {
471+
Some(0)
472+
} else if sortition == &self.signer_1_sortition {
473+
Some(1)
474+
} else {
475+
None
476+
}
477+
}
478+
479+
/// Get all of the sortitions whose winners are included in .miners
480+
pub fn get_sortitions(&self) -> [&ConsensusHash; 2] {
481+
[&self.signer_0_sortition, &self.signer_1_sortition]
482+
}
483+
484+
/// Get the index of the latest sortition winner in .miners
485+
pub fn get_latest_winner_index(&self) -> u16 {
486+
self.latest_winner
487+
}
488+
}
489+
458490
/// Calculated matured miner rewards, from scheduled rewards
459491
#[derive(Debug, Clone)]
460492
pub struct MaturedMinerRewards {
@@ -4039,7 +4071,7 @@ impl NakamotoChainState {
40394071
pub fn make_miners_stackerdb_config(
40404072
sortdb: &SortitionDB,
40414073
tip: &BlockSnapshot,
4042-
) -> Result<StackerDBConfig, ChainstateError> {
4074+
) -> Result<(StackerDBConfig, MinersDBInformation), ChainstateError> {
40434075
let ih = sortdb.index_handle(&tip.sortition_id);
40444076
let last_winner_snapshot = ih.get_last_snapshot_with_sortition(tip.block_height)?;
40454077
let parent_winner_snapshot = ih.get_last_snapshot_with_sortition(
@@ -4051,13 +4083,13 @@ impl NakamotoChainState {
40514083
// go get their corresponding leader keys, but preserve the miner's relative position in
40524084
// the stackerdb signer list -- if a miner was in slot 0, then it should stay in slot 0
40534085
// after a sortition (and vice versa for 1)
4054-
let sns = if last_winner_snapshot.num_sortitions % 2 == 0 {
4055-
[last_winner_snapshot, parent_winner_snapshot]
4086+
let (latest_winner_idx, sns) = if last_winner_snapshot.num_sortitions % 2 == 0 {
4087+
(0, [last_winner_snapshot, parent_winner_snapshot])
40564088
} else {
4057-
[parent_winner_snapshot, last_winner_snapshot]
4089+
(1, [parent_winner_snapshot, last_winner_snapshot])
40584090
};
40594091

4060-
for sn in sns {
4092+
for sn in sns.iter() {
40614093
// find the commit
40624094
let Some(block_commit) =
40634095
ih.get_block_commit_by_txid(&sn.sortition_id, &sn.winning_block_txid)?
@@ -4088,6 +4120,12 @@ impl NakamotoChainState {
40884120
);
40894121
}
40904122

4123+
let miners_db_info = MinersDBInformation {
4124+
signer_0_sortition: sns[0].consensus_hash,
4125+
signer_1_sortition: sns[1].consensus_hash,
4126+
latest_winner: latest_winner_idx,
4127+
};
4128+
40914129
let signers = miner_key_hash160s
40924130
.into_iter()
40934131
.map(|hash160|
@@ -4101,14 +4139,17 @@ impl NakamotoChainState {
41014139
))
41024140
.collect();
41034141

4104-
Ok(StackerDBConfig {
4105-
chunk_size: MAX_PAYLOAD_LEN.into(),
4106-
signers,
4107-
write_freq: 5,
4108-
max_writes: u32::MAX, // no limit on number of writes
4109-
max_neighbors: 200, // TODO: const -- just has to be equal to or greater than the number of signers
4110-
hint_replicas: vec![], // TODO: is there a way to get the IP addresses of stackers' preferred nodes?
4111-
})
4142+
Ok((
4143+
StackerDBConfig {
4144+
chunk_size: MAX_PAYLOAD_LEN.into(),
4145+
signers,
4146+
write_freq: 0,
4147+
max_writes: u32::MAX, // no limit on number of writes
4148+
max_neighbors: 200, // TODO: const -- just has to be equal to or greater than the number of signers
4149+
hint_replicas: vec![], // TODO: is there a way to get the IP addresses of stackers' preferred nodes?
4150+
},
4151+
miners_db_info,
4152+
))
41124153
}
41134154

41144155
/// Get the slot range for the given miner's public key.
@@ -4119,33 +4160,33 @@ impl NakamotoChainState {
41194160
pub fn get_miner_slot(
41204161
sortdb: &SortitionDB,
41214162
tip: &BlockSnapshot,
4122-
miner_pubkey: &StacksPublicKey,
4163+
election_sortition: &ConsensusHash,
41234164
) -> Result<Option<Range<u32>>, ChainstateError> {
4124-
let miner_hash160 = Hash160::from_node_public_key(&miner_pubkey);
4125-
let stackerdb_config = Self::make_miners_stackerdb_config(sortdb, &tip)?;
4165+
let (stackerdb_config, miners_info) = Self::make_miners_stackerdb_config(sortdb, &tip)?;
41264166

41274167
// find out which slot we're in
4128-
let mut slot_index = 0;
4129-
let mut slot_id_result = None;
4130-
for (addr, slot_count) in stackerdb_config.signers.iter() {
4131-
if addr.bytes == miner_hash160 {
4132-
slot_id_result = Some(Range {
4133-
start: slot_index,
4134-
end: slot_index + slot_count,
4135-
});
4136-
break;
4137-
}
4138-
slot_index += slot_count;
4139-
}
4140-
4141-
let Some(slot_id_range) = slot_id_result else {
4142-
// miner key does not match any slot
4168+
let Some(signer_ix) = miners_info
4169+
.get_signer_index(election_sortition)
4170+
.map(usize::from)
4171+
else {
41434172
warn!("Miner is not in the miners StackerDB config";
4144-
"miner" => %miner_hash160,
4145-
"stackerdb_slots" => format!("{:?}", &stackerdb_config.signers));
4146-
4173+
"stackerdb_slots" => ?stackerdb_config.signers,
4174+
"queried_sortition" => %election_sortition,
4175+
"sortition_hashes" => ?miners_info.get_sortitions());
41474176
return Ok(None);
41484177
};
4178+
let mut signer_ranges = stackerdb_config.signer_ranges();
4179+
if signer_ix >= signer_ranges.len() {
4180+
// should be unreachable, but always good to be careful
4181+
warn!("Miner is not in the miners StackerDB config";
4182+
"stackerdb_slots" => ?stackerdb_config.signers,
4183+
"queried_sortition" => %election_sortition,
4184+
"sortition_hashes" => ?miners_info.get_sortitions());
4185+
4186+
return Ok(None);
4187+
}
4188+
let slot_id_range = signer_ranges.swap_remove(signer_ix);
4189+
41494190
Ok(Some(slot_id_range))
41504191
}
41514192

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,8 +2049,9 @@ fn test_make_miners_stackerdb_config() {
20492049

20502050
let tip = SortitionDB::get_canonical_burn_chain_tip(sort_db.conn()).unwrap();
20512051
// check the stackerdb config as of this chain tip
2052-
let stackerdb_config =
2053-
NakamotoChainState::make_miners_stackerdb_config(sort_db, &tip).unwrap();
2052+
let stackerdb_config = NakamotoChainState::make_miners_stackerdb_config(sort_db, &tip)
2053+
.unwrap()
2054+
.0;
20542055
eprintln!(
20552056
"stackerdb_config at i = {} (sorition? {}): {:?}",
20562057
&i, sortition, &stackerdb_config
@@ -2079,7 +2080,7 @@ fn test_make_miners_stackerdb_config() {
20792080
let tip = SortitionDB::get_canonical_burn_chain_tip(sort_db.conn()).unwrap();
20802081
let miner_privkey = &miner_keys[i];
20812082
let miner_pubkey = StacksPublicKey::from_private(miner_privkey);
2082-
let slot_id = NakamotoChainState::get_miner_slot(&sort_db, &tip, &miner_pubkey)
2083+
let slot_id = NakamotoChainState::get_miner_slot(&sort_db, &tip, &tip.consensus_hash)
20832084
.expect("Failed to get miner slot");
20842085
if sortition {
20852086
let slot_id = slot_id.expect("No miner slot exists for this miner").start;

stackslib/src/net/api/getneighbors.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,43 @@ pub struct RPCNeighbor {
5151
pub public_key_hash: Hash160,
5252
pub authenticated: bool,
5353
#[serde(skip_serializing_if = "Option::is_none")]
54+
#[serde(with = "serde_opt_vec_qci")]
5455
pub stackerdbs: Option<Vec<QualifiedContractIdentifier>>,
5556
}
5657

58+
/// Serialize and deserialize `Option<Vec<QualifiedContractIdentifier>>`
59+
/// using the `to_string()` and `parse()` implementations of `QualifiedContractIdentifier`.
60+
mod serde_opt_vec_qci {
61+
use clarity::vm::types::QualifiedContractIdentifier;
62+
use serde::{Deserialize, Serialize};
63+
64+
pub fn serialize<S: serde::Serializer>(
65+
opt: &Option<Vec<QualifiedContractIdentifier>>,
66+
serializer: S,
67+
) -> Result<S::Ok, S::Error> {
68+
let serialize_as: Option<Vec<_>> = opt
69+
.as_ref()
70+
.map(|vec_qci| vec_qci.iter().map(ToString::to_string).collect());
71+
serialize_as.serialize(serializer)
72+
}
73+
74+
pub fn deserialize<'de, D>(de: D) -> Result<Option<Vec<QualifiedContractIdentifier>>, D::Error>
75+
where
76+
D: serde::Deserializer<'de>,
77+
{
78+
let from_str: Option<Vec<String>> = Deserialize::deserialize(de)?;
79+
let Some(vec_str) = from_str else {
80+
return Ok(None);
81+
};
82+
let parse_opt: Result<Vec<QualifiedContractIdentifier>, _> = vec_str
83+
.into_iter()
84+
.map(|x| QualifiedContractIdentifier::parse(&x).map_err(serde::de::Error::custom))
85+
.collect();
86+
let out_vec = parse_opt?;
87+
Ok(Some(out_vec))
88+
}
89+
}
90+
5791
impl RPCNeighbor {
5892
pub fn from_neighbor_key_and_pubkh(
5993
nk: NeighborKey,

stackslib/src/net/rpc.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -554,12 +554,12 @@ impl ConversationHttp {
554554
)?;
555555

556556
info!("Handled StacksHTTPRequest";
557-
"verb" => %verb,
558-
"path" => %request_path,
559-
"processing_time_ms" => start_time.elapsed().as_millis(),
560-
"latency_ms" => latency,
561-
"conn_id" => self.conn_id,
562-
"peer_addr" => &self.peer_addr);
557+
"verb" => %verb,
558+
"path" => %request_path,
559+
"processing_time_ms" => start_time.elapsed().as_millis(),
560+
"latency_ms" => latency,
561+
"conn_id" => self.conn_id,
562+
"peer_addr" => &self.peer_addr);
563563

564564
if let Some(msg) = msg_opt {
565565
ret.push(msg);

stackslib/src/net/stackerdb/config.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -555,10 +555,17 @@ impl StackerDBConfig {
555555
reason,
556556
));
557557
} else if let Some(Err(e)) = res {
558-
warn!(
559-
"Could not use contract {} for StackerDB: {:?}",
560-
contract_id, &e
561-
);
558+
if contract_id.is_boot() {
559+
debug!(
560+
"Could not use contract {} for StackerDB: {:?}",
561+
contract_id, &e
562+
);
563+
} else {
564+
warn!(
565+
"Could not use contract {} for StackerDB: {:?}",
566+
contract_id, &e
567+
);
568+
}
562569
return Err(e);
563570
}
564571

stackslib/src/net/stackerdb/mod.rs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub mod db;
119119
pub mod sync;
120120

121121
use std::collections::{HashMap, HashSet};
122+
use std::ops::Range;
122123

123124
use clarity::vm::types::QualifiedContractIdentifier;
124125
use libstackerdb::{SlotMetadata, STACKERDB_MAX_CHUNK_SIZE};
@@ -205,6 +206,22 @@ impl StackerDBConfig {
205206
pub fn num_slots(&self) -> u32 {
206207
self.signers.iter().fold(0, |acc, s| acc + s.1)
207208
}
209+
210+
/// What are the slot index ranges for each signer?
211+
/// Returns the ranges in the same ordering as `self.signers`
212+
pub fn signer_ranges(&self) -> Vec<Range<u32>> {
213+
let mut slot_index = 0;
214+
let mut result = Vec::with_capacity(self.signers.len());
215+
for (_, slot_count) in self.signers.iter() {
216+
let end = slot_index + *slot_count;
217+
result.push(Range {
218+
start: slot_index,
219+
end,
220+
});
221+
slot_index = end;
222+
}
223+
result
224+
}
208225
}
209226

210227
/// This is the set of replicated chunks in all stacker DBs that this node subscribes to.
@@ -280,14 +297,16 @@ impl StackerDBs {
280297
== boot_code_id(MINERS_NAME, chainstate.mainnet)
281298
{
282299
// .miners contract -- directly generate the config
283-
NakamotoChainState::make_miners_stackerdb_config(sortdb, &tip).unwrap_or_else(|e| {
284-
warn!(
285-
"Failed to generate .miners StackerDB config";
286-
"contract" => %stackerdb_contract_id,
287-
"err" => ?e,
288-
);
289-
StackerDBConfig::noop()
290-
})
300+
NakamotoChainState::make_miners_stackerdb_config(sortdb, &tip)
301+
.map(|(config, _)| config)
302+
.unwrap_or_else(|e| {
303+
warn!(
304+
"Failed to generate .miners StackerDB config";
305+
"contract" => %stackerdb_contract_id,
306+
"err" => ?e,
307+
);
308+
StackerDBConfig::noop()
309+
})
291310
} else {
292311
// attempt to load the config from the contract itself
293312
StackerDBConfig::from_smart_contract(
@@ -297,11 +316,20 @@ impl StackerDBs {
297316
num_neighbors,
298317
)
299318
.unwrap_or_else(|e| {
300-
warn!(
301-
"Failed to load StackerDB config";
302-
"contract" => %stackerdb_contract_id,
303-
"err" => ?e,
304-
);
319+
if matches!(e, net_error::NoSuchStackerDB(_)) && stackerdb_contract_id.is_boot()
320+
{
321+
debug!(
322+
"Failed to load StackerDB config";
323+
"contract" => %stackerdb_contract_id,
324+
"err" => ?e,
325+
);
326+
} else {
327+
warn!(
328+
"Failed to load StackerDB config";
329+
"contract" => %stackerdb_contract_id,
330+
"err" => ?e,
331+
);
332+
}
305333
StackerDBConfig::noop()
306334
})
307335
};
@@ -401,6 +429,10 @@ pub struct StackerDBSync<NC: NeighborComms> {
401429
num_attempted_connections: u64,
402430
/// How many connections have been made in the last pass (gets reset)
403431
num_connections: u64,
432+
/// Number of state machine passes
433+
rounds: u128,
434+
/// Round when we last pushed
435+
push_round: u128,
404436
}
405437

406438
impl StackerDBSyncResult {

0 commit comments

Comments
 (0)