Skip to content

Commit

Permalink
feat: include recovered signer pubkeys in new block payload (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
zone117x authored Oct 24, 2024
1 parent cc93873 commit b5ad4ba
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ pub fn create_stacks_new_block(
tenure_height: Some(1122),
signer_bitvec: Some("000800000001ff".to_owned()),
signer_signature: Some(vec!["1234".to_owned(), "2345".to_owned()]),
signer_signature_hash: None,
cycle_number: Some(1),
reward_set: Some(RewardSet {
pox_ustx_threshold: "50000".to_owned(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -108,7 +109,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -195,7 +197,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -283,7 +286,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -370,7 +374,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -459,7 +464,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -547,7 +553,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -635,7 +642,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -724,7 +732,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -812,7 +821,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -900,7 +910,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -988,7 +999,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -1076,7 +1088,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down Expand Up @@ -1174,7 +1187,8 @@
"cycle_number": null,
"reward_set": null,
"signer_bitvec": null,
"signer_signature": null
"signer_signature": null,
"signer_public_keys": null
},
"parent_block_identifier": {
"hash": "0x",
Expand Down
54 changes: 54 additions & 0 deletions components/chainhook-sdk/src/indexer/stacks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ pub struct NewBlock {
#[serde(skip_serializing_if = "Option::is_none")]
pub signer_bitvec: Option<String>,

#[serde(skip_serializing_if = "Option::is_none")]
pub signer_signature_hash: Option<String>,

#[serde(skip_serializing_if = "Option::is_none")]
pub signer_signature: Option<Vec<String>>,

Expand Down Expand Up @@ -472,6 +475,13 @@ pub fn standardize_stacks_block(
})
};

let signer_sig_hash = block
.signer_signature_hash
.as_ref()
.map(|hash| {
hex::decode(&hash[2..]).expect("unable to decode signer_signature hex")
});

let block = StacksBlockData {
block_identifier: BlockIdentifier {
hash: block.index_block_hash.clone(),
Expand Down Expand Up @@ -502,6 +512,20 @@ pub fn standardize_stacks_block(
signer_bitvec: block.signer_bitvec.clone(),
signer_signature: block.signer_signature.clone(),

signer_public_keys: match (signer_sig_hash, &block.signer_signature) {
(Some(signer_sig_hash), Some(signatures)) => {
Some(signatures.iter().map(|sig_hex| {
let sig_msg = clarity::util::secp256k1::MessageSignature::from_hex(sig_hex)
.map_err(|e| format!("unable to parse signer signature message: {}", e))?;
let pubkey = get_signer_pubkey_from_message_hash(&signer_sig_hash, &sig_msg)
.map_err(|e| format!("unable to recover signer sig pubkey: {}", e))?;
Ok(format!("0x{}", hex::encode(pubkey)))
})
.collect::<Result<Vec<_>, String>>()?)
}
_ => None,
},

cycle_number: block.cycle_number,
reward_set: block.reward_set.as_ref().and_then(|r| {
Some(StacksBlockMetadataRewardSet {
Expand Down Expand Up @@ -848,6 +872,36 @@ fn get_nakamoto_index_block_hash(
Ok(format!("0x{}", hex::encode(hash)))
}

pub fn get_signer_pubkey_from_message_hash(
message_hash: &Vec<u8>,
signature: &clarity::util::secp256k1::MessageSignature,
) -> Result<[u8; 33], String> {
use miniscript::bitcoin::{
key::Secp256k1,
secp256k1::{
ecdsa::{RecoverableSignature, RecoveryId},
Message,
},
};

let (first, sig) = signature.0.split_at(1);
let rec_id = first[0];

let secp = Secp256k1::new();
let recovery_id =
RecoveryId::from_i32(rec_id as i32).map_err(|e| format!("invalid recovery id: {e}"))?;
let signature = RecoverableSignature::from_compact(&sig, recovery_id)
.map_err(|e| format!("invalid signature: {e}"))?;
let message =
Message::from_digest_slice(&message_hash).map_err(|e| format!("invalid digest message: {e}"))?;

let pubkey = secp
.recover_ecdsa(&message, &signature)
.map_err(|e| format!("unable to recover pubkey: {e}"))?;

Ok(pubkey.serialize())
}

#[cfg(feature = "stacks-signers")]
pub fn get_signer_pubkey_from_stackerdb_chunk_slot(
slot: &NewSignerModifiedSlot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub fn generate_test_stacks_block(
tenure_height: Some(1122),
signer_bitvec: Some("1010101010101".to_owned()),
signer_signature: Some(vec!["1234".to_owned(), "2345".to_owned()]),
signer_public_keys: Some(vec!["12".to_owned(), "23".to_owned()]),
cycle_number: Some(1),
reward_set: Some(StacksBlockMetadataRewardSet {
pox_ustx_threshold: "50000".to_owned(),
Expand Down
1 change: 1 addition & 0 deletions components/chainhook-types-js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ export interface StacksBlockMetadata {
tenure_height?: number | null;
signer_bitvec?: string | null;
signer_signature?: string[] | null;
signer_public_keys?: string[] | null;
cycle_number?: number | null;
reward_set?: {
pox_ustx_threshold: string;
Expand Down
1 change: 1 addition & 0 deletions components/chainhook-types-rs/src/rosetta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ pub struct StacksBlockMetadata {
pub block_time: Option<u64>,
pub signer_bitvec: Option<String>,
pub signer_signature: Option<Vec<String>>,
pub signer_public_keys: Option<Vec<String>>,

// Available starting in epoch3, only included in blocks where the pox cycle rewards are first calculated
pub cycle_number: Option<u64>,
Expand Down
1 change: 1 addition & 0 deletions components/client/typescript/src/schemas/stacks/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const StacksEventMetadataSchema = Type.Object({
block_time: Nullable(Type.Integer()),
signer_bitvec: Nullable(Type.String()),
signer_signature: Nullable(Type.Array(Type.String())),
signer_public_keys: Nullable(Type.Array(Type.String())),

// Available starting in epoch3, only included in blocks where the pox cycle rewards are first calculated
cycle_number: Nullable(Type.Integer()),
Expand Down

0 comments on commit b5ad4ba

Please sign in to comment.