diff --git a/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs b/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs index a13198e36..7cc7ff22f 100644 --- a/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs +++ b/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs @@ -1,7 +1,7 @@ use crate::scan::stacks::{Record, RecordKind}; use crate::service::tests::helpers::mock_bitcoin_rpc::TipData; use chainhook_sdk::indexer::bitcoin::NewBitcoinBlock; -use chainhook_sdk::indexer::stacks::{NewBlock, NewEvent, NewTransaction}; +use chainhook_sdk::indexer::stacks::{NewBlock, NewEvent, NewTransaction, RewardSet, RewardSetSigner}; use chainhook_sdk::types::{ FTBurnEventData, FTMintEventData, FTTransferEventData, NFTBurnEventData, NFTMintEventData, NFTTransferEventData, STXBurnEventData, STXLockEventData, STXMintEventData, @@ -260,6 +260,26 @@ pub fn create_stacks_new_block( transactions: (0..4).map(create_stacks_new_transaction).collect(), events, matured_miner_rewards: vec![], + block_time: Some(12345), + signer_bitvec: Some("000800000001ff".to_owned()), + signer_signature: Some(vec!["1234".to_owned(), "2345".to_owned()]), + cycle_number: Some(1), + reward_set: Some(RewardSet { + pox_ustx_threshold: "50000".to_owned(), + rewarded_addresses: vec![], + signers: Some(vec![ + RewardSetSigner { + signing_key: "0123".to_owned(), + weight: 123, + stacked_amt: "555555".to_owned(), + }, + RewardSetSigner { + signing_key: "2345".to_owned(), + weight: 234, + stacked_amt: "6677777".to_owned(), + }, + ]), + }), } } diff --git a/components/chainhook-sdk/src/indexer/stacks/mod.rs b/components/chainhook-sdk/src/indexer/stacks/mod.rs index 25b80c7d1..005cd10c0 100644 --- a/components/chainhook-sdk/src/indexer/stacks/mod.rs +++ b/components/chainhook-sdk/src/indexer/stacks/mod.rs @@ -35,6 +35,36 @@ pub struct NewBlock { pub transactions: Vec, pub events: Vec, pub matured_miner_rewards: Vec, + + #[serde(skip_serializing_if = "Option::is_none")] + pub block_time: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub signer_bitvec: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub signer_signature: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub cycle_number: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub reward_set: Option, +} + +#[derive(Deserialize, Serialize)] +pub struct RewardSet { + pub pox_ustx_threshold: String, + pub rewarded_addresses: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub signers: Option>, +} + +#[derive(Deserialize, Serialize)] +pub struct RewardSetSigner { + pub signing_key: String, + pub weight: u32, + pub stacked_amt: String, } #[derive(Deserialize, Serialize, Default, Clone)] @@ -432,6 +462,29 @@ pub fn standardize_stacks_block( pox_cycle_length: pox_cycle_length.try_into().unwrap(), confirm_microblock_identifier, stacks_block_hash: block.block_hash.clone(), + + block_time: block.block_time, + // TODO: decode `signer_bitvec` into an easy to use bit string representation (e.g. "01010101") + signer_bitvec: block.signer_bitvec.clone(), + signer_signature: block.signer_signature.clone(), + + cycle_number: block.cycle_number, + reward_set: block.reward_set.as_ref().and_then(|r| { + Some(StacksBlockMetadataRewardSet { + pox_ustx_threshold: r.pox_ustx_threshold.clone(), + rewarded_addresses: r.rewarded_addresses.clone(), + signers: r.signers.as_ref().map(|signers| { + signers + .into_iter() + .map(|signer| StacksBlockMetadataRewardSetSigner { + signing_key: signer.signing_key.clone(), + weight: signer.weight, + stacked_amt: signer.stacked_amt.clone(), + }) + .collect() + }), + }) + }), }, transactions, }; diff --git a/components/chainhook-sdk/src/indexer/tests/helpers/stacks_blocks.rs b/components/chainhook-sdk/src/indexer/tests/helpers/stacks_blocks.rs index 95e4208a4..d41d9cb47 100644 --- a/components/chainhook-sdk/src/indexer/tests/helpers/stacks_blocks.rs +++ b/components/chainhook-sdk/src/indexer/tests/helpers/stacks_blocks.rs @@ -1,6 +1,6 @@ use super::BlockEvent; use chainhook_types::{ - BlockIdentifier, StacksBlockData, StacksBlockMetadata, StacksTransactionData, + BlockIdentifier, StacksBlockData, StacksBlockMetadata, StacksBlockMetadataRewardSet, StacksBlockMetadataRewardSetSigner, StacksTransactionData }; pub fn generate_test_stacks_block( @@ -72,6 +72,26 @@ pub fn generate_test_stacks_block( pox_cycle_length: 100, confirm_microblock_identifier, stacks_block_hash: String::new(), + block_time: Some(12345), + signer_bitvec: Some("1010101010101".to_owned()), + signer_signature: Some(vec!["1234".to_owned(), "2345".to_owned()]), + cycle_number: Some(1), + reward_set: Some(StacksBlockMetadataRewardSet { + pox_ustx_threshold: "50000".to_owned(), + rewarded_addresses: vec![], + signers: Some(vec![ + StacksBlockMetadataRewardSetSigner { + signing_key: "0123".to_owned(), + weight: 123, + stacked_amt: "555555".to_owned(), + }, + StacksBlockMetadataRewardSetSigner { + signing_key: "2345".to_owned(), + weight: 234, + stacked_amt: "6677777".to_owned(), + }, + ]), + }), }, }) } diff --git a/components/chainhook-types-js/src/index.ts b/components/chainhook-types-js/src/index.ts index 2075a4138..dc4473582 100644 --- a/components/chainhook-types-js/src/index.ts +++ b/components/chainhook-types-js/src/index.ts @@ -694,6 +694,23 @@ export interface StacksBlockMetadata { * @memberof StacksBlockMetadata */ pox_cycle_length: number; + + block_time?: number | null; + signer_bitvec?: string | null; + signer_signature?: string[] | null; + cycle_number?: number | null; + reward_set?: { + pox_ustx_threshold: string; + rewarded_addresses: string[]; + signers?: { + signing_key: string; + weight: number; + stacked_amt: string; + }[] | null; + start_cycle_state: { + missed_reward_slots: []; + }; + } | null; } /** diff --git a/components/chainhook-types-rs/src/rosetta.rs b/components/chainhook-types-rs/src/rosetta.rs index 9ebbb18e6..6e3d6750c 100644 --- a/components/chainhook-types-rs/src/rosetta.rs +++ b/components/chainhook-types-rs/src/rosetta.rs @@ -114,6 +114,29 @@ pub struct StacksBlockMetadata { pub pox_cycle_length: u32, pub confirm_microblock_identifier: Option, pub stacks_block_hash: String, + + // Fields included in Nakamoto block headers + pub block_time: Option, + pub signer_bitvec: Option, + pub signer_signature: Option>, + + // Available starting in epoch3, only included in blocks where the pox cycle rewards are first calculated + pub cycle_number: Option, + pub reward_set: Option, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct StacksBlockMetadataRewardSet { + pub pox_ustx_threshold: String, + pub rewarded_addresses: Vec, + pub signers: Option>, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct StacksBlockMetadataRewardSetSigner { + pub signing_key: String, + pub weight: u32, + pub stacked_amt: String, } /// BitcoinBlock contain an array of Transactions that occurred at a particular diff --git a/components/client/typescript/src/schemas/stacks/payload.ts b/components/client/typescript/src/schemas/stacks/payload.ts index db1c6ea8a..cc1653e1e 100644 --- a/components/client/typescript/src/schemas/stacks/payload.ts +++ b/components/client/typescript/src/schemas/stacks/payload.ts @@ -66,6 +66,29 @@ export const StacksEventMetadataSchema = Type.Object({ pox_cycle_length: Type.Integer(), pox_cycle_position: Type.Integer(), stacks_block_hash: Type.String(), + + // Fields included in Nakamoto block headers + block_time: Nullable(Type.Integer()), + signer_bitvec: Nullable(Type.String()), + signer_signature: 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()), + reward_set: Nullable( + Type.Object({ + pox_ustx_threshold: Type.String(), + rewarded_addresses: Type.Array(Type.String()), + signers: Nullable( + Type.Array( + Type.Object({ + signing_key: Type.String(), + weight: Type.Integer(), + stacked_amt: Type.String(), + }) + ) + ), + }) + ), }); export type StacksEventMetadata = Static;