|
| 1 | +use std::str::FromStr; |
| 2 | + |
| 3 | +use crate::bitcoin::bip32::Xpub; |
| 4 | +use crate::error::WasmMiniscriptError; |
| 5 | +use crate::try_from_js_value::{get_buffer_field, get_field, get_nested_field}; |
| 6 | +use wasm_bindgen::JsValue; |
| 7 | + |
| 8 | +fn try_xpub_from_bip32_properties(bip32_key: &JsValue) -> Result<Xpub, WasmMiniscriptError> { |
| 9 | + // Extract properties using helper functions |
| 10 | + let version: u32 = get_nested_field(bip32_key, "network.bip32.public")?; |
| 11 | + let depth: u8 = get_field(bip32_key, "depth")?; |
| 12 | + let parent_fingerprint: u32 = get_field(bip32_key, "parentFingerprint")?; |
| 13 | + let index: u32 = get_field(bip32_key, "index")?; |
| 14 | + let chain_code_bytes: [u8; 32] = get_buffer_field(bip32_key, "chainCode")?; |
| 15 | + let public_key_bytes: [u8; 33] = get_buffer_field(bip32_key, "publicKey")?; |
| 16 | + |
| 17 | + // Build BIP32 serialization (78 bytes total) |
| 18 | + let mut data = Vec::with_capacity(78); |
| 19 | + data.extend_from_slice(&version.to_be_bytes()); // 4 bytes: version |
| 20 | + data.push(depth); // 1 byte: depth |
| 21 | + data.extend_from_slice(&parent_fingerprint.to_be_bytes()); // 4 bytes: parent fingerprint |
| 22 | + data.extend_from_slice(&index.to_be_bytes()); // 4 bytes: index |
| 23 | + data.extend_from_slice(&chain_code_bytes); // 32 bytes: chain code |
| 24 | + data.extend_from_slice(&public_key_bytes); // 33 bytes: public key |
| 25 | + |
| 26 | + // Use the Xpub::decode method which properly handles network detection and constructs the Xpub |
| 27 | + Xpub::decode(&data) |
| 28 | + .map_err(|e| WasmMiniscriptError::new(&format!("Failed to decode xpub: {}", e))) |
| 29 | +} |
| 30 | + |
| 31 | +fn xpub_from_base58_method(bip32_key: &JsValue) -> Result<Xpub, WasmMiniscriptError> { |
| 32 | + // Fallback: Call toBase58() method on BIP32Interface |
| 33 | + let to_base58 = js_sys::Reflect::get(bip32_key, &JsValue::from_str("toBase58")) |
| 34 | + .map_err(|_| WasmMiniscriptError::new("Failed to get 'toBase58' method"))?; |
| 35 | + |
| 36 | + if !to_base58.is_function() { |
| 37 | + return Err(WasmMiniscriptError::new("'toBase58' is not a function")); |
| 38 | + } |
| 39 | + |
| 40 | + let to_base58_fn = js_sys::Function::from(to_base58); |
| 41 | + let xpub_str = to_base58_fn |
| 42 | + .call0(bip32_key) |
| 43 | + .map_err(|_| WasmMiniscriptError::new("Failed to call 'toBase58'"))?; |
| 44 | + |
| 45 | + let xpub_string = xpub_str |
| 46 | + .as_string() |
| 47 | + .ok_or_else(|| WasmMiniscriptError::new("'toBase58' did not return a string"))?; |
| 48 | + |
| 49 | + Xpub::from_str(&xpub_string) |
| 50 | + .map_err(|e| WasmMiniscriptError::new(&format!("Failed to parse xpub: {}", e))) |
| 51 | +} |
| 52 | + |
| 53 | +pub fn xpub_from_bip32interface(bip32_key: &JsValue) -> Result<Xpub, WasmMiniscriptError> { |
| 54 | + // Try to construct from properties first, fall back to toBase58() if that fails |
| 55 | + try_xpub_from_bip32_properties(bip32_key).or_else(|_| xpub_from_base58_method(bip32_key)) |
| 56 | +} |
0 commit comments