#![allow(dead_code)]
use std::str::FromStr;
use bitcoin::NetworkKind;
use bitcoin::bip32::{ChainCode, ChildNumber, DerivationPath, Fingerprint, Xpub};
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use mk_codec::KeyCard;
use proptest::prelude::*;
pub const STANDARD_PATHS: &[&str] = &[
"m/44'/0'/0'",
"m/49'/0'/0'",
"m/84'/0'/0'",
"m/86'/0'/0'",
"m/48'/0'/0'/2'",
"m/48'/0'/0'/1'",
"m/87'/0'/0'",
"m/44'/1'/0'",
"m/49'/1'/0'",
"m/84'/1'/0'",
"m/86'/1'/0'",
"m/48'/1'/0'/2'",
"m/48'/1'/0'/1'",
"m/87'/1'/0'",
];
pub fn path_strategy() -> impl Strategy<Value = DerivationPath> {
let standard = (0..STANDARD_PATHS.len())
.prop_map(|i| DerivationPath::from_str(STANDARD_PATHS[i]).expect("valid standard path"));
let explicit = prop::collection::vec(
(0u32..0x8000_0000u32, any::<bool>()).prop_map(|(idx, hardened)| {
if hardened {
ChildNumber::from_hardened_idx(idx).expect("idx < 2^31")
} else {
ChildNumber::from_normal_idx(idx).expect("idx < 2^31")
}
}),
1..=10usize,
)
.prop_map(DerivationPath::from);
let empty = Just(DerivationPath::from_str("m").expect("empty path parses"));
prop_oneof![standard, explicit, empty].boxed()
}
pub fn xpub_strategy(path: DerivationPath) -> impl Strategy<Value = Xpub> {
let components: Vec<ChildNumber> = path.as_ref().to_vec();
let depth = components.len() as u8;
let child_number = components
.last()
.copied()
.unwrap_or(ChildNumber::Normal { index: 0 });
(
any::<[u8; 32]>().prop_filter("valid secp256k1 scalar", |b| {
SecretKey::from_slice(b).is_ok()
}),
any::<[u8; 32]>(),
any::<[u8; 4]>(),
any::<bool>(),
)
.prop_map(move |(sk_bytes, cc, pfp, mainnet)| {
let secp = Secp256k1::new();
let sk = SecretKey::from_slice(&sk_bytes).expect("filtered to valid scalar");
let pk = PublicKey::from_secret_key(&secp, &sk);
Xpub {
network: if mainnet {
NetworkKind::Main
} else {
NetworkKind::Test
},
depth,
parent_fingerprint: Fingerprint::from(pfp),
child_number,
public_key: pk,
chain_code: ChainCode::from(cc),
}
})
}
pub fn keycard_strategy() -> impl Strategy<Value = KeyCard> {
(
prop::collection::vec(any::<[u8; 4]>(), 1..=8usize),
prop::option::of(any::<[u8; 4]>().prop_map(Fingerprint::from)),
path_strategy(),
)
.prop_flat_map(|(stubs, fp, path)| {
let p = path.clone();
xpub_strategy(path)
.prop_map(move |xpub| KeyCard::new(stubs.clone(), fp, p.clone(), xpub))
})
.boxed()
}
pub fn csid_strategy() -> impl Strategy<Value = u32> {
0u32..=0x000F_FFFFu32
}
pub fn flip_chars(s: &str, positions: &[usize]) -> String {
let mut chars: Vec<char> = s.chars().collect();
for &p in positions {
chars[p] = if chars[p] == 'q' { 'p' } else { 'q' };
}
chars.into_iter().collect()
}