policy: Build SatisfiableItem::*Signature based on the context
				
					
				
			Also refactor our code to lookup signatures in PSBTs to use the context
This commit is contained in:
		
							parent
							
								
									ff1abc63e0
								
							
						
					
					
						commit
						572c3ee70d
					
				| @ -572,13 +572,12 @@ impl Policy { | |||||||
|         Ok(Some(policy)) |         Ok(Some(policy)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn make_multisig( |     fn make_multisig<Ctx: ScriptContext + 'static>( | ||||||
|         keys: &[DescriptorPublicKey], |         keys: &[DescriptorPublicKey], | ||||||
|         signers: &SignersContainer, |         signers: &SignersContainer, | ||||||
|         build_sat: BuildSatisfaction, |         build_sat: BuildSatisfaction, | ||||||
|         threshold: usize, |         threshold: usize, | ||||||
|         sorted: bool, |         sorted: bool, | ||||||
|         is_ecdsa: bool, |  | ||||||
|         secp: &SecpCtx, |         secp: &SecpCtx, | ||||||
|     ) -> Result<Option<Policy>, PolicyError> { |     ) -> Result<Option<Policy>, PolicyError> { | ||||||
|         if threshold == 0 { |         if threshold == 0 { | ||||||
| @ -607,9 +606,7 @@ impl Policy { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if let Some(psbt) = build_sat.psbt() { |             if let Some(psbt) = build_sat.psbt() { | ||||||
|                 if is_ecdsa && ecdsa_signature_in_psbt(psbt, key, secp) |                 if Ctx::find_signature(psbt, key, secp) { | ||||||
|                     || !is_ecdsa && schnorr_signature_in_psbt(psbt, key, secp) |  | ||||||
|                 { |  | ||||||
|                     satisfaction.add( |                     satisfaction.add( | ||||||
|                         &Satisfaction::Complete { |                         &Satisfaction::Complete { | ||||||
|                             condition: Default::default(), |                             condition: Default::default(), | ||||||
| @ -737,13 +734,15 @@ fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn signature( | fn make_generic_signature<M: Fn() -> SatisfiableItem, F: Fn(&Psbt) -> bool>( | ||||||
|     key: &DescriptorPublicKey, |     key: &DescriptorPublicKey, | ||||||
|     signers: &SignersContainer, |     signers: &SignersContainer, | ||||||
|     build_sat: BuildSatisfaction, |     build_sat: BuildSatisfaction, | ||||||
|     secp: &SecpCtx, |     secp: &SecpCtx, | ||||||
|  |     make_policy: M, | ||||||
|  |     find_sig: F, | ||||||
| ) -> Policy { | ) -> Policy { | ||||||
|     let mut policy: Policy = SatisfiableItem::EcdsaSignature(PkOrF::from_key(key, secp)).into(); |     let mut policy: Policy = make_policy().into(); | ||||||
| 
 | 
 | ||||||
|     policy.contribution = if signers.find(signer_id(key, secp)).is_some() { |     policy.contribution = if signers.find(signer_id(key, secp)).is_some() { | ||||||
|         Satisfaction::Complete { |         Satisfaction::Complete { | ||||||
| @ -754,7 +753,7 @@ fn signature( | |||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if let Some(psbt) = build_sat.psbt() { |     if let Some(psbt) = build_sat.psbt() { | ||||||
|         policy.satisfaction = if ecdsa_signature_in_psbt(psbt, key, secp) { |         policy.satisfaction = if find_sig(psbt) { | ||||||
|             Satisfaction::Complete { |             Satisfaction::Complete { | ||||||
|                 condition: Default::default(), |                 condition: Default::default(), | ||||||
|             } |             } | ||||||
| @ -794,39 +793,78 @@ fn generic_sig_in_psbt< | |||||||
|     }) |     }) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn ecdsa_signature_in_psbt(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool { | trait SigExt: ScriptContext { | ||||||
|     generic_sig_in_psbt( |     fn make_signature( | ||||||
|         psbt, |         key: &DescriptorPublicKey, | ||||||
|         key, |         signers: &SignersContainer, | ||||||
|         secp, |         build_sat: BuildSatisfaction, | ||||||
|         |pk| SinglePubKey::FullKey(PublicKey::new(*pk)), |         secp: &SecpCtx, | ||||||
|         |input, pk| match pk { |     ) -> Policy; | ||||||
|             SinglePubKey::FullKey(pk) => input.partial_sigs.contains_key(pk), | 
 | ||||||
|             _ => false, |     fn find_signature(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool; | ||||||
|         }, |  | ||||||
|     ) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn schnorr_signature_in_psbt(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool { | impl<T: ScriptContext + 'static> SigExt for T { | ||||||
|     generic_sig_in_psbt( |     fn make_signature( | ||||||
|         psbt, |         key: &DescriptorPublicKey, | ||||||
|         key, |         signers: &SignersContainer, | ||||||
|         secp, |         build_sat: BuildSatisfaction, | ||||||
|         |pk| SinglePubKey::XOnly((*pk).into()), |         secp: &SecpCtx, | ||||||
|         |input, pk| { |     ) -> Policy { | ||||||
|             let pk = match pk { |         if T::as_enum().is_taproot() { | ||||||
|                 SinglePubKey::XOnly(pk) => pk, |             make_generic_signature( | ||||||
|                 _ => return false, |                 key, | ||||||
|             }; |                 signers, | ||||||
|  |                 build_sat, | ||||||
|  |                 secp, | ||||||
|  |                 || SatisfiableItem::SchnorrSignature(PkOrF::from_key(key, secp)), | ||||||
|  |                 |psbt| Self::find_signature(psbt, key, secp), | ||||||
|  |             ) | ||||||
|  |         } else { | ||||||
|  |             make_generic_signature( | ||||||
|  |                 key, | ||||||
|  |                 signers, | ||||||
|  |                 build_sat, | ||||||
|  |                 secp, | ||||||
|  |                 || SatisfiableItem::EcdsaSignature(PkOrF::from_key(key, secp)), | ||||||
|  |                 |psbt| Self::find_signature(psbt, key, secp), | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|             // This assumes the internal key is never used in the script leaves, which I think is
 |     fn find_signature(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool { | ||||||
|             // reasonable
 |         if T::as_enum().is_taproot() { | ||||||
|             match &input.tap_internal_key { |             generic_sig_in_psbt( | ||||||
|                 Some(ik) if ik == pk => input.tap_key_sig.is_some(), |                 psbt, | ||||||
|                 _ => input.tap_script_sigs.keys().any(|(sk, _)| sk == pk), |                 key, | ||||||
|             } |                 secp, | ||||||
|         }, |                 |pk| SinglePubKey::XOnly((*pk).into()), | ||||||
|     ) |                 |input, pk| { | ||||||
|  |                     let pk = match pk { | ||||||
|  |                         SinglePubKey::XOnly(pk) => pk, | ||||||
|  |                         _ => return false, | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     if input.tap_internal_key == Some(*pk) && input.tap_key_sig.is_some() { | ||||||
|  |                         true | ||||||
|  |                     } else { | ||||||
|  |                         input.tap_script_sigs.keys().any(|(sk, _)| sk == pk) | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |             ) | ||||||
|  |         } else { | ||||||
|  |             generic_sig_in_psbt( | ||||||
|  |                 psbt, | ||||||
|  |                 key, | ||||||
|  |                 secp, | ||||||
|  |                 |pk| SinglePubKey::FullKey(PublicKey::new(*pk)), | ||||||
|  |                 |input, pk| match pk { | ||||||
|  |                     SinglePubKey::FullKey(pk) => input.partial_sigs.contains_key(pk), | ||||||
|  |                     _ => false, | ||||||
|  |                 }, | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> { | impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> { | ||||||
| @ -839,8 +877,10 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic | |||||||
|         Ok(match &self.node { |         Ok(match &self.node { | ||||||
|             // Leaves
 |             // Leaves
 | ||||||
|             Terminal::True | Terminal::False => None, |             Terminal::True | Terminal::False => None, | ||||||
|             Terminal::PkK(pubkey) => Some(signature(pubkey, signers, build_sat, secp)), |             Terminal::PkK(pubkey) => Some(Ctx::make_signature(pubkey, signers, build_sat, secp)), | ||||||
|             Terminal::PkH(pubkey_hash) => Some(signature(pubkey_hash, signers, build_sat, secp)), |             Terminal::PkH(pubkey_hash) => { | ||||||
|  |                 Some(Ctx::make_signature(pubkey_hash, signers, build_sat, secp)) | ||||||
|  |             } | ||||||
|             Terminal::After(value) => { |             Terminal::After(value) => { | ||||||
|                 let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into(); |                 let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into(); | ||||||
|                 policy.contribution = Satisfaction::Complete { |                 policy.contribution = Satisfaction::Complete { | ||||||
| @ -901,15 +941,9 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic | |||||||
|             Terminal::Hash160(hash) => { |             Terminal::Hash160(hash) => { | ||||||
|                 Some(SatisfiableItem::Hash160Preimage { hash: *hash }.into()) |                 Some(SatisfiableItem::Hash160Preimage { hash: *hash }.into()) | ||||||
|             } |             } | ||||||
|             Terminal::Multi(k, pks) | Terminal::MultiA(k, pks) => Policy::make_multisig( |             Terminal::Multi(k, pks) | Terminal::MultiA(k, pks) => { | ||||||
|                 pks, |                 Policy::make_multisig::<Ctx>(pks, signers, build_sat, *k, false, secp)? | ||||||
|                 signers, |             } | ||||||
|                 build_sat, |  | ||||||
|                 *k, |  | ||||||
|                 false, |  | ||||||
|                 !Ctx::as_enum().is_taproot(), |  | ||||||
|                 secp, |  | ||||||
|             )?, |  | ||||||
|             // Identities
 |             // Identities
 | ||||||
|             Terminal::Alt(inner) |             Terminal::Alt(inner) | ||||||
|             | Terminal::Swap(inner) |             | Terminal::Swap(inner) | ||||||
| @ -999,28 +1033,42 @@ impl ExtractPolicy for Descriptor<DescriptorPublicKey> { | |||||||
|         build_sat: BuildSatisfaction, |         build_sat: BuildSatisfaction, | ||||||
|         secp: &SecpCtx, |         secp: &SecpCtx, | ||||||
|     ) -> Result<Option<Policy>, Error> { |     ) -> Result<Option<Policy>, Error> { | ||||||
|         fn make_sortedmulti<Ctx: ScriptContext>( |         fn make_sortedmulti<Ctx: ScriptContext + 'static>( | ||||||
|             keys: &SortedMultiVec<DescriptorPublicKey, Ctx>, |             keys: &SortedMultiVec<DescriptorPublicKey, Ctx>, | ||||||
|             signers: &SignersContainer, |             signers: &SignersContainer, | ||||||
|             build_sat: BuildSatisfaction, |             build_sat: BuildSatisfaction, | ||||||
|             secp: &SecpCtx, |             secp: &SecpCtx, | ||||||
|         ) -> Result<Option<Policy>, Error> { |         ) -> Result<Option<Policy>, Error> { | ||||||
|             Ok(Policy::make_multisig( |             Ok(Policy::make_multisig::<Ctx>( | ||||||
|                 keys.pks.as_ref(), |                 keys.pks.as_ref(), | ||||||
|                 signers, |                 signers, | ||||||
|                 build_sat, |                 build_sat, | ||||||
|                 keys.k, |                 keys.k, | ||||||
|                 true, |                 true, | ||||||
|                 true, |  | ||||||
|                 secp, |                 secp, | ||||||
|             )?) |             )?) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         match self { |         match self { | ||||||
|             Descriptor::Pkh(pk) => Ok(Some(signature(pk.as_inner(), signers, build_sat, secp))), |             Descriptor::Pkh(pk) => Ok(Some(miniscript::Legacy::make_signature( | ||||||
|             Descriptor::Wpkh(pk) => Ok(Some(signature(pk.as_inner(), signers, build_sat, secp))), |                 pk.as_inner(), | ||||||
|  |                 signers, | ||||||
|  |                 build_sat, | ||||||
|  |                 secp, | ||||||
|  |             ))), | ||||||
|  |             Descriptor::Wpkh(pk) => Ok(Some(miniscript::Segwitv0::make_signature( | ||||||
|  |                 pk.as_inner(), | ||||||
|  |                 signers, | ||||||
|  |                 build_sat, | ||||||
|  |                 secp, | ||||||
|  |             ))), | ||||||
|             Descriptor::Sh(sh) => match sh.as_inner() { |             Descriptor::Sh(sh) => match sh.as_inner() { | ||||||
|                 ShInner::Wpkh(pk) => Ok(Some(signature(pk.as_inner(), signers, build_sat, secp))), |                 ShInner::Wpkh(pk) => Ok(Some(miniscript::Segwitv0::make_signature( | ||||||
|  |                     pk.as_inner(), | ||||||
|  |                     signers, | ||||||
|  |                     build_sat, | ||||||
|  |                     secp, | ||||||
|  |                 ))), | ||||||
|                 ShInner::Ms(ms) => Ok(ms.extract_policy(signers, build_sat, secp)?), |                 ShInner::Ms(ms) => Ok(ms.extract_policy(signers, build_sat, secp)?), | ||||||
|                 ShInner::SortedMulti(ref keys) => make_sortedmulti(keys, signers, build_sat, secp), |                 ShInner::SortedMulti(ref keys) => make_sortedmulti(keys, signers, build_sat, secp), | ||||||
|                 ShInner::Wsh(wsh) => match wsh.as_inner() { |                 ShInner::Wsh(wsh) => match wsh.as_inner() { | ||||||
| @ -1036,17 +1084,26 @@ impl ExtractPolicy for Descriptor<DescriptorPublicKey> { | |||||||
|             }, |             }, | ||||||
|             Descriptor::Bare(ms) => Ok(ms.as_inner().extract_policy(signers, build_sat, secp)?), |             Descriptor::Bare(ms) => Ok(ms.as_inner().extract_policy(signers, build_sat, secp)?), | ||||||
|             Descriptor::Tr(tr) => { |             Descriptor::Tr(tr) => { | ||||||
|                 let mut items = vec![signature(tr.internal_key(), signers, build_sat, secp)]; |                 // If there's no tap tree, treat this as a single sig, otherwise build a `Thresh`
 | ||||||
|                 items.append( |                 // node with threshold = 1 and the key spend signature plus all the tree leaves
 | ||||||
|                     &mut tr |                 let key_spend_sig = | ||||||
|                         .iter_scripts() |                     miniscript::Tap::make_signature(tr.internal_key(), signers, build_sat, secp); | ||||||
|                         .filter_map(|(_, ms)| { |  | ||||||
|                             ms.extract_policy(signers, build_sat, secp).transpose() |  | ||||||
|                         }) |  | ||||||
|                         .collect::<Result<Vec<_>, _>>()?, |  | ||||||
|                 ); |  | ||||||
| 
 | 
 | ||||||
|                 Ok(Policy::make_thresh(items, 1)?) |                 if tr.taptree().is_none() { | ||||||
|  |                     Ok(Some(key_spend_sig)) | ||||||
|  |                 } else { | ||||||
|  |                     let mut items = vec![key_spend_sig]; | ||||||
|  |                     items.append( | ||||||
|  |                         &mut tr | ||||||
|  |                             .iter_scripts() | ||||||
|  |                             .filter_map(|(_, ms)| { | ||||||
|  |                                 ms.extract_policy(signers, build_sat, secp).transpose() | ||||||
|  |                             }) | ||||||
|  |                             .collect::<Result<Vec<_>, _>>()?, | ||||||
|  |                     ); | ||||||
|  | 
 | ||||||
|  |                     Ok(Policy::make_thresh(items, 1)?) | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -4223,7 +4223,7 @@ pub(crate) mod test { | |||||||
|         let (wallet, _, _) = get_funded_wallet(get_test_tr_repeated_key()); |         let (wallet, _, _) = get_funded_wallet(get_test_tr_repeated_key()); | ||||||
|         let addr = wallet.get_address(AddressIndex::New).unwrap(); |         let addr = wallet.get_address(AddressIndex::New).unwrap(); | ||||||
| 
 | 
 | ||||||
|         let path = vec![("u6ugnnck".to_string(), vec![0])] |         let path = vec![("rn4nre9c".to_string(), vec![0])] | ||||||
|             .into_iter() |             .into_iter() | ||||||
|             .collect(); |             .collect(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user