diff --git a/stacks-signer/src/client/stackerdb.rs b/stacks-signer/src/client/stackerdb.rs index 62d2e80ed47..ed97adcc506 100644 --- a/stacks-signer/src/client/stackerdb.rs +++ b/stacks-signer/src/client/stackerdb.rs @@ -65,6 +65,7 @@ pub enum StackerDBMessage { // TODO: update this to use a struct that lists optional error code if the block is invalid // to prove that the signers have considered the block but rejected it. This should include // hints about how to fix the block + // Update to use NakamotoBlockProposal. Depends on https://github.com/stacks-network/stacks-core/pull/4084 Block(NakamotoBlock), /// DKG and Signing round data for other signers to observe Packet(Packet), diff --git a/stacks-signer/src/client/stacks_client.rs b/stacks-signer/src/client/stacks_client.rs index e1fbbb61cda..d3fb3c570fd 100644 --- a/stacks-signer/src/client/stacks_client.rs +++ b/stacks-signer/src/client/stacks_client.rs @@ -1,4 +1,5 @@ use blockstack_lib::burnchains::Txid; +use blockstack_lib::chainstate::nakamoto::NakamotoBlock; use blockstack_lib::chainstate::stacks::{ StacksTransaction, StacksTransactionSigner, TransactionAnchorMode, TransactionAuth, TransactionContractCall, TransactionPayload, TransactionPostConditionMode, @@ -50,6 +51,28 @@ impl From<&Config> for StacksClient { } impl StacksClient { + /// Retrieve the current miner public key + pub fn get_miner_public_key(&self) -> Result { + // TODO: Depends on https://github.com/stacks-network/stacks-core/issues/4018 + todo!("Get the miner public key from the stacks node to verify the miner blocks were signed by the correct miner"); + } + + /// Check if the proposed Nakamoto block is a valid block + pub fn is_valid_nakamoto_block(&self, _block: &NakamotoBlock) -> Result { + // TODO: Depends on https://github.com/stacks-network/stacks-core/issues/3866 + let send_request = || { + self.stacks_node_client + .get(self.block_proposal_path()) + .send() + .map_err(backoff::Error::transient) + }; + let response = retry_with_exponential_backoff(send_request)?; + if !response.status().is_success() { + return Err(ClientError::RequestFailure(response.status())); + } + todo!("Call the appropriate RPC endpoint to check if the proposed Nakamoto block is valid"); + } + /// Retrieve the current DKG aggregate public key pub fn get_aggregate_public_key(&self) -> Result, ClientError> { let reward_cycle = self.get_current_reward_cycle()?; @@ -300,6 +323,10 @@ impl StacksClient { self.http_origin ) } + + fn block_proposal_path(&self) -> String { + format!("{}/v2/block-proposal", self.http_origin) + } } #[cfg(test)] diff --git a/stacks-signer/src/runloop.rs b/stacks-signer/src/runloop.rs index 88c3c04f594..0f86966b34c 100644 --- a/stacks-signer/src/runloop.rs +++ b/stacks-signer/src/runloop.rs @@ -190,6 +190,27 @@ impl RunLoop { // Received a block proposal from the miner. // If the signer is the coordinator, then trigger a Signing round for the block if coordinator_id == self.signing_round.signer_id { + // Don't bother triggering a signing round for the block if it is invalid + if !self.stacks_client.is_valid_nakamoto_block(&block).unwrap_or_else(|e| { + warn!("Failed to validate block: {:?}", e); + false + }) { + warn!("Received an invalid block proposal from the miner. Ignoring block proposal: {:?}", block); + return; + } + + // TODO: dependent on https://github.com/stacks-network/stacks-core/issues/4018 + // let miner_public_key = self.stacks_client.get_miner_public_key().expect("Failed to get miner public key. Cannot verify blocks."); + // let Some(block_miner_public_key) = block.header.recover_miner_pk() else { + // warn!("Failed to recover miner public key from block. Ignoring block proposal: {:?}", block); + // return; + // }; + // if block_miner_public_key != miner_public_key { + // warn!("Received a block proposal signed with an invalid miner public key. Ignoring block proposal: {:?}.", block); + // return; + // } + + // This is a block proposal from the miner. Trigger a signing round for it. self.commands.push_back(RunLoopCommand::Sign { message: block.serialize_to_vec(), is_taproot: false, @@ -236,6 +257,7 @@ impl RunLoop { }) .collect(); // First process all messages as a signer + // TODO: deserialize the packet into a block and verify its contents let mut outbound_messages = self .signing_round .process_inbound_messages(&inbound_messages)