@@ -8,11 +8,12 @@ use crate::fixed_script_wallet::bitgo_psbt::{
88 ProprietaryKeySubtype ,
99} ;
1010use crate :: fixed_script_wallet:: wallet_scripts:: {
11- build_multisig_script_2_of_3, build_p2tr_ns_script, ScriptP2tr ,
11+ build_multisig_script_2_of_3, build_p2tr_ns_script, ScriptP2mr , ScriptP2tr ,
1212} ;
1313use crate :: fixed_script_wallet:: { to_pub_triple, Chain , PubTriple , RootWalletKeys , WalletScripts } ;
1414use crate :: networks:: Network ;
1515
16+ use miniscript:: bitcoin:: hashes:: Hash ;
1617use miniscript:: bitcoin:: taproot:: { LeafVersion , TapLeafHash } ;
1718use miniscript:: bitcoin:: { Amount , ScriptBuf , Transaction , TxIn , TxOut } ;
1819
@@ -140,6 +141,45 @@ pub fn add_bip322_input(
140141 create_bip32_derivation ( wallet_keys, chain, index) ;
141142 inner_psbt. inputs [ input_index] . witness_script = Some ( script. witness_script . clone ( ) ) ;
142143 }
144+ WalletScripts :: P2mr ( script) => {
145+ // P2MR is always script-path (no key-path). Same sighash as P2TR
146+ // (BIP-360 reuses BIP-342 common signature message).
147+ //
148+ // Unlike P2trLegacy, we use the precomputed leaf hashes from ScriptP2mr
149+ // rather than re-deriving keys and rebuilding scripts. The tree is fixed:
150+ // leaf[0]: user+bitgo (key indices {0,2})
151+ // leaf[1]: user+backup (key indices {0,1})
152+ // leaf[2]: backup+bitgo (key indices {1,2})
153+ //
154+ // tap_scripts is skipped because P2MR control blocks (no internal key)
155+ // can't be represented as rust-bitcoin's ControlBlock type.
156+ let ( signer_idx, cosigner_idx) =
157+ sign_path. ok_or ( "signer and cosigner are required for p2mr inputs" ) ?;
158+
159+ let mut pair = [ signer_idx, cosigner_idx] ;
160+ pair. sort ( ) ;
161+ let leaf_idx = match pair {
162+ [ 0 , 2 ] => 0 ,
163+ [ 0 , 1 ] => 1 ,
164+ [ 1 , 2 ] => 2 ,
165+ _ => {
166+ return Err ( format ! (
167+ "Invalid signer pair: ({}, {})" ,
168+ signer_idx, cosigner_idx
169+ ) )
170+ }
171+ } ;
172+
173+ let leaf_hash = TapLeafHash :: from_byte_array ( script. leaves [ leaf_idx] . leaf_hash ) ;
174+
175+ inner_psbt. inputs [ input_index] . tap_key_origins = create_tap_bip32_derivation (
176+ wallet_keys,
177+ chain,
178+ index,
179+ & [ signer_idx, cosigner_idx] ,
180+ Some ( leaf_hash) ,
181+ ) ;
182+ }
143183 WalletScripts :: P2trLegacy ( script) | WalletScripts :: P2trMusig2 ( script) => {
144184 // For taproot, sign_path is required
145185 let ( signer_idx, cosigner_idx) =
@@ -428,7 +468,7 @@ pub fn verify_bip322_psbt_input(
428468///
429469/// # Arguments
430470/// * `pubkeys` - The three wallet pubkeys [user, backup, bitgo]
431- /// * `script_type` - One of: "p2sh", "p2shP2wsh", "p2wsh", "p2tr", "p2trMusig2"
471+ /// * `script_type` - One of: "p2sh", "p2shP2wsh", "p2wsh", "p2tr", "p2trMusig2", "p2mr"
432472///
433473/// # Returns
434474/// The output script (scriptPubKey)
@@ -458,8 +498,12 @@ fn build_output_script_from_pubkeys(
458498 let script_p2tr = ScriptP2tr :: new ( pubkeys, true ) ;
459499 Ok ( script_p2tr. output_script ( ) )
460500 }
501+ "p2mr" => {
502+ let script_p2mr = ScriptP2mr :: new ( pubkeys) ;
503+ Ok ( script_p2mr. output_script ( ) )
504+ }
461505 _ => Err ( format ! (
462- "Unknown script type '{}'. Expected: p2sh, p2shP2wsh, p2wsh, p2tr, p2trMusig2" ,
506+ "Unknown script type '{}'. Expected: p2sh, p2shP2wsh, p2wsh, p2tr, p2trMusig2, p2mr " ,
463507 script_type
464508 ) ) ,
465509 }
@@ -537,7 +581,7 @@ fn verify_bip322_tx_structure(tx: &Transaction, input_index: usize) -> Result<()
537581/// * `input_index` - The index of the input to verify
538582/// * `message` - The message that was signed
539583/// * `pubkeys` - The three wallet pubkeys [user, backup, bitgo]
540- /// * `script_type` - One of: "p2sh", "p2shP2wsh", "p2wsh", "p2tr", "p2trMusig2"
584+ /// * `script_type` - One of: "p2sh", "p2shP2wsh", "p2wsh", "p2tr", "p2trMusig2", "p2mr"
541585/// * `is_script_path` - For taproot types, whether script path was used (None for non-taproot)
542586/// * `tag` - Optional custom tag for message hashing
543587///
@@ -614,7 +658,7 @@ pub fn verify_bip322_psbt_input_with_pubkeys(
614658/// * `input_index` - The index of the input to verify
615659/// * `message` - The message that was signed
616660/// * `pubkeys` - The three wallet pubkeys [user, backup, bitgo]
617- /// * `script_type` - One of: "p2sh", "p2shP2wsh", "p2wsh", "p2tr", "p2trMusig2"
661+ /// * `script_type` - One of: "p2sh", "p2shP2wsh", "p2wsh", "p2tr", "p2trMusig2", "p2mr"
618662/// * `is_script_path` - For taproot types, whether script path was used (None for non-taproot)
619663/// * `tag` - Optional custom tag for message hashing
620664///
0 commit comments