@@ -17,7 +17,7 @@ use expression::{self, FromTree};
17
17
use miniscript:: { limits:: TAPROOT_MAX_NODE_COUNT , Miniscript } ;
18
18
use std:: cmp:: { self , max} ;
19
19
use std:: hash;
20
- use std:: sync:: { Arc , RwLock } ;
20
+ use std:: sync:: { Arc , Mutex } ;
21
21
use std:: { fmt, str:: FromStr } ;
22
22
use Tap ;
23
23
use { Error , MiniscriptKey } ;
@@ -46,19 +46,21 @@ pub struct Tr<Pk: MiniscriptKey> {
46
46
/// This will be [`None`] when the descriptor is not derived.
47
47
/// This information will be cached automatically when it is required
48
48
//
49
- // It is also possible to wrap this in a further Arc<RwLock...> so that the cache
50
- // is shared across clones of the this descriptor. But this (slightly) complicates
51
- // the code, and it might be desirable for clone to keep separate caches
52
- spend_info : RwLock < Option < Arc < TaprootSpendInfo > > > ,
49
+ // The inner `Arc` here is because Rust does not allow us to return a reference
50
+ // to the contents of the `Option` from inside a `MutexGuard`. There is no outer
51
+ // `Arc` because when this structure is cloned, we create a whole new mutex.
52
+ spend_info : Mutex < Option < Arc < TaprootSpendInfo > > > ,
53
53
}
54
54
55
55
impl < Pk : MiniscriptKey > Clone for Tr < Pk > {
56
56
fn clone ( & self ) -> Self {
57
+ // When cloning, construct a new Mutex so that distinct clones don't
58
+ // cause blocking between each other. We clone only the internal `Arc`,
59
+ // so the clone is always cheap (in both time and space)
57
60
Self {
58
61
internal_key : self . internal_key . clone ( ) ,
59
62
tree : self . tree . clone ( ) ,
60
- // Cloning creates a new instance of RwLock
61
- spend_info : RwLock :: new ( None ) ,
63
+ spend_info : Mutex :: new ( self . spend_info . lock ( ) . expect ( "Lock poisoned" ) . clone ( ) ) ,
62
64
}
63
65
}
64
66
}
@@ -169,7 +171,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
169
171
Ok ( Self {
170
172
internal_key,
171
173
tree,
172
- spend_info : RwLock :: new ( None ) ,
174
+ spend_info : Mutex :: new ( None ) ,
173
175
} )
174
176
} else {
175
177
Err ( Error :: MaxRecursiveDepthExceeded )
@@ -212,64 +214,54 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
212
214
{
213
215
// If the value is already cache, read it
214
216
// read only panics if the lock is poisoned (meaning other thread having a lock panicked)
215
- let spend_info = self
216
- . spend_info
217
- . read ( )
218
- . expect ( "Lock poisoned" )
219
- . as_ref ( )
220
- . map ( Arc :: clone) ;
221
-
222
- match spend_info {
223
- Some ( spend_info) => spend_info,
224
- None => {
225
- // Get a new secp context
226
- // This would be cheap operation after static context support from upstream
227
- let secp = secp256k1:: Secp256k1 :: verification_only ( ) ;
228
- // Key spend path with no merkle root
229
- let data = if self . tree . is_none ( ) {
230
- TaprootSpendInfo :: new_key_spend (
231
- & secp,
232
- self . internal_key . to_x_only_pubkey ( ) ,
233
- None ,
234
- )
235
- } else {
236
- let mut builder = TaprootBuilder :: new ( ) ;
237
- for ( depth, ms) in self . iter_scripts ( ) {
238
- let script = ms. encode ( ) ;
239
- builder = builder
240
- . add_leaf ( depth, script)
241
- . expect ( "Computing spend data on a valid Tree should always succeed" ) ;
217
+ let read_lock = self . spend_info . lock ( ) . expect ( "Lock poisoned" ) ;
218
+ if let Some ( ref spend_info) = * read_lock {
219
+ return spend_info. clone ( ) ;
220
+ }
221
+ drop ( read_lock) ;
222
+
223
+ // Get a new secp context
224
+ // This would be cheap operation after static context support from upstream
225
+ let secp = secp256k1:: Secp256k1 :: verification_only ( ) ;
226
+ // Key spend path with no merkle root
227
+ let data = if self . tree . is_none ( ) {
228
+ TaprootSpendInfo :: new_key_spend ( & secp, self . internal_key . to_x_only_pubkey ( ) , None )
229
+ } else {
230
+ let mut builder = TaprootBuilder :: new ( ) ;
231
+ for ( depth, ms) in self . iter_scripts ( ) {
232
+ let script = ms. encode ( ) ;
233
+ builder = builder
234
+ . add_leaf ( depth, script)
235
+ . expect ( "Computing spend data on a valid Tree should always succeed" ) ;
236
+ }
237
+ // Assert builder cannot error here because we have a well formed descriptor
238
+ match builder. finalize ( & secp, self . internal_key . to_x_only_pubkey ( ) ) {
239
+ Ok ( data) => data,
240
+ Err ( e) => match e {
241
+ TaprootBuilderError :: InvalidMerkleTreeDepth ( _) => {
242
+ unreachable ! ( "Depth checked in struct construction" )
243
+ }
244
+ TaprootBuilderError :: NodeNotInDfsOrder => {
245
+ unreachable ! ( "Insertion is called in DFS order" )
246
+ }
247
+ TaprootBuilderError :: OverCompleteTree => {
248
+ unreachable ! ( "Taptree is a well formed tree" )
242
249
}
243
- // Assert builder cannot error here because we have a well formed descriptor
244
- match builder. finalize ( & secp, self . internal_key . to_x_only_pubkey ( ) ) {
245
- Ok ( data) => data,
246
- Err ( e) => match e {
247
- TaprootBuilderError :: InvalidMerkleTreeDepth ( _) => {
248
- unreachable ! ( "Depth checked in struct construction" )
249
- }
250
- TaprootBuilderError :: NodeNotInDfsOrder => {
251
- unreachable ! ( "Insertion is called in DFS order" )
252
- }
253
- TaprootBuilderError :: OverCompleteTree => {
254
- unreachable ! ( "Taptree is a well formed tree" )
255
- }
256
- TaprootBuilderError :: InvalidInternalKey ( _) => {
257
- unreachable ! ( "Internal key checked for validity" )
258
- }
259
- TaprootBuilderError :: IncompleteTree => {
260
- unreachable ! ( "Taptree is a well formed tree" )
261
- }
262
- TaprootBuilderError :: EmptyTree => {
263
- unreachable ! ( "Taptree is a well formed tree with atleast 1 element" )
264
- }
265
- } ,
250
+ TaprootBuilderError :: InvalidInternalKey ( _) => {
251
+ unreachable ! ( "Internal key checked for validity" )
266
252
}
267
- } ;
268
- let spend_info = Arc :: new ( data) ;
269
- * self . spend_info . write ( ) . expect ( "Lock poisoned" ) = Some ( Arc :: clone ( & spend_info) ) ;
270
- spend_info
253
+ TaprootBuilderError :: IncompleteTree => {
254
+ unreachable ! ( "Taptree is a well formed tree" )
255
+ }
256
+ TaprootBuilderError :: EmptyTree => {
257
+ unreachable ! ( "Taptree is a well formed tree with atleast 1 element" )
258
+ }
259
+ } ,
271
260
}
272
- }
261
+ } ;
262
+ let spend_info = Arc :: new ( data) ;
263
+ * self . spend_info . lock ( ) . expect ( "Lock poisoned" ) = Some ( spend_info. clone ( ) ) ;
264
+ spend_info
273
265
}
274
266
}
275
267
@@ -387,7 +379,7 @@ where
387
379
Ok ( Tr {
388
380
internal_key : expression:: terminal ( key, Pk :: from_str) ?,
389
381
tree : None ,
390
- spend_info : RwLock :: new ( None ) ,
382
+ spend_info : Mutex :: new ( None ) ,
391
383
} )
392
384
}
393
385
2 => {
@@ -403,7 +395,7 @@ where
403
395
Ok ( Tr {
404
396
internal_key : expression:: terminal ( key, Pk :: from_str) ?,
405
397
tree : Some ( ret) ,
406
- spend_info : RwLock :: new ( None ) ,
398
+ spend_info : Mutex :: new ( None ) ,
407
399
} )
408
400
}
409
401
_ => {
@@ -668,7 +660,7 @@ impl<P: MiniscriptKey, Q: MiniscriptKey> TranslatePk<P, Q> for Tr<P> {
668
660
Some ( tree) => Some ( tree. translate_helper ( & mut translatefpk, & mut translatefpkh) ?) ,
669
661
None => None ,
670
662
} ,
671
- spend_info : RwLock :: new ( None ) ,
663
+ spend_info : Mutex :: new ( None ) ,
672
664
} ;
673
665
Ok ( translate_desc)
674
666
}
0 commit comments