Skip to content

Commit 71abebc

Browse files
authored
Merge pull request #4660 from stacks-network/debug/dkg-results
Debug/dkg results
2 parents 8874fd2 + df5431e commit 71abebc

File tree

2 files changed

+104
-44
lines changed

2 files changed

+104
-44
lines changed

stacks-signer/src/runloop.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,10 +383,9 @@ impl SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for RunLoop {
383383
if event_parity == Some(other_signer_parity) {
384384
continue;
385385
}
386-
387386
if signer.approved_aggregate_public_key.is_none() {
388-
if let Err(e) = signer.update_dkg(&self.stacks_client) {
389-
error!("{signer}: failed to update DKG: {e}");
387+
if let Err(e) = signer.refresh_dkg(&self.stacks_client) {
388+
error!("{signer}: failed to refresh DKG: {e}");
390389
}
391390
}
392391
signer.refresh_coordinator();

stacks-signer/src/signer.rs

Lines changed: 102 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use std::time::Instant;
2121
use blockstack_lib::chainstate::burn::ConsensusHashExtensions;
2222
use blockstack_lib::chainstate::nakamoto::signer_set::NakamotoSigners;
2323
use blockstack_lib::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockVote};
24+
use blockstack_lib::chainstate::stacks::boot::SIGNERS_VOTING_FUNCTION_NAME;
2425
use blockstack_lib::chainstate::stacks::StacksTransaction;
2526
use blockstack_lib::net::api::postblock_proposal::BlockValidateResponse;
2627
use hashbrown::HashSet;
@@ -123,13 +124,22 @@ pub enum Command {
123124
},
124125
}
125126

127+
/// The specific operations that a signer can perform
128+
#[derive(PartialEq, Eq, Debug, Clone)]
129+
pub enum Operation {
130+
/// A DKG operation
131+
Dkg,
132+
/// A Sign operation
133+
Sign,
134+
}
135+
126136
/// The Signer state
127137
#[derive(PartialEq, Eq, Debug, Clone)]
128138
pub enum State {
129139
/// The signer is idle, waiting for messages and commands
130140
Idle,
131141
/// The signer is executing a DKG or Sign round
132-
OperationInProgress,
142+
OperationInProgress(Operation),
133143
}
134144

135145
/// The stacks signer registered for the reward cycle
@@ -343,8 +353,8 @@ impl Signer {
343353
}
344354

345355
/// Update operation
346-
fn update_operation(&mut self) {
347-
self.state = State::OperationInProgress;
356+
fn update_operation(&mut self, operation: Operation) {
357+
self.state = State::OperationInProgress(operation);
348358
self.coordinator_selector.last_message_time = Some(Instant::now());
349359
}
350360

@@ -374,6 +384,7 @@ impl Signer {
374384
Ok(msg) => {
375385
let ack = self.stackerdb.send_message_with_retry(msg.into());
376386
debug!("{self}: ACK: {ack:?}",);
387+
self.update_operation(Operation::Dkg);
377388
}
378389
Err(e) => {
379390
error!("{self}: Failed to start DKG: {e:?}",);
@@ -419,6 +430,7 @@ impl Signer {
419430
.unwrap_or_else(|e| {
420431
error!("{self}: Failed to insert block in DB: {e:?}");
421432
});
433+
self.update_operation(Operation::Sign);
422434
}
423435
Err(e) => {
424436
error!("{self}: Failed to start signing block: {e:?}",);
@@ -427,7 +439,6 @@ impl Signer {
427439
}
428440
}
429441
}
430-
self.update_operation();
431442
}
432443

433444
/// Attempt to process the next command in the queue, and update state accordingly
@@ -460,10 +471,10 @@ impl Signer {
460471
.expect("BUG: Already asserted that the command queue was not empty");
461472
self.execute_command(stacks_client, &command);
462473
}
463-
State::OperationInProgress => {
474+
State::OperationInProgress(op) => {
464475
// We cannot execute the next command until the current one is finished...
465476
debug!(
466-
"{self}: Waiting for operation to finish. Coordinator state = {:?}",
477+
"{self}: Waiting for {op:?} operation to finish. Coordinator state = {:?}",
467478
self.coordinator.state
468479
);
469480
}
@@ -696,9 +707,26 @@ impl Signer {
696707
self.process_operation_results(stacks_client, &operation_results);
697708
self.send_operation_results(res, operation_results);
698709
self.finish_operation();
699-
} else if !packets.is_empty() && self.coordinator.state != CoordinatorState::Idle {
700-
// We have received a message and are in the middle of an operation. Update our state accordingly
701-
self.update_operation();
710+
} else if !packets.is_empty() {
711+
// We have received a message. Update our state accordingly
712+
// Let us be extra explicit in case a new state type gets added to wsts' state machine
713+
match &self.coordinator.state {
714+
CoordinatorState::Idle => {}
715+
CoordinatorState::DkgPublicDistribute
716+
| CoordinatorState::DkgPublicGather
717+
| CoordinatorState::DkgPrivateDistribute
718+
| CoordinatorState::DkgPrivateGather
719+
| CoordinatorState::DkgEndDistribute
720+
| CoordinatorState::DkgEndGather => {
721+
self.update_operation(Operation::Dkg);
722+
}
723+
CoordinatorState::NonceRequest(_, _)
724+
| CoordinatorState::NonceGather(_, _)
725+
| CoordinatorState::SigShareRequest(_, _)
726+
| CoordinatorState::SigShareGather(_, _) => {
727+
self.update_operation(Operation::Sign);
728+
}
729+
}
702730
}
703731

704732
debug!("{self}: Saving signer state");
@@ -1016,6 +1044,10 @@ impl Signer {
10161044
/// Process a dkg result by broadcasting a vote to the stacks node
10171045
fn process_dkg(&mut self, stacks_client: &StacksClient, dkg_public_key: &Point) {
10181046
let mut dkg_results_bytes = vec![];
1047+
debug!(
1048+
"{self}: Received DKG result. Broadcasting vote to the stacks node...";
1049+
"dkg_public_key" => %dkg_public_key
1050+
);
10191051
if let Err(e) = SignerMessage::serialize_dkg_result(
10201052
&mut dkg_results_bytes,
10211053
dkg_public_key,
@@ -1273,7 +1305,49 @@ impl Signer {
12731305
}
12741306
}
12751307

1308+
/// Refresh DKG value and queue DKG command if necessary
1309+
pub fn refresh_dkg(&mut self, stacks_client: &StacksClient) -> Result<(), ClientError> {
1310+
// First check if we should queue DKG based on contract vote state and stackerdb transactions
1311+
let should_queue = self.should_queue_dkg(stacks_client)?;
1312+
// Before queueing the command, check one last time if DKG has been
1313+
// approved. It could have happened after the last call to
1314+
// `get_approved_aggregate_key` but before the theshold check in
1315+
// `should_queue_dkg`.
1316+
let old_dkg = self.approved_aggregate_public_key;
1317+
self.approved_aggregate_public_key =
1318+
stacks_client.get_approved_aggregate_key(self.reward_cycle)?;
1319+
if self.approved_aggregate_public_key.is_some() {
1320+
// TODO: this will never work as is. We need to have stored our party shares on the side etc for this particular aggregate key.
1321+
// Need to update state to store the necessary info, check against it to see if we have participated in the winning round and
1322+
// then overwrite our value accordingly. Otherwise, we will be locked out of the round and should not participate.
1323+
self.coordinator
1324+
.set_aggregate_public_key(self.approved_aggregate_public_key);
1325+
if old_dkg != self.approved_aggregate_public_key {
1326+
warn!(
1327+
"{self}: updated DKG value to {:?}.",
1328+
self.approved_aggregate_public_key
1329+
);
1330+
}
1331+
if let State::OperationInProgress(Operation::Dkg) = self.state {
1332+
debug!(
1333+
"{self}: DKG has already been set. Aborting DKG operation {}.",
1334+
self.coordinator.current_dkg_id
1335+
);
1336+
self.finish_operation();
1337+
}
1338+
} else if should_queue {
1339+
if self.commands.front() != Some(&Command::Dkg) {
1340+
info!("{self} is the current coordinator and must trigger DKG. Queuing DKG command...");
1341+
self.commands.push_front(Command::Dkg);
1342+
} else {
1343+
debug!("{self}: DKG command already queued...");
1344+
}
1345+
}
1346+
Ok(())
1347+
}
1348+
12761349
/// Should DKG be queued to the current signer's command queue
1350+
/// This assumes that no key has been approved by the contract yet
12771351
pub fn should_queue_dkg(&mut self, stacks_client: &StacksClient) -> Result<bool, ClientError> {
12781352
if self.state != State::Idle
12791353
|| self.signer_id != self.get_coordinator_dkg().0
@@ -1283,6 +1357,25 @@ impl Signer {
12831357
return Ok(false);
12841358
}
12851359
let signer_address = stacks_client.get_signer_address();
1360+
let account_nonces = self.get_account_nonces(stacks_client, &[*signer_address]);
1361+
let old_transactions = self.get_signer_transactions(&account_nonces).map_err(|e| {
1362+
warn!("{self}: Failed to get old signer transactions: {e:?}. May trigger DKG unnecessarily");
1363+
}).unwrap_or_default();
1364+
// Check if we have an existing vote transaction for the same round and reward cycle
1365+
for transaction in old_transactions.iter() {
1366+
let params =
1367+
NakamotoSigners::parse_vote_for_aggregate_public_key(transaction).unwrap_or_else(|| panic!("BUG: {self}: Received an invalid {SIGNERS_VOTING_FUNCTION_NAME} transaction in an already filtered list: {transaction:?}"));
1368+
if Some(params.aggregate_key) == self.coordinator.aggregate_public_key
1369+
&& params.voting_round == self.coordinator.current_dkg_id
1370+
{
1371+
debug!("{self}: Not triggering a DKG round. Already have a pending vote transaction.";
1372+
"txid" => %transaction.txid(),
1373+
"aggregate_key" => %params.aggregate_key,
1374+
"voting_round" => params.voting_round
1375+
);
1376+
return Ok(false);
1377+
}
1378+
}
12861379
if let Some(aggregate_key) = stacks_client.get_vote_for_aggregate_public_key(
12871380
self.coordinator.current_dkg_id,
12881381
self.reward_cycle,
@@ -1311,12 +1404,6 @@ impl Signer {
13111404
);
13121405
return Ok(false);
13131406
}
1314-
warn!("{self}: Vote for DKG failed.";
1315-
"voting_round" => self.coordinator.current_dkg_id,
1316-
"aggregate_key" => %aggregate_key,
1317-
"round_weight" => round_weight,
1318-
"threshold_weight" => threshold_weight
1319-
);
13201407
} else {
13211408
// Have I already voted, but the vote is still pending in StackerDB? Check stackerdb for the same round number and reward cycle vote transaction
13221409
// Only get the account nonce of THIS signer as we only care about our own votes, not other signer votes
@@ -1363,32 +1450,6 @@ impl Signer {
13631450
Ok(true)
13641451
}
13651452

1366-
/// Update the DKG for the provided signer info, triggering it if required
1367-
pub fn update_dkg(&mut self, stacks_client: &StacksClient) -> Result<(), ClientError> {
1368-
let old_dkg = self.approved_aggregate_public_key;
1369-
self.approved_aggregate_public_key =
1370-
stacks_client.get_approved_aggregate_key(self.reward_cycle)?;
1371-
if self.approved_aggregate_public_key.is_some() {
1372-
// TODO: this will never work as is. We need to have stored our party shares on the side etc for this particular aggregate key.
1373-
// Need to update state to store the necessary info, check against it to see if we have participated in the winning round and
1374-
// then overwrite our value accordingly. Otherwise, we will be locked out of the round and should not participate.
1375-
self.coordinator
1376-
.set_aggregate_public_key(self.approved_aggregate_public_key);
1377-
if old_dkg != self.approved_aggregate_public_key {
1378-
warn!(
1379-
"{self}: updated DKG value to {:?}.",
1380-
self.approved_aggregate_public_key
1381-
);
1382-
}
1383-
return Ok(());
1384-
};
1385-
if self.should_queue_dkg(stacks_client)? {
1386-
info!("{self} is the current coordinator and must trigger DKG. Queuing DKG command...");
1387-
self.commands.push_front(Command::Dkg);
1388-
}
1389-
Ok(())
1390-
}
1391-
13921453
/// Process the event
13931454
pub fn process_event(
13941455
&mut self,

0 commit comments

Comments
 (0)