Skip to content

Commit 5f4c42f

Browse files
committed
feat: add signer message metadata to block responses
1 parent 540fcd4 commit 5f4c42f

File tree

11 files changed

+183
-39
lines changed

11 files changed

+183
-39
lines changed

libsigner/src/libsigner.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ use std::cmp::Eq;
4949
use std::fmt::Debug;
5050
use std::hash::Hash;
5151

52+
use blockstack_lib::version_string;
5253
use clarity::codec::StacksMessageCodec;
5354
use clarity::vm::types::QualifiedContractIdentifier;
55+
use lazy_static::lazy_static;
5456

5557
pub use crate::error::{EventError, RPCError};
5658
pub use crate::events::{
@@ -74,3 +76,11 @@ pub trait SignerMessage<T: MessageSlotID>: StacksMessageCodec {
7476
/// The contract identifier for the message slot in stacker db
7577
fn msg_id(&self) -> Option<T>;
7678
}
79+
80+
lazy_static! {
81+
/// The version string for the signer
82+
pub static ref VERSION_STRING: String = {
83+
let pkg_version = option_env!("STACKS_NODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
84+
version_string("stacks-signer", pkg_version)
85+
};
86+
}

libsigner/src/v0/messages.rs

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ use crate::http::{decode_http_body, decode_http_request};
7070
use crate::stacks_common::types::PublicKey;
7171
use crate::{
7272
BlockProposal, EventError, MessageSlotID as MessageSlotIDTrait,
73-
SignerMessage as SignerMessageTrait,
73+
SignerMessage as SignerMessageTrait, VERSION_STRING,
7474
};
7575

7676
define_u8_enum!(
@@ -615,15 +615,15 @@ impl std::fmt::Display for BlockResponse {
615615
BlockResponse::Accepted(a) => {
616616
write!(
617617
f,
618-
"BlockAccepted: signer_sighash = {}, signature = {}",
619-
a.signer_signature_hash, a.signature
618+
"BlockAccepted: signer_sighash = {}, signature = {}, version = {}",
619+
a.signer_signature_hash, a.signature, a.metadata.server_version
620620
)
621621
}
622622
BlockResponse::Rejected(r) => {
623623
write!(
624624
f,
625-
"BlockRejected: signer_sighash = {}, code = {}, reason = {}, signature = {}",
626-
r.reason_code, r.reason, r.signer_signature_hash, r.signature
625+
"BlockRejected: signer_sighash = {}, code = {}, reason = {}, signature = {}, version = {}",
626+
r.reason_code, r.reason, r.signer_signature_hash, r.signature, r.metadata.server_version
627627
)
628628
}
629629
}
@@ -636,6 +636,7 @@ impl BlockResponse {
636636
Self::Accepted(BlockAccepted {
637637
signer_signature_hash: hash,
638638
signature: sig,
639+
metadata: SignerMessageMetadata::default(),
639640
})
640641
}
641642

@@ -681,32 +682,99 @@ impl StacksMessageCodec for BlockResponse {
681682
}
682683
}
683684

685+
/// Metadata for signer messages
686+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
687+
pub struct SignerMessageMetadata {
688+
/// The signer's server version
689+
pub server_version: String,
690+
}
691+
692+
/// To ensure backwards compatibility, when deserializing,
693+
/// if no bytes are found, return empty metadata
694+
impl StacksMessageCodec for SignerMessageMetadata {
695+
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
696+
write_next(fd, &self.server_version.as_bytes().to_vec())?;
697+
Ok(())
698+
}
699+
700+
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<Self, CodecError> {
701+
match read_next::<Vec<u8>, _>(fd) {
702+
Ok(server_version) => {
703+
let server_version = String::from_utf8(server_version).map_err(|e| {
704+
CodecError::DeserializeError(format!(
705+
"Failed to decode server version: {:?}",
706+
&e
707+
))
708+
})?;
709+
Ok(Self { server_version })
710+
}
711+
Err(_) => {
712+
// For backwards compatibility, return empty metadata
713+
Ok(Self::empty())
714+
}
715+
}
716+
}
717+
}
718+
719+
impl Default for SignerMessageMetadata {
720+
fn default() -> Self {
721+
Self {
722+
server_version: VERSION_STRING.to_string(),
723+
}
724+
}
725+
}
726+
727+
impl SignerMessageMetadata {
728+
/// Empty metadata
729+
pub fn empty() -> Self {
730+
Self {
731+
server_version: String::new(),
732+
}
733+
}
734+
}
735+
684736
/// A rejection response from a signer for a proposed block
685737
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
686738
pub struct BlockAccepted {
687739
/// The signer signature hash of the block that was accepted
688740
pub signer_signature_hash: Sha512Trunc256Sum,
689741
/// The signer's signature across the acceptance
690742
pub signature: MessageSignature,
743+
/// Signer message metadata
744+
pub metadata: SignerMessageMetadata,
691745
}
692746

693747
impl StacksMessageCodec for BlockAccepted {
694748
fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), CodecError> {
695749
write_next(fd, &self.signer_signature_hash)?;
696750
write_next(fd, &self.signature)?;
751+
write_next(fd, &self.metadata)?;
697752
Ok(())
698753
}
699754

700755
fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<Self, CodecError> {
701756
let signer_signature_hash = read_next::<Sha512Trunc256Sum, _>(fd)?;
702757
let signature = read_next::<MessageSignature, _>(fd)?;
758+
let metadata = read_next::<SignerMessageMetadata, _>(fd)?;
703759
Ok(Self {
704760
signer_signature_hash,
705761
signature,
762+
metadata,
706763
})
707764
}
708765
}
709766

767+
impl BlockAccepted {
768+
/// Create a new BlockAccepted for the provided block signer signature hash and signature
769+
pub fn new(signer_signature_hash: Sha512Trunc256Sum, signature: MessageSignature) -> Self {
770+
Self {
771+
signer_signature_hash,
772+
signature,
773+
metadata: SignerMessageMetadata::default(),
774+
}
775+
}
776+
}
777+
710778
/// A rejection response from a signer for a proposed block
711779
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
712780
pub struct BlockRejection {
@@ -720,6 +788,8 @@ pub struct BlockRejection {
720788
pub signature: MessageSignature,
721789
/// The chain id
722790
pub chain_id: u32,
791+
/// Signer message metadata
792+
pub metadata: SignerMessageMetadata,
723793
}
724794

725795
impl BlockRejection {
@@ -741,6 +811,7 @@ impl BlockRejection {
741811
signer_signature_hash,
742812
signature: MessageSignature::empty(),
743813
chain_id,
814+
metadata: SignerMessageMetadata::default(),
744815
};
745816
rejection
746817
.sign(private_key)
@@ -765,6 +836,7 @@ impl BlockRejection {
765836
signer_signature_hash: reject.signer_signature_hash,
766837
chain_id,
767838
signature: MessageSignature::empty(),
839+
metadata: SignerMessageMetadata::default(),
768840
};
769841
rejection
770842
.sign(private_key)
@@ -814,6 +886,7 @@ impl StacksMessageCodec for BlockRejection {
814886
write_next(fd, &self.signer_signature_hash)?;
815887
write_next(fd, &self.chain_id)?;
816888
write_next(fd, &self.signature)?;
889+
write_next(fd, &self.metadata)?;
817890
Ok(())
818891
}
819892

@@ -826,12 +899,14 @@ impl StacksMessageCodec for BlockRejection {
826899
let signer_signature_hash = read_next::<Sha512Trunc256Sum, _>(fd)?;
827900
let chain_id = read_next::<u32, _>(fd)?;
828901
let signature = read_next::<MessageSignature, _>(fd)?;
902+
let metadata = read_next::<SignerMessageMetadata, _>(fd)?;
829903
Ok(Self {
830904
reason,
831905
reason_code,
832906
signer_signature_hash,
833907
chain_id,
834908
signature,
909+
metadata,
835910
})
836911
}
837912
}
@@ -988,6 +1063,7 @@ mod test {
9881063
let accepted = BlockAccepted {
9891064
signer_signature_hash: Sha512Trunc256Sum([0u8; 32]),
9901065
signature: MessageSignature::empty(),
1066+
metadata: SignerMessageMetadata::default(),
9911067
};
9921068
let response = BlockResponse::Accepted(accepted);
9931069
let serialized_response = response.serialize_to_vec();
@@ -1012,6 +1088,7 @@ mod test {
10121088
let accepted = BlockAccepted {
10131089
signer_signature_hash: Sha512Trunc256Sum([2u8; 32]),
10141090
signature: MessageSignature::empty(),
1091+
metadata: SignerMessageMetadata::default(),
10151092
};
10161093
let signer_message = SignerMessage::BlockResponse(BlockResponse::Accepted(accepted));
10171094
let serialized_signer_message = signer_message.serialize_to_vec();
@@ -1178,6 +1255,55 @@ mod test {
11781255
169, 171, 178, 41, 98, 13, 216, 224, 242, 37, 214, 52, 1, 227, 108, 100, 129,
11791256
127, 178, 158, 108, 5, 89, 29, 203, 233, 92, 81, 45, 243,
11801257
]),
1258+
metadata: SignerMessageMetadata::empty(),
1259+
}))
1260+
);
1261+
1262+
assert_eq!(
1263+
block_accepted,
1264+
SignerMessage::BlockResponse(BlockResponse::Accepted(BlockAccepted {
1265+
signer_signature_hash: Sha512Trunc256Sum([
1266+
17, 113, 113, 73, 103, 124, 42, 201, 125, 21, 174, 89, 84, 247, 167, 22, 241,
1267+
1, 0, 185, 203, 129, 162, 191, 39, 85, 27, 47, 46, 84, 239, 25
1268+
]),
1269+
metadata: SignerMessageMetadata::empty(),
1270+
signature: MessageSignature([
1271+
0, 28, 105, 79, 129, 52, 197, 201, 15, 47, 43, 205, 51, 14, 159, 66, 50, 4,
1272+
136, 79, 0, 27, 93, 240, 5, 15, 54, 162, 196, 255, 121, 221, 147, 82, 43, 178,
1273+
174, 57, 94, 168, 125, 228, 150, 72, 134, 68, 117, 7, 193, 131, 116, 183, 164,
1274+
110, 226, 227, 113, 233, 191, 51, 47, 7, 6, 163, 232
1275+
]),
1276+
}))
1277+
);
1278+
}
1279+
1280+
#[test]
1281+
fn test_block_response_metadata() {
1282+
let block_rejected_hex = "010100000050426c6f636b206973206e6f7420612074656e7572652d737461727420626c6f636b2c20616e642068617320616e20756e7265636f676e697a65642074656e75726520636f6e73656e7375732068617368000691f95f84b7045f7dce7757052caa986ef042cb58f7df5031a3b5b5d0e3dda63e80000000006fb349212e1a1af1a3c712878d5159b5ec14636adb6f70be00a6da4ad4f88a9934d8a9abb229620dd8e0f225d63401e36c64817fb29e6c05591dcbe95c512df30000000b48656c6c6f20776f726c64";
1283+
let block_rejected_bytes = hex_bytes(&block_rejected_hex).unwrap();
1284+
let block_accepted_hex = "010011717149677c2ac97d15ae5954f7a716f10100b9cb81a2bf27551b2f2e54ef19001c694f8134c5c90f2f2bcd330e9f423204884f001b5df0050f36a2c4ff79dd93522bb2ae395ea87de4964886447507c18374b7a46ee2e371e9bf332f0706a3e80000000b48656c6c6f20776f726c64";
1285+
let block_accepted_bytes = hex_bytes(&block_accepted_hex).unwrap();
1286+
let block_rejected = read_next::<SignerMessage, _>(&mut &block_rejected_bytes[..])
1287+
.expect("Failed to deserialize BlockRejection");
1288+
let block_accepted = read_next::<SignerMessage, _>(&mut &block_accepted_bytes[..])
1289+
.expect("Failed to deserialize BlockRejection");
1290+
1291+
assert_eq!(
1292+
block_rejected,
1293+
SignerMessage::BlockResponse(BlockResponse::Rejected(BlockRejection {
1294+
reason_code: RejectCode::ValidationFailed(ValidateRejectCode::NoSuchTenure),
1295+
reason: "Block is not a tenure-start block, and has an unrecognized tenure consensus hash".to_string(),
1296+
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]),
1297+
chain_id: CHAIN_ID_TESTNET,
1298+
signature: MessageSignature([
1299+
0, 111, 179, 73, 33, 46, 26, 26, 241, 163, 199, 18, 135, 141, 81, 89, 181, 236,
1300+
20, 99, 106, 219, 111, 112, 190, 0, 166, 218, 74, 212, 248, 138, 153, 52, 216,
1301+
169, 171, 178, 41, 98, 13, 216, 224, 242, 37, 214, 52, 1, 227, 108, 100, 129,
1302+
127, 178, 158, 108, 5, 89, 29, 203, 233, 92, 81, 45, 243,
1303+
]),
1304+
metadata: SignerMessageMetadata {
1305+
server_version: "Hello world".to_string(),
1306+
},
11811307
}))
11821308
);
11831309

@@ -1188,6 +1314,9 @@ mod test {
11881314
17, 113, 113, 73, 103, 124, 42, 201, 125, 21, 174, 89, 84, 247, 167, 22, 241,
11891315
1, 0, 185, 203, 129, 162, 191, 39, 85, 27, 47, 46, 84, 239, 25
11901316
]),
1317+
metadata: SignerMessageMetadata {
1318+
server_version: "Hello world".to_string(),
1319+
},
11911320
signature: MessageSignature([
11921321
0, 28, 105, 79, 129, 52, 197, 201, 15, 47, 43, 205, 51, 14, 159, 66, 50, 4,
11931322
136, 79, 0, 27, 93, 240, 5, 15, 54, 162, 196, 255, 121, 221, 147, 82, 43, 178,
@@ -1197,4 +1326,13 @@ mod test {
11971326
}))
11981327
);
11991328
}
1329+
1330+
#[test]
1331+
fn test_empty_metadata() {
1332+
let serialized_metadata = [0u8; 0];
1333+
let deserialized_metadata =
1334+
read_next::<SignerMessageMetadata, _>(&mut &serialized_metadata[..])
1335+
.expect("Failed to deserialize SignerMessageMetadata");
1336+
assert_eq!(deserialized_metadata, SignerMessageMetadata::empty());
1337+
}
12001338
}

stacks-signer/src/cli.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use clarity::util::hash::Sha256Sum;
2929
use clarity::util::secp256k1::MessageSignature;
3030
use clarity::vm::types::{QualifiedContractIdentifier, TupleData};
3131
use clarity::vm::Value;
32+
use libsigner::VERSION_STRING;
3233
use serde::{Deserialize, Serialize};
3334
use stacks_common::address::{
3435
b58, AddressHashMode, C32_ADDRESS_VERSION_MAINNET_MULTISIG,
@@ -38,8 +39,6 @@ use stacks_common::address::{
3839
use stacks_common::define_u8_enum;
3940
use stacks_common::types::chainstate::StacksPrivateKey;
4041

41-
use crate::VERSION_STRING;
42-
4342
extern crate alloc;
4443

4544
#[derive(Parser, Debug)]

stacks-signer/src/client/stackerdb.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,9 @@ mod tests {
235235
use blockstack_lib::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader};
236236
use clarity::util::hash::{MerkleTree, Sha512Trunc256Sum};
237237
use clarity::util::secp256k1::MessageSignature;
238-
use libsigner::v0::messages::{BlockRejection, BlockResponse, RejectCode, SignerMessage};
238+
use libsigner::v0::messages::{
239+
BlockRejection, BlockResponse, RejectCode, SignerMessage, SignerMessageMetadata,
240+
};
239241
use rand::{thread_rng, RngCore};
240242

241243
use super::*;
@@ -283,6 +285,7 @@ mod tests {
283285
signer_signature_hash: block.header.signer_signature_hash(),
284286
chain_id: thread_rng().next_u32(),
285287
signature: MessageSignature::empty(),
288+
metadata: SignerMessageMetadata::empty(),
286289
};
287290
let signer_message = SignerMessage::BlockResponse(BlockResponse::Rejected(block_reject));
288291
let ack = StackerDBChunkAckData {

stacks-signer/src/lib.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,9 @@ mod tests;
4848
use std::fmt::{Debug, Display};
4949
use std::sync::mpsc::{channel, Receiver, Sender};
5050

51-
use blockstack_lib::version_string;
5251
use chainstate::SortitionsView;
5352
use config::GlobalConfig;
54-
use lazy_static::lazy_static;
55-
use libsigner::{SignerEvent, SignerEventReceiver, SignerEventTrait};
53+
use libsigner::{SignerEvent, SignerEventReceiver, SignerEventTrait, VERSION_STRING};
5654
use runloop::SignerResult;
5755
use slog::{slog_info, slog_warn};
5856
use stacks_common::{info, warn};
@@ -61,14 +59,6 @@ use crate::client::StacksClient;
6159
use crate::config::SignerConfig;
6260
use crate::runloop::RunLoop;
6361

64-
lazy_static! {
65-
/// The version string for the signer
66-
pub static ref VERSION_STRING: String = {
67-
let pkg_version = option_env!("STACKS_NODE_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
68-
version_string("stacks-signer", pkg_version)
69-
};
70-
}
71-
7262
/// A trait which provides a common `Signer` interface for `v0` and `v1`
7363
pub trait Signer<T: SignerEventTrait>: Debug + Display {
7464
/// Create a new `Signer` instance

stacks-signer/src/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use blockstack_lib::util_lib::signed_structured_data::pox4::make_pox_4_signer_ke
3232
use clap::Parser;
3333
use clarity::types::chainstate::StacksPublicKey;
3434
use clarity::util::sleep_ms;
35-
use libsigner::SignerSession;
35+
use libsigner::{SignerSession, VERSION_STRING};
3636
use libstackerdb::StackerDBChunkData;
3737
use slog::{slog_debug, slog_error};
3838
use stacks_common::util::hash::to_hex;
@@ -47,7 +47,6 @@ use stacks_signer::config::GlobalConfig;
4747
use stacks_signer::monitor_signers::SignerMonitor;
4848
use stacks_signer::utils::stackerdb_session;
4949
use stacks_signer::v0::SpawnedSigner;
50-
use stacks_signer::VERSION_STRING;
5150
use tracing_subscriber::prelude::*;
5251
use tracing_subscriber::{fmt, EnvFilter};
5352

0 commit comments

Comments
 (0)