Skip to content

Commit 09db15f

Browse files
OttoAllmendingerllm-git
andcommitted
feat(wasm-utxo): add supportsScriptType function
Add `supportsScriptType` function to check if a given network supports a particular wallet script type (p2sh, p2wsh, p2tr, etc). This provides a simple API for frontends to determine script compatibility before attempting to create wallets. Major improvements: - Refactored script type handling to use a dedicated OutputScriptType enum - Separated script type from derivation chain/scope concerns - Added network compatibility checks for script types - Exposed JS API with proper TypeScript definitions Issue: BTC-2916 Co-authored-by: llm-git <llm-git@ttll.de>
1 parent a665edb commit 09db15f

File tree

9 files changed

+332
-189
lines changed

9 files changed

+332
-189
lines changed

packages/wasm-utxo/js/fixedScriptWallet/index.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { FixedScriptWalletNamespace } from "../wasm/wasm_utxo.js";
2+
import type { CoinName } from "../coinName.js";
3+
14
export { RootWalletKeys, type WalletKeysArg, type IWalletKeys } from "./RootWalletKeys.js";
25
export { ReplayProtection, type ReplayProtectionArg } from "./ReplayProtection.js";
36
export { outputScript, address } from "./address.js";
@@ -26,3 +29,36 @@ export {
2629
type ZcashNetworkName,
2730
type CreateEmptyZcashOptions,
2831
} from "./ZcashBitGoPsbt.js";
32+
33+
/**
34+
* Fixed-script wallet script types (2-of-3 multisig)
35+
*
36+
* This type represents the abstract script type, independent of chain (external/internal).
37+
* Use this for checking network support or when you need the script type without derivation info.
38+
*/
39+
export type ScriptType = "p2sh" | "p2shP2wsh" | "p2wsh" | "p2tr" | "p2trMusig2";
40+
41+
/**
42+
* Check if a network supports a given fixed-script wallet script type
43+
*
44+
* @param coin - Coin name (e.g., "btc", "ltc", "doge")
45+
* @param scriptType - Script type to check
46+
* @returns `true` if the network supports the script type, `false` otherwise
47+
*
48+
* @example
49+
* ```typescript
50+
* // Bitcoin supports all script types
51+
* supportsScriptType("btc", "p2tr"); // true
52+
*
53+
* // Litecoin supports segwit but not taproot
54+
* supportsScriptType("ltc", "p2wsh"); // true
55+
* supportsScriptType("ltc", "p2tr"); // false
56+
*
57+
* // Dogecoin only supports legacy scripts
58+
* supportsScriptType("doge", "p2sh"); // true
59+
* supportsScriptType("doge", "p2wsh"); // false
60+
* ```
61+
*/
62+
export function supportsScriptType(coin: CoinName, scriptType: ScriptType): boolean {
63+
return FixedScriptWalletNamespace.supports_script_type(coin, scriptType);
64+
}

packages/wasm-utxo/src/address/networks.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use super::{
1313
ZCASH_TEST,
1414
};
1515
use crate::bitcoin::Script;
16+
use crate::fixed_script_wallet::wallet_scripts::OutputScriptType;
1617
use crate::networks::Network;
1718
use miniscript::bitcoin::WitnessVersion;
1819

@@ -120,6 +121,15 @@ impl OutputScriptSupport {
120121
}
121122
Ok(())
122123
}
124+
125+
/// Check if the network supports a given fixed-script wallet script type
126+
pub fn supports_script_type(&self, script_type: OutputScriptType) -> bool {
127+
match script_type {
128+
OutputScriptType::P2sh => true, // all networks support legacy scripts
129+
OutputScriptType::P2shP2wsh | OutputScriptType::P2wsh => self.segwit,
130+
OutputScriptType::P2trLegacy | OutputScriptType::P2trMusig2 => self.taproot,
131+
}
132+
}
123133
}
124134

125135
impl Network {

packages/wasm-utxo/src/fixed_script_wallet/bitgo_psbt/mod.rs

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,13 @@ fn get_default_sighash_type(
184184
network: Network,
185185
chain: crate::fixed_script_wallet::wallet_scripts::Chain,
186186
) -> miniscript::bitcoin::psbt::PsbtSighashType {
187-
use crate::fixed_script_wallet::wallet_scripts::Chain;
187+
use crate::fixed_script_wallet::wallet_scripts::OutputScriptType;
188188
use miniscript::bitcoin::sighash::{EcdsaSighashType, TapSighashType};
189189

190190
// For taproot, always use Default
191191
if matches!(
192-
chain,
193-
Chain::P2trInternal
194-
| Chain::P2trExternal
195-
| Chain::P2trMusig2Internal
196-
| Chain::P2trMusig2External
192+
chain.script_type,
193+
OutputScriptType::P2trLegacy | OutputScriptType::P2trMusig2
197194
) {
198195
return TapSighashType::Default.into();
199196
}
@@ -758,7 +755,7 @@ impl BitGoPsbt {
758755
options: WalletInputOptions,
759756
) -> Result<usize, String> {
760757
use crate::fixed_script_wallet::to_pub_triple;
761-
use crate::fixed_script_wallet::wallet_scripts::{Chain, WalletScripts};
758+
use crate::fixed_script_wallet::wallet_scripts::{Chain, OutputScriptType, WalletScripts};
762759
use miniscript::bitcoin::psbt::Input;
763760
use miniscript::bitcoin::taproot::{LeafVersion, TapLeafHash};
764761
use miniscript::bitcoin::{transaction::Sequence, Amount, OutPoint, TxIn, TxOut};
@@ -799,18 +796,8 @@ impl BitGoPsbt {
799796
// Create the PSBT input
800797
let mut psbt_input = Input::default();
801798

802-
// Determine if segwit based on chain type
803-
let is_segwit = matches!(
804-
chain_enum,
805-
Chain::P2shP2wshExternal
806-
| Chain::P2shP2wshInternal
807-
| Chain::P2wshExternal
808-
| Chain::P2wshInternal
809-
| Chain::P2trInternal
810-
| Chain::P2trExternal
811-
| Chain::P2trMusig2Internal
812-
| Chain::P2trMusig2External
813-
);
799+
// Determine if segwit based on chain type (all types except P2sh are segwit)
800+
let is_segwit = chain_enum.script_type != OutputScriptType::P2sh;
814801

815802
if let (false, Some(tx_bytes)) = (is_segwit, options.prev_tx) {
816803
// Non-segwit with prev_tx: use non_witness_utxo

packages/wasm-utxo/src/fixed_script_wallet/bitgo_psbt/psbt_wallet_input.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use miniscript::bitcoin::secp256k1::{self, PublicKey};
44
use miniscript::bitcoin::{OutPoint, ScriptBuf, TapLeafHash, XOnlyPublicKey};
55

66
use crate::bitcoin::bip32::KeySource;
7-
use crate::fixed_script_wallet::{Chain, ReplayProtection, RootWalletKeys, WalletScripts};
7+
use crate::fixed_script_wallet::{
8+
Chain, OutputScriptType, ReplayProtection, RootWalletKeys, WalletScripts,
9+
};
810
use crate::Network;
911

1012
pub type Bip32DerivationMap = std::collections::BTreeMap<PublicKey, KeySource>;
@@ -655,12 +657,12 @@ pub enum InputScriptType {
655657
impl InputScriptType {
656658
pub fn from_script_id(script_id: ScriptId, psbt_input: &Input) -> Result<Self, String> {
657659
let chain = Chain::try_from(script_id.chain).map_err(|e| e.to_string())?;
658-
match chain {
659-
Chain::P2shExternal | Chain::P2shInternal => Ok(InputScriptType::P2sh),
660-
Chain::P2shP2wshExternal | Chain::P2shP2wshInternal => Ok(InputScriptType::P2shP2wsh),
661-
Chain::P2wshExternal | Chain::P2wshInternal => Ok(InputScriptType::P2wsh),
662-
Chain::P2trInternal | Chain::P2trExternal => Ok(InputScriptType::P2trLegacy),
663-
Chain::P2trMusig2Internal | Chain::P2trMusig2External => {
660+
match chain.script_type {
661+
OutputScriptType::P2sh => Ok(InputScriptType::P2sh),
662+
OutputScriptType::P2shP2wsh => Ok(InputScriptType::P2shP2wsh),
663+
OutputScriptType::P2wsh => Ok(InputScriptType::P2wsh),
664+
OutputScriptType::P2trLegacy => Ok(InputScriptType::P2trLegacy),
665+
OutputScriptType::P2trMusig2 => {
664666
// check if tap_script_sigs or tap_scripts are set
665667
if !psbt_input.tap_script_sigs.is_empty() || !psbt_input.tap_scripts.is_empty() {
666668
Ok(InputScriptType::P2trMusig2ScriptPath)

packages/wasm-utxo/src/fixed_script_wallet/test_utils/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub mod fixtures;
44
pub mod psbt_compare;
55

66
use super::wallet_keys::XpubTriple;
7-
use super::wallet_scripts::{Chain, WalletScripts};
7+
use super::wallet_scripts::{Chain, OutputScriptType, Scope, WalletScripts};
88
use crate::bitcoin::bip32::{DerivationPath, Fingerprint, Xpriv, Xpub};
99
use crate::bitcoin::psbt::{Input as PsbtInput, Output as PsbtOutput, Psbt};
1010
use crate::bitcoin::{Transaction, TxIn, TxOut};
@@ -38,7 +38,7 @@ pub fn create_external_output(seed: &str) -> PsbtOutput {
3838
let xpubs = get_test_wallet_keys(seed);
3939
let _scripts = WalletScripts::from_wallet_keys(
4040
&RootWalletKeys::new(xpubs),
41-
Chain::P2wshExternal,
41+
Chain::new(OutputScriptType::P2wsh, Scope::External),
4242
0,
4343
&Network::Bitcoin.output_script_support(),
4444
)

packages/wasm-utxo/src/fixed_script_wallet/wallet_scripts/checkmultisig.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,13 @@ mod tests {
100100
use crate::bitcoin::blockdata::script::Builder;
101101
use crate::fixed_script_wallet::wallet_keys::tests::get_test_wallet_keys;
102102
use crate::fixed_script_wallet::wallet_keys::to_pub_triple;
103-
use crate::fixed_script_wallet::wallet_scripts::Chain;
104103

105104
#[test]
106105
fn test_parse_multisig_script_2_of_3_valid() {
107106
// Get test keys
108107
let wallet_keys = get_test_wallet_keys("test_parse");
109108
let derived_keys = wallet_keys
110-
.derive_for_chain_and_index(Chain::P2shExternal as u32, 0)
109+
.derive_for_chain_and_index(0, 0)
111110
.unwrap();
112111
let pub_triple = to_pub_triple(&derived_keys);
113112

@@ -127,7 +126,7 @@ mod tests {
127126
for seed in ["seed1", "seed2", "seed3"] {
128127
let wallet_keys = get_test_wallet_keys(seed);
129128
let derived_keys = wallet_keys
130-
.derive_for_chain_and_index(Chain::P2shExternal as u32, 42)
129+
.derive_for_chain_and_index(0, 42)
131130
.unwrap();
132131
let original_keys = to_pub_triple(&derived_keys);
133132

@@ -169,7 +168,7 @@ mod tests {
169168
// Create a valid key for testing
170169
let wallet_keys = get_test_wallet_keys("test_wrong_quorum");
171170
let derived_keys = wallet_keys
172-
.derive_for_chain_and_index(Chain::P2shExternal as u32, 0)
171+
.derive_for_chain_and_index(0, 0)
173172
.unwrap();
174173
let pub_triple = to_pub_triple(&derived_keys);
175174

@@ -195,7 +194,7 @@ mod tests {
195194
// Create a valid key for testing
196195
let wallet_keys = get_test_wallet_keys("test_wrong_total");
197196
let derived_keys = wallet_keys
198-
.derive_for_chain_and_index(Chain::P2shExternal as u32, 0)
197+
.derive_for_chain_and_index(0, 0)
199198
.unwrap();
200199
let pub_triple = to_pub_triple(&derived_keys);
201200

@@ -221,7 +220,7 @@ mod tests {
221220
// Create a valid key for testing
222221
let wallet_keys = get_test_wallet_keys("test_missing_checkmultisig");
223222
let derived_keys = wallet_keys
224-
.derive_for_chain_and_index(Chain::P2shExternal as u32, 0)
223+
.derive_for_chain_and_index(0, 0)
225224
.unwrap();
226225
let pub_triple = to_pub_triple(&derived_keys);
227226

0 commit comments

Comments
 (0)