Skip to content

Commit

Permalink
Merge pull request #4807 from stacks-network/feat/miner-gather-v0-sig…
Browse files Browse the repository at this point in the history
…natures

feat: gather v0 block signatures from stackerdb
  • Loading branch information
hstove authored May 31, 2024
2 parents dbcd10f + 4c35571 commit ba9df14
Show file tree
Hide file tree
Showing 11 changed files with 381 additions and 110 deletions.
1 change: 1 addition & 0 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ jobs:
- tests::nakamoto_integrations::follower_bootup
- tests::nakamoto_integrations::forked_tenure_is_ignored
- tests::signer::v0::block_proposal_rejection
- tests::signer::v0::miner_gather_signatures
- tests::signer::v1::dkg
- tests::signer::v1::sign_request_rejected
# TODO: enable these once v1 signer is fixed
Expand Down
7 changes: 6 additions & 1 deletion stacks-signer/src/monitoring/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ impl MonitoringServer {
public_key,
format!("http://{}", config.node_host),
);
server.update_metrics()?;
if let Err(e) = server.update_metrics() {
warn!(
"Monitoring: Error updating metrics when starting server: {:?}",
e
);
};
server.main_loop()
}

Expand Down
13 changes: 10 additions & 3 deletions stacks-signer/src/v0/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ impl Signer {
"block_id" => %block_proposal.block.block_id(),
);
let block_info = BlockInfo::from(block_proposal.clone());
crate::monitoring::increment_block_proposals_received();
stacks_client
.submit_block_for_validation(block_info.block.clone())
.unwrap_or_else(|e| {
Expand Down Expand Up @@ -312,11 +313,17 @@ impl Signer {
};
// Submit a proposal response to the .signers contract for miners
debug!("{self}: Broadcasting a block response to stacks node: {response:?}");
if let Err(e) = self
match self
.stackerdb
.send_message_with_retry::<SignerMessage>(response.into())
.send_message_with_retry::<SignerMessage>(response.clone().into())
{
warn!("{self}: Failed to send block rejection to stacker-db: {e:?}",);
Ok(_) => {
let accepted = matches!(response, BlockResponse::Accepted(..));
crate::monitoring::increment_block_responses_sent(accepted);
}
Err(e) => {
warn!("{self}: Failed to send block rejection to stacker-db: {e:?}",);
}
}
self.signer_db
.insert_block(&block_info)
Expand Down
7 changes: 3 additions & 4 deletions stackslib/src/chainstate/nakamoto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,10 +516,9 @@ impl NakamotoBlockHeader {
// `last_index` is used to prevent out-of-order signatures
let mut last_index = None;

let total_weight = signers.iter().map(|s| s.weight).fold(0, |w, acc| {
acc.checked_add(w)
.expect("FATAL: Total signer weight > u32::MAX")
});
let total_weight = reward_set
.total_signing_weight()
.map_err(|_| ChainstateError::NoRegisteredSigners(0))?;

// HashMap of <PublicKey, (Signer, Index)>
let signers_by_pk: HashMap<_, _> = signers
Expand Down
17 changes: 17 additions & 0 deletions stackslib/src/chainstate/stacks/boot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,23 @@ impl RewardSet {
pub fn metadata_deserialize(from: &str) -> Result<RewardSet, String> {
serde_json::from_str(from).map_err(|e| e.to_string())
}

/// Return the total `weight` of all signers in the reward set.
/// If there are no reward set signers, a ChainstateError is returned.
pub fn total_signing_weight(&self) -> Result<u32, String> {
let Some(ref reward_set_signers) = self.signers else {
return Err(format!(
"Unable to calculate total weight - No signers in reward set"
));
};
Ok(reward_set_signers
.iter()
.map(|s| s.weight)
.fold(0, |s, acc| {
acc.checked_add(s)
.expect("FATAL: Total signer weight > u32::MAX")
}))
}
}

impl RewardSetData {
Expand Down
2 changes: 1 addition & 1 deletion testnet/stacks-node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ name = "stacks-events"
path = "src/stacks_events.rs"

[features]
monitoring_prom = ["stacks/monitoring_prom", "libsigner/monitoring_prom"]
monitoring_prom = ["stacks/monitoring_prom", "libsigner/monitoring_prom", "stacks-signer/monitoring_prom"]
slog_json = ["stacks/slog_json", "stacks-common/slog_json", "clarity/slog_json"]
prod-genesis-chainstate = []
default = []
96 changes: 40 additions & 56 deletions testnet/stacks-node/src/nakamoto_node/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,15 @@ impl BlockMinerThread {
self.mined_blocks.push(new_block);
}

let sort_db = SortitionDB::open(
let Ok(sort_db) = SortitionDB::open(
&self.config.get_burn_db_file_path(),
true,
self.burnchain.pox_constants.clone(),
)
.expect("FATAL: could not open sortition DB");
) else {
error!("Failed to open sortition DB. Will try mining again.");
continue;
};

let wait_start = Instant::now();
while wait_start.elapsed() < self.config.miner.wait_on_interim_blocks {
thread::sleep(Duration::from_millis(ABORT_TRY_AGAIN_MS));
Expand Down Expand Up @@ -277,19 +280,6 @@ impl BlockMinerThread {
})
})?;

let reward_cycle = self
.burnchain
.pox_constants
.block_height_to_reward_cycle(
self.burnchain.first_block_height,
self.burn_block.block_height,
)
.ok_or_else(|| {
NakamotoNodeError::SigningCoordinatorFailure(
"Building on a burn block that is before the first burn block".into(),
)
})?;

let reward_info = match sort_db.get_preprocessed_reward_set_of(&tip.sortition_id) {
Ok(x) => x,
Err(e) => {
Expand All @@ -308,20 +298,14 @@ impl BlockMinerThread {
// NOTE: this is a placeholder until the API can be fixed
let aggregate_public_key = Point::new();
let miner_privkey_as_scalar = Scalar::from(miner_privkey.as_slice().clone());
let mut coordinator = SignCoordinator::new(
&reward_set,
reward_cycle,
miner_privkey_as_scalar,
// TODO: placeholder until the signer is working
aggregate_public_key,
&stackerdbs,
&self.config,
)
.map_err(|e| {
NakamotoNodeError::SigningCoordinatorFailure(format!(
"Failed to initialize the signing coordinator. Cannot mine! {e:?}"
))
})?;
let mut coordinator =
SignCoordinator::new(&reward_set, miner_privkey_as_scalar, &self.config).map_err(
|e| {
NakamotoNodeError::SigningCoordinatorFailure(format!(
"Failed to initialize the signing coordinator. Cannot mine! {e:?}"
))
},
)?;

*attempts += 1;
let signature = coordinator.begin_sign_v1(
Expand All @@ -338,7 +322,7 @@ impl BlockMinerThread {
Ok((aggregate_public_key, signature))
}

/// Gather signatures from the signers for the block
/// Gather a list of signatures from the signers for the block
fn gather_signatures(
&mut self,
new_block: &mut NakamotoBlock,
Expand All @@ -356,22 +340,27 @@ impl BlockMinerThread {
true,
self.burnchain.pox_constants.clone(),
)
.expect("FATAL: could not open sortition DB");
.map_err(|e| {
NakamotoNodeError::SigningCoordinatorFailure(format!(
"Failed to open sortition DB. Cannot mine! {e:?}"
))
})?;

let tip = SortitionDB::get_block_snapshot_consensus(
sort_db.conn(),
&new_block.header.consensus_hash,
)
.expect("FATAL: could not retrieve chain tip")
.expect("FATAL: could not retrieve chain tip");

let reward_cycle = self
.burnchain
.pox_constants
.block_height_to_reward_cycle(
self.burnchain.first_block_height,
self.burn_block.block_height,
)
.expect("FATAL: building on a burn block that is before the first burn block");
.map_err(|e| {
NakamotoNodeError::SigningCoordinatorFailure(format!(
"Failed to retrieve chain tip: {:?}",
e
))
})
.and_then(|result| {
result.ok_or_else(|| {
NakamotoNodeError::SigningCoordinatorFailure("Failed to retrieve chain tip".into())
})
})?;

let reward_info = match sort_db.get_preprocessed_reward_set_of(&tip.sortition_id) {
Ok(x) => x,
Expand All @@ -389,19 +378,14 @@ impl BlockMinerThread {
};

let miner_privkey_as_scalar = Scalar::from(miner_privkey.as_slice().clone());
let mut coordinator = SignCoordinator::new(
&reward_set,
reward_cycle,
miner_privkey_as_scalar,
Point::new(),
&stackerdbs,
&self.config,
)
.map_err(|e| {
NakamotoNodeError::SigningCoordinatorFailure(format!(
"Failed to initialize the signing coordinator. Cannot mine! {e:?}"
))
})?;
let mut coordinator =
SignCoordinator::new(&reward_set, miner_privkey_as_scalar, &self.config).map_err(
|e| {
NakamotoNodeError::SigningCoordinatorFailure(format!(
"Failed to initialize the signing coordinator. Cannot mine! {e:?}"
))
},
)?;

*attempts += 1;
let signature = coordinator.begin_sign_v0(
Expand Down
Loading

0 comments on commit ba9df14

Please sign in to comment.