Skip to content

Commit 218d599

Browse files
author
Ludo Galabru
committed
feat: re-approach stacks block commit schema
1 parent e1b4cc5 commit 218d599

File tree

3 files changed

+136
-39
lines changed

3 files changed

+136
-39
lines changed

components/chainhook-event-observer/src/chainhooks/types.rs

+23-8
Original file line numberDiff line numberDiff line change
@@ -536,33 +536,48 @@ pub struct PoxConfig {
536536

537537
impl PoxConfig {
538538
pub fn is_consensus_rewarding_participants_at_block_height(&self, block_height: u64) -> bool {
539-
(block_height.saturating_div(self.genesis_block_height) % self.get_pox_cycle_len())
540-
>= self.prepare_phase_len
539+
self.get_pos_in_pox_cycle(block_height) < self.reward_phase_len
541540
}
542541

543542
pub fn get_pox_cycle_len(&self) -> u64 {
544543
self.prepare_phase_len + self.reward_phase_len
545544
}
545+
546+
pub fn get_pox_cycle_id(&self, block_height: u64) -> u64 {
547+
(block_height.saturating_sub(self.genesis_block_height)) / self.get_pox_cycle_len()
548+
}
549+
550+
pub fn get_pos_in_pox_cycle(&self, block_height: u64) -> u64 {
551+
(block_height.saturating_sub(self.genesis_block_height)) % self.get_pox_cycle_len()
552+
}
553+
554+
pub fn get_burn_address(&self) -> &str {
555+
match self.genesis_block_height {
556+
666050 => "1111111111111111111114oLvT2",
557+
2000000 => "burn-address-regtest",
558+
_ => "burn-address",
559+
}
560+
}
546561
}
547562

548563
const POX_CONFIG_MAINNET: PoxConfig = PoxConfig {
549564
genesis_block_height: 666050,
550-
prepare_phase_len: 2100,
551-
reward_phase_len: 100,
565+
prepare_phase_len: 100,
566+
reward_phase_len: 2100,
552567
rewarded_addresses_per_block: 2,
553568
};
554569

555570
const POX_CONFIG_TESTNET: PoxConfig = PoxConfig {
556571
genesis_block_height: 2000000,
557-
prepare_phase_len: 1050,
558-
reward_phase_len: 50,
572+
prepare_phase_len: 50,
573+
reward_phase_len: 1050,
559574
rewarded_addresses_per_block: 2,
560575
};
561576

562577
const POX_CONFIG_DEVNET: PoxConfig = PoxConfig {
563578
genesis_block_height: 100,
564-
prepare_phase_len: 10,
565-
reward_phase_len: 4,
579+
prepare_phase_len: 4,
580+
reward_phase_len: 10,
566581
rewarded_addresses_per_block: 2,
567582
};
568583

components/chainhook-event-observer/src/indexer/bitcoin/mod.rs

+100-27
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,14 @@ pub fn standardize_bitcoin_block(
278278
ctx.try_log(|logger| slog::debug!(logger, "Standardizing Bitcoin transaction {txid}"));
279279

280280
let mut stacks_operations = vec![];
281-
if let Some(op) =
282-
try_parse_stacks_operation(&tx.vout, &pox_config, &expected_magic_bytes, ctx)
283-
{
281+
if let Some(op) = try_parse_stacks_operation(
282+
block_height,
283+
&tx.vin,
284+
&tx.vout,
285+
&pox_config,
286+
&expected_magic_bytes,
287+
ctx,
288+
) {
284289
stacks_operations.push(op);
285290
}
286291

@@ -440,6 +445,8 @@ fn try_parse_ordinal_operation(
440445
}
441446

442447
fn try_parse_stacks_operation(
448+
block_height: u64,
449+
inputs: &Vec<BitcoinTransactionInputFullBreakdown>,
443450
outputs: &Vec<BitcoinTransactionOutputFullBreakdown>,
444451
pox_config: &PoxConfig,
445452
expected_magic_bytes: &[u8; 2],
@@ -492,34 +499,100 @@ fn try_parse_stacks_operation(
492499
}
493500
StacksOpcodes::BlockCommit => {
494501
let res = try_parse_block_commit_op(&op_return_output[5..])?;
502+
let mut pox_sats_burnt = 0;
503+
let mut pox_sats_transferred = vec![];
504+
495505
// We need to determine wether the transaction was a PoB or a Pox commitment
496-
// if pox_config.is_consensus_rewarding_participants_at_block_height(block_height) {
497-
if outputs.len() < 1 + pox_config.rewarded_addresses_per_block {
498-
return None;
499-
}
500-
let mut rewards = vec![];
501-
for output in outputs[1..pox_config.rewarded_addresses_per_block].into_iter() {
502-
rewards.push(PoxReward {
503-
recipient: format!("0x{}", hex::encode(&output.script_pub_key.hex)),
504-
amount: output.value.to_sat(),
505-
});
506+
let mining_output_index = if pox_config
507+
.is_consensus_rewarding_participants_at_block_height(block_height)
508+
{
509+
// Output 0 is OP_RETURN
510+
// Output 1 is rewarding Address 1
511+
let pox_output_1 = outputs
512+
.get(1)
513+
.ok_or(format!("expected pox output 1 not found"))
514+
.ok()?;
515+
let pox_script_1 = pox_output_1
516+
.script_pub_key
517+
.script()
518+
.map_err(|_e| format!("expected pox output 1 corrupted"))
519+
.ok()?;
520+
let pox_address_1 = Address::from_script(&pox_script_1, bitcoin::Network::Bitcoin)
521+
.map_err(|_e| format!("expected pox output 1 corrupted"))
522+
.ok()?;
523+
if pox_address_1.to_string().eq(&pox_config.get_burn_address()) {
524+
pox_sats_burnt += pox_output_1.value.to_sat();
525+
} else {
526+
pox_sats_transferred.push(PoxReward {
527+
recipient_address: pox_address_1.to_string(),
528+
amount: pox_output_1.value.to_sat(),
529+
});
530+
}
531+
// Output 2 is rewarding Address 2
532+
let pox_output_2 = outputs
533+
.get(2)
534+
.ok_or(format!("expected pox output 2 not found"))
535+
.ok()?;
536+
let pox_script_2 = pox_output_2
537+
.script_pub_key
538+
.script()
539+
.map_err(|_e| format!("expected pox output 2 corrupted"))
540+
.ok()?;
541+
let pox_address_2 = Address::from_script(&pox_script_2, bitcoin::Network::Bitcoin)
542+
.map_err(|_e| format!("expected pox output 2 corrupted"))
543+
.ok()?;
544+
if pox_address_2.to_string().eq(&pox_config.get_burn_address()) {
545+
pox_sats_burnt += pox_output_2.value.to_sat();
546+
} else {
547+
pox_sats_transferred.push(PoxReward {
548+
recipient_address: pox_address_2.to_string(),
549+
amount: pox_output_2.value.to_sat(),
550+
});
551+
}
552+
// Output 3 is used for miner chained commitments
553+
3
554+
} else {
555+
// Output 0 is OP_RETURN
556+
// Output 1 should be a Burn Address
557+
let burn_output = outputs
558+
.get(1)
559+
.ok_or(format!("expected burn address not found"))
560+
.ok()?;
561+
// Todo: Ensure that we're looking at a burn address
562+
pox_sats_burnt += burn_output.value.to_sat();
563+
// Output 2 is used for miner chained commitments
564+
2
565+
};
566+
567+
let mut mining_sats_left = 0;
568+
let mut mining_address_post_commit = None;
569+
if let Some(mining_post_commit) = outputs.get(mining_output_index) {
570+
mining_sats_left = mining_post_commit.value.to_sat();
571+
mining_address_post_commit = match mining_post_commit.script_pub_key.script() {
572+
Ok(script) => Address::from_script(&script, bitcoin::Network::Bitcoin)
573+
.and_then(|a| Ok(a.to_string()))
574+
.ok(),
575+
Err(_) => None,
576+
};
506577
}
507-
StacksBaseChainOperation::BlockCommitted(StacksBlockCommitmentData {
508-
signers: vec![], // todo(lgalabru)
509-
stacks_block_hash: res.stacks_block_hash.clone(),
510-
rewards,
511-
})
512-
// } else {
513-
// if outputs.len() < 2 {
514-
// return None;
515578
// }
516-
// let amount = outputs[1].value;
517-
// StacksBaseChainOperation::BlockCommitted(StacksBlockCommitmentData {
518-
// signers: vec![], // todo(lgalabru)
519-
// stacks_block_hash: res.stacks_block_hash.clone(),
520-
// amount: amount.to_sat(),
521-
// })
522579
// }
580+
581+
let pox_cycle_id = pox_config.get_pox_cycle_id(block_height);
582+
let pox_cycle_len = pox_config.get_pox_cycle_len();
583+
let pox_cycle_pos = pox_config.get_pos_in_pox_cycle(block_height);
584+
585+
StacksBaseChainOperation::BlockCommitted(StacksBlockCommitmentData {
586+
block_hash: res.stacks_block_hash,
587+
pox_cycle_id,
588+
pox_cycle_len,
589+
pox_cycle_pos,
590+
pox_sats_burnt,
591+
pox_sats_transferred,
592+
mining_address_pre_commit: None,
593+
mining_address_post_commit,
594+
mining_sats_left,
595+
})
523596
}
524597
};
525598

components/chainhook-types-rs/src/rosetta.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ pub struct OrdinalInscriptionRevealData {
308308
}
309309

310310
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
311+
#[serde(rename_all = "snake_case")]
311312
pub enum StacksBaseChainOperation {
312313
BlockCommitted(StacksBlockCommitmentData),
313314
LeaderRegistered(KeyRegistrationData),
@@ -316,15 +317,23 @@ pub enum StacksBaseChainOperation {
316317
}
317318

318319
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
320+
#[serde(rename_all = "snake_case")]
319321
pub struct StacksBlockCommitmentData {
320-
pub signers: Vec<String>,
321-
pub stacks_block_hash: String,
322-
pub rewards: Vec<PoxReward>,
322+
pub block_hash: String,
323+
pub pox_cycle_id: u64,
324+
pub pox_cycle_len: u64,
325+
pub pox_cycle_pos: u64,
326+
pub pox_sats_burnt: u64,
327+
pub pox_sats_transferred: Vec<PoxReward>,
328+
// pub mining_address_pre_commit: Option<String>,
329+
pub mining_address_post_commit: Option<String>,
330+
pub mining_sats_left: u64,
323331
}
324332

325333
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
334+
#[serde(rename_all = "snake_case")]
326335
pub struct PoxReward {
327-
pub recipient: String,
336+
pub recipient_address: String,
328337
pub amount: u64,
329338
}
330339

0 commit comments

Comments
 (0)