Skip to content

Commit 9e60cbc

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 b56eeaf commit 9e60cbc

File tree

2 files changed

+84
-9
lines changed

2 files changed

+84
-9
lines changed

src/descriptor/tr.rs

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> {
461461
}
462462

463463
impl<'a, Pk: ToPublicKey, Ext: ParseableExt> TapLeafScript<'a, Pk, Ext> {
464-
/// Encode as Bitcoin Script.
464+
/// Encode the leaf script as Bitcoin script (script-pubkey).
465465
pub fn encode(&self) -> Script {
466466
match self {
467467
TapLeafScript::Miniscript(ms) => ms.encode(),
@@ -471,6 +471,46 @@ impl<'a, Pk: ToPublicKey, Ext: ParseableExt> TapLeafScript<'a, Pk, Ext> {
471471
}
472472
}
473473
}
474+
475+
/// Return the byte size of the encoded leaf script (script-pubkey).
476+
pub fn script_size(&self) -> usize {
477+
match self {
478+
TapLeafScript::Miniscript(ms) => ms.script_size(),
479+
// Simplicity's script-pubkey is always a 32-byte CMR
480+
TapLeafScript::Simplicity(..) => 32,
481+
}
482+
}
483+
484+
/// Return the version of the leaf.
485+
pub fn version(&self) -> LeafVersion {
486+
match self {
487+
TapLeafScript::Miniscript(..) => LeafVersion::default(),
488+
TapLeafScript::Simplicity(..) => simplicity::leaf_version(),
489+
}
490+
}
491+
492+
/// Attempt to produce 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 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+
// There doesn't (yet?) exist a malleable satisfaction of Simplicity policy
506+
TapLeafScript::Simplicity(sim) => {
507+
let satisfier = crate::simplicity::SatisfierWrapper::new(satisfier);
508+
let program = sim.satisfy(&satisfier).map_err(|_| Error::CouldNotSatisfy)?;
509+
let program_and_witness_bytes = program.encode_to_vec();
510+
Ok(vec![program_and_witness_bytes])
511+
},
512+
}
513+
}
474514
}
475515

476516
/// Iterator over the leaves of a tap tree.
@@ -815,15 +855,14 @@ where
815855
// Since we have the complete descriptor we can ignore the satisfier. We don't use the control block
816856
// map (lookup_control_block) from the satisfier here.
817857
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();
858+
for (depth, script) in desc.iter_scripts() {
820859
let mut wit = if allow_mall {
821-
match ms.satisfy_malleable(&satisfier) {
860+
match script.satisfy_malleable(&satisfier) {
822861
Ok(wit) => wit,
823862
Err(..) => continue, // No witness for this script in tr descriptor, look for next one
824863
}
825864
} else {
826-
match ms.satisfy(&satisfier) {
865+
match script.satisfy(&satisfier) {
827866
Ok(wit) => wit,
828867
Err(..) => continue, // No witness for this script in tr descriptor, look for next one
829868
}
@@ -833,12 +872,13 @@ where
833872
// The extra +2 elements are control block and script itself
834873
let wit_size = witness_size(&wit)
835874
+ control_block_len(depth)
836-
+ ms.script_size()
837-
+ varint_len(ms.script_size());
875+
+ script.script_size()
876+
+ varint_len(script.script_size());
877+
838878
if min_wit_len.is_some() && Some(wit_size) > min_wit_len {
839879
continue;
840880
} else {
841-
let leaf_script = (ms.encode(), LeafVersion::default());
881+
let leaf_script = (script.encode(), script.version());
842882
let control_block = spend_info
843883
.control_block(&leaf_script)
844884
.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)