kangaroo 0.8.0

Pollard's Kangaroo ECDLP solver for secp256k1 using Vulkan/Metal/DX12 compute
Documentation
//! Boha provider - crypto puzzles and bounties data

use super::ProviderResult;
use anyhow::{anyhow, Result};
use boha::Status;

pub fn resolve(path: &str) -> Result<ProviderResult> {
    let puzzle_id = path.replace(':', "/");

    let puzzle = boha::get(&puzzle_id).map_err(|e| anyhow!("{}", e))?;

    let pubkey: Option<String> = puzzle.pubkey_str().map(str::to_string);

    let (start, end, range_bits) = match puzzle.key_range_big() {
        Some((start_big, end_big)) => {
            let start_hex = format!("{:x}", start_big);
            let end_hex = format!("{:x}", end_big);
            let bits = puzzle.key.as_ref().and_then(|k| k.bits).map(|b| b as u32);
            (Some(start_hex), Some(end_hex), bits)
        }
        None => (None, None, None),
    };

    Ok(ProviderResult {
        id: puzzle_id,
        pubkey,
        start,
        end,
        range_bits,
    })
}

pub fn list_available() -> Vec<(String, String, String, Option<u32>, bool)> {
    boha::b1000::all()
        .filter(|p| p.status == Status::Unsolved)
        .map(|p| {
            let bits = p.key.as_ref().and_then(|k| k.bits).map(|b| b as u32);
            let has_pubkey = p.pubkey.is_some();
            (
                "boha".to_string(),
                p.id.to_string(),
                p.address.value.to_string(),
                bits,
                has_pubkey,
            )
        })
        .collect()
}

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

    #[test]
    fn test_resolve_valid_puzzle() {
        let result = resolve("b1000/66").unwrap();
        assert_eq!(result.id, "b1000/66");
        assert!(result.pubkey.is_some());
        assert!(result.start.is_some());
        assert!(result.end.is_some());
        assert_eq!(result.range_bits, Some(66));
    }

    #[test]
    fn test_resolve_colon_format() {
        let result = resolve("b1000:66").unwrap();
        assert_eq!(result.id, "b1000/66");
    }

    #[test]
    fn test_resolve_invalid_puzzle() {
        let result = resolve("b1000/9999");
        assert!(result.is_err());
    }

    #[test]
    fn test_list_available_not_empty() {
        let list = list_available();
        assert!(!list.is_empty());

        for (provider, id, address, bits, _has_pubkey) in &list {
            assert_eq!(provider, "boha");
            assert!(id.starts_with("b1000/"));
            assert!(!address.is_empty());
            assert!(bits.is_some());
        }
    }
}