Skip to content

Commit

Permalink
feat: re-approach stacks block commit schema
Browse files Browse the repository at this point in the history
  • Loading branch information
lgalabru committed Mar 30, 2023
1 parent e1b4cc5 commit 218d599
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 39 deletions.
31 changes: 23 additions & 8 deletions components/chainhook-event-observer/src/chainhooks/types.rs
Expand Up @@ -536,33 +536,48 @@ pub struct PoxConfig {

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

pub fn get_pox_cycle_len(&self) -> u64 {
self.prepare_phase_len + self.reward_phase_len
}

pub fn get_pox_cycle_id(&self, block_height: u64) -> u64 {
(block_height.saturating_sub(self.genesis_block_height)) / self.get_pox_cycle_len()
}

pub fn get_pos_in_pox_cycle(&self, block_height: u64) -> u64 {
(block_height.saturating_sub(self.genesis_block_height)) % self.get_pox_cycle_len()
}

pub fn get_burn_address(&self) -> &str {
match self.genesis_block_height {
666050 => "1111111111111111111114oLvT2",
2000000 => "burn-address-regtest",
_ => "burn-address",
}
}
}

const POX_CONFIG_MAINNET: PoxConfig = PoxConfig {
genesis_block_height: 666050,
prepare_phase_len: 2100,
reward_phase_len: 100,
prepare_phase_len: 100,
reward_phase_len: 2100,
rewarded_addresses_per_block: 2,
};

const POX_CONFIG_TESTNET: PoxConfig = PoxConfig {
genesis_block_height: 2000000,
prepare_phase_len: 1050,
reward_phase_len: 50,
prepare_phase_len: 50,
reward_phase_len: 1050,
rewarded_addresses_per_block: 2,
};

const POX_CONFIG_DEVNET: PoxConfig = PoxConfig {
genesis_block_height: 100,
prepare_phase_len: 10,
reward_phase_len: 4,
prepare_phase_len: 4,
reward_phase_len: 10,
rewarded_addresses_per_block: 2,
};

Expand Down
127 changes: 100 additions & 27 deletions components/chainhook-event-observer/src/indexer/bitcoin/mod.rs
Expand Up @@ -278,9 +278,14 @@ pub fn standardize_bitcoin_block(
ctx.try_log(|logger| slog::debug!(logger, "Standardizing Bitcoin transaction {txid}"));

let mut stacks_operations = vec![];
if let Some(op) =
try_parse_stacks_operation(&tx.vout, &pox_config, &expected_magic_bytes, ctx)
{
if let Some(op) = try_parse_stacks_operation(
block_height,
&tx.vin,
&tx.vout,
&pox_config,
&expected_magic_bytes,
ctx,
) {
stacks_operations.push(op);
}

Expand Down Expand Up @@ -440,6 +445,8 @@ fn try_parse_ordinal_operation(
}

fn try_parse_stacks_operation(
block_height: u64,
inputs: &Vec<BitcoinTransactionInputFullBreakdown>,
outputs: &Vec<BitcoinTransactionOutputFullBreakdown>,
pox_config: &PoxConfig,
expected_magic_bytes: &[u8; 2],
Expand Down Expand Up @@ -492,34 +499,100 @@ fn try_parse_stacks_operation(
}
StacksOpcodes::BlockCommit => {
let res = try_parse_block_commit_op(&op_return_output[5..])?;
let mut pox_sats_burnt = 0;
let mut pox_sats_transferred = vec![];

// We need to determine wether the transaction was a PoB or a Pox commitment
// if pox_config.is_consensus_rewarding_participants_at_block_height(block_height) {
if outputs.len() < 1 + pox_config.rewarded_addresses_per_block {
return None;
}
let mut rewards = vec![];
for output in outputs[1..pox_config.rewarded_addresses_per_block].into_iter() {
rewards.push(PoxReward {
recipient: format!("0x{}", hex::encode(&output.script_pub_key.hex)),
amount: output.value.to_sat(),
});
let mining_output_index = if pox_config
.is_consensus_rewarding_participants_at_block_height(block_height)
{
// Output 0 is OP_RETURN
// Output 1 is rewarding Address 1
let pox_output_1 = outputs
.get(1)
.ok_or(format!("expected pox output 1 not found"))
.ok()?;
let pox_script_1 = pox_output_1
.script_pub_key
.script()
.map_err(|_e| format!("expected pox output 1 corrupted"))
.ok()?;
let pox_address_1 = Address::from_script(&pox_script_1, bitcoin::Network::Bitcoin)
.map_err(|_e| format!("expected pox output 1 corrupted"))
.ok()?;
if pox_address_1.to_string().eq(&pox_config.get_burn_address()) {
pox_sats_burnt += pox_output_1.value.to_sat();
} else {
pox_sats_transferred.push(PoxReward {
recipient_address: pox_address_1.to_string(),
amount: pox_output_1.value.to_sat(),
});
}
// Output 2 is rewarding Address 2
let pox_output_2 = outputs
.get(2)
.ok_or(format!("expected pox output 2 not found"))
.ok()?;
let pox_script_2 = pox_output_2
.script_pub_key
.script()
.map_err(|_e| format!("expected pox output 2 corrupted"))
.ok()?;
let pox_address_2 = Address::from_script(&pox_script_2, bitcoin::Network::Bitcoin)
.map_err(|_e| format!("expected pox output 2 corrupted"))
.ok()?;
if pox_address_2.to_string().eq(&pox_config.get_burn_address()) {
pox_sats_burnt += pox_output_2.value.to_sat();
} else {
pox_sats_transferred.push(PoxReward {
recipient_address: pox_address_2.to_string(),
amount: pox_output_2.value.to_sat(),
});
}
// Output 3 is used for miner chained commitments
3
} else {
// Output 0 is OP_RETURN
// Output 1 should be a Burn Address
let burn_output = outputs
.get(1)
.ok_or(format!("expected burn address not found"))
.ok()?;
// Todo: Ensure that we're looking at a burn address
pox_sats_burnt += burn_output.value.to_sat();
// Output 2 is used for miner chained commitments
2
};

let mut mining_sats_left = 0;
let mut mining_address_post_commit = None;
if let Some(mining_post_commit) = outputs.get(mining_output_index) {
mining_sats_left = mining_post_commit.value.to_sat();
mining_address_post_commit = match mining_post_commit.script_pub_key.script() {
Ok(script) => Address::from_script(&script, bitcoin::Network::Bitcoin)
.and_then(|a| Ok(a.to_string()))
.ok(),
Err(_) => None,
};
}
StacksBaseChainOperation::BlockCommitted(StacksBlockCommitmentData {
signers: vec![], // todo(lgalabru)
stacks_block_hash: res.stacks_block_hash.clone(),
rewards,
})
// } else {
// if outputs.len() < 2 {
// return None;
// }
// let amount = outputs[1].value;
// StacksBaseChainOperation::BlockCommitted(StacksBlockCommitmentData {
// signers: vec![], // todo(lgalabru)
// stacks_block_hash: res.stacks_block_hash.clone(),
// amount: amount.to_sat(),
// })
// }

let pox_cycle_id = pox_config.get_pox_cycle_id(block_height);
let pox_cycle_len = pox_config.get_pox_cycle_len();
let pox_cycle_pos = pox_config.get_pos_in_pox_cycle(block_height);

StacksBaseChainOperation::BlockCommitted(StacksBlockCommitmentData {
block_hash: res.stacks_block_hash,
pox_cycle_id,
pox_cycle_len,
pox_cycle_pos,
pox_sats_burnt,
pox_sats_transferred,
mining_address_pre_commit: None,
mining_address_post_commit,
mining_sats_left,
})
}
};

Expand Down
17 changes: 13 additions & 4 deletions components/chainhook-types-rs/src/rosetta.rs
Expand Up @@ -308,6 +308,7 @@ pub struct OrdinalInscriptionRevealData {
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum StacksBaseChainOperation {
BlockCommitted(StacksBlockCommitmentData),
LeaderRegistered(KeyRegistrationData),
Expand All @@ -316,15 +317,23 @@ pub enum StacksBaseChainOperation {
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct StacksBlockCommitmentData {
pub signers: Vec<String>,
pub stacks_block_hash: String,
pub rewards: Vec<PoxReward>,
pub block_hash: String,
pub pox_cycle_id: u64,
pub pox_cycle_len: u64,
pub pox_cycle_pos: u64,
pub pox_sats_burnt: u64,
pub pox_sats_transferred: Vec<PoxReward>,
// pub mining_address_pre_commit: Option<String>,
pub mining_address_post_commit: Option<String>,
pub mining_sats_left: u64,
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct PoxReward {
pub recipient: String,
pub recipient_address: String,
pub amount: u64,
}

Expand Down

0 comments on commit 218d599

Please sign in to comment.