use sha2::{Digest, Sha256}; use bitcoin::bip32::Xpub; use std::str::FromStr; use bitcoin::bip32::DerivationPath; use bitcoin::key::Secp256k1; use bitcoin::Address; use bitcoin::ScriptBuf; use bitcoin::WPubkeyHash; use bitcoin::Network; use bitcoin::hashes::Hash; // Mainnet (BIP44/BIP49/BIP84) enum BS58Prefix{ Xpub, //Ypub, //Zpub, //Tpub, //Vpub, //Upub } const XPUB_PREFIX:[u8; 4] = [0x04, 0x88, 0xB2, 0x1E]; // xpub (Legacy P2PKH) //const YPUB_PREFIX:[u8; 4] = [0x04, 0x9D, 0x7C, 0xB2]; // ypub (Nested SegWit P2SH-P2WPKH) //const ZPUB_PREFIX:[u8; 4] = [0x04, 0xB2, 0x47, 0x46]; // zpub (Native SegWit P2WPKH) //const TPUB_PREFIX:[u8; 4] = [0x04, 0x35, 0x87, 0xCF]; // tpub (Testnet Legacy P2PKH) //const VPUB_PREFIX:[u8; 4] = [0x04, 0x5F, 0x1C, 0xF6]; // vpub (Testnet Nested SegWit) //const UPUB_PREFIX:[u8; 4] = [0x04, 0x4A, 0x52, 0x62]; // upub (RegTest Nested SegWit) fn base58check_decode(s: &str) -> Result, String> { let data = bs58::decode(s).into_vec().map_err(|e| e.to_string())?; if data.len() < 4 { return Err("Data troppo corta".to_string()); } let (payload, checksum) = data.split_at(data.len() - 4); let hash = Sha256::digest(Sha256::digest(payload)); if hash[0..4] != checksum[..] { return Err("Checksum invalido".to_string()); } Ok(payload.to_vec()) } fn base58check_encode(data: &[u8]) -> String { let checksum = &Sha256::digest(Sha256::digest(data))[0..4]; let full = [data, checksum].concat(); bs58::encode(full).into_string() } fn convert_to(zpub: &str,prefix: BS58Prefix) -> Result { let mut data = base58check_decode(zpub)?; if data.len() < 4 { return Err("Non รจ una zpub valida.".to_string()); } data.splice(0..4, match prefix { BS58Prefix::Xpub => XPUB_PREFIX, //BS58Prefix::Ypub => YPUB_PREFIX, //BS58Prefix::Zpub => ZPUB_PREFIX, //BS58Prefix::Vpub => VPUB_PREFIX, //BS58Prefix::Tpub => TPUB_PREFIX, //BS58Prefix::Upub => UPUB_PREFIX, }); Ok(base58check_encode(&data)) } pub fn new_address_from_xpub(zpub: &str, index: i64,network: Network)-> Result<(String,String), Box>{ let xpub = Xpub::from_str(&convert_to(zpub,BS58Prefix::Xpub)?)?; let path = format!("m/0/{}",index); let derivation_path = DerivationPath::from_str(path.as_str())?; let secp = Secp256k1::new(); let derived_xpub = xpub.derive_pub(&secp, &derivation_path)?; let public_key = derived_xpub.public_key; let pubkey_bytes = public_key.serialize(); let witness_program = WPubkeyHash::hash(&pubkey_bytes); let redeem_script = ScriptBuf::new_p2wpkh(&witness_program); //let script_pubkey = ScriptBuf::new_p2sh(&redeem_script.script_hash()); let address = Address::from_script(&redeem_script, network)?; //let address = Address::from_script(&script_pubkey, network)?; Ok((address.to_string(),path.to_string())) } /* fn main() -> Result<(), Box>{ //let zpub = "xpub6C29v8gxCXREHUzoGNfqqFqZWxTVEmYtmZshuzfSwBKNmfYQxoizRziCkkUUA4WwJZkJs2i7nttRiC6MQG7mxZpouXeYkTZe3U52RyPAeo2"; //let zpub = "vpub5Ut36m34VebUUjdhYaxJCjSPqk3ZR8bA2MXLmbHRQCycAxy5Q1GFPJspLkJywJjBgQnvU3rmwPKTPp1ELLWeXrve3zBufpZR4MRCCTNHzsn"; let zpub = "zpub6qdfveGrxBQN3z8paZ88EHpCn5MGXpUoHwQmHhPbj4rPQtUjbWyCHrJFYZGVY7MsmVbDaeu4JYqRqcdLzMx78wZFEWbLrF9FG3gr2MPQC5H"; match convert_to(zpub,BS58Prefix::Xpub) { Ok(xpub) => println!("XPUB: {}", xpub), Err(e) => eprintln!("Errore: {}", e), } let xpub = Xpub::from_str(&convert_to(zpub,BS58Prefix::Xpub)?)?; let derivation_path = DerivationPath::from_str("m/0/0")?; let secp = Secp256k1::new(); let derived_xpub = xpub.derive_pub(&secp, &derivation_path)?; let public_key = derived_xpub.public_key; let pubkey_bytes = public_key.serialize(); let witness_program = WPubkeyHash::hash(&pubkey_bytes); let redeem_script = ScriptBuf::new_p2wpkh(&witness_program); let script_pubkey = ScriptBuf::new_p2sh(&redeem_script.script_hash()); // Generate the Bitcoin SegWit (BIP49) address let network = Network::Bitcoin; let address = Address::from_script(&redeem_script, network)?; let address = Address::from_script(&script_pubkey, network)?; Ok(()) } */