19
19
// See the License for the specific language governing permissions and
20
20
// limitations under the License.
21
21
22
- use std:: collections:: { BTreeSet , HashMap } ;
22
+ use std:: collections:: { BTreeMap , BTreeSet , HashMap } ;
23
23
use std:: fmt:: { self , Display , Formatter } ;
24
24
use std:: iter;
25
25
use std:: str:: FromStr ;
@@ -38,17 +38,9 @@ use bpstd::{
38
38
use commit_verify:: CommitVerify ;
39
39
use indexmap:: IndexMap ;
40
40
41
- #[ derive( Copy , Clone , Eq , PartialEq , Debug , Display , Error ) ]
42
- #[ display( "terminal derivation {0} already has a taptweak assigned" ) ]
43
- pub struct TapTweakAlreadyAssigned ( pub Terminal ) ;
44
-
45
41
pub trait DescriptorRgb < K = XpubDerivable , V = ( ) > : Descriptor < K , V > {
46
42
fn close_method ( & self ) -> CloseMethod ;
47
- fn add_tapret_tweak (
48
- & mut self ,
49
- terminal : Terminal ,
50
- tweak : TapretCommitment ,
51
- ) -> Result < ( ) , TapTweakAlreadyAssigned > ;
43
+ fn add_tapret_tweak ( & mut self , terminal : Terminal , tweak : TapretCommitment ) ;
52
44
}
53
45
54
46
#[ derive( Copy , Clone , Ord , PartialOrd , Eq , PartialEq , Hash , Debug , Display ) ]
@@ -119,19 +111,24 @@ impl From<RgbKeychain> for Keychain {
119
111
) ]
120
112
pub struct TapretKey < K : DeriveXOnly = XpubDerivable > {
121
113
pub tr : TrKey < K > ,
122
- // TODO: Allow multiple tweaks per index by introducing derivation using new Terminal trait
123
- pub tweaks : HashMap < Terminal , TapretCommitment > ,
114
+ pub tweaks : BTreeMap < Terminal , BTreeSet < TapretCommitment > > ,
124
115
}
125
116
126
117
impl < K : DeriveXOnly + Display > Display for TapretKey < K > {
127
118
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
128
119
write ! ( f, "tapret({},tweaks(" , self . tr. as_internal_key( ) ) ?;
129
120
let mut iter = self . tweaks . iter ( ) . peekable ( ) ;
130
- while let Some ( ( term, tweak ) ) = iter. next ( ) {
121
+ while let Some ( ( term, tweaks ) ) = iter. next ( ) {
131
122
if term. keychain != RgbKeychain :: Tapret . into ( ) {
132
- write ! ( f, "{}/" , term. keychain) ?;
123
+ write ! ( f, "{}/{}=" , term. keychain, term. index) ?;
124
+ }
125
+ let mut commitment_iter = tweaks. iter ( ) . peekable ( ) ;
126
+ while let Some ( tweak) = commitment_iter. next ( ) {
127
+ write ! ( f, "{tweak}" ) ?;
128
+ if commitment_iter. peek ( ) . is_some ( ) {
129
+ f. write_str ( "," ) ?;
130
+ }
133
131
}
134
- write ! ( f, "{}={tweak}" , term. index) ?;
135
132
if iter. peek ( ) . is_some ( ) {
136
133
f. write_str ( ";" ) ?;
137
134
}
@@ -159,19 +156,21 @@ impl<K: DeriveXOnly> Derive<DerivedScript> for TapretKey<K> {
159
156
& self ,
160
157
keychain : impl Into < Keychain > ,
161
158
index : impl Into < NormalIndex > ,
162
- ) -> DerivedScript {
159
+ ) -> impl Iterator < Item = DerivedScript > {
163
160
let keychain = keychain. into ( ) ;
164
161
let index = index. into ( ) ;
165
162
let terminal = Terminal :: new ( keychain, index) ;
166
- let internal_key = self . tr . as_internal_key ( ) . derive ( keychain, index) ;
167
- if keychain. into_inner ( ) == RgbKeychain :: Tapret as u8 {
168
- if let Some ( tweak) = self . tweaks . get ( & terminal) {
163
+ let mut derived_scripts = Vec :: with_capacity ( self . tweaks . len ( ) + 1 ) ;
164
+ for internal_key in self . tr . as_internal_key ( ) . derive ( keychain, index) {
165
+ derived_scripts. push ( DerivedScript :: TaprootKeyOnly ( internal_key. into ( ) ) ) ;
166
+ for tweak in self . tweaks . get ( & terminal) . into_iter ( ) . flatten ( ) {
169
167
let script_commitment = TapScript :: commit ( tweak) ;
170
168
let tap_tree = TapTree :: with_single_leaf ( script_commitment) ;
171
- return DerivedScript :: TaprootScript ( internal_key. into ( ) , tap_tree) ;
169
+ let script = DerivedScript :: TaprootScript ( internal_key. into ( ) , tap_tree) ;
170
+ derived_scripts. push ( script) ;
172
171
}
173
172
}
174
- DerivedScript :: TaprootKeyOnly ( internal_key . into ( ) )
173
+ derived_scripts . into_iter ( )
175
174
}
176
175
}
177
176
@@ -229,16 +228,8 @@ impl<K: DeriveXOnly> Descriptor<K> for TapretKey<K> {
229
228
impl < K : DeriveXOnly > DescriptorRgb < K > for TapretKey < K > {
230
229
fn close_method ( & self ) -> CloseMethod { CloseMethod :: TapretFirst }
231
230
232
- fn add_tapret_tweak (
233
- & mut self ,
234
- terminal : Terminal ,
235
- tweak : TapretCommitment ,
236
- ) -> Result < ( ) , TapTweakAlreadyAssigned > {
237
- if self . tweaks . contains_key ( & terminal) {
238
- return Err ( TapTweakAlreadyAssigned ( terminal) ) ;
239
- }
240
- self . tweaks . insert ( terminal, tweak) ;
241
- Ok ( ( ) )
231
+ fn add_tapret_tweak ( & mut self , terminal : Terminal , tweak : TapretCommitment ) {
232
+ self . tweaks . entry ( terminal) . or_default ( ) . insert ( tweak) ;
242
233
}
243
234
}
244
235
@@ -292,10 +283,15 @@ impl<S: DeriveSet> Derive<DerivedScript> for RgbDescr<S> {
292
283
}
293
284
}
294
285
295
- fn derive ( & self , change : impl Into < Keychain > , index : impl Into < NormalIndex > ) -> DerivedScript {
286
+ fn derive (
287
+ & self ,
288
+ change : impl Into < Keychain > ,
289
+ index : impl Into < NormalIndex > ,
290
+ ) -> impl Iterator < Item = DerivedScript > {
291
+ // collecting as a workaround for different opaque types
296
292
match self {
297
- RgbDescr :: Wpkh ( d) => d. derive ( change, index) ,
298
- RgbDescr :: TapretKey ( d) => d. derive ( change, index) ,
293
+ RgbDescr :: Wpkh ( d) => d. derive ( change, index) . collect :: < Vec < _ > > ( ) . into_iter ( ) ,
294
+ RgbDescr :: TapretKey ( d) => d. derive ( change, index) . collect :: < Vec < _ > > ( ) . into_iter ( ) ,
299
295
}
300
296
}
301
297
}
@@ -375,11 +371,7 @@ where Self: Derive<DerivedScript>
375
371
}
376
372
}
377
373
378
- fn add_tapret_tweak (
379
- & mut self ,
380
- terminal : Terminal ,
381
- tweak : TapretCommitment ,
382
- ) -> Result < ( ) , TapTweakAlreadyAssigned > {
374
+ fn add_tapret_tweak ( & mut self , terminal : Terminal , tweak : TapretCommitment ) {
383
375
match self {
384
376
RgbDescr :: Wpkh ( _) => panic ! ( "adding tapret tweak to non-taproot descriptor" ) ,
385
377
RgbDescr :: TapretKey ( d) => d. add_tapret_tweak ( terminal, tweak) ,
@@ -396,3 +388,60 @@ impl From<StdDescr> for RgbDescr {
396
388
}
397
389
}
398
390
}
391
+
392
+ #[ cfg( test) ]
393
+ mod test {
394
+ use std:: str:: FromStr ;
395
+
396
+ use bp:: dbc:: tapret:: TapretCommitment ;
397
+ use bpstd:: { DeriveSet , Idx , Keychain , NormalIndex , Terminal , TrKey , XpubDerivable } ;
398
+ use commit_verify:: mpc:: Commitment ;
399
+ use strict_types:: StrictDumb ;
400
+
401
+ use super :: TapretKey ;
402
+ use crate :: DescriptorRgb ;
403
+
404
+ #[ test]
405
+ fn tapret_key_display ( ) {
406
+ let xpub_str = "[643a7adc/86h/1h/0h]tpubDCNiWHaiSkgnQjuhsg9kjwaUzaxQjUcmhagvYzqQ3TYJTgFGJstVaqnu4yhtFktBhCVFmBNLQ5sN53qKzZbMksm3XEyGJsEhQPfVZdWmTE2/<0;1>/*" ;
407
+ let xpub = XpubDerivable :: from_str ( xpub_str) . unwrap ( ) ;
408
+ let internal_key: TrKey < <XpubDerivable as DeriveSet >:: XOnly > = TrKey :: from ( xpub. clone ( ) ) ;
409
+
410
+ // no tweaks
411
+ let mut tapret_key = TapretKey :: from ( internal_key) ;
412
+ assert_eq ! ( format!( "{tapret_key}" ) , format!( "tapret({xpub_str},tweaks())" ) ) ;
413
+
414
+ // add a tweak to a new terminal
415
+ let terminal = Terminal :: new ( Keychain :: INNER , NormalIndex :: ZERO ) ;
416
+ let tweak = TapretCommitment :: with ( Commitment :: strict_dumb ( ) , 2 ) ;
417
+ tapret_key. add_tapret_tweak ( terminal, tweak) ;
418
+ assert_eq ! (
419
+ format!( "{tapret_key}" ) ,
420
+ format!( "tapret({xpub_str},tweaks(1/0=00000000000000000000000000000000000000000s))" )
421
+ ) ;
422
+
423
+ // add another tweak to a new terminal
424
+ let terminal = Terminal :: new ( Keychain :: from ( 7 ) , NormalIndex :: from ( 12u8 ) ) ;
425
+ let tweak = TapretCommitment :: with ( Commitment :: strict_dumb ( ) , 5 ) ;
426
+ tapret_key. add_tapret_tweak ( terminal, tweak. clone ( ) ) ;
427
+ assert_eq ! (
428
+ format!( "{tapret_key}" ) ,
429
+ format!(
430
+ "tapret({xpub_str},tweaks(1/0=00000000000000000000000000000000000000000s;7/\
431
+ 12=00000000000000000000000000000000000000001p))"
432
+ )
433
+ ) ;
434
+
435
+ // add another tweak to an existing terminal
436
+ let tweak = TapretCommitment :: with ( Commitment :: strict_dumb ( ) , 2 ) ;
437
+ tapret_key. add_tapret_tweak ( terminal, tweak) ;
438
+ assert_eq ! (
439
+ format!( "{tapret_key}" ) ,
440
+ format!(
441
+ "tapret({xpub_str},tweaks(1/0=00000000000000000000000000000000000000000s;7/\
442
+ 12=00000000000000000000000000000000000000000s,\
443
+ 00000000000000000000000000000000000000001p))"
444
+ )
445
+ ) ;
446
+ }
447
+ }
0 commit comments