bitcoin-heuristics 0.1.0

Pattern detection heuristics for Bitcoin on-chain analytics — consolidations, distributions, CoinJoin, fee spikes, dormant supply reactivation and more
Documentation
use crate::models::{LightningIndicator, ScriptType};

pub fn detect_lightning_indicator(
    output_values: &[i64],
    output_scripts: &[ScriptType],
    input_sequences: &[i64],
) -> LightningIndicator {
    for (i, &val) in output_values.iter().enumerate() {
        if val == 330 {
            return LightningIndicator::AnchorOutput;
        }
        if val > 100_000 && output_scripts.get(i) == Some(&ScriptType::P2WSH) {
            return LightningIndicator::ChannelOpen;
        }
    }
    for &seq in input_sequences {
        if seq > 0 && seq < 0xFFFF_0000 {
            return LightningIndicator::ForceClose;
        }
    }
    LightningIndicator::NoSignal
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_no_signal() {
        let result = detect_lightning_indicator(
            &[100_000, 200_000],
            &[ScriptType::P2WPKH, ScriptType::P2WPKH],
            &[],
        );
        assert_eq!(result, LightningIndicator::NoSignal);
    }

    #[test]
    fn test_anchor_output_330_sats() {
        let result = detect_lightning_indicator(
            &[330, 100_000],
            &[ScriptType::P2WSH, ScriptType::P2WPKH],
            &[],
        );
        assert_eq!(result, LightningIndicator::AnchorOutput);
    }

    #[test]
    fn test_channel_open_p2wsh() {
        let result = detect_lightning_indicator(
            &[500_000, 50_000],
            &[ScriptType::P2WSH, ScriptType::P2WPKH],
            &[],
        );
        assert_eq!(result, LightningIndicator::ChannelOpen);
    }

    #[test]
    fn test_force_close_via_sequence() {
        let result = detect_lightning_indicator(&[200_000], &[ScriptType::P2WPKH], &[144]);
        assert_eq!(result, LightningIndicator::ForceClose);
    }

    #[test]
    fn test_sequence_zero_no_force_close() {
        let result = detect_lightning_indicator(&[200_000], &[ScriptType::P2WPKH], &[0]);
        assert_eq!(result, LightningIndicator::NoSignal);
    }
}