Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions packages/wasm-utxo/src/address/cashaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,9 @@ fn polymod(values: &[u8]) -> u64 {
let c0 = (c >> 35) as u8;
c = ((c & 0x07ffffffff) << 5) ^ (d as u64);

for i in 0..5 {
for (i, &generator) in generators.iter().enumerate() {
if (c0 & (1 << i)) != 0 {
c ^= generators[i];
c ^= generator;
}
}
}
Expand Down Expand Up @@ -457,7 +457,6 @@ mod tests {
///
/// Using bech32 crate's `ByteIterExt::bytes_to_fes()` or checksum functions would fail these tests
/// because they implement Bech32/Bech32m logic, not CashAddr logic.

// Test vector: 20-byte P2PKH payload
const TEST_HASH_20: &str = "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9";

Expand All @@ -474,7 +473,7 @@ mod tests {
// Test roundtrip
let (decoded_hash, is_p2sh) = decode_cashaddr(&address, "bitcoincash").unwrap();
assert_eq!(decoded_hash, hash);
assert_eq!(is_p2sh, false);
assert!(!is_p2sh);
}

#[test]
Expand All @@ -490,7 +489,7 @@ mod tests {
// Test roundtrip
let (decoded_hash, is_p2sh) = decode_cashaddr(&address, "bchtest").unwrap();
assert_eq!(decoded_hash, hash);
assert_eq!(is_p2sh, true);
assert!(is_p2sh);
}

#[test]
Expand All @@ -503,7 +502,7 @@ mod tests {
// Test roundtrip
let (decoded_hash, is_p2sh) = decode_cashaddr(&address, "pref").unwrap();
assert_eq!(decoded_hash, hash);
assert_eq!(is_p2sh, true);
assert!(is_p2sh);
}

#[test]
Expand Down Expand Up @@ -585,7 +584,7 @@ mod tests {
let (hash, is_p2sh) = decode_cashaddr(uppercase, "bitcoincash").unwrap();

assert_eq!(hex::encode(hash).to_uppercase(), TEST_HASH_20);
assert_eq!(is_p2sh, false);
assert!(!is_p2sh);
}

#[test]
Expand Down Expand Up @@ -628,7 +627,7 @@ mod tests {
// Test roundtrip
let (decoded_hash, is_p2sh) = decode_cashaddr(&address, "ecash").unwrap();
assert_eq!(decoded_hash, hash);
assert_eq!(is_p2sh, false);
assert!(!is_p2sh);
}

#[test]
Expand Down
39 changes: 22 additions & 17 deletions packages/wasm-utxo/src/descriptor.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::error::WasmMiniscriptError;
use crate::try_into_js_value::TryIntoJsValue;
use miniscript::bitcoin::secp256k1::{Context, Secp256k1, Signing};
use miniscript::bitcoin::secp256k1::{Secp256k1, Signing};
use miniscript::bitcoin::ScriptBuf;
use miniscript::descriptor::KeyMap;
use miniscript::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey};
use std::fmt;
use std::str::FromStr;
use wasm_bindgen::prelude::*;

Expand All @@ -27,12 +28,9 @@ impl WrapDescriptor {
}

#[wasm_bindgen(js_name = toString)]
#[allow(clippy::inherent_to_string_shadow_display)]
pub fn to_string(&self) -> String {
match &self.0 {
WrapDescriptorEnum::Derivable(desc, _) => desc.to_string(),
WrapDescriptorEnum::Definite(desc) => desc.to_string(),
WrapDescriptorEnum::String(desc) => desc.to_string(),
}
format!("{}", self)
}

#[wasm_bindgen(js_name = hasWildcard)]
Expand Down Expand Up @@ -115,7 +113,7 @@ impl WrapDescriptor {
secp: &Secp256k1<C>,
descriptor: &str,
) -> Result<WrapDescriptor, WasmMiniscriptError> {
let (desc, keys) = Descriptor::parse_descriptor(&secp, descriptor)?;
let (desc, keys) = Descriptor::parse_descriptor(secp, descriptor)?;
Ok(WrapDescriptor(WrapDescriptorEnum::Derivable(desc, keys)))
}

Expand Down Expand Up @@ -199,6 +197,16 @@ impl WrapDescriptor {
}
}

impl fmt::Display for WrapDescriptor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
WrapDescriptorEnum::Derivable(desc, _) => write!(f, "{}", desc),
WrapDescriptorEnum::Definite(desc) => write!(f, "{}", desc),
WrapDescriptorEnum::String(desc) => write!(f, "{}", desc),
}
}
}

impl FromStr for WrapDescriptor {
type Err = WasmMiniscriptError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Expand All @@ -217,15 +225,12 @@ mod tests {
)
.unwrap();

assert_eq!(desc.has_wildcard(), false);
assert_eq!(
match desc {
WrapDescriptor {
0: crate::descriptor::WrapDescriptorEnum::Definite(_),
} => true,
_ => false,
},
true
);
assert!(!desc.has_wildcard());
assert!(matches!(
desc,
WrapDescriptor {
0: crate::descriptor::WrapDescriptorEnum::Definite(_),
}
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn build_multisig_script_2_of_3(keys: &PubTriple) -> ScriptBuf {
let total_count = 3;
let mut builder = Builder::default().push_int(quorum as i64);
for key in keys {
builder = builder.push_slice(&key.to_bytes())
builder = builder.push_slice(key.to_bytes())
}
builder
.push_int(total_count as i64)
Expand Down Expand Up @@ -51,21 +51,21 @@ pub fn parse_multisig_script_2_of_3(script: &ScriptBuf) -> Result<PubTriple, Str

// Extract the three public keys
let mut keys = Vec::new();
for i in 1..4 {
match &instructions[i] {
for (idx, instruction) in instructions.iter().enumerate().skip(1).take(3) {
match instruction {
Instruction::PushBytes(bytes) => {
let key = CompressedPublicKey::from_slice(bytes.as_bytes()).map_err(|e| {
format!(
"Failed to parse compressed public key at position {}: {}",
i, e
idx, e
)
})?;
keys.push(key);
}
_ => {
return Err(format!(
"Instruction at position {} should be a push bytes instruction",
i
idx
));
}
}
Expand Down Expand Up @@ -150,7 +150,7 @@ mod tests {
// Test script with wrong number of instructions
let script = Builder::new()
.push_opcode(OP_PUSHNUM_2)
.push_slice(&[0x02; 33]) // Only one key instead of three
.push_slice([0x02; 33]) // Only one key instead of three
.push_opcode(OP_PUSHNUM_3)
.push_opcode(OP_CHECKMULTISIG)
.into_script();
Expand All @@ -173,9 +173,9 @@ mod tests {
// Build script with wrong quorum (OP_1 instead of OP_2)
let script = Builder::new()
.push_opcode(OP_PUSHNUM_1)
.push_slice(&pub_triple[0].to_bytes())
.push_slice(&pub_triple[1].to_bytes())
.push_slice(&pub_triple[2].to_bytes())
.push_slice(pub_triple[0].to_bytes())
.push_slice(pub_triple[1].to_bytes())
.push_slice(pub_triple[2].to_bytes())
.push_opcode(OP_PUSHNUM_3)
.push_opcode(OP_CHECKMULTISIG)
.into_script();
Expand All @@ -198,9 +198,9 @@ mod tests {
// Build script with wrong total (OP_4 instead of OP_3)
let script = Builder::new()
.push_opcode(OP_PUSHNUM_2)
.push_slice(&pub_triple[0].to_bytes())
.push_slice(&pub_triple[1].to_bytes())
.push_slice(&pub_triple[2].to_bytes())
.push_slice(pub_triple[0].to_bytes())
.push_slice(pub_triple[1].to_bytes())
.push_slice(pub_triple[2].to_bytes())
.push_opcode(OP_PUSHNUM_4)
.push_opcode(OP_CHECKMULTISIG)
.into_script();
Expand All @@ -223,9 +223,9 @@ mod tests {
// Build script without OP_CHECKMULTISIG
let script = Builder::new()
.push_opcode(OP_PUSHNUM_2)
.push_slice(&pub_triple[0].to_bytes())
.push_slice(&pub_triple[1].to_bytes())
.push_slice(&pub_triple[2].to_bytes())
.push_slice(pub_triple[0].to_bytes())
.push_slice(pub_triple[1].to_bytes())
.push_slice(pub_triple[2].to_bytes())
.push_opcode(OP_PUSHNUM_3)
.push_opcode(OP_PUSHNUM_1) // Wrong opcode instead of OP_CHECKMULTISIG
.into_script();
Expand All @@ -242,9 +242,9 @@ mod tests {
// Build script with invalid public key data
let script = Builder::new()
.push_opcode(OP_PUSHNUM_2)
.push_slice(&[0x00; 10]) // Invalid public key (too short)
.push_slice(&[0x02; 33]) // Valid compressed pubkey format
.push_slice(&[0x03; 33]) // Valid compressed pubkey format
.push_slice([0x00; 10]) // Invalid public key (too short)
.push_slice([0x02; 33]) // Valid compressed pubkey format
.push_slice([0x03; 33]) // Valid compressed pubkey format
.push_opcode(OP_PUSHNUM_3)
.push_opcode(OP_CHECKMULTISIG)
.into_script();
Expand All @@ -262,8 +262,8 @@ mod tests {
let script = Builder::new()
.push_opcode(OP_PUSHNUM_2)
.push_opcode(OP_PUSHNUM_1) // Wrong: should be pubkey bytes
.push_slice(&[0x02; 33])
.push_slice(&[0x03; 33])
.push_slice([0x02; 33])
.push_slice([0x03; 33])
.push_opcode(OP_PUSHNUM_3)
.push_opcode(OP_CHECKMULTISIG)
.into_script();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub fn build_p2tr_ns_script(keys: &[CompressedPublicKey]) -> ScriptBuf {
for (i, key) in keys.iter().enumerate() {
// convert to xonly key
let key_bytes = to_xonly_pubkey(*key);
builder = builder.push_slice(&key_bytes);
builder = builder.push_slice(key_bytes);
if i == keys.len() - 1 {
builder = builder.push_opcode(OP_CHECKSIG);
} else {
Expand Down Expand Up @@ -89,11 +89,11 @@ impl ScriptP2tr {

pub fn output_script(&self) -> ScriptBuf {
let output_key = self.spend_info.output_key().to_inner();
let output_script = Builder::new()

Builder::new()
.push_int(1)
.push_slice(output_key.serialize())
.into_script();
output_script
.into_script()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ impl std::fmt::Display for WalletScripts {
f,
"{}",
match self {
WalletScripts::P2sh(_) => format!("P2sh"),
WalletScripts::P2shP2wsh(_) => format!("P2shP2wsh"),
WalletScripts::P2wsh(_) => format!("P2wsh"),
WalletScripts::P2trLegacy(_) => format!("P2trLegacy"),
WalletScripts::P2trMusig2(_) => format!("P2trMusig2"),
WalletScripts::P2sh(_) => "P2sh".to_string(),
WalletScripts::P2shP2wsh(_) => "P2shP2wsh".to_string(),
WalletScripts::P2wsh(_) => "P2wsh".to_string(),
WalletScripts::P2trLegacy(_) => "P2trLegacy".to_string(),
WalletScripts::P2trMusig2(_) => "P2trMusig2".to_string(),
}
)
}
Expand Down Expand Up @@ -165,7 +165,7 @@ pub fn derive_xpubs_with_path(
) -> XpubTriple {
let derived = xpubs
.iter()
.map(|k| k.derive_pub(&ctx, &p).unwrap())
.map(|k| k.derive_pub(ctx, &p).unwrap())
.collect::<Vec<_>>();
derived.try_into().expect("could not convert vec to array")
}
Expand All @@ -182,7 +182,7 @@ pub fn derive_xpubs(
index: chain as u32,
})
.child(ChildNumber::Normal { index });
derive_xpubs_with_path(&xpubs, ctx, p)
derive_xpubs_with_path(xpubs, ctx, p)
}

#[cfg(test)]
Expand Down Expand Up @@ -383,14 +383,14 @@ mod tests {
.expect("Failed to find input with script type");

let (chain, index) =
parse_fixture_paths(&input_fixture).expect("Failed to parse fixture paths");
parse_fixture_paths(input_fixture).expect("Failed to parse fixture paths");
let scripts = WalletScripts::from_xpubs(&xpubs, chain, index);

// Use the new helper methods for validation
match (scripts, input_fixture) {
(WalletScripts::P2sh(scripts), fixtures::PsbtInputFixture::P2sh(fixture_input)) => {
let vout = fixture.inputs[input_index].index as usize;
let output_script = get_output_script_from_non_witness_utxo(&fixture_input, vout);
let output_script = get_output_script_from_non_witness_utxo(fixture_input, vout);
fixture_input
.assert_matches_wallet_scripts(&scripts, &output_script)
.expect("P2sh validation failed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::bitcoin::{CompressedPublicKey, ScriptBuf};
/// Build bare p2pk script (used for p2sh-p2pk replay protection)
pub fn build_p2pk_script(key: CompressedPublicKey) -> ScriptBuf {
Builder::default()
.push_slice(&key.to_bytes())
.push_slice(key.to_bytes())
.push_opcode(OP_CHECKSIG)
.into_script()
}
Expand Down Expand Up @@ -58,7 +58,7 @@ mod tests {
let expected_redeem_script = &p2shp2pk_input.redeem_script;
let expected_pubkey = p2shp2pk_input
.partial_sig
.get(0)
.first()
.map(|sig| &sig.pubkey)
.expect("No partial signature found");

Expand Down
1 change: 1 addition & 0 deletions packages/wasm-utxo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod try_into_js_value;
pub use ::miniscript::bitcoin;

pub use address::utxolib_compat;
pub use address::*;
pub use descriptor::WrapDescriptor;
pub use miniscript::WrapMiniscript;
pub use psbt::WrapPsbt;
Expand Down
10 changes: 9 additions & 1 deletion packages/wasm-utxo/src/miniscript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::error::WasmMiniscriptError;
use crate::try_into_js_value::TryIntoJsValue;
use miniscript::bitcoin::{PublicKey, XOnlyPublicKey};
use miniscript::{bitcoin, Legacy, Miniscript, Segwitv0, Tap};
use std::fmt;
use std::str::FromStr;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
Expand Down Expand Up @@ -35,8 +36,9 @@ impl WrapMiniscript {
}

#[wasm_bindgen(js_name = toString)]
#[allow(clippy::inherent_to_string_shadow_display)]
pub fn to_string(&self) -> String {
unwrap_apply!(&self.0, |ms| ms.to_string())
format!("{}", self)
}

#[wasm_bindgen(js_name = encode)]
Expand Down Expand Up @@ -112,3 +114,9 @@ impl From<Miniscript<PublicKey, Legacy>> for WrapMiniscript {
WrapMiniscript(WrapMiniscriptEnum::Legacy(miniscript))
}
}

impl fmt::Display for WrapMiniscript {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unwrap_apply!(&self.0, |ms| write!(f, "{}", ms))
}
}
Loading