Skip to content

Commit

Permalink
feat: add signer message metadata to block responses
Browse files Browse the repository at this point in the history
  • Loading branch information
hstove committed Oct 18, 2024
1 parent 540fcd4 commit 5f4c42f
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 39 deletions.
10 changes: 10 additions & 0 deletions libsigner/src/libsigner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ use std::cmp::Eq;
use std::fmt::Debug;
use std::hash::Hash;

use blockstack_lib::version_string;
use clarity::codec::StacksMessageCodec;
use clarity::vm::types::QualifiedContractIdentifier;
use lazy_static::lazy_static;

pub use crate::error::{EventError, RPCError};
pub use crate::events::{
Expand All @@ -74,3 +76,11 @@ pub trait SignerMessage<T: MessageSlotID>: StacksMessageCodec {
/// The contract identifier for the message slot in stacker db
fn msg_id(&self) -> Option<T>;
}

lazy_static! {
/// The version string for the signer
pub static ref VERSION_STRING: String = {
let pkg_version = option_env!("STACKS_NODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
version_string("stacks-signer", pkg_version)
};
}
148 changes: 143 additions & 5 deletions libsigner/src/v0/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ use crate::http::{decode_http_body, decode_http_request};
use crate::stacks_common::types::PublicKey;
use crate::{
BlockProposal, EventError, MessageSlotID as MessageSlotIDTrait,
SignerMessage as SignerMessageTrait,
SignerMessage as SignerMessageTrait, VERSION_STRING,
};

define_u8_enum!(
Expand Down Expand Up @@ -615,15 +615,15 @@ impl std::fmt::Display for BlockResponse {
BlockResponse::Accepted(a) => {
write!(
f,
"BlockAccepted: signer_sighash = {}, signature = {}",
a.signer_signature_hash, a.signature
"BlockAccepted: signer_sighash = {}, signature = {}, version = {}",
a.signer_signature_hash, a.signature, a.metadata.server_version
)
}
BlockResponse::Rejected(r) => {
write!(
f,
"BlockRejected: signer_sighash = {}, code = {}, reason = {}, signature = {}",
r.reason_code, r.reason, r.signer_signature_hash, r.signature
"BlockRejected: signer_sighash = {}, code = {}, reason = {}, signature = {}, version = {}",
r.reason_code, r.reason, r.signer_signature_hash, r.signature, r.metadata.server_version
)
}
}
Expand All @@ -636,6 +636,7 @@ impl BlockResponse {
Self::Accepted(BlockAccepted {
signer_signature_hash: hash,
signature: sig,
metadata: SignerMessageMetadata::default(),
})
}

Expand Down Expand Up @@ -681,32 +682,99 @@ impl StacksMessageCodec for BlockResponse {
}
}

/// Metadata for signer messages
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SignerMessageMetadata {
/// The signer's server version
pub server_version: String,
}

/// To ensure backwards compatibility, when deserializing,
/// if no bytes are found, return empty metadata
impl StacksMessageCodec for SignerMessageMetadata {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &self.server_version.as_bytes().to_vec())?;
Ok(())
}

fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<Self, CodecError> {
match read_next::<Vec<u8>, _>(fd) {
Ok(server_version) => {
let server_version = String::from_utf8(server_version).map_err(|e| {
CodecError::DeserializeError(format!(
"Failed to decode server version: {:?}",
&e
))
})?;
Ok(Self { server_version })
}
Err(_) => {
// For backwards compatibility, return empty metadata
Ok(Self::empty())
}
}
}
}

impl Default for SignerMessageMetadata {
fn default() -> Self {
Self {
server_version: VERSION_STRING.to_string(),
}
}
}

impl SignerMessageMetadata {
/// Empty metadata
pub fn empty() -> Self {
Self {
server_version: String::new(),
}
}
}

/// A rejection response from a signer for a proposed block
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct BlockAccepted {
/// The signer signature hash of the block that was accepted
pub signer_signature_hash: Sha512Trunc256Sum,
/// The signer's signature across the acceptance
pub signature: MessageSignature,
/// Signer message metadata
pub metadata: SignerMessageMetadata,
}

impl StacksMessageCodec for BlockAccepted {
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
write_next(fd, &self.signer_signature_hash)?;
write_next(fd, &self.signature)?;
write_next(fd, &self.metadata)?;
Ok(())
}

fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<Self, CodecError> {
let signer_signature_hash = read_next::<Sha512Trunc256Sum, _>(fd)?;
let signature = read_next::<MessageSignature, _>(fd)?;
let metadata = read_next::<SignerMessageMetadata, _>(fd)?;
Ok(Self {
signer_signature_hash,
signature,
metadata,
})
}
}

impl BlockAccepted {
/// Create a new BlockAccepted for the provided block signer signature hash and signature
pub fn new(signer_signature_hash: Sha512Trunc256Sum, signature: MessageSignature) -> Self {
Self {
signer_signature_hash,
signature,
metadata: SignerMessageMetadata::default(),
}
}
}

/// A rejection response from a signer for a proposed block
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct BlockRejection {
Expand All @@ -720,6 +788,8 @@ pub struct BlockRejection {
pub signature: MessageSignature,
/// The chain id
pub chain_id: u32,
/// Signer message metadata
pub metadata: SignerMessageMetadata,
}

impl BlockRejection {
Expand All @@ -741,6 +811,7 @@ impl BlockRejection {
signer_signature_hash,
signature: MessageSignature::empty(),
chain_id,
metadata: SignerMessageMetadata::default(),
};
rejection
.sign(private_key)
Expand All @@ -765,6 +836,7 @@ impl BlockRejection {
signer_signature_hash: reject.signer_signature_hash,
chain_id,
signature: MessageSignature::empty(),
metadata: SignerMessageMetadata::default(),
};
rejection
.sign(private_key)
Expand Down Expand Up @@ -814,6 +886,7 @@ impl StacksMessageCodec for BlockRejection {
write_next(fd, &self.signer_signature_hash)?;
write_next(fd, &self.chain_id)?;
write_next(fd, &self.signature)?;
write_next(fd, &self.metadata)?;
Ok(())
}

Expand All @@ -826,12 +899,14 @@ impl StacksMessageCodec for BlockRejection {
let signer_signature_hash = read_next::<Sha512Trunc256Sum, _>(fd)?;
let chain_id = read_next::<u32, _>(fd)?;
let signature = read_next::<MessageSignature, _>(fd)?;
let metadata = read_next::<SignerMessageMetadata, _>(fd)?;
Ok(Self {
reason,
reason_code,
signer_signature_hash,
chain_id,
signature,
metadata,
})
}
}
Expand Down Expand Up @@ -988,6 +1063,7 @@ mod test {
let accepted = BlockAccepted {
signer_signature_hash: Sha512Trunc256Sum([0u8; 32]),
signature: MessageSignature::empty(),
metadata: SignerMessageMetadata::default(),
};
let response = BlockResponse::Accepted(accepted);
let serialized_response = response.serialize_to_vec();
Expand All @@ -1012,6 +1088,7 @@ mod test {
let accepted = BlockAccepted {
signer_signature_hash: Sha512Trunc256Sum([2u8; 32]),
signature: MessageSignature::empty(),
metadata: SignerMessageMetadata::default(),
};
let signer_message = SignerMessage::BlockResponse(BlockResponse::Accepted(accepted));
let serialized_signer_message = signer_message.serialize_to_vec();
Expand Down Expand Up @@ -1178,6 +1255,55 @@ mod test {
169, 171, 178, 41, 98, 13, 216, 224, 242, 37, 214, 52, 1, 227, 108, 100, 129,
127, 178, 158, 108, 5, 89, 29, 203, 233, 92, 81, 45, 243,
]),
metadata: SignerMessageMetadata::empty(),
}))
);

assert_eq!(
block_accepted,
SignerMessage::BlockResponse(BlockResponse::Accepted(BlockAccepted {
signer_signature_hash: Sha512Trunc256Sum([
17, 113, 113, 73, 103, 124, 42, 201, 125, 21, 174, 89, 84, 247, 167, 22, 241,
1, 0, 185, 203, 129, 162, 191, 39, 85, 27, 47, 46, 84, 239, 25
]),
metadata: SignerMessageMetadata::empty(),
signature: MessageSignature([
0, 28, 105, 79, 129, 52, 197, 201, 15, 47, 43, 205, 51, 14, 159, 66, 50, 4,
136, 79, 0, 27, 93, 240, 5, 15, 54, 162, 196, 255, 121, 221, 147, 82, 43, 178,
174, 57, 94, 168, 125, 228, 150, 72, 134, 68, 117, 7, 193, 131, 116, 183, 164,
110, 226, 227, 113, 233, 191, 51, 47, 7, 6, 163, 232
]),
}))
);
}

#[test]
fn test_block_response_metadata() {
let block_rejected_hex = "010100000050426c6f636b206973206e6f7420612074656e7572652d737461727420626c6f636b2c20616e642068617320616e20756e7265636f676e697a65642074656e75726520636f6e73656e7375732068617368000691f95f84b7045f7dce7757052caa986ef042cb58f7df5031a3b5b5d0e3dda63e80000000006fb349212e1a1af1a3c712878d5159b5ec14636adb6f70be00a6da4ad4f88a9934d8a9abb229620dd8e0f225d63401e36c64817fb29e6c05591dcbe95c512df30000000b48656c6c6f20776f726c64";
let block_rejected_bytes = hex_bytes(&block_rejected_hex).unwrap();
let block_accepted_hex = "010011717149677c2ac97d15ae5954f7a716f10100b9cb81a2bf27551b2f2e54ef19001c694f8134c5c90f2f2bcd330e9f423204884f001b5df0050f36a2c4ff79dd93522bb2ae395ea87de4964886447507c18374b7a46ee2e371e9bf332f0706a3e80000000b48656c6c6f20776f726c64";
let block_accepted_bytes = hex_bytes(&block_accepted_hex).unwrap();
let block_rejected = read_next::<SignerMessage, _>(&mut &block_rejected_bytes[..])
.expect("Failed to deserialize BlockRejection");
let block_accepted = read_next::<SignerMessage, _>(&mut &block_accepted_bytes[..])
.expect("Failed to deserialize BlockRejection");

assert_eq!(
block_rejected,
SignerMessage::BlockResponse(BlockResponse::Rejected(BlockRejection {
reason_code: RejectCode::ValidationFailed(ValidateRejectCode::NoSuchTenure),
reason: "Block is not a tenure-start block, and has an unrecognized tenure consensus hash".to_string(),
signer_signature_hash: Sha512Trunc256Sum([145, 249, 95, 132, 183, 4, 95, 125, 206, 119, 87, 5, 44, 170, 152, 110, 240, 66, 203, 88, 247, 223, 80, 49, 163, 181, 181, 208, 227, 221, 166, 62]),
chain_id: CHAIN_ID_TESTNET,
signature: MessageSignature([
0, 111, 179, 73, 33, 46, 26, 26, 241, 163, 199, 18, 135, 141, 81, 89, 181, 236,
20, 99, 106, 219, 111, 112, 190, 0, 166, 218, 74, 212, 248, 138, 153, 52, 216,
169, 171, 178, 41, 98, 13, 216, 224, 242, 37, 214, 52, 1, 227, 108, 100, 129,
127, 178, 158, 108, 5, 89, 29, 203, 233, 92, 81, 45, 243,
]),
metadata: SignerMessageMetadata {
server_version: "Hello world".to_string(),
},
}))
);

Expand All @@ -1188,6 +1314,9 @@ mod test {
17, 113, 113, 73, 103, 124, 42, 201, 125, 21, 174, 89, 84, 247, 167, 22, 241,
1, 0, 185, 203, 129, 162, 191, 39, 85, 27, 47, 46, 84, 239, 25
]),
metadata: SignerMessageMetadata {
server_version: "Hello world".to_string(),
},
signature: MessageSignature([
0, 28, 105, 79, 129, 52, 197, 201, 15, 47, 43, 205, 51, 14, 159, 66, 50, 4,
136, 79, 0, 27, 93, 240, 5, 15, 54, 162, 196, 255, 121, 221, 147, 82, 43, 178,
Expand All @@ -1197,4 +1326,13 @@ mod test {
}))
);
}

#[test]
fn test_empty_metadata() {
let serialized_metadata = [0u8; 0];
let deserialized_metadata =
read_next::<SignerMessageMetadata, _>(&mut &serialized_metadata[..])
.expect("Failed to deserialize SignerMessageMetadata");
assert_eq!(deserialized_metadata, SignerMessageMetadata::empty());
}
}
3 changes: 1 addition & 2 deletions stacks-signer/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use clarity::util::hash::Sha256Sum;
use clarity::util::secp256k1::MessageSignature;
use clarity::vm::types::{QualifiedContractIdentifier, TupleData};
use clarity::vm::Value;
use libsigner::VERSION_STRING;
use serde::{Deserialize, Serialize};
use stacks_common::address::{
b58, AddressHashMode, C32_ADDRESS_VERSION_MAINNET_MULTISIG,
Expand All @@ -38,8 +39,6 @@ use stacks_common::address::{
use stacks_common::define_u8_enum;
use stacks_common::types::chainstate::StacksPrivateKey;

use crate::VERSION_STRING;

extern crate alloc;

#[derive(Parser, Debug)]
Expand Down
5 changes: 4 additions & 1 deletion stacks-signer/src/client/stackerdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,9 @@ mod tests {
use blockstack_lib::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader};
use clarity::util::hash::{MerkleTree, Sha512Trunc256Sum};
use clarity::util::secp256k1::MessageSignature;
use libsigner::v0::messages::{BlockRejection, BlockResponse, RejectCode, SignerMessage};
use libsigner::v0::messages::{
BlockRejection, BlockResponse, RejectCode, SignerMessage, SignerMessageMetadata,
};
use rand::{thread_rng, RngCore};

use super::*;
Expand Down Expand Up @@ -283,6 +285,7 @@ mod tests {
signer_signature_hash: block.header.signer_signature_hash(),
chain_id: thread_rng().next_u32(),
signature: MessageSignature::empty(),
metadata: SignerMessageMetadata::empty(),
};
let signer_message = SignerMessage::BlockResponse(BlockResponse::Rejected(block_reject));
let ack = StackerDBChunkAckData {
Expand Down
12 changes: 1 addition & 11 deletions stacks-signer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,9 @@ mod tests;
use std::fmt::{Debug, Display};
use std::sync::mpsc::{channel, Receiver, Sender};

use blockstack_lib::version_string;
use chainstate::SortitionsView;
use config::GlobalConfig;
use lazy_static::lazy_static;
use libsigner::{SignerEvent, SignerEventReceiver, SignerEventTrait};
use libsigner::{SignerEvent, SignerEventReceiver, SignerEventTrait, VERSION_STRING};
use runloop::SignerResult;
use slog::{slog_info, slog_warn};
use stacks_common::{info, warn};
Expand All @@ -61,14 +59,6 @@ use crate::client::StacksClient;
use crate::config::SignerConfig;
use crate::runloop::RunLoop;

lazy_static! {
/// The version string for the signer
pub static ref VERSION_STRING: String = {
let pkg_version = option_env!("STACKS_NODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
version_string("stacks-signer", pkg_version)
};
}

/// A trait which provides a common `Signer` interface for `v0` and `v1`
pub trait Signer<T: SignerEventTrait>: Debug + Display {
/// Create a new `Signer` instance
Expand Down
3 changes: 1 addition & 2 deletions stacks-signer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use blockstack_lib::util_lib::signed_structured_data::pox4::make_pox_4_signer_ke
use clap::Parser;
use clarity::types::chainstate::StacksPublicKey;
use clarity::util::sleep_ms;
use libsigner::SignerSession;
use libsigner::{SignerSession, VERSION_STRING};
use libstackerdb::StackerDBChunkData;
use slog::{slog_debug, slog_error};
use stacks_common::util::hash::to_hex;
Expand All @@ -47,7 +47,6 @@ use stacks_signer::config::GlobalConfig;
use stacks_signer::monitor_signers::SignerMonitor;
use stacks_signer::utils::stackerdb_session;
use stacks_signer::v0::SpawnedSigner;
use stacks_signer::VERSION_STRING;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};

Expand Down
Loading

0 comments on commit 5f4c42f

Please sign in to comment.