Skip to content

Commit

Permalink
feat: push blocks to signer set
Browse files Browse the repository at this point in the history
* add /v3/blocks/upload, and relayer handling for it
* push blocks from miner to signer set, signer set pushes to their stacks-node
* configure the signer set test to rely on signers to push the block (by disabling p2p block push)
  • Loading branch information
kantai committed Jun 24, 2024
1 parent 75d5bc9 commit 4f0f716
Show file tree
Hide file tree
Showing 15 changed files with 478 additions and 136 deletions.
2 changes: 1 addition & 1 deletion libsigner/src/libsigner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,5 @@ pub trait MessageSlotID: Sized + Eq + Hash + Debug + Copy {
/// A trait for signer messages used in signer communciation
pub trait SignerMessage<T: MessageSlotID>: StacksMessageCodec {
/// The contract identifier for the message slot in stacker db
fn msg_id(&self) -> T;
fn msg_id(&self) -> Option<T>;
}
65 changes: 36 additions & 29 deletions libsigner/src/v0/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,19 @@ define_u8_enum!(
/// Enum representing the stackerdb message identifier: this is
/// the contract index in the signers contracts (i.e., X in signers-0-X)
MessageSlotID {
/// Block Proposal message from miners
BlockProposal = 0,
/// Block Response message from signers
BlockResponse = 1
});

define_u8_enum!(
/// Enum representing the slots used by the miner
MinerSlotID {
/// Block proposal from the miner
BlockProposal = 0,
/// Block pushed from the miner
BlockPushed = 1
});

impl MessageSlotIDTrait for MessageSlotID {
fn stacker_db_contract(&self, mainnet: bool, reward_cycle: u64) -> QualifiedContractIdentifier {
NakamotoSigners::make_signers_db_contract_id(reward_cycle, self.to_u32(), mainnet)
Expand All @@ -80,7 +87,7 @@ impl MessageSlotIDTrait for MessageSlotID {
}

impl SignerMessageTrait<MessageSlotID> for SignerMessage {
fn msg_id(&self) -> MessageSlotID {
fn msg_id(&self) -> Option<MessageSlotID> {
self.msg_id()
}
}
Expand All @@ -91,7 +98,9 @@ SignerMessageTypePrefix {
/// Block Proposal message from miners
BlockProposal = 0,
/// Block Response message from signers
BlockResponse = 1
BlockResponse = 1,
/// Block Pushed message from miners
BlockPushed = 2
});

#[cfg_attr(test, mutants::skip)]
Expand Down Expand Up @@ -133,67 +142,65 @@ impl From<&SignerMessage> for SignerMessageTypePrefix {
match message {
SignerMessage::BlockProposal(_) => SignerMessageTypePrefix::BlockProposal,
SignerMessage::BlockResponse(_) => SignerMessageTypePrefix::BlockResponse,
SignerMessage::BlockPushed(_) => SignerMessageTypePrefix::BlockPushed,
}
}
}

/// The messages being sent through the stacker db contracts
#[derive(Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SignerMessage {
/// The block proposal from miners for signers to observe and sign
BlockProposal(BlockProposal),
/// The block response from signers for miners to observe
BlockResponse(BlockResponse),
}

impl Debug for SignerMessage {
#[cfg_attr(test, mutants::skip)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BlockProposal(b) => Debug::fmt(b, f),
Self::BlockResponse(b) => Debug::fmt(b, f),
}
}
/// A block pushed from miners to the signers set
BlockPushed(NakamotoBlock),
}

impl SignerMessage {
/// Helper function to determine the slot ID for the provided stacker-db writer id
/// Not every message has a `MessageSlotID`: messages from the miner do not
/// broadcast over `.signers-0-X` contracts.
#[cfg_attr(test, mutants::skip)]
pub fn msg_id(&self) -> MessageSlotID {
pub fn msg_id(&self) -> Option<MessageSlotID> {
match self {
Self::BlockProposal(_) => MessageSlotID::BlockProposal,
Self::BlockResponse(_) => MessageSlotID::BlockResponse,
Self::BlockProposal(_) | Self::BlockPushed(_) => None,
Self::BlockResponse(_) => Some(MessageSlotID::BlockResponse),
}
}
}

impl StacksMessageCodec for SignerMessage {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &(SignerMessageTypePrefix::from(self) as u8))?;
SignerMessageTypePrefix::from(self)
.to_u8()
.consensus_serialize(fd)?;
match self {
SignerMessage::BlockProposal(block_proposal) => {
write_next(fd, block_proposal)?;
}
SignerMessage::BlockResponse(block_response) => {
write_next(fd, block_response)?;
}
};
SignerMessage::BlockProposal(block_proposal) => block_proposal.consensus_serialize(fd),
SignerMessage::BlockResponse(block_response) => block_response.consensus_serialize(fd),
SignerMessage::BlockPushed(block) => block.consensus_serialize(fd),
}?;
Ok(())
}

#[cfg_attr(test, mutants::skip)]
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<Self, CodecError> {
let type_prefix_byte = read_next::<u8, _>(fd)?;
let type_prefix_byte = u8::consensus_deserialize(fd)?;
let type_prefix = SignerMessageTypePrefix::try_from(type_prefix_byte)?;
let message = match type_prefix {
SignerMessageTypePrefix::BlockProposal => {
let block_proposal = read_next::<BlockProposal, _>(fd)?;
let block_proposal = StacksMessageCodec::consensus_deserialize(fd)?;
SignerMessage::BlockProposal(block_proposal)
}
SignerMessageTypePrefix::BlockResponse => {
let block_response = read_next::<BlockResponse, _>(fd)?;
let block_response = StacksMessageCodec::consensus_deserialize(fd)?;
SignerMessage::BlockResponse(block_response)
}
SignerMessageTypePrefix::BlockPushed => {
let block = StacksMessageCodec::consensus_deserialize(fd)?;
SignerMessage::BlockPushed(block)
}
};
Ok(message)
}
Expand Down
4 changes: 2 additions & 2 deletions libsigner/src/v1/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ impl MessageSlotIDTrait for MessageSlotID {
}

impl SignerMessageTrait<MessageSlotID> for SignerMessage {
fn msg_id(&self) -> MessageSlotID {
self.msg_id()
fn msg_id(&self) -> Option<MessageSlotID> {
Some(self.msg_id())
}
}

Expand Down
6 changes: 5 additions & 1 deletion stacks-signer/src/client/stackerdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ impl<M: MessageSlotID + 'static> StackerDB<M> {
&mut self,
message: T,
) -> Result<StackerDBChunkAckData, ClientError> {
let msg_id = message.msg_id();
let msg_id = message.msg_id().ok_or_else(|| {
ClientError::PutChunkRejected(
"Tried to send a SignerMessage which does not have a corresponding .signers slot identifier".into()
)
})?;
let message_bytes = message.serialize_to_vec();
self.send_message_bytes_with_retry(&msg_id, message_bytes)
}
Expand Down
19 changes: 19 additions & 0 deletions stacks-signer/src/client/stacks_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ use blockstack_lib::net::api::getinfo::RPCPeerInfoData;
use blockstack_lib::net::api::getpoxinfo::RPCPoxInfoData;
use blockstack_lib::net::api::getsortition::{SortitionInfo, RPC_SORTITION_INFO_PATH};
use blockstack_lib::net::api::getstackers::GetStackersResponse;
use blockstack_lib::net::api::postblock::StacksBlockAcceptedData;
use blockstack_lib::net::api::postblock_proposal::NakamotoBlockProposal;
use blockstack_lib::net::api::postblock_v3;
use blockstack_lib::net::api::postfeerate::{FeeRateEstimateRequestBody, RPCFeeEstimateResponse};
use blockstack_lib::util_lib::boot::{boot_code_addr, boot_code_id};
use clarity::util::hash::to_hex;
Expand Down Expand Up @@ -655,6 +657,23 @@ impl StacksClient {
Ok(unsigned_tx)
}

/// Try to post a completed nakamoto block to our connected stacks-node
/// Returns `true` if the block was accepted or `false` if the block
/// was rejected.
pub fn post_block(&self, block: &NakamotoBlock) -> Result<bool, ClientError> {
let response = self
.stacks_node_client
.post(format!("{}{}", self.http_origin, postblock_v3::PATH))
.header("Content-Type", "application/octet-stream")
.body(block.serialize_to_vec())
.send()?;
if !response.status().is_success() {
return Err(ClientError::RequestFailure(response.status()));
}
let post_block_resp = response.json::<StacksBlockAcceptedData>()?;
Ok(post_block_resp.accepted)
}

/// Helper function to submit a transaction to the Stacks mempool
pub fn submit_transaction(&self, tx: &StacksTransaction) -> Result<Txid, ClientError> {
let txid = tx.txid();
Expand Down
20 changes: 16 additions & 4 deletions stacks-signer/src/v0/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ use clarity::types::PrivateKey;
use clarity::util::hash::MerkleHashFunc;
use libsigner::v0::messages::{BlockResponse, MessageSlotID, RejectCode, SignerMessage};
use libsigner::{BlockProposal, SignerEvent};
use slog::{slog_debug, slog_error, slog_warn};
use slog::{slog_debug, slog_error, slog_info, slog_warn};
use stacks_common::types::chainstate::StacksAddress;
use stacks_common::{debug, error, warn};
use stacks_common::{debug, error, info, warn};

use crate::client::{SignerSlotID, StackerDB, StacksClient};
use crate::config::SignerConfig;
Expand Down Expand Up @@ -118,8 +118,20 @@ impl SignerTrait<SignerMessage> for Signer {
messages.len();
);
for message in messages {
if let SignerMessage::BlockProposal(block_proposal) = message {
self.handle_block_proposal(stacks_client, block_proposal);
match message {
SignerMessage::BlockProposal(block_proposal) => {
self.handle_block_proposal(stacks_client, block_proposal);
}
SignerMessage::BlockPushed(b) => {
let block_push_result = stacks_client.post_block(&b);
info!(
"{self}: Got block pushed message";
"block_id" => %b.block_id(),
"signer_sighash" => %b.header.signer_signature_hash(),
"push_result" => ?block_push_result,
);
}
_ => {}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions stackslib/src/net/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub mod gettransaction_unconfirmed;
pub mod liststackerdbreplicas;
pub mod postblock;
pub mod postblock_proposal;
#[warn(unused_imports)]
pub mod postblock_v3;
pub mod postfeerate;
pub mod postmempoolquery;
pub mod postmicroblock;
Expand Down Expand Up @@ -129,6 +131,7 @@ impl StacksHttp {
self.register_rpc_endpoint(postblock_proposal::RPCBlockProposalRequestHandler::new(
self.block_proposal_token.clone(),
));
self.register_rpc_endpoint(postblock_v3::RPCPostBlockRequestHandler::default());
self.register_rpc_endpoint(postfeerate::RPCPostFeeRateRequestHandler::new());
self.register_rpc_endpoint(postmempoolquery::RPCMempoolQueryRequestHandler::new());
self.register_rpc_endpoint(postmicroblock::RPCPostMicroblockRequestHandler::new());
Expand Down
Loading

0 comments on commit 4f0f716

Please sign in to comment.