bdk/nursery/tmp_plan/src/plan_impls.rs

326 lines
11 KiB
Rust
Raw Normal View History

use bdk_chain::{bitcoin, miniscript};
2023-06-26 12:18:15 +02:00
use bitcoin::locktime::absolute;
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> {
2023-06-26 12:18:15 +02:00
pub min_locktime: Option<absolute::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?;
2023-06-26 12:18:15 +02:00
let locktime = absolute::LockTime::from(*locktime);
let (height, time) = match max_locktime {
2023-06-26 12:18:15 +02:00
absolute::LockTime::Blocks(height) => {
(height, absolute::Time::from_consensus(0).unwrap())
}
absolute::LockTime::Seconds(seconds) => (absolute::Height::ZERO, 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 {
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(chain): use `minimal_non_dust()` instead of `dust_value()` fix(chain): use `compute_txid()` instead of `txid` deps(testenv): bump `electrsd` to `0.28.0` deps(electrum): bump `electrum-client` to `0.20.0` fix(electrum): use `compute_txid()` instead of `txid` deps(esplora): bump `esplora-client` to `0.8.0` deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to `0.19.0` fix(bitcoind_rpc): use `compute_txid()` instead of `txid` fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected `Signature` fields fix(sqlite): use `compute_txid()` instead of `txid` deps(hwi): bump `hwi` to `0.9.0` deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(wallet): use `compute_txid()` and `minimal_non_dust()` - update to use `compute_txid()` instead of deprecated `txid()` - update to use `minimal_non_dust()` instead of `dust_value()` - remove unused `bitcoin::hex::FromHex`. fix(wallet): uses `.into` conversion on `Network` for `NetworkKind` - uses `.into()` when appropriate, otherwise use the explicit `NetworkKind`, and it's `.is_mainnet()` method. fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError` fix(wallet): fields on taproot, and ecdsa `Signature` structure fix(wallet/wallet): convert `Weight` to `usize` for now - converts the `bitcoin-units::Weight` type to `usize` with help of `to_wu()` method. - it should be updated/refactored in the future to handle the `Weight` type throughout the code instead of current `usize`, only converting it for now. - allows the usage of deprecated `is_provably_unspendable()`, needs further discussion if suggested `is_op_return` is suitable. - update the expect field to `signature`, as it was renamed from `sig`. fix(wallet/wallet): use `is_op_return` instead of `is_provably_unspendable` fix(wallet/wallet): use `relative::Locktime` instead of `Sequence` fix(wallet/descriptor): use `ParsePublicKeyError` fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and `RelLockTime` to `absolute::LockTime` and `relative::LockTime` fix(wallet/wallet): use `Message::from_digest()` instead of relying on deprecated `ThirtyTwoByteHash` trait. fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it internally fix(wallet/wallet): remove `0x` prefix from expected `TxId` display fix(examples): use `compute_txid()` instead of `txid` fix(ci): remove usage of `bitcoin/no-std` feature - remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
min_sequence: Some((*older).into()),
..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),
}
}
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(chain): use `minimal_non_dust()` instead of `dust_value()` fix(chain): use `compute_txid()` instead of `txid` deps(testenv): bump `electrsd` to `0.28.0` deps(electrum): bump `electrum-client` to `0.20.0` fix(electrum): use `compute_txid()` instead of `txid` deps(esplora): bump `esplora-client` to `0.8.0` deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to `0.19.0` fix(bitcoind_rpc): use `compute_txid()` instead of `txid` fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected `Signature` fields fix(sqlite): use `compute_txid()` instead of `txid` deps(hwi): bump `hwi` to `0.9.0` deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(wallet): use `compute_txid()` and `minimal_non_dust()` - update to use `compute_txid()` instead of deprecated `txid()` - update to use `minimal_non_dust()` instead of `dust_value()` - remove unused `bitcoin::hex::FromHex`. fix(wallet): uses `.into` conversion on `Network` for `NetworkKind` - uses `.into()` when appropriate, otherwise use the explicit `NetworkKind`, and it's `.is_mainnet()` method. fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError` fix(wallet): fields on taproot, and ecdsa `Signature` structure fix(wallet/wallet): convert `Weight` to `usize` for now - converts the `bitcoin-units::Weight` type to `usize` with help of `to_wu()` method. - it should be updated/refactored in the future to handle the `Weight` type throughout the code instead of current `usize`, only converting it for now. - allows the usage of deprecated `is_provably_unspendable()`, needs further discussion if suggested `is_op_return` is suitable. - update the expect field to `signature`, as it was renamed from `sig`. fix(wallet/wallet): use `is_op_return` instead of `is_provably_unspendable` fix(wallet/wallet): use `relative::Locktime` instead of `Sequence` fix(wallet/descriptor): use `ParsePublicKeyError` fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and `RelLockTime` to `absolute::LockTime` and `relative::LockTime` fix(wallet/wallet): use `Message::from_digest()` instead of relying on deprecated `ThirtyTwoByteHash` trait. fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it internally fix(wallet/wallet): remove `0x` prefix from expected `TxId` display fix(examples): use `compute_txid()` instead of `txid` fix(ci): remove usage of `bitcoin/no-std` feature - remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
Terminal::Thresh(_) => todo!(),
Terminal::Multi(_) => todo!(),
Terminal::MultiA(_) => todo!(),
}
}