Skip to content

Commit 588b5b7

Browse files
committed
Merge branch 'develop' of https://github.com/stacks-network/stacks-core into feat/signers-read-stackerdb
2 parents 8005780 + e534515 commit 588b5b7

File tree

11 files changed

+234
-344
lines changed

11 files changed

+234
-344
lines changed

libsigner/src/events.rs

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,9 @@ pub struct BlockProposalSigners {
6868
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
6969
pub enum SignerEvent {
7070
/// A miner sent a message over .miners
71-
/// The `Vec<BlockProposalSigners>` will contain any block proposals made by the miner during this StackerDB event.
7271
/// The `Vec<SignerMessage>` will contain any signer WSTS messages made by the miner while acting as a coordinator.
73-
/// The `Option<StacksPublicKey>` will contain the message sender's public key if either of the vecs is non-empty.
74-
MinerMessages(
75-
Vec<BlockProposalSigners>,
76-
Vec<SignerMessage>,
77-
Option<StacksPublicKey>,
78-
),
72+
/// The `Option<StacksPublicKey>` will contain the message sender's public key if the vec is non-empty.
73+
MinerMessages(Vec<SignerMessage>, Option<StacksPublicKey>),
7974
/// The signer messages for other signers and miners to observe
8075
/// The u32 is the signer set to which the message belongs (either 0 or 1)
8176
SignerMessages(u32, Vec<SignerMessage>),
@@ -417,7 +412,6 @@ impl TryFrom<StackerDBChunksEvent> for SignerEvent {
417412
let signer_event = if event.contract_id.name.as_str() == MINERS_NAME
418413
&& event.contract_id.is_boot()
419414
{
420-
let mut blocks = vec![];
421415
let mut messages = vec![];
422416
let mut miner_pk = None;
423417
for chunk in event.modified_slots {
@@ -426,28 +420,13 @@ impl TryFrom<StackerDBChunksEvent> for SignerEvent {
426420
"Failed to recover PK from StackerDB chunk: {e}"
427421
))
428422
})?);
429-
if chunk.slot_id % MINER_SLOT_COUNT == 0 {
430-
// block
431-
let Ok(block) =
432-
BlockProposalSigners::consensus_deserialize(&mut chunk.data.as_slice())
433-
else {
434-
continue;
435-
};
436-
blocks.push(block);
437-
} else if chunk.slot_id % MINER_SLOT_COUNT == 1 {
438-
// message
439-
let Ok(msg) = SignerMessage::consensus_deserialize(&mut chunk.data.as_slice())
440-
else {
441-
continue;
442-
};
443-
messages.push(msg);
444-
} else {
445-
return Err(EventError::UnrecognizedEvent(
446-
"Unrecognized slot_id for miners contract".into(),
447-
));
423+
let Ok(msg) = SignerMessage::consensus_deserialize(&mut chunk.data.as_slice())
424+
else {
425+
continue;
448426
};
427+
messages.push(msg);
449428
}
450-
SignerEvent::MinerMessages(blocks, messages, miner_pk)
429+
SignerEvent::MinerMessages(messages, miner_pk)
451430
} else if event.contract_id.name.starts_with(SIGNERS_NAME) && event.contract_id.is_boot() {
452431
let Some((signer_set, _)) =
453432
get_signers_db_signer_set_message_id(event.contract_id.name.as_str())

stacks-signer/src/main.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@ use std::path::{Path, PathBuf};
3232
use std::sync::mpsc::{channel, Receiver, Sender};
3333
use std::time::Duration;
3434

35-
use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
3635
use blockstack_lib::util_lib::signed_structured_data::pox4::make_pox_4_signer_key_signature;
3736
use clap::Parser;
3837
use clarity::vm::types::QualifiedContractIdentifier;
39-
use libsigner::{RunningSigner, Signer, SignerEventReceiver, SignerSession, StackerDBSession};
38+
use libsigner::{
39+
BlockProposalSigners, RunningSigner, Signer, SignerEventReceiver, SignerSession,
40+
StackerDBSession,
41+
};
4042
use libstackerdb::StackerDBChunkData;
4143
use slog::{slog_debug, slog_error, slog_info};
4244
use stacks_common::codec::read_next;
@@ -208,15 +210,16 @@ fn handle_dkg(args: RunDkgArgs) {
208210
fn handle_sign(args: SignArgs) {
209211
debug!("Signing message...");
210212
let spawned_signer = spawn_running_signer(&args.config);
211-
let Some(block) = read_next::<NakamotoBlock, _>(&mut &args.data[..]).ok() else {
212-
error!("Unable to parse provided message as a NakamotoBlock.");
213+
let Some(block_proposal) = read_next::<BlockProposalSigners, _>(&mut &args.data[..]).ok()
214+
else {
215+
error!("Unable to parse provided message as a BlockProposalSigners.");
213216
spawned_signer.running_signer.stop();
214217
return;
215218
};
216219
let sign_command = RunLoopCommand {
217220
reward_cycle: args.reward_cycle,
218221
command: SignerCommand::Sign {
219-
block,
222+
block_proposal,
220223
is_taproot: false,
221224
merkle_root: None,
222225
},
@@ -230,8 +233,9 @@ fn handle_sign(args: SignArgs) {
230233
fn handle_dkg_sign(args: SignArgs) {
231234
debug!("Running DKG and signing message...");
232235
let spawned_signer = spawn_running_signer(&args.config);
233-
let Some(block) = read_next::<NakamotoBlock, _>(&mut &args.data[..]).ok() else {
234-
error!("Unable to parse provided message as a NakamotoBlock.");
236+
let Some(block_proposal) = read_next::<BlockProposalSigners, _>(&mut &args.data[..]).ok()
237+
else {
238+
error!("Unable to parse provided message as a BlockProposalSigners.");
235239
spawned_signer.running_signer.stop();
236240
return;
237241
};
@@ -242,7 +246,7 @@ fn handle_dkg_sign(args: SignArgs) {
242246
let sign_command = RunLoopCommand {
243247
reward_cycle: args.reward_cycle,
244248
command: SignerCommand::Sign {
245-
block,
249+
block_proposal,
246250
is_taproot: false,
247251
merkle_root: None,
248252
},

stacks-signer/src/signer.rs

Lines changed: 51 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ impl std::fmt::Display for SignerSlotID {
7272
pub struct BlockInfo {
7373
/// The block we are considering
7474
pub block: NakamotoBlock,
75+
/// The burn block height at which the block was proposed
76+
pub burn_block_height: u64,
77+
/// The reward cycle the block belongs to
78+
pub reward_cycle: u64,
7579
/// Our vote on the block if we have one yet
7680
pub vote: Option<NakamotoBlockVote>,
7781
/// Whether the block contents are valid
@@ -82,27 +86,29 @@ pub struct BlockInfo {
8286
pub signed_over: bool,
8387
}
8488

85-
impl BlockInfo {
86-
/// Create a new BlockInfo
87-
pub const fn new(block: NakamotoBlock) -> Self {
89+
impl From<BlockProposalSigners> for BlockInfo {
90+
fn from(value: BlockProposalSigners) -> Self {
8891
Self {
89-
block,
92+
block: value.block,
93+
burn_block_height: value.burn_height,
94+
reward_cycle: value.reward_cycle,
9095
vote: None,
9196
valid: None,
9297
nonce_request: None,
9398
signed_over: false,
9499
}
95100
}
96-
101+
}
102+
impl BlockInfo {
97103
/// Create a new BlockInfo with an associated nonce request packet
98-
pub const fn new_with_request(block: NakamotoBlock, nonce_request: NonceRequest) -> Self {
99-
Self {
100-
block,
101-
vote: None,
102-
valid: None,
103-
nonce_request: Some(nonce_request),
104-
signed_over: true,
105-
}
104+
pub fn new_with_request(
105+
block_proposal: BlockProposalSigners,
106+
nonce_request: NonceRequest,
107+
) -> Self {
108+
let mut block_info = BlockInfo::from(block_proposal);
109+
block_info.nonce_request = Some(nonce_request);
110+
block_info.signed_over = true;
111+
block_info
106112
}
107113

108114
/// Return the block's signer signature hash
@@ -119,7 +125,7 @@ pub enum Command {
119125
/// Sign a message
120126
Sign {
121127
/// The block to sign over
122-
block: NakamotoBlock,
128+
block_proposal: BlockProposalSigners,
123129
/// Whether to make a taproot signature
124130
is_taproot: bool,
125131
/// Taproot merkle root
@@ -439,31 +445,31 @@ impl Signer {
439445
self.update_operation(Operation::Dkg);
440446
}
441447
Command::Sign {
442-
block,
448+
block_proposal,
443449
is_taproot,
444450
merkle_root,
445451
} => {
446452
if self.approved_aggregate_public_key.is_none() {
447453
debug!("{self}: Cannot sign a block without an approved aggregate public key. Ignore it.");
448454
return;
449455
}
450-
let signer_signature_hash = block.header.signer_signature_hash();
456+
let signer_signature_hash = block_proposal.block.header.signer_signature_hash();
451457
let mut block_info = self
452458
.signer_db
453459
.block_lookup(self.reward_cycle, &signer_signature_hash)
454-
.unwrap_or_else(|_| Some(BlockInfo::new(block.clone())))
455-
.unwrap_or_else(|| BlockInfo::new(block.clone()));
460+
.unwrap_or_else(|_| Some(BlockInfo::from(block_proposal.clone())))
461+
.unwrap_or_else(|| BlockInfo::from(block_proposal.clone()));
456462
if block_info.signed_over {
457463
debug!("{self}: Received a sign command for a block we are already signing over. Ignore it.");
458464
return;
459465
}
460466
info!("{self}: Signing block";
461-
"block_consensus_hash" => %block.header.consensus_hash,
462-
"block_height" => block.header.chain_length,
463-
"pre_sign_block_id" => %block.block_id(),
467+
"block_consensus_hash" => %block_proposal.block.header.consensus_hash,
468+
"block_height" => block_proposal.block.header.chain_length,
469+
"pre_sign_block_id" => %block_proposal.block.block_id(),
464470
);
465471
match self.coordinator.start_signing_round(
466-
&block.serialize_to_vec(),
472+
&block_proposal.serialize_to_vec(),
467473
*is_taproot,
468474
*merkle_root,
469475
) {
@@ -472,7 +478,7 @@ impl Signer {
472478
debug!("{self}: ACK: {ack:?}",);
473479
block_info.signed_over = true;
474480
self.signer_db
475-
.insert_block(self.reward_cycle, &block_info)
481+
.insert_block(&block_info)
476482
.unwrap_or_else(|e| {
477483
error!("{self}: Failed to insert block in DB: {e:?}");
478484
});
@@ -562,7 +568,7 @@ impl Signer {
562568
let is_valid = self.verify_block_transactions(stacks_client, &block_info.block);
563569
block_info.valid = Some(is_valid);
564570
self.signer_db
565-
.insert_block(self.reward_cycle, &block_info)
571+
.insert_block(&block_info)
566572
.unwrap_or_else(|_| panic!("{self}: Failed to insert block in DB"));
567573
info!(
568574
"{self}: Treating block validation for block {} as valid: {:?}",
@@ -619,7 +625,7 @@ impl Signer {
619625
"signed_over" => block_info.signed_over,
620626
);
621627
self.signer_db
622-
.insert_block(self.reward_cycle, &block_info)
628+
.insert_block(&block_info)
623629
.unwrap_or_else(|_| panic!("{self}: Failed to insert block in DB"));
624630
}
625631

@@ -652,63 +658,6 @@ impl Signer {
652658
self.handle_packets(stacks_client, res, &packets, current_reward_cycle);
653659
}
654660

655-
/// Handle proposed blocks submitted by the miners to stackerdb
656-
fn handle_proposed_blocks(
657-
&mut self,
658-
stacks_client: &StacksClient,
659-
proposals: &[BlockProposalSigners],
660-
) {
661-
for proposal in proposals {
662-
if proposal.reward_cycle != self.reward_cycle {
663-
debug!(
664-
"{self}: Received proposal for block outside of my reward cycle, ignoring.";
665-
"proposal_reward_cycle" => proposal.reward_cycle,
666-
"proposal_burn_height" => proposal.burn_height,
667-
);
668-
continue;
669-
}
670-
let sig_hash = proposal.block.header.signer_signature_hash();
671-
match self.signer_db.block_lookup(self.reward_cycle, &sig_hash) {
672-
Ok(Some(block)) => {
673-
debug!(
674-
"{self}: Received proposal for block already known, ignoring new proposal.";
675-
"signer_sighash" => %sig_hash,
676-
"proposal_burn_height" => proposal.burn_height,
677-
"vote" => ?block.vote.as_ref().map(|v| {
678-
if v.rejected {
679-
"REJECT"
680-
} else {
681-
"ACCEPT"
682-
}
683-
}),
684-
"signed_over" => block.signed_over,
685-
);
686-
continue;
687-
}
688-
Ok(None) => {
689-
// Store the block in our cache
690-
self.signer_db
691-
.insert_block(self.reward_cycle, &BlockInfo::new(proposal.block.clone()))
692-
.unwrap_or_else(|e| {
693-
error!("{self}: Failed to insert block in DB: {e:?}");
694-
});
695-
// Submit the block for validation
696-
stacks_client
697-
.submit_block_for_validation(proposal.block.clone())
698-
.unwrap_or_else(|e| {
699-
warn!("{self}: Failed to submit block for validation: {e:?}");
700-
});
701-
}
702-
Err(e) => {
703-
error!(
704-
"{self}: Failed to lookup block in DB: {e:?}. Dropping proposal request."
705-
);
706-
continue;
707-
}
708-
}
709-
}
710-
}
711-
712661
/// Helper function for determining if the provided message is a DKG specific message
713662
fn is_dkg_message(msg: &Message) -> bool {
714663
matches!(
@@ -853,26 +802,35 @@ impl Signer {
853802
stacks_client: &StacksClient,
854803
nonce_request: &mut NonceRequest,
855804
) -> Option<BlockInfo> {
856-
let Some(block) =
857-
NakamotoBlock::consensus_deserialize(&mut nonce_request.message.as_slice()).ok()
805+
let Some(block_proposal) =
806+
BlockProposalSigners::consensus_deserialize(&mut nonce_request.message.as_slice()).ok()
858807
else {
859-
// We currently reject anything that is not a block
808+
// We currently reject anything that is not a valid block proposal
860809
warn!("{self}: Received a nonce request for an unknown message stream. Reject it.",);
861810
return None;
862811
};
863-
let signer_signature_hash = block.header.signer_signature_hash();
812+
if block_proposal.reward_cycle != self.reward_cycle {
813+
// We are not signing for this reward cycle. Reject the block
814+
warn!(
815+
"{self}: Received a nonce request for a different reward cycle. Reject it.";
816+
"requested_reward_cycle" => block_proposal.reward_cycle,
817+
);
818+
return None;
819+
}
820+
// TODO: could add a check to ignore an old burn block height if we know its oudated. Would require us to store the burn block height we last saw on the side.
821+
let signer_signature_hash = block_proposal.block.header.signer_signature_hash();
864822
let Some(mut block_info) = self
865823
.signer_db
866824
.block_lookup(self.reward_cycle, &signer_signature_hash)
867825
.expect("Failed to connect to signer DB")
868826
else {
869827
debug!(
870-
"{self}: We have received a block sign request for a block we have not seen before. Cache the nonce request and submit the block for validation...";
871-
"signer_sighash" => %block.header.signer_signature_hash(),
828+
"{self}: received a nonce request for a new block. Submit block for validation. ";
829+
"signer_sighash" => %signer_signature_hash,
872830
);
873-
let block_info = BlockInfo::new_with_request(block.clone(), nonce_request.clone());
831+
let block_info = BlockInfo::new_with_request(block_proposal, nonce_request.clone());
874832
stacks_client
875-
.submit_block_for_validation(block)
833+
.submit_block_for_validation(block_info.block.clone())
876834
.unwrap_or_else(|e| {
877835
warn!("{self}: Failed to submit block for validation: {e:?}",);
878836
});
@@ -1045,7 +1003,7 @@ impl Signer {
10451003
return None;
10461004
};
10471005
self.signer_db
1048-
.insert_block(self.reward_cycle, &updated_block_info)
1006+
.insert_block(&updated_block_info)
10491007
.unwrap_or_else(|_| panic!("{self}: Failed to insert block in DB"));
10501008
let process_request = updated_block_info.vote.is_some();
10511009
if !process_request {
@@ -1614,7 +1572,7 @@ impl Signer {
16141572
);
16151573
self.handle_signer_messages(stacks_client, res, messages, current_reward_cycle);
16161574
}
1617-
Some(SignerEvent::MinerMessages(blocks, messages, miner_key)) => {
1575+
Some(SignerEvent::MinerMessages(messages, miner_key)) => {
16181576
if let Some(miner_key) = miner_key {
16191577
let miner_key = PublicKey::try_from(miner_key.to_bytes_compressed().as_slice())
16201578
.expect("FATAL: could not convert from StacksPublicKey to PublicKey");
@@ -1626,13 +1584,11 @@ impl Signer {
16261584
return Ok(());
16271585
}
16281586
debug!(
1629-
"{self}: Received {} block proposals and {} messages from the miner",
1630-
blocks.len(),
1587+
"{self}: Received {} messages from the miner",
16311588
messages.len();
16321589
"miner_key" => ?miner_key,
16331590
);
16341591
self.handle_signer_messages(stacks_client, res, messages, current_reward_cycle);
1635-
self.handle_proposed_blocks(stacks_client, blocks);
16361592
}
16371593
Some(SignerEvent::StatusCheck) => {
16381594
debug!("{self}: Received a status check event.")

0 commit comments

Comments
 (0)