fix: rm duplicate bdk_tmp_plan module
				
					
				
			This commit is contained in:
		
							parent
							
								
									af705da1a8
								
							
						
					
					
						commit
						315e7e0b4b
					
				| @ -1,13 +0,0 @@ | ||||
| [package] | ||||
| name = "bdk_tmp_plan" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| 
 | ||||
| [dependencies] | ||||
| bdk_chain = {  path = "../../../crates/chain", version = "0.3.1", features = ["miniscript"] } | ||||
| 
 | ||||
| [features] | ||||
| default = ["std"] | ||||
| std = [] | ||||
| @ -1,3 +0,0 @@ | ||||
| # Temporary planning module | ||||
| 
 | ||||
| A temporary place to hold the planning module until https://github.com/rust-bitcoin/rust-miniscript/pull/481 is merged and released | ||||
| @ -1,436 +0,0 @@ | ||||
| #![allow(unused)] | ||||
| #![allow(missing_docs)] | ||||
| //! A spending plan or *plan* for short is a representation of a particular spending path on a
 | ||||
| //! descriptor. This allows us to analayze a choice of spending path without producing any
 | ||||
| //! signatures or other witness data for it.
 | ||||
| //!
 | ||||
| //! To make a plan you provide the descriptor with "assets" like which keys you are able to use, hash
 | ||||
| //! pre-images you have access to, the current block height etc.
 | ||||
| //!
 | ||||
| //! Once you've got a plan it can tell you its expected satisfaction weight which can be useful for
 | ||||
| //! doing coin selection. Furthermore it provides which subset of those keys and hash pre-images you
 | ||||
| //! will actually need as well as what locktime or sequence number you need to set.
 | ||||
| //!
 | ||||
| //! Once you've obstained signatures, hash pre-images etc required by the plan, it can create a
 | ||||
| //! witness/script_sig for the input.
 | ||||
| use bdk_chain::{bitcoin, collections::*, miniscript}; | ||||
| use bitcoin::{ | ||||
|     blockdata::{locktime::LockTime, transaction::Sequence}, | ||||
|     hashes::{hash160, ripemd160, sha256}, | ||||
|     secp256k1::Secp256k1, | ||||
|     util::{ | ||||
|         address::WitnessVersion, | ||||
|         bip32::{DerivationPath, Fingerprint, KeySource}, | ||||
|         taproot::{LeafVersion, TapBranchHash, TapLeafHash}, | ||||
|     }, | ||||
|     EcdsaSig, SchnorrSig, Script, TxIn, Witness, | ||||
| }; | ||||
| use miniscript::{ | ||||
|     descriptor::{InnerXKey, Tr}, | ||||
|     hash256, DefiniteDescriptorKey, Descriptor, DescriptorPublicKey, ScriptContext, ToPublicKey, | ||||
| }; | ||||
| 
 | ||||
| pub(crate) fn varint_len(v: usize) -> usize { | ||||
|     bitcoin::VarInt(v as u64).len() as usize | ||||
| } | ||||
| 
 | ||||
| mod plan_impls; | ||||
| mod requirements; | ||||
| mod template; | ||||
| pub use requirements::*; | ||||
| pub use template::PlanKey; | ||||
| use template::TemplateItem; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| enum TrSpend { | ||||
|     KeySpend, | ||||
|     LeafSpend { | ||||
|         script: Script, | ||||
|         leaf_version: LeafVersion, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| enum Target { | ||||
|     Legacy, | ||||
|     Segwitv0 { | ||||
|         script_code: Script, | ||||
|     }, | ||||
|     Segwitv1 { | ||||
|         tr: Tr<DefiniteDescriptorKey>, | ||||
|         tr_plan: TrSpend, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl Target {} | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| /// A plan represents a particular spending path for a descriptor.
 | ||||
| ///
 | ||||
| /// See the module level documentation for more info.
 | ||||
| pub struct Plan<AK> { | ||||
|     template: Vec<TemplateItem<AK>>, | ||||
|     target: Target, | ||||
|     set_locktime: Option<LockTime>, | ||||
|     set_sequence: Option<Sequence>, | ||||
| } | ||||
| 
 | ||||
| impl Default for Target { | ||||
|     fn default() -> Self { | ||||
|         Target::Legacy | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, Default)] | ||||
| /// Signatures and hash pre-images that can be used to complete a plan.
 | ||||
| pub struct SatisfactionMaterial { | ||||
|     /// Schnorr signautres under their keys
 | ||||
|     pub schnorr_sigs: BTreeMap<DefiniteDescriptorKey, SchnorrSig>, | ||||
|     /// ECDSA signatures under their keys
 | ||||
|     pub ecdsa_sigs: BTreeMap<DefiniteDescriptorKey, EcdsaSig>, | ||||
|     /// SHA256 pre-images under their images
 | ||||
|     pub sha256_preimages: BTreeMap<sha256::Hash, Vec<u8>>, | ||||
|     /// hash160 pre-images under their images
 | ||||
|     pub hash160_preimages: BTreeMap<hash160::Hash, Vec<u8>>, | ||||
|     /// hash256 pre-images under their images
 | ||||
|     pub hash256_preimages: BTreeMap<hash256::Hash, Vec<u8>>, | ||||
|     /// ripemd160 pre-images under their images
 | ||||
|     pub ripemd160_preimages: BTreeMap<ripemd160::Hash, Vec<u8>>, | ||||
| } | ||||
| 
 | ||||
| impl<Ak> Plan<Ak> | ||||
| where | ||||
|     Ak: Clone, | ||||
| { | ||||
|     /// The expected satisfaction weight for the plan if it is completed.
 | ||||
|     pub fn expected_weight(&self) -> usize { | ||||
|         let script_sig_size = match self.target { | ||||
|             Target::Legacy => unimplemented!(), // self
 | ||||
|             // .template
 | ||||
|             // .iter()
 | ||||
|             // .map(|step| {
 | ||||
|             //     let size = step.expected_size();
 | ||||
|             //     size + push_opcode_size(size)
 | ||||
|             // })
 | ||||
|             // .sum()
 | ||||
|             Target::Segwitv0 { .. } | Target::Segwitv1 { .. } => 1, | ||||
|         }; | ||||
|         let witness_elem_sizes: Option<Vec<usize>> = match &self.target { | ||||
|             Target::Legacy => None, | ||||
|             Target::Segwitv0 { .. } => Some( | ||||
|                 self.template | ||||
|                     .iter() | ||||
|                     .map(|step| step.expected_size()) | ||||
|                     .collect(), | ||||
|             ), | ||||
|             Target::Segwitv1 { tr, tr_plan } => { | ||||
|                 let mut witness_elems = self | ||||
|                     .template | ||||
|                     .iter() | ||||
|                     .map(|step| step.expected_size()) | ||||
|                     .collect::<Vec<_>>(); | ||||
| 
 | ||||
|                 if let TrSpend::LeafSpend { | ||||
|                     script, | ||||
|                     leaf_version, | ||||
|                 } = tr_plan | ||||
|                 { | ||||
|                     let control_block = tr | ||||
|                         .spend_info() | ||||
|                         .control_block(&(script.clone(), *leaf_version)) | ||||
|                         .expect("must exist"); | ||||
|                     witness_elems.push(script.len()); | ||||
|                     witness_elems.push(control_block.size()); | ||||
|                 } | ||||
| 
 | ||||
|                 Some(witness_elems) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         let witness_size: usize = match witness_elem_sizes { | ||||
|             Some(elems) => { | ||||
|                 varint_len(elems.len()) | ||||
|                     + elems | ||||
|                         .into_iter() | ||||
|                         .map(|elem| varint_len(elem) + elem) | ||||
|                         .sum::<usize>() | ||||
|             } | ||||
|             None => 0, | ||||
|         }; | ||||
| 
 | ||||
|         script_sig_size * 4 + witness_size | ||||
|     } | ||||
| 
 | ||||
|     pub fn requirements(&self) -> Requirements<Ak> { | ||||
|         match self.try_complete(&SatisfactionMaterial::default()) { | ||||
|             PlanState::Complete { .. } => Requirements::default(), | ||||
|             PlanState::Incomplete(requirements) => requirements, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn try_complete(&self, auth_data: &SatisfactionMaterial) -> PlanState<Ak> { | ||||
|         let unsatisfied_items = self | ||||
|             .template | ||||
|             .iter() | ||||
|             .filter(|step| match step { | ||||
|                 TemplateItem::Sign(key) => { | ||||
|                     !auth_data.schnorr_sigs.contains_key(&key.descriptor_key) | ||||
|                 } | ||||
|                 TemplateItem::Hash160(image) => !auth_data.hash160_preimages.contains_key(image), | ||||
|                 TemplateItem::Hash256(image) => !auth_data.hash256_preimages.contains_key(image), | ||||
|                 TemplateItem::Sha256(image) => !auth_data.sha256_preimages.contains_key(image), | ||||
|                 TemplateItem::Ripemd160(image) => { | ||||
|                     !auth_data.ripemd160_preimages.contains_key(image) | ||||
|                 } | ||||
|                 TemplateItem::Pk { .. } | TemplateItem::One | TemplateItem::Zero => false, | ||||
|             }) | ||||
|             .collect::<Vec<_>>(); | ||||
| 
 | ||||
|         if unsatisfied_items.is_empty() { | ||||
|             let mut witness = self | ||||
|                 .template | ||||
|                 .iter() | ||||
|                 .flat_map(|step| step.to_witness_stack(&auth_data)) | ||||
|                 .collect::<Vec<_>>(); | ||||
|             match &self.target { | ||||
|                 Target::Segwitv0 { .. } => todo!(), | ||||
|                 Target::Legacy => todo!(), | ||||
|                 Target::Segwitv1 { | ||||
|                     tr_plan: TrSpend::KeySpend, | ||||
|                     .. | ||||
|                 } => PlanState::Complete { | ||||
|                     final_script_sig: None, | ||||
|                     final_script_witness: Some(Witness::from_vec(witness)), | ||||
|                 }, | ||||
|                 Target::Segwitv1 { | ||||
|                     tr, | ||||
|                     tr_plan: | ||||
|                         TrSpend::LeafSpend { | ||||
|                             script, | ||||
|                             leaf_version, | ||||
|                         }, | ||||
|                 } => { | ||||
|                     let spend_info = tr.spend_info(); | ||||
|                     let control_block = spend_info | ||||
|                         .control_block(&(script.clone(), *leaf_version)) | ||||
|                         .expect("must exist"); | ||||
|                     witness.push(script.clone().into_bytes()); | ||||
|                     witness.push(control_block.serialize()); | ||||
| 
 | ||||
|                     PlanState::Complete { | ||||
|                         final_script_sig: None, | ||||
|                         final_script_witness: Some(Witness::from_vec(witness)), | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             let mut requirements = Requirements::default(); | ||||
| 
 | ||||
|             match &self.target { | ||||
|                 Target::Legacy => { | ||||
|                     todo!() | ||||
|                 } | ||||
|                 Target::Segwitv0 { .. } => { | ||||
|                     todo!() | ||||
|                 } | ||||
|                 Target::Segwitv1 { tr, tr_plan } => { | ||||
|                     let spend_info = tr.spend_info(); | ||||
|                     match tr_plan { | ||||
|                         TrSpend::KeySpend => match &self.template[..] { | ||||
|                             [TemplateItem::Sign(ref plan_key)] => { | ||||
|                                 requirements.signatures = RequiredSignatures::TapKey { | ||||
|                                     merkle_root: spend_info.merkle_root(), | ||||
|                                     plan_key: plan_key.clone(), | ||||
|                                 }; | ||||
|                             } | ||||
|                             _ => unreachable!("tapkey spend will always have only one sign step"), | ||||
|                         }, | ||||
|                         TrSpend::LeafSpend { | ||||
|                             script, | ||||
|                             leaf_version, | ||||
|                         } => { | ||||
|                             let leaf_hash = TapLeafHash::from_script(&script, *leaf_version); | ||||
|                             requirements.signatures = RequiredSignatures::TapScript { | ||||
|                                 leaf_hash, | ||||
|                                 plan_keys: vec![], | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             let required_signatures = match requirements.signatures { | ||||
|                 RequiredSignatures::Legacy { .. } => todo!(), | ||||
|                 RequiredSignatures::Segwitv0 { .. } => todo!(), | ||||
|                 RequiredSignatures::TapKey { .. } => return PlanState::Incomplete(requirements), | ||||
|                 RequiredSignatures::TapScript { | ||||
|                     plan_keys: ref mut keys, | ||||
|                     .. | ||||
|                 } => keys, | ||||
|             }; | ||||
| 
 | ||||
|             for step in unsatisfied_items { | ||||
|                 match step { | ||||
|                     TemplateItem::Sign(plan_key) => { | ||||
|                         required_signatures.push(plan_key.clone()); | ||||
|                     } | ||||
|                     TemplateItem::Hash160(image) => { | ||||
|                         requirements.hash160_images.insert(image.clone()); | ||||
|                     } | ||||
|                     TemplateItem::Hash256(image) => { | ||||
|                         requirements.hash256_images.insert(image.clone()); | ||||
|                     } | ||||
|                     TemplateItem::Sha256(image) => { | ||||
|                         requirements.sha256_images.insert(image.clone()); | ||||
|                     } | ||||
|                     TemplateItem::Ripemd160(image) => { | ||||
|                         requirements.ripemd160_images.insert(image.clone()); | ||||
|                     } | ||||
|                     TemplateItem::Pk { .. } | TemplateItem::One | TemplateItem::Zero => { /* no requirements */ | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             PlanState::Incomplete(requirements) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Witness version for the plan
 | ||||
|     pub fn witness_version(&self) -> Option<WitnessVersion> { | ||||
|         match self.target { | ||||
|             Target::Legacy => None, | ||||
|             Target::Segwitv0 { .. } => Some(WitnessVersion::V0), | ||||
|             Target::Segwitv1 { .. } => Some(WitnessVersion::V1), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The minimum required locktime height or time on the transaction using the plan.
 | ||||
|     pub fn required_locktime(&self) -> Option<LockTime> { | ||||
|         self.set_locktime.clone() | ||||
|     } | ||||
| 
 | ||||
|     /// The minimum required sequence (height or time) on the input to satisfy the plan
 | ||||
|     pub fn required_sequence(&self) -> Option<Sequence> { | ||||
|         self.set_sequence.clone() | ||||
|     } | ||||
| 
 | ||||
|     /// The minmum required transaction version required on the transaction using the plan.
 | ||||
|     pub fn min_version(&self) -> Option<u32> { | ||||
|         if let Some(_) = self.set_sequence { | ||||
|             Some(2) | ||||
|         } else { | ||||
|             Some(1) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// The returned value from [`Plan::try_complete`].
 | ||||
| pub enum PlanState<Ak> { | ||||
|     /// The plan is complete
 | ||||
|     Complete { | ||||
|         /// The script sig that should be set on the input
 | ||||
|         final_script_sig: Option<Script>, | ||||
|         /// The witness that should be set on the input
 | ||||
|         final_script_witness: Option<Witness>, | ||||
|     }, | ||||
|     Incomplete(Requirements<Ak>), | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct Assets<K> { | ||||
|     pub keys: Vec<K>, | ||||
|     pub txo_age: Option<Sequence>, | ||||
|     pub max_locktime: Option<LockTime>, | ||||
|     pub sha256: Vec<sha256::Hash>, | ||||
|     pub hash256: Vec<hash256::Hash>, | ||||
|     pub ripemd160: Vec<ripemd160::Hash>, | ||||
|     pub hash160: Vec<hash160::Hash>, | ||||
| } | ||||
| 
 | ||||
| impl<K> Default for Assets<K> { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             keys: Default::default(), | ||||
|             txo_age: Default::default(), | ||||
|             max_locktime: Default::default(), | ||||
|             sha256: Default::default(), | ||||
|             hash256: Default::default(), | ||||
|             ripemd160: Default::default(), | ||||
|             hash160: Default::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait CanDerive { | ||||
|     fn can_derive(&self, key: &DefiniteDescriptorKey) -> Option<DerivationPath>; | ||||
| } | ||||
| 
 | ||||
| impl CanDerive for KeySource { | ||||
|     fn can_derive(&self, key: &DefiniteDescriptorKey) -> Option<DerivationPath> { | ||||
|         match DescriptorPublicKey::from(key.clone()) { | ||||
|             DescriptorPublicKey::Single(single_pub) => { | ||||
|                 path_to_child(self, single_pub.origin.as_ref()?, None) | ||||
|             } | ||||
|             DescriptorPublicKey::XPub(dxk) => { | ||||
|                 let origin = dxk.origin.clone().unwrap_or_else(|| { | ||||
|                     let secp = Secp256k1::signing_only(); | ||||
|                     (dxk.xkey.xkey_fingerprint(&secp), DerivationPath::master()) | ||||
|                 }); | ||||
| 
 | ||||
|                 path_to_child(self, &origin, Some(&dxk.derivation_path)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CanDerive for DescriptorPublicKey { | ||||
|     fn can_derive(&self, key: &DefiniteDescriptorKey) -> Option<DerivationPath> { | ||||
|         match (self, DescriptorPublicKey::from(key.clone())) { | ||||
|             (parent, child) if parent == &child => Some(DerivationPath::master()), | ||||
|             (DescriptorPublicKey::XPub(parent), _) => { | ||||
|                 let origin = parent.origin.clone().unwrap_or_else(|| { | ||||
|                     let secp = Secp256k1::signing_only(); | ||||
|                     ( | ||||
|                         parent.xkey.xkey_fingerprint(&secp), | ||||
|                         DerivationPath::master(), | ||||
|                     ) | ||||
|                 }); | ||||
|                 KeySource::from(origin).can_derive(key) | ||||
|             } | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn path_to_child( | ||||
|     parent: &KeySource, | ||||
|     child_origin: &(Fingerprint, DerivationPath), | ||||
|     child_derivation: Option<&DerivationPath>, | ||||
| ) -> Option<DerivationPath> { | ||||
|     if parent.0 == child_origin.0 { | ||||
|         let mut remaining_derivation = | ||||
|             DerivationPath::from(child_origin.1[..].strip_prefix(&parent.1[..])?); | ||||
|         remaining_derivation = | ||||
|             remaining_derivation.extend(child_derivation.unwrap_or(&DerivationPath::master())); | ||||
|         Some(remaining_derivation) | ||||
|     } else { | ||||
|         None | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn plan_satisfaction<Ak>( | ||||
|     desc: &Descriptor<DefiniteDescriptorKey>, | ||||
|     assets: &Assets<Ak>, | ||||
| ) -> Option<Plan<Ak>> | ||||
| where | ||||
|     Ak: CanDerive + Clone, | ||||
| { | ||||
|     match desc { | ||||
|         Descriptor::Bare(_) => todo!(), | ||||
|         Descriptor::Pkh(_) => todo!(), | ||||
|         Descriptor::Wpkh(_) => todo!(), | ||||
|         Descriptor::Sh(_) => todo!(), | ||||
|         Descriptor::Wsh(_) => todo!(), | ||||
|         Descriptor::Tr(tr) => crate::plan_impls::plan_satisfaction_tr(tr, assets), | ||||
|     } | ||||
| } | ||||
| @ -1,323 +0,0 @@ | ||||
| use bdk_chain::{bitcoin, miniscript}; | ||||
| use bitcoin::locktime::{Height, Time}; | ||||
| use miniscript::Terminal; | ||||
| 
 | ||||
| use super::*; | ||||
| 
 | ||||
| impl<Ak> TermPlan<Ak> { | ||||
|     fn combine(self, other: Self) -> Option<Self> { | ||||
|         let min_locktime = { | ||||
|             match (self.min_locktime, other.min_locktime) { | ||||
|                 (Some(lhs), Some(rhs)) => { | ||||
|                     if lhs.is_same_unit(rhs) { | ||||
|                         Some(if lhs.to_consensus_u32() > rhs.to_consensus_u32() { | ||||
|                             lhs | ||||
|                         } else { | ||||
|                             rhs | ||||
|                         }) | ||||
|                     } else { | ||||
|                         return None; | ||||
|                     } | ||||
|                 } | ||||
|                 _ => self.min_locktime.or(other.min_locktime), | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         let min_sequence = { | ||||
|             match (self.min_sequence, other.min_sequence) { | ||||
|                 (Some(lhs), Some(rhs)) => { | ||||
|                     if lhs.is_height_locked() == rhs.is_height_locked() { | ||||
|                         Some(if lhs.to_consensus_u32() > rhs.to_consensus_u32() { | ||||
|                             lhs | ||||
|                         } else { | ||||
|                             rhs | ||||
|                         }) | ||||
|                     } else { | ||||
|                         return None; | ||||
|                     } | ||||
|                 } | ||||
|                 _ => self.min_sequence.or(other.min_sequence), | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         let mut template = self.template; | ||||
|         template.extend(other.template); | ||||
| 
 | ||||
|         Some(Self { | ||||
|             min_locktime, | ||||
|             min_sequence, | ||||
|             template, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub(crate) fn expected_size(&self) -> usize { | ||||
|         self.template.iter().map(|step| step.expected_size()).sum() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // impl crate::descriptor::Pkh<DefiniteDescriptorKey> {
 | ||||
| //     pub(crate) fn plan_satisfaction<Ak>(&self, assets: &Assets<Ak>) -> Option<Plan<Ak>>
 | ||||
| //     where
 | ||||
| //         Ak: CanDerive + Clone,
 | ||||
| //     {
 | ||||
| //         let (asset_key, derivation_hint) = assets.keys.iter().find_map(|asset_key| {
 | ||||
| //             let derivation_hint = asset_key.can_derive(self.as_inner())?;
 | ||||
| //             Some((asset_key, derivation_hint))
 | ||||
| //         })?;
 | ||||
| 
 | ||||
| //         Some(Plan {
 | ||||
| //             template: vec![TemplateItem::Sign(PlanKey {
 | ||||
| //                 asset_key: asset_key.clone(),
 | ||||
| //                 descriptor_key: self.as_inner().clone(),
 | ||||
| //                 derivation_hint,
 | ||||
| //             })],
 | ||||
| //             target: Target::Legacy,
 | ||||
| //             set_locktime: None,
 | ||||
| //             set_sequence: None,
 | ||||
| //         })
 | ||||
| //     }
 | ||||
| // }
 | ||||
| 
 | ||||
| // impl crate::descriptor::Wpkh<DefiniteDescriptorKey> {
 | ||||
| //     pub(crate) fn plan_satisfaction<Ak>(&self, assets: &Assets<Ak>) -> Option<Plan<Ak>>
 | ||||
| //     where
 | ||||
| //         Ak: CanDerive + Clone,
 | ||||
| //     {
 | ||||
| //         let (asset_key, derivation_hint) = assets.keys.iter().find_map(|asset_key| {
 | ||||
| //             let derivation_hint = asset_key.can_derive(self.as_inner())?;
 | ||||
| //             Some((asset_key, derivation_hint))
 | ||||
| //         })?;
 | ||||
| 
 | ||||
| //         Some(Plan {
 | ||||
| //             template: vec![TemplateItem::Sign(PlanKey {
 | ||||
| //                 asset_key: asset_key.clone(),
 | ||||
| //                 descriptor_key: self.as_inner().clone(),
 | ||||
| //                 derivation_hint,
 | ||||
| //             })],
 | ||||
| //             target: Target::Segwitv0,
 | ||||
| //             set_locktime: None,
 | ||||
| //             set_sequence: None,
 | ||||
| //         })
 | ||||
| //     }
 | ||||
| // }
 | ||||
| 
 | ||||
| pub(crate) fn plan_satisfaction_tr<Ak>( | ||||
|     tr: &miniscript::descriptor::Tr<DefiniteDescriptorKey>, | ||||
|     assets: &Assets<Ak>, | ||||
| ) -> Option<Plan<Ak>> | ||||
| where | ||||
|     Ak: CanDerive + Clone, | ||||
| { | ||||
|     let key_path_spend = assets.keys.iter().find_map(|asset_key| { | ||||
|         let derivation_hint = asset_key.can_derive(tr.internal_key())?; | ||||
|         Some((asset_key, derivation_hint)) | ||||
|     }); | ||||
| 
 | ||||
|     if let Some((asset_key, derivation_hint)) = key_path_spend { | ||||
|         return Some(Plan { | ||||
|             template: vec![TemplateItem::Sign(PlanKey { | ||||
|                 asset_key: asset_key.clone(), | ||||
|                 descriptor_key: tr.internal_key().clone(), | ||||
|                 derivation_hint, | ||||
|             })], | ||||
|             target: Target::Segwitv1 { | ||||
|                 tr: tr.clone(), | ||||
|                 tr_plan: TrSpend::KeySpend, | ||||
|             }, | ||||
|             set_locktime: None, | ||||
|             set_sequence: None, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     let mut plans = tr | ||||
|         .iter_scripts() | ||||
|         .filter_map(|(_, ms)| Some((ms, (plan_steps(&ms.node, assets)?)))) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     plans.sort_by_cached_key(|(_, plan)| plan.expected_size()); | ||||
| 
 | ||||
|     let (script, best_plan) = plans.into_iter().next()?; | ||||
| 
 | ||||
|     Some(Plan { | ||||
|         target: Target::Segwitv1 { | ||||
|             tr: tr.clone(), | ||||
|             tr_plan: TrSpend::LeafSpend { | ||||
|                 script: script.encode(), | ||||
|                 leaf_version: LeafVersion::TapScript, | ||||
|             }, | ||||
|         }, | ||||
|         set_locktime: best_plan.min_locktime.clone(), | ||||
|         set_sequence: best_plan.min_sequence.clone(), | ||||
|         template: best_plan.template, | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| struct TermPlan<Ak> { | ||||
|     pub min_locktime: Option<LockTime>, | ||||
|     pub min_sequence: Option<Sequence>, | ||||
|     pub template: Vec<TemplateItem<Ak>>, | ||||
| } | ||||
| 
 | ||||
| impl<Ak> TermPlan<Ak> { | ||||
|     fn new(template: Vec<TemplateItem<Ak>>) -> Self { | ||||
|         TermPlan { | ||||
|             template, | ||||
|             ..Default::default() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Ak> Default for TermPlan<Ak> { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             min_locktime: Default::default(), | ||||
|             min_sequence: Default::default(), | ||||
|             template: Default::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn plan_steps<Ak: Clone + CanDerive, Ctx: ScriptContext>( | ||||
|     term: &Terminal<DefiniteDescriptorKey, Ctx>, | ||||
|     assets: &Assets<Ak>, | ||||
| ) -> Option<TermPlan<Ak>> { | ||||
|     match term { | ||||
|         Terminal::True => Some(TermPlan::new(vec![])), | ||||
|         Terminal::False => return None, | ||||
|         Terminal::PkH(key) => { | ||||
|             let (asset_key, derivation_hint) = assets | ||||
|                 .keys | ||||
|                 .iter() | ||||
|                 .find_map(|asset_key| Some((asset_key, asset_key.can_derive(key)?)))?; | ||||
|             Some(TermPlan::new(vec![ | ||||
|                 TemplateItem::Sign(PlanKey { | ||||
|                     asset_key: asset_key.clone(), | ||||
|                     derivation_hint, | ||||
|                     descriptor_key: key.clone(), | ||||
|                 }), | ||||
|                 TemplateItem::Pk { key: key.clone() }, | ||||
|             ])) | ||||
|         } | ||||
|         Terminal::PkK(key) => { | ||||
|             let (asset_key, derivation_hint) = assets | ||||
|                 .keys | ||||
|                 .iter() | ||||
|                 .find_map(|asset_key| Some((asset_key, asset_key.can_derive(key)?)))?; | ||||
|             Some(TermPlan::new(vec![TemplateItem::Sign(PlanKey { | ||||
|                 asset_key: asset_key.clone(), | ||||
|                 derivation_hint, | ||||
|                 descriptor_key: key.clone(), | ||||
|             })])) | ||||
|         } | ||||
|         Terminal::RawPkH(_pk_hash) => { | ||||
|             /* TODO */ | ||||
|             None | ||||
|         } | ||||
|         Terminal::After(locktime) => { | ||||
|             let max_locktime = assets.max_locktime?; | ||||
|             let locktime = LockTime::from(locktime); | ||||
|             let (height, time) = match max_locktime { | ||||
|                 LockTime::Blocks(height) => (height, Time::from_consensus(0).unwrap()), | ||||
|                 LockTime::Seconds(seconds) => (Height::from_consensus(0).unwrap(), seconds), | ||||
|             }; | ||||
|             if max_locktime.is_satisfied_by(height, time) { | ||||
|                 Some(TermPlan { | ||||
|                     min_locktime: Some(locktime), | ||||
|                     ..Default::default() | ||||
|                 }) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Terminal::Older(older) => { | ||||
|             // FIXME: older should be a height or time not a sequence.
 | ||||
|             let max_sequence = assets.txo_age?; | ||||
|             //TODO: this whole thing is probably wrong but upstream should provide a way of
 | ||||
|             // doing it properly.
 | ||||
|             if max_sequence.is_height_locked() == older.is_height_locked() { | ||||
|                 if max_sequence.to_consensus_u32() >= older.to_consensus_u32() { | ||||
|                     Some(TermPlan { | ||||
|                         min_sequence: Some(*older), | ||||
|                         ..Default::default() | ||||
|                     }) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Terminal::Sha256(image) => { | ||||
|             if assets.sha256.contains(&image) { | ||||
|                 Some(TermPlan::new(vec![TemplateItem::Sha256(image.clone())])) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Terminal::Hash256(image) => { | ||||
|             if assets.hash256.contains(image) { | ||||
|                 Some(TermPlan::new(vec![TemplateItem::Hash256(image.clone())])) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Terminal::Ripemd160(image) => { | ||||
|             if assets.ripemd160.contains(&image) { | ||||
|                 Some(TermPlan::new(vec![TemplateItem::Ripemd160(image.clone())])) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Terminal::Hash160(image) => { | ||||
|             if assets.hash160.contains(&image) { | ||||
|                 Some(TermPlan::new(vec![TemplateItem::Hash160(image.clone())])) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Terminal::Alt(ms) | ||||
|         | Terminal::Swap(ms) | ||||
|         | Terminal::Check(ms) | ||||
|         | Terminal::Verify(ms) | ||||
|         | Terminal::NonZero(ms) | ||||
|         | Terminal::ZeroNotEqual(ms) => plan_steps(&ms.node, assets), | ||||
|         Terminal::DupIf(ms) => { | ||||
|             let mut plan = plan_steps(&ms.node, assets)?; | ||||
|             plan.template.push(TemplateItem::One); | ||||
|             Some(plan) | ||||
|         } | ||||
|         Terminal::AndV(l, r) | Terminal::AndB(l, r) => { | ||||
|             let lhs = plan_steps(&l.node, assets)?; | ||||
|             let rhs = plan_steps(&r.node, assets)?; | ||||
|             lhs.combine(rhs) | ||||
|         } | ||||
|         Terminal::AndOr(_, _, _) => todo!(), | ||||
|         Terminal::OrB(_, _) => todo!(), | ||||
|         Terminal::OrD(_, _) => todo!(), | ||||
|         Terminal::OrC(_, _) => todo!(), | ||||
|         Terminal::OrI(lhs, rhs) => { | ||||
|             let lplan = plan_steps(&lhs.node, assets).map(|mut plan| { | ||||
|                 plan.template.push(TemplateItem::One); | ||||
|                 plan | ||||
|             }); | ||||
|             let rplan = plan_steps(&rhs.node, assets).map(|mut plan| { | ||||
|                 plan.template.push(TemplateItem::Zero); | ||||
|                 plan | ||||
|             }); | ||||
|             match (lplan, rplan) { | ||||
|                 (Some(lplan), Some(rplan)) => { | ||||
|                     if lplan.expected_size() <= rplan.expected_size() { | ||||
|                         Some(lplan) | ||||
|                     } else { | ||||
|                         Some(rplan) | ||||
|                     } | ||||
|                 } | ||||
|                 (lplan, rplan) => lplan.or(rplan), | ||||
|             } | ||||
|         } | ||||
|         Terminal::Thresh(_, _) => todo!(), | ||||
|         Terminal::Multi(_, _) => todo!(), | ||||
|         Terminal::MultiA(_, _) => todo!(), | ||||
|     } | ||||
| } | ||||
| @ -1,218 +0,0 @@ | ||||
| use bdk_chain::{bitcoin, collections::*, miniscript}; | ||||
| use core::ops::Deref; | ||||
| 
 | ||||
| use bitcoin::{ | ||||
|     hashes::{hash160, ripemd160, sha256}, | ||||
|     psbt::Prevouts, | ||||
|     secp256k1::{KeyPair, Message, PublicKey, Signing, Verification}, | ||||
|     util::{bip32, sighash, sighash::SighashCache, taproot}, | ||||
|     EcdsaSighashType, SchnorrSighashType, Transaction, TxOut, XOnlyPublicKey, | ||||
| }; | ||||
| 
 | ||||
| use super::*; | ||||
| use miniscript::{ | ||||
|     descriptor::{DescriptorSecretKey, KeyMap}, | ||||
|     hash256, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| /// Signatures and hash pre-images that must be provided to complete the plan.
 | ||||
| pub struct Requirements<Ak> { | ||||
|     /// required signatures
 | ||||
|     pub signatures: RequiredSignatures<Ak>, | ||||
|     /// required sha256 pre-images
 | ||||
|     pub sha256_images: HashSet<sha256::Hash>, | ||||
|     /// required hash160 pre-images
 | ||||
|     pub hash160_images: HashSet<hash160::Hash>, | ||||
|     /// required hash256 pre-images
 | ||||
|     pub hash256_images: HashSet<hash256::Hash>, | ||||
|     /// required ripemd160 pre-images
 | ||||
|     pub ripemd160_images: HashSet<ripemd160::Hash>, | ||||
| } | ||||
| 
 | ||||
| impl<Ak> Default for RequiredSignatures<Ak> { | ||||
|     fn default() -> Self { | ||||
|         RequiredSignatures::Legacy { | ||||
|             keys: Default::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Ak> Default for Requirements<Ak> { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             signatures: Default::default(), | ||||
|             sha256_images: Default::default(), | ||||
|             hash160_images: Default::default(), | ||||
|             hash256_images: Default::default(), | ||||
|             ripemd160_images: Default::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Ak> Requirements<Ak> { | ||||
|     /// Whether any hash pre-images are required in the plan
 | ||||
|     pub fn requires_hash_preimages(&self) -> bool { | ||||
|         !(self.sha256_images.is_empty() | ||||
|             && self.hash160_images.is_empty() | ||||
|             && self.hash256_images.is_empty() | ||||
|             && self.ripemd160_images.is_empty()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// The signatures required to complete the plan
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum RequiredSignatures<Ak> { | ||||
|     /// Legacy ECDSA signatures are required
 | ||||
|     Legacy { keys: Vec<PlanKey<Ak>> }, | ||||
|     /// Segwitv0 ECDSA signatures are required
 | ||||
|     Segwitv0 { keys: Vec<PlanKey<Ak>> }, | ||||
|     /// A Taproot key spend signature is required
 | ||||
|     TapKey { | ||||
|         /// the internal key
 | ||||
|         plan_key: PlanKey<Ak>, | ||||
|         /// The merkle root of the taproot output
 | ||||
|         merkle_root: Option<TapBranchHash>, | ||||
|     }, | ||||
|     /// Taproot script path signatures are required
 | ||||
|     TapScript { | ||||
|         /// The leaf hash of the script being used
 | ||||
|         leaf_hash: TapLeafHash, | ||||
|         /// The keys in the script that require signatures
 | ||||
|         plan_keys: Vec<PlanKey<Ak>>, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum SigningError { | ||||
|     SigHashError(sighash::Error), | ||||
|     DerivationError(bip32::Error), | ||||
| } | ||||
| 
 | ||||
| impl From<sighash::Error> for SigningError { | ||||
|     fn from(e: sighash::Error) -> Self { | ||||
|         Self::SigHashError(e) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl core::fmt::Display for SigningError { | ||||
|     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||||
|         match self { | ||||
|             SigningError::SigHashError(e) => e.fmt(f), | ||||
|             SigningError::DerivationError(e) => e.fmt(f), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<bip32::Error> for SigningError { | ||||
|     fn from(e: bip32::Error) -> Self { | ||||
|         Self::DerivationError(e) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "std")] | ||||
| impl std::error::Error for SigningError {} | ||||
| 
 | ||||
| impl RequiredSignatures<DescriptorPublicKey> { | ||||
|     pub fn sign_with_keymap<T: Deref<Target = Transaction>>( | ||||
|         &self, | ||||
|         input_index: usize, | ||||
|         keymap: &KeyMap, | ||||
|         prevouts: &Prevouts<'_, impl core::borrow::Borrow<TxOut>>, | ||||
|         schnorr_sighashty: Option<SchnorrSighashType>, | ||||
|         _ecdsa_sighashty: Option<EcdsaSighashType>, | ||||
|         sighash_cache: &mut SighashCache<T>, | ||||
|         auth_data: &mut SatisfactionMaterial, | ||||
|         secp: &Secp256k1<impl Signing + Verification>, | ||||
|     ) -> Result<bool, SigningError> { | ||||
|         match self { | ||||
|             RequiredSignatures::Legacy { .. } | RequiredSignatures::Segwitv0 { .. } => todo!(), | ||||
|             RequiredSignatures::TapKey { | ||||
|                 plan_key, | ||||
|                 merkle_root, | ||||
|             } => { | ||||
|                 let schnorr_sighashty = schnorr_sighashty.unwrap_or(SchnorrSighashType::Default); | ||||
|                 let sighash = sighash_cache.taproot_key_spend_signature_hash( | ||||
|                     input_index, | ||||
|                     prevouts, | ||||
|                     schnorr_sighashty, | ||||
|                 )?; | ||||
|                 let secret_key = match keymap.get(&plan_key.asset_key) { | ||||
|                     Some(secret_key) => secret_key, | ||||
|                     None => return Ok(false), | ||||
|                 }; | ||||
|                 let secret_key = match secret_key { | ||||
|                     DescriptorSecretKey::Single(single) => single.key.inner, | ||||
|                     DescriptorSecretKey::XPrv(xprv) => { | ||||
|                         xprv.xkey | ||||
|                             .derive_priv(&secp, &plan_key.derivation_hint)? | ||||
|                             .private_key | ||||
|                     } | ||||
|                 }; | ||||
| 
 | ||||
|                 let pubkey = PublicKey::from_secret_key(&secp, &secret_key); | ||||
|                 let x_only_pubkey = XOnlyPublicKey::from(pubkey); | ||||
| 
 | ||||
|                 let tweak = | ||||
|                     taproot::TapTweakHash::from_key_and_tweak(x_only_pubkey, merkle_root.clone()); | ||||
|                 let keypair = KeyPair::from_secret_key(&secp, &secret_key.clone()) | ||||
|                     .add_xonly_tweak(&secp, &tweak.to_scalar()) | ||||
|                     .unwrap(); | ||||
| 
 | ||||
|                 let msg = Message::from_slice(sighash.as_ref()).expect("Sighashes are 32 bytes"); | ||||
|                 let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair); | ||||
| 
 | ||||
|                 let bitcoin_sig = SchnorrSig { | ||||
|                     sig, | ||||
|                     hash_ty: schnorr_sighashty, | ||||
|                 }; | ||||
| 
 | ||||
|                 auth_data | ||||
|                     .schnorr_sigs | ||||
|                     .insert(plan_key.descriptor_key.clone(), bitcoin_sig); | ||||
|                 Ok(true) | ||||
|             } | ||||
|             RequiredSignatures::TapScript { | ||||
|                 leaf_hash, | ||||
|                 plan_keys, | ||||
|             } => { | ||||
|                 let sighash_type = schnorr_sighashty.unwrap_or(SchnorrSighashType::Default); | ||||
|                 let sighash = sighash_cache.taproot_script_spend_signature_hash( | ||||
|                     input_index, | ||||
|                     prevouts, | ||||
|                     *leaf_hash, | ||||
|                     sighash_type, | ||||
|                 )?; | ||||
| 
 | ||||
|                 let mut modified = false; | ||||
| 
 | ||||
|                 for plan_key in plan_keys { | ||||
|                     if let Some(secret_key) = keymap.get(&plan_key.asset_key) { | ||||
|                         let secret_key = match secret_key { | ||||
|                             DescriptorSecretKey::Single(single) => single.key.inner, | ||||
|                             DescriptorSecretKey::XPrv(xprv) => { | ||||
|                                 xprv.xkey | ||||
|                                     .derive_priv(&secp, &plan_key.derivation_hint)? | ||||
|                                     .private_key | ||||
|                             } | ||||
|                         }; | ||||
|                         let keypair = KeyPair::from_secret_key(&secp, &secret_key.clone()); | ||||
|                         let msg = | ||||
|                             Message::from_slice(sighash.as_ref()).expect("Sighashes are 32 bytes"); | ||||
|                         let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair); | ||||
|                         let bitcoin_sig = SchnorrSig { | ||||
|                             sig, | ||||
|                             hash_ty: sighash_type, | ||||
|                         }; | ||||
| 
 | ||||
|                         auth_data | ||||
|                             .schnorr_sigs | ||||
|                             .insert(plan_key.descriptor_key.clone(), bitcoin_sig); | ||||
|                         modified = true; | ||||
|                     } | ||||
|                 } | ||||
|                 Ok(modified) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,76 +0,0 @@ | ||||
| use bdk_chain::{bitcoin, miniscript}; | ||||
| use bitcoin::{ | ||||
|     hashes::{hash160, ripemd160, sha256}, | ||||
|     util::bip32::DerivationPath, | ||||
| }; | ||||
| 
 | ||||
| use super::*; | ||||
| use crate::{hash256, varint_len, DefiniteDescriptorKey}; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub(crate) enum TemplateItem<Ak> { | ||||
|     Sign(PlanKey<Ak>), | ||||
|     Pk { key: DefiniteDescriptorKey }, | ||||
|     One, | ||||
|     Zero, | ||||
|     Sha256(sha256::Hash), | ||||
|     Hash256(hash256::Hash), | ||||
|     Ripemd160(ripemd160::Hash), | ||||
|     Hash160(hash160::Hash), | ||||
| } | ||||
| 
 | ||||
| /// A plan key contains the asset key originally provided along with key in the descriptor it
 | ||||
| /// purports to be able to derive for along with a "hint" on how to derive it.
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct PlanKey<Ak> { | ||||
|     /// The key the planner will sign with
 | ||||
|     pub asset_key: Ak, | ||||
|     /// A hint from how to get from the asset key to the concrete key we need to sign with.
 | ||||
|     pub derivation_hint: DerivationPath, | ||||
|     /// The key that was in the descriptor that we are satisfying with the signature from the asset
 | ||||
|     /// key.
 | ||||
|     pub descriptor_key: DefiniteDescriptorKey, | ||||
| } | ||||
| 
 | ||||
| impl<Ak> TemplateItem<Ak> { | ||||
|     pub fn expected_size(&self) -> usize { | ||||
|         match self { | ||||
|             TemplateItem::Sign { .. } => 64, /*size of sig TODO: take into consideration sighash falg*/ | ||||
|             TemplateItem::Pk { .. } => 32, | ||||
|             TemplateItem::One => varint_len(1), | ||||
|             TemplateItem::Zero => 0, /* zero means an empty witness element */ | ||||
|             // I'm not sure if it should be 32 here (it's a 20 byte hash) but that's what other
 | ||||
|             // parts of the code were doing.
 | ||||
|             TemplateItem::Hash160(_) | TemplateItem::Ripemd160(_) => 32, | ||||
|             TemplateItem::Sha256(_) | TemplateItem::Hash256(_) => 32, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // this can only be called if we are sure that auth_data has what we need
 | ||||
|     pub(super) fn to_witness_stack(&self, auth_data: &SatisfactionMaterial) -> Vec<Vec<u8>> { | ||||
|         match self { | ||||
|             TemplateItem::Sign(plan_key) => { | ||||
|                 vec![auth_data | ||||
|                     .schnorr_sigs | ||||
|                     .get(&plan_key.descriptor_key) | ||||
|                     .unwrap() | ||||
|                     .to_vec()] | ||||
|             } | ||||
|             TemplateItem::One => vec![vec![1]], | ||||
|             TemplateItem::Zero => vec![vec![]], | ||||
|             TemplateItem::Sha256(image) => { | ||||
|                 vec![auth_data.sha256_preimages.get(image).unwrap().to_vec()] | ||||
|             } | ||||
|             TemplateItem::Hash160(image) => { | ||||
|                 vec![auth_data.hash160_preimages.get(image).unwrap().to_vec()] | ||||
|             } | ||||
|             TemplateItem::Ripemd160(image) => { | ||||
|                 vec![auth_data.ripemd160_preimages.get(image).unwrap().to_vec()] | ||||
|             } | ||||
|             TemplateItem::Hash256(image) => { | ||||
|                 vec![auth_data.hash256_preimages.get(image).unwrap().to_vec()] | ||||
|             } | ||||
|             TemplateItem::Pk { key } => vec![key.to_public_key().to_bytes()], | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user