mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-01-12 16:53:21 +08:00
feat: use block proposal struct for miner -> signers comms. check claimed reward-cycle
This commit is contained in:
@@ -50,11 +50,22 @@ use wsts::state_machine::signer;
|
||||
use crate::http::{decode_http_body, decode_http_request};
|
||||
use crate::{EventError, SignerMessage};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
/// BlockProposal sent to signers
|
||||
pub struct BlockProposalSigners {
|
||||
/// The block itself
|
||||
pub block: NakamotoBlock,
|
||||
/// The burn height the block is mined during
|
||||
pub burn_height: u64,
|
||||
/// The reward cycle the block is mined during
|
||||
pub reward_cycle: u64,
|
||||
}
|
||||
|
||||
/// Event enum for newly-arrived signer subscribed events
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum SignerEvent {
|
||||
/// The miner proposed blocks for signers to observe and sign
|
||||
ProposedBlocks(Vec<NakamotoBlock>),
|
||||
ProposedBlocks(Vec<BlockProposalSigners>),
|
||||
/// The signer messages for other signers and miners to observe
|
||||
/// The u32 is the signer set to which the message belongs (either 0 or 1)
|
||||
SignerMessages(u32, Vec<SignerMessage>),
|
||||
@@ -64,6 +75,26 @@ pub enum SignerEvent {
|
||||
StatusCheck,
|
||||
}
|
||||
|
||||
impl StacksMessageCodec for BlockProposalSigners {
|
||||
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
|
||||
self.block.consensus_serialize(fd)?;
|
||||
self.burn_height.consensus_serialize(fd)?;
|
||||
self.reward_cycle.consensus_serialize(fd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<Self, CodecError> {
|
||||
let block = NakamotoBlock::consensus_deserialize(fd)?;
|
||||
let burn_height = u64::consensus_deserialize(fd)?;
|
||||
let reward_cycle = u64::consensus_deserialize(fd)?;
|
||||
Ok(BlockProposalSigners {
|
||||
block,
|
||||
burn_height,
|
||||
reward_cycle,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to implement a stop-signaler for the event receiver thread.
|
||||
/// The caller calls `send()` and the event receiver loop (which lives in a separate thread) will
|
||||
/// terminate.
|
||||
@@ -337,10 +368,10 @@ fn process_stackerdb_event(
|
||||
.map_err(|e| EventError::Deserialize(format!("Could not decode body to JSON: {:?}", &e)))?;
|
||||
|
||||
let signer_event = if event.contract_id == boot_code_id(MINERS_NAME, is_mainnet) {
|
||||
let blocks: Vec<NakamotoBlock> = event
|
||||
let blocks: Vec<BlockProposalSigners> = event
|
||||
.modified_slots
|
||||
.iter()
|
||||
.filter_map(|chunk| read_next::<NakamotoBlock, _>(&mut &chunk.data[..]).ok())
|
||||
.filter_map(|chunk| read_next::<BlockProposalSigners, _>(&mut &chunk.data[..]).ok())
|
||||
.collect();
|
||||
SignerEvent::ProposedBlocks(blocks)
|
||||
} else if event.contract_id.name.to_string().starts_with(SIGNERS_NAME)
|
||||
|
||||
@@ -45,7 +45,8 @@ mod session;
|
||||
|
||||
pub use crate::error::{EventError, RPCError};
|
||||
pub use crate::events::{
|
||||
EventReceiver, EventStopSignaler, SignerEvent, SignerEventReceiver, SignerStopSignaler,
|
||||
BlockProposalSigners, EventReceiver, EventStopSignaler, SignerEvent, SignerEventReceiver,
|
||||
SignerStopSignaler,
|
||||
};
|
||||
pub use crate::messages::{
|
||||
BlockRejection, BlockResponse, RejectCode, SignerMessage, BLOCK_MSG_ID, TRANSACTIONS_MSG_ID,
|
||||
|
||||
@@ -24,7 +24,9 @@ use blockstack_lib::chainstate::stacks::boot::SIGNERS_VOTING_FUNCTION_NAME;
|
||||
use blockstack_lib::chainstate::stacks::StacksTransaction;
|
||||
use blockstack_lib::net::api::postblock_proposal::BlockValidateResponse;
|
||||
use hashbrown::HashSet;
|
||||
use libsigner::{BlockRejection, BlockResponse, RejectCode, SignerEvent, SignerMessage};
|
||||
use libsigner::{
|
||||
BlockProposalSigners, BlockRejection, BlockResponse, RejectCode, SignerEvent, SignerMessage,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use slog::{slog_debug, slog_error, slog_info, slog_warn};
|
||||
use stacks_common::codec::{read_next, StacksMessageCodec};
|
||||
@@ -497,17 +499,30 @@ impl Signer {
|
||||
}
|
||||
|
||||
/// Handle proposed blocks submitted by the miners to stackerdb
|
||||
fn handle_proposed_blocks(&mut self, stacks_client: &StacksClient, blocks: &[NakamotoBlock]) {
|
||||
for block in blocks {
|
||||
fn handle_proposed_blocks(
|
||||
&mut self,
|
||||
stacks_client: &StacksClient,
|
||||
proposals: &[BlockProposalSigners],
|
||||
) {
|
||||
for proposal in proposals {
|
||||
if proposal.reward_cycle != self.reward_cycle {
|
||||
debug!(
|
||||
"Signer #{}: Received proposal for block outside of my reward cycle, ignoring.",
|
||||
self.signer_id;
|
||||
"proposal_reward_cycle" => proposal.reward_cycle,
|
||||
"proposal_burn_height" => proposal.burn_height,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// Store the block in our cache
|
||||
self.signer_db
|
||||
.insert_block(&BlockInfo::new(block.clone()))
|
||||
.insert_block(&BlockInfo::new(proposal.block.clone()))
|
||||
.unwrap_or_else(|e| {
|
||||
error!("{self}: Failed to insert block in DB: {e:?}");
|
||||
});
|
||||
// Submit the block for validation
|
||||
stacks_client
|
||||
.submit_block_for_validation(block.clone())
|
||||
.submit_block_for_validation(proposal.block.clone())
|
||||
.unwrap_or_else(|e| {
|
||||
warn!("{self}: Failed to submit block for validation: {e:?}");
|
||||
});
|
||||
|
||||
@@ -524,11 +524,11 @@ impl NakamotoBlockBuilder {
|
||||
/// Returns Some(chunk) if the given key corresponds to one of the expected miner slots
|
||||
/// Returns None if not
|
||||
/// Returns an error on signing or DB error
|
||||
pub fn make_stackerdb_block_proposal(
|
||||
pub fn make_stackerdb_block_proposal<T: StacksMessageCodec>(
|
||||
sortdb: &SortitionDB,
|
||||
tip: &BlockSnapshot,
|
||||
stackerdbs: &StackerDBs,
|
||||
block: &NakamotoBlock,
|
||||
block: &T,
|
||||
miner_privkey: &StacksPrivateKey,
|
||||
miners_contract_id: &QualifiedContractIdentifier,
|
||||
) -> Result<Option<StackerDBChunkData>, Error> {
|
||||
|
||||
@@ -23,8 +23,8 @@ use clarity::vm::clarity::ClarityConnection;
|
||||
use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier};
|
||||
use hashbrown::HashSet;
|
||||
use libsigner::{
|
||||
BlockResponse, RejectCode, SignerMessage, SignerSession, StackerDBSession, BLOCK_MSG_ID,
|
||||
TRANSACTIONS_MSG_ID,
|
||||
BlockProposalSigners, BlockResponse, RejectCode, SignerMessage, SignerSession,
|
||||
StackerDBSession, BLOCK_MSG_ID, TRANSACTIONS_MSG_ID,
|
||||
};
|
||||
use stacks::burnchains::{Burnchain, BurnchainParameters};
|
||||
use stacks::chainstate::burn::db::sortdb::SortitionDB;
|
||||
@@ -199,37 +199,53 @@ impl BlockMinerThread {
|
||||
.expect("FATAL: could not open sortition DB");
|
||||
let tip = SortitionDB::get_canonical_burn_chain_tip(sort_db.conn())
|
||||
.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");
|
||||
if let Some(new_block) = new_block {
|
||||
match NakamotoBlockBuilder::make_stackerdb_block_proposal(
|
||||
let proposal_msg = BlockProposalSigners {
|
||||
block: new_block.clone(),
|
||||
burn_height: self.burn_block.block_height,
|
||||
reward_cycle,
|
||||
};
|
||||
let proposal = match NakamotoBlockBuilder::make_stackerdb_block_proposal(
|
||||
&sort_db,
|
||||
&tip,
|
||||
&stackerdbs,
|
||||
&new_block,
|
||||
&proposal_msg,
|
||||
&miner_privkey,
|
||||
&miners_contract_id,
|
||||
) {
|
||||
Ok(Some(chunk)) => {
|
||||
// Propose the block to the observing signers through the .miners stackerdb instance
|
||||
let miner_contract_id = boot_code_id(MINERS_NAME, self.config.is_mainnet());
|
||||
let mut miners_stackerdb =
|
||||
StackerDBSession::new(&self.config.node.rpc_bind, miner_contract_id);
|
||||
match miners_stackerdb.put_chunk(&chunk) {
|
||||
Ok(ack) => {
|
||||
info!("Proposed block to stackerdb: {ack:?}");
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to propose block to stackerdb {e:?}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(chunk)) => chunk,
|
||||
Ok(None) => {
|
||||
warn!("Failed to propose block to stackerdb: no slot available");
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to propose block to stackerdb: {e:?}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Propose the block to the observing signers through the .miners stackerdb instance
|
||||
let miner_contract_id = boot_code_id(MINERS_NAME, self.config.is_mainnet());
|
||||
let mut miners_stackerdb =
|
||||
StackerDBSession::new(&self.config.node.rpc_bind, miner_contract_id);
|
||||
match miners_stackerdb.put_chunk(&proposal) {
|
||||
Ok(ack) => {
|
||||
info!("Proposed block to stackerdb: {ack:?}");
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to propose block to stackerdb {e:?}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.globals.counters.bump_naka_proposed_blocks();
|
||||
|
||||
if let Err(e) =
|
||||
|
||||
Reference in New Issue
Block a user