Skip to content

Commit 597a82c

Browse files
committed
tr: Spend Simplicity desctriptors
TapLeafScript is extended to handle everything that is required for spending in a generic way. The witness for Simplicity looks familiar: The "witness stack", the "script_pubkey" and the control block. What these are is slighly different: "Witness stack" is one large byte blob that deserializes to the Simplicity program plus its witness data. There is always exactly one item on the witness stack, unlike Miniscript where there are often multiple items. "Script_pubkey" is a Bitcoin Script that contains the CMR. The control block is as usual. Even though there are these differences, the code for computing the "witness size" should be general enough to work for both Miniscript and Simplicity.
1 parent a374492 commit 597a82c

File tree

2 files changed

+82
-8
lines changed

2 files changed

+82
-8
lines changed

src/descriptor/tr.rs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,23 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> {
458458
_ => None,
459459
}
460460
}
461+
462+
/// Return the byte size of the encoded leaf script (witness script).
463+
pub fn script_size(&self) -> usize {
464+
match self {
465+
TapLeafScript::Miniscript(ms) => ms.script_size(),
466+
// Simplicity's witness script is always a 32-byte CMR
467+
TapLeafScript::Simplicity(..) => 32,
468+
}
469+
}
470+
471+
/// Return the version of the leaf.
472+
pub fn version(&self) -> LeafVersion {
473+
match self {
474+
TapLeafScript::Miniscript(..) => LeafVersion::default(),
475+
TapLeafScript::Simplicity(..) => simplicity::leaf_version(),
476+
}
477+
}
461478
}
462479

463480
impl<'a, Pk: ToPublicKey, Ext: ParseableExt> TapLeafScript<'a, Pk, Ext> {
@@ -471,6 +488,28 @@ impl<'a, Pk: ToPublicKey, Ext: ParseableExt> TapLeafScript<'a, Pk, Ext> {
471488
}
472489
}
473490
}
491+
492+
/// Attempt to produce a malleable satisfying witness for the leaf script.
493+
pub fn satisfy_malleable<S: Satisfier<Pk>>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error> {
494+
match self {
495+
TapLeafScript::Miniscript(ms) => ms.satisfy_malleable(satisfier),
496+
// There doesn't (yet?) exist a malleable satisfaction of Simplicity policy
497+
TapLeafScript::Simplicity(..) => self.satisfy(satisfier),
498+
}
499+
}
500+
501+
/// Attempt to produce a non-malleable satisfying witness for the leaf script.
502+
pub fn satisfy<S: Satisfier<Pk>>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error> {
503+
match self {
504+
TapLeafScript::Miniscript(ms) => ms.satisfy(satisfier),
505+
TapLeafScript::Simplicity(sim) => {
506+
let satisfier = crate::simplicity::SatisfierWrapper::new(satisfier);
507+
let program = sim.satisfy(&satisfier).map_err(|_| Error::CouldNotSatisfy)?;
508+
let program_and_witness_bytes = program.encode_to_vec();
509+
Ok(vec![program_and_witness_bytes])
510+
},
511+
}
512+
}
474513
}
475514

476515
/// Iterator over the leaves of a tap tree.
@@ -815,15 +854,14 @@ where
815854
// Since we have the complete descriptor we can ignore the satisfier. We don't use the control block
816855
// map (lookup_control_block) from the satisfier here.
817856
let (mut min_wit, mut min_wit_len) = (None, None);
818-
for (depth, ms) in desc.iter_scripts() {
819-
let ms = ms.as_miniscript().unwrap();
857+
for (depth, script) in desc.iter_scripts() {
820858
let mut wit = if allow_mall {
821-
match ms.satisfy_malleable(&satisfier) {
859+
match script.satisfy_malleable(&satisfier) {
822860
Ok(wit) => wit,
823861
Err(..) => continue, // No witness for this script in tr descriptor, look for next one
824862
}
825863
} else {
826-
match ms.satisfy(&satisfier) {
864+
match script.satisfy(&satisfier) {
827865
Ok(wit) => wit,
828866
Err(..) => continue, // No witness for this script in tr descriptor, look for next one
829867
}
@@ -833,12 +871,13 @@ where
833871
// The extra +2 elements are control block and script itself
834872
let wit_size = witness_size(&wit)
835873
+ control_block_len(depth)
836-
+ ms.script_size()
837-
+ varint_len(ms.script_size());
874+
+ script.script_size()
875+
+ varint_len(script.script_size());
876+
838877
if min_wit_len.is_some() && Some(wit_size) > min_wit_len {
839878
continue;
840879
} else {
841-
let leaf_script = (ms.encode(), LeafVersion::default());
880+
let leaf_script = (script.encode(), script.version());
842881
let control_block = spend_info
843882
.control_block(&leaf_script)
844883
.expect("Control block must exist in script map for every known leaf");

src/simplicity.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// SPDX-License-Identifier: CC0-1.0
22
use std::fmt;
3+
use std::marker::PhantomData;
34
use std::str::FromStr;
45
use std::sync::Arc;
56

6-
use simplicity::{Policy, FailEntropy};
7+
use bitcoin_miniscript::ToPublicKey;
8+
use elements::{LockTime, SchnorrSig, Sequence};
9+
use elements::taproot::TapLeafHash;
10+
use simplicity::{Policy, FailEntropy, Preimage32};
711

812
use crate::policy::concrete::PolicyError;
913
use crate::{expression, Error, MiniscriptKey};
@@ -121,6 +125,37 @@ where
121125
true
122126
}
123127

128+
// We could make crate::Satisfier a subtrait of simplicity::Satisfier,
129+
// but then we would have to implement simplicity::Satisfier for all the blanket implementations
130+
// of crate::Satisfier, such as HashMap<Pk, ElementsSig>, which is annoying
131+
// We might choose to do so in the future, but for now a crate-local wrapper is easier
132+
// This wrapper is internally used by `Tr` and is never encountered by users
133+
pub(crate) struct SatisfierWrapper<Pk: ToPublicKey, S: crate::Satisfier<Pk>>(S, PhantomData<Pk>);
134+
135+
impl<Pk: ToPublicKey, S: crate::Satisfier<Pk>> SatisfierWrapper<Pk, S> {
136+
pub fn new(satisfier: S) -> Self {
137+
Self(satisfier, PhantomData)
138+
}
139+
}
140+
141+
impl<Pk: ToPublicKey, S: crate::Satisfier<Pk>> simplicity::Satisfier<Pk> for SatisfierWrapper<Pk, S> {
142+
fn lookup_tap_leaf_script_sig(&self, pk: &Pk, hash: &TapLeafHash) -> Option<SchnorrSig> {
143+
self.0.lookup_tap_leaf_script_sig(pk, hash)
144+
}
145+
146+
fn lookup_sha256(&self, hash: &Pk::Sha256) -> Option<Preimage32> {
147+
self.0.lookup_sha256(hash)
148+
}
149+
150+
fn check_older(&self, sequence: Sequence) -> bool {
151+
self.0.check_older(sequence)
152+
}
153+
154+
fn check_after(&self, locktime: LockTime) -> bool {
155+
self.0.check_after(locktime)
156+
}
157+
}
158+
124159
#[cfg(test)]
125160
mod tests {
126161
use secp256k1::XOnlyPublicKey;

0 commit comments

Comments
 (0)