forked from bitcoinafterlife/bal-server
formatting code
This commit is contained in:
177
src/xpub.rs
177
src/xpub.rs
@@ -1,3 +1,4 @@
|
||||
//use bs58;
|
||||
use bitcoin::Address;
|
||||
use bitcoin::Network;
|
||||
use bitcoin::ScriptBuf;
|
||||
@@ -12,18 +13,141 @@ use std::str::FromStr;
|
||||
// Mainnet (BIP44/BIP49/BIP84)
|
||||
enum BS58Prefix {
|
||||
Xpub,
|
||||
//Ypub,
|
||||
//Zpub,
|
||||
//Tpub,
|
||||
//Vpub,
|
||||
//Upub
|
||||
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)
|
||||
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)
|
||||
// Constants from Bitcoin Core's checksum algorithm
|
||||
const INPUT_CHARSET: &[u8] = b"0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
|
||||
const CHECKSUM_CHARSET: &[u8] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
// Polynomial modulo function used in checksum calculation (same as in Bitcoin Core)
|
||||
fn poly_mod(mut c: u64, val: u64) -> u64 {
|
||||
let c0 = c >> 35;
|
||||
c = ((c & 0x7ffffffff) << 5) ^ val;
|
||||
if c0 & 1 > 0 {
|
||||
c ^= 0xf5dee51989
|
||||
};
|
||||
if c0 & 2 > 0 {
|
||||
c ^= 0xa9fdca3312
|
||||
};
|
||||
if c0 & 4 > 0 {
|
||||
c ^= 0x1bab10e32d
|
||||
};
|
||||
if c0 & 8 > 0 {
|
||||
c ^= 0x3706b1677a
|
||||
};
|
||||
if c0 & 16 > 0 {
|
||||
c ^= 0x644d626ffd
|
||||
};
|
||||
|
||||
c
|
||||
}
|
||||
|
||||
// Calculate checksum for a descriptor string
|
||||
fn calc_checksum(desc: &str) -> Result<String, String> {
|
||||
// Separate descriptor from any existing checksum
|
||||
let desc = match desc.split_once('#') {
|
||||
Some((d, _)) => d,
|
||||
None => desc,
|
||||
};
|
||||
|
||||
let mut c: u64 = 1;
|
||||
let mut cls: u64 = 0;
|
||||
let mut clscount: u64 = 0;
|
||||
|
||||
// Process each character in the descriptor
|
||||
for ch in desc.as_bytes() {
|
||||
let pos = match INPUT_CHARSET.iter().position(|b| b == ch) {
|
||||
Some(p) => p as u64,
|
||||
None => return Err(format!("Invalid character in descriptor: {}", *ch as char)),
|
||||
};
|
||||
|
||||
c = poly_mod(c, pos & 31);
|
||||
cls = cls * 3 + (pos >> 5);
|
||||
clscount += 1;
|
||||
|
||||
if clscount == 3 {
|
||||
c = poly_mod(c, cls);
|
||||
cls = 0;
|
||||
clscount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if clscount > 0 {
|
||||
c = poly_mod(c, cls);
|
||||
}
|
||||
|
||||
// Final steps in checksum calculation
|
||||
for _ in 0..8 {
|
||||
c = poly_mod(c, 0);
|
||||
}
|
||||
c ^= 1;
|
||||
|
||||
// Convert checksum to characters
|
||||
let mut checksum = String::with_capacity(8);
|
||||
for j in 0..8 {
|
||||
let idx = ((c >> (5 * (7 - j))) & 31) as usize;
|
||||
checksum.push(CHECKSUM_CHARSET[idx] as char);
|
||||
}
|
||||
|
||||
Ok(checksum)
|
||||
}
|
||||
|
||||
pub fn get_bitcoincore_descriptor(xpub: &String) -> String {
|
||||
let fingerprint = calculate_fingerprint(xpub);
|
||||
let mut bip = 84;
|
||||
let cpub = xpub.to_string();
|
||||
match &xpub[0..4] {
|
||||
"vpub" => {
|
||||
bip = 84;
|
||||
}
|
||||
"zpub" => {
|
||||
bip = 84;
|
||||
}
|
||||
&_ => {
|
||||
bip = 84;
|
||||
}
|
||||
};
|
||||
let descriptor = format!(
|
||||
"wpkh([{}/84h/0h/0h]{}/0/*)",
|
||||
fingerprint,
|
||||
convert_xpub(xpub)
|
||||
);
|
||||
let descriptor = match calc_checksum(&descriptor) {
|
||||
Ok(checksum) => {
|
||||
let clean_descriptor = descriptor.split('#').next().unwrap_or(&descriptor);
|
||||
format!("{}#{}", clean_descriptor, checksum)
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Error: {}", err);
|
||||
"".to_string()
|
||||
}
|
||||
};
|
||||
descriptor
|
||||
//format!("{}#{}",descriptor,checksum)
|
||||
}
|
||||
fn convert_xpub(xpub: &String) -> String {
|
||||
if xpub[0..4] == *"xpub" || xpub[0..4] == *"ypub" || xpub[0..4] == *"zpub" {
|
||||
return convert_to(xpub, BS58Prefix::Xpub).unwrap();
|
||||
} else {
|
||||
return convert_to(xpub, BS58Prefix::Tpub).unwrap();
|
||||
}
|
||||
}
|
||||
pub fn calculate_fingerprint(tpub: &str) -> String {
|
||||
let xpub = Xpub::from_str(&convert_to(tpub, BS58Prefix::Xpub).unwrap()).unwrap();
|
||||
let fp = xpub.fingerprint();
|
||||
let pp = xpub.parent_fingerprint;
|
||||
format!("{}", fp)
|
||||
}
|
||||
|
||||
fn base58check_decode(s: &str) -> Result<Vec<u8>, String> {
|
||||
let data = bs58::decode(s).into_vec().map_err(|e| e.to_string())?;
|
||||
@@ -31,7 +155,7 @@ fn base58check_decode(s: &str) -> Result<Vec<u8>, String> {
|
||||
return Err("Data troppo corta".to_string());
|
||||
}
|
||||
let (payload, checksum) = data.split_at(data.len() - 4);
|
||||
let hash = Sha256::digest(Sha256::digest(payload));
|
||||
let hash = Sha256::digest(&Sha256::digest(payload));
|
||||
if hash[0..4] != checksum[..] {
|
||||
return Err("Checksum invalido".to_string());
|
||||
}
|
||||
@@ -39,7 +163,7 @@ fn base58check_decode(s: &str) -> Result<Vec<u8>, String> {
|
||||
}
|
||||
|
||||
fn base58check_encode(data: &[u8]) -> String {
|
||||
let checksum = &Sha256::digest(Sha256::digest(data))[0..4];
|
||||
let checksum = &Sha256::digest(&Sha256::digest(data))[0..4];
|
||||
let full = [data, checksum].concat();
|
||||
bs58::encode(full).into_string()
|
||||
}
|
||||
@@ -54,11 +178,11 @@ fn convert_to(zpub: &str, prefix: BS58Prefix) -> Result<String, String> {
|
||||
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,
|
||||
BS58Prefix::Ypub => YPUB_PREFIX,
|
||||
BS58Prefix::Zpub => ZPUB_PREFIX,
|
||||
BS58Prefix::Vpub => VPUB_PREFIX,
|
||||
BS58Prefix::Tpub => TPUB_PREFIX,
|
||||
BS58Prefix::Upub => UPUB_PREFIX,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -71,7 +195,7 @@ pub fn new_address_from_xpub(
|
||||
) -> Result<(String, String), Box<dyn std::error::Error>> {
|
||||
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 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;
|
||||
@@ -85,15 +209,17 @@ pub fn new_address_from_xpub(
|
||||
}
|
||||
/*
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>>{
|
||||
//let zpub = "xpub6C29v8gxCXREHUzoGNfqqFqZWxTVEmYtmZshuzfSwBKNmfYQxoizRziCkkUUA4WwJZkJs2i7nttRiC6MQG7mxZpouXeYkTZe3U52RyPAeo2";
|
||||
//let zpub = "vpub5Ut36m34VebUUjdhYaxJCjSPqk3ZR8bA2MXLmbHRQCycAxy5Q1GFPJspLkJywJjBgQnvU3rmwPKTPp1ELLWeXrve3zBufpZR4MRCCTNHzsn";
|
||||
let zpub = "zpub6qdfveGrxBQN3z8paZ88EHpCn5MGXpUoHwQmHhPbj4rPQtUjbWyCHrJFYZGVY7MsmVbDaeu4JYqRqcdLzMx78wZFEWbLrF9FG3gr2MPQC5H";
|
||||
match convert_to(zpub,BS58Prefix::Xpub) {
|
||||
Ok(xpub) => println!("XPUB: {}", xpub),
|
||||
match convert_to(zpub,BS58Prefix::Tpub) {
|
||||
Ok(tpub) => println!("XPUB: {}", tpub),
|
||||
Err(e) => eprintln!("Errore: {}", e),
|
||||
}
|
||||
let xpub = Xpub::from_str(&convert_to(zpub,BS58Prefix::Xpub)?)?;
|
||||
let fingerprint = base58check_encode(&calculate_fingerprint(zpub));
|
||||
println!("ZPUB: {}, FINGERPRINT: {}",zpub,fingerprint);
|
||||
|
||||
let xpub = Xpub::from_str(&convert_to(zpub,BS58Prefix::Xpub)?)?;
|
||||
let tpub = convert_to(zpub,BS58Prefix::Tpub)?;
|
||||
let fingerprint = base58check_encode(&calculate_fingerprint(&tpub));
|
||||
println!("TPUB: {}, FINGERPRINT: {}",tpub,fingerprint);
|
||||
let derivation_path = DerivationPath::from_str("m/0/0")?;
|
||||
let secp = Secp256k1::new();
|
||||
let derived_xpub = xpub.derive_pub(&secp, &derivation_path)?;
|
||||
@@ -110,5 +236,4 @@ fn main() -> Result<(), Box<dyn std::error::Error>>{
|
||||
let address = Address::from_script(&script_pubkey, network)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
*/
|
||||
}*/
|
||||
|
||||
Reference in New Issue
Block a user