use crate::TxFeatureFlags;
use crate::processor::txout::ProcessedOutput;
use bitcoin::{PublicKey, TxOut, script::Instruction};
use brk_types::OutputType;
#[derive(Default)]
pub(super) struct Scanner {
p2wsh_count: usize,
payload_size: usize,
}
impl Scanner {
pub(super) fn scan(
&mut self,
txout: &TxOut,
output: &ProcessedOutput,
flags: &mut TxFeatureFlags,
) {
flags.insert_type(output.output_type);
match output.output_type {
OutputType::P2PK33 | OutputType::P2PK65 => {
if txout.script_pubkey.p2pk_public_key().is_none() {
flags.insert(TxFeatureFlags::FAKE_PUBKEY);
}
self.p2wsh_count = 0;
}
OutputType::P2MS => {
if has_fake_multisig_key(&txout.script_pubkey) {
flags.insert(TxFeatureFlags::FAKE_PUBKEY);
}
self.p2wsh_count = 0;
}
OutputType::P2WSH => {
let program = &txout.script_pubkey.as_bytes()[2..];
if self.p2wsh_count == 0 {
self.payload_size = u16::from_be_bytes([program[0], program[1]]) as usize;
}
self.p2wsh_count += 1;
let output_count = (self.payload_size + 33) / 32;
if self.p2wsh_count == output_count {
let padding = output_count * 32 - self.payload_size - 2;
if program[32 - padding..].iter().all(|byte| *byte == 0) {
flags.insert(TxFeatureFlags::FAKE_SCRIPTHASH);
}
}
}
_ => self.p2wsh_count = 0,
}
}
}
fn has_fake_multisig_key(script: &bitcoin::Script) -> bool {
script.instructions().any(|instruction| {
let Ok(Instruction::PushBytes(bytes)) = instruction else {
return false;
};
let bytes = bytes.as_bytes();
is_burn_key(bytes) || PublicKey::from_slice(bytes).is_err()
})
}
fn is_burn_key(bytes: &[u8]) -> bool {
bytes.len() == 33
&& matches!(
bytes,
[0x02, rest @ ..]
if rest.iter().all(|byte| *byte == 0x02)
|| rest.iter().all(|byte| *byte == 0x22)
)
|| bytes.len() == 33
&& matches!(
bytes,
[0x03, rest @ ..]
if rest.iter().all(|byte| *byte == 0x03)
|| rest.iter().all(|byte| *byte == 0x33)
)
}