Skip to content

Commit e534515

Browse files
authored
Merge pull request #4695 from stacks-network/chore/remove-block-proposal-message
Remove redundant block proposal message and fix tests
2 parents 5be291e + 799b55d commit e534515

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
@@ -399,31 +405,31 @@ impl Signer {
399405
}
400406
}
401407
Command::Sign {
402-
block,
408+
block_proposal,
403409
is_taproot,
404410
merkle_root,
405411
} => {
406412
if self.approved_aggregate_public_key.is_none() {
407413
debug!("{self}: Cannot sign a block without an approved aggregate public key. Ignore it.");
408414
return;
409415
}
410-
let signer_signature_hash = block.header.signer_signature_hash();
416+
let signer_signature_hash = block_proposal.block.header.signer_signature_hash();
411417
let mut block_info = self
412418
.signer_db
413419
.block_lookup(self.reward_cycle, &signer_signature_hash)
414-
.unwrap_or_else(|_| Some(BlockInfo::new(block.clone())))
415-
.unwrap_or_else(|| BlockInfo::new(block.clone()));
420+
.unwrap_or_else(|_| Some(BlockInfo::from(block_proposal.clone())))
421+
.unwrap_or_else(|| BlockInfo::from(block_proposal.clone()));
416422
if block_info.signed_over {
417423
debug!("{self}: Received a sign command for a block we are already signing over. Ignore it.");
418424
return;
419425
}
420426
info!("{self}: Signing block";
421-
"block_consensus_hash" => %block.header.consensus_hash,
422-
"block_height" => block.header.chain_length,
423-
"pre_sign_block_id" => %block.block_id(),
427+
"block_consensus_hash" => %block_proposal.block.header.consensus_hash,
428+
"block_height" => block_proposal.block.header.chain_length,
429+
"pre_sign_block_id" => %block_proposal.block.block_id(),
424430
);
425431
match self.coordinator.start_signing_round(
426-
&block.serialize_to_vec(),
432+
&block_proposal.serialize_to_vec(),
427433
*is_taproot,
428434
*merkle_root,
429435
) {
@@ -432,7 +438,7 @@ impl Signer {
432438
debug!("{self}: ACK: {ack:?}",);
433439
block_info.signed_over = true;
434440
self.signer_db
435-
.insert_block(self.reward_cycle, &block_info)
441+
.insert_block(&block_info)
436442
.unwrap_or_else(|e| {
437443
error!("{self}: Failed to insert block in DB: {e:?}");
438444
});
@@ -517,7 +523,7 @@ impl Signer {
517523
let is_valid = self.verify_block_transactions(stacks_client, &block_info.block);
518524
block_info.valid = Some(is_valid);
519525
self.signer_db
520-
.insert_block(self.reward_cycle, &block_info)
526+
.insert_block(&block_info)
521527
.unwrap_or_else(|_| panic!("{self}: Failed to insert block in DB"));
522528
info!(
523529
"{self}: Treating block validation for block {} as valid: {:?}",
@@ -574,7 +580,7 @@ impl Signer {
574580
"signed_over" => block_info.signed_over,
575581
);
576582
self.signer_db
577-
.insert_block(self.reward_cycle, &block_info)
583+
.insert_block(&block_info)
578584
.unwrap_or_else(|_| panic!("{self}: Failed to insert block in DB"));
579585
}
580586

@@ -607,63 +613,6 @@ impl Signer {
607613
self.handle_packets(stacks_client, res, &packets, current_reward_cycle);
608614
}
609615

610-
/// Handle proposed blocks submitted by the miners to stackerdb
611-
fn handle_proposed_blocks(
612-
&mut self,
613-
stacks_client: &StacksClient,
614-
proposals: &[BlockProposalSigners],
615-
) {
616-
for proposal in proposals {
617-
if proposal.reward_cycle != self.reward_cycle {
618-
debug!(
619-
"{self}: Received proposal for block outside of my reward cycle, ignoring.";
620-
"proposal_reward_cycle" => proposal.reward_cycle,
621-
"proposal_burn_height" => proposal.burn_height,
622-
);
623-
continue;
624-
}
625-
let sig_hash = proposal.block.header.signer_signature_hash();
626-
match self.signer_db.block_lookup(self.reward_cycle, &sig_hash) {
627-
Ok(Some(block)) => {
628-
debug!(
629-
"{self}: Received proposal for block already known, ignoring new proposal.";
630-
"signer_sighash" => %sig_hash,
631-
"proposal_burn_height" => proposal.burn_height,
632-
"vote" => ?block.vote.as_ref().map(|v| {
633-
if v.rejected {
634-
"REJECT"
635-
} else {
636-
"ACCEPT"
637-
}
638-
}),
639-
"signed_over" => block.signed_over,
640-
);
641-
continue;
642-
}
643-
Ok(None) => {
644-
// Store the block in our cache
645-
self.signer_db
646-
.insert_block(self.reward_cycle, &BlockInfo::new(proposal.block.clone()))
647-
.unwrap_or_else(|e| {
648-
error!("{self}: Failed to insert block in DB: {e:?}");
649-
});
650-
// Submit the block for validation
651-
stacks_client
652-
.submit_block_for_validation(proposal.block.clone())
653-
.unwrap_or_else(|e| {
654-
warn!("{self}: Failed to submit block for validation: {e:?}");
655-
});
656-
}
657-
Err(e) => {
658-
error!(
659-
"{self}: Failed to lookup block in DB: {e:?}. Dropping proposal request."
660-
);
661-
continue;
662-
}
663-
}
664-
}
665-
}
666-
667616
/// Helper function for determining if the provided message is a DKG specific message
668617
fn is_dkg_message(msg: &Message) -> bool {
669618
matches!(
@@ -808,26 +757,35 @@ impl Signer {
808757
stacks_client: &StacksClient,
809758
nonce_request: &mut NonceRequest,
810759
) -> Option<BlockInfo> {
811-
let Some(block) =
812-
NakamotoBlock::consensus_deserialize(&mut nonce_request.message.as_slice()).ok()
760+
let Some(block_proposal) =
761+
BlockProposalSigners::consensus_deserialize(&mut nonce_request.message.as_slice()).ok()
813762
else {
814-
// We currently reject anything that is not a block
763+
// We currently reject anything that is not a valid block proposal
815764
warn!("{self}: Received a nonce request for an unknown message stream. Reject it.",);
816765
return None;
817766
};
818-
let signer_signature_hash = block.header.signer_signature_hash();
767+
if block_proposal.reward_cycle != self.reward_cycle {
768+
// We are not signing for this reward cycle. Reject the block
769+
warn!(
770+
"{self}: Received a nonce request for a different reward cycle. Reject it.";
771+
"requested_reward_cycle" => block_proposal.reward_cycle,
772+
);
773+
return None;
774+
}
775+
// 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.
776+
let signer_signature_hash = block_proposal.block.header.signer_signature_hash();
819777
let Some(mut block_info) = self
820778
.signer_db
821779
.block_lookup(self.reward_cycle, &signer_signature_hash)
822780
.expect("Failed to connect to signer DB")
823781
else {
824782
debug!(
825-
"{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...";
826-
"signer_sighash" => %block.header.signer_signature_hash(),
783+
"{self}: received a nonce request for a new block. Submit block for validation. ";
784+
"signer_sighash" => %signer_signature_hash,
827785
);
828-
let block_info = BlockInfo::new_with_request(block.clone(), nonce_request.clone());
786+
let block_info = BlockInfo::new_with_request(block_proposal, nonce_request.clone());
829787
stacks_client
830-
.submit_block_for_validation(block)
788+
.submit_block_for_validation(block_info.block.clone())
831789
.unwrap_or_else(|e| {
832790
warn!("{self}: Failed to submit block for validation: {e:?}",);
833791
});
@@ -1000,7 +958,7 @@ impl Signer {
1000958
return None;
1001959
};
1002960
self.signer_db
1003-
.insert_block(self.reward_cycle, &updated_block_info)
961+
.insert_block(&updated_block_info)
1004962
.unwrap_or_else(|_| panic!("{self}: Failed to insert block in DB"));
1005963
let process_request = updated_block_info.vote.is_some();
1006964
if !process_request {
@@ -1533,7 +1491,7 @@ impl Signer {
15331491
);
15341492
self.handle_signer_messages(stacks_client, res, messages, current_reward_cycle);
15351493
}
1536-
Some(SignerEvent::MinerMessages(blocks, messages, miner_key)) => {
1494+
Some(SignerEvent::MinerMessages(messages, miner_key)) => {
15371495
if let Some(miner_key) = miner_key {
15381496
let miner_key = PublicKey::try_from(miner_key.to_bytes_compressed().as_slice())
15391497
.expect("FATAL: could not convert from StacksPublicKey to PublicKey");
@@ -1545,13 +1503,11 @@ impl Signer {
15451503
return Ok(());
15461504
}
15471505
debug!(
1548-
"{self}: Received {} block proposals and {} messages from the miner",
1549-
blocks.len(),
1506+
"{self}: Received {} messages from the miner",
15501507
messages.len();
15511508
"miner_key" => ?miner_key,
15521509
);
15531510
self.handle_signer_messages(stacks_client, res, messages, current_reward_cycle);
1554-
self.handle_proposed_blocks(stacks_client, blocks);
15551511
}
15561512
Some(SignerEvent::StatusCheck) => {
15571513
debug!("{self}: Received a status check event.")

0 commit comments

Comments
 (0)