Skip to content

Commit

Permalink
[wip] Remove explicit signatures from Assets
Browse files Browse the repository at this point in the history
Instead of storing keys and signatures in the `Assets` struct, only
store keys with extra conditions on how the key can sign
  • Loading branch information
afilini committed Dec 4, 2022
1 parent c3ba4d3 commit 816bf94
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 201 deletions.
11 changes: 8 additions & 3 deletions src/descriptor/tr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use sync::Arc;
use super::checksum::{self, verify_checksum};
use crate::descriptor::{DefiniteDescriptorKey, DescriptorType};
use crate::expression::{self, FromTree};
use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness, WitnessTemplate};
use crate::miniscript::satisfy::{
Placeholder, Satisfaction, SchnorrSigType, Witness, WitnessTemplate,
};
use crate::miniscript::Miniscript;
use crate::plan::{AssetProvider, Plan};
use crate::policy::semantic::Policy;
Expand Down Expand Up @@ -656,11 +658,14 @@ where
{
let spend_info = desc.spend_info();
// First try the key spend path
if provider.lookup_tap_key_spend_sig(&desc.internal_key) {
if let Some(size) = provider.lookup_tap_key_spend_sig(&desc.internal_key) {
Satisfaction {
stack: Witness::Stack(vec![Placeholder::SchnorrSig(
desc.internal_key.clone(),
None,
SchnorrSigType::KeySpend {
merkle_root: spend_info.merkle_root(),
},
size,
)]),
has_sig: true,
absolute_timelock: None,
Expand Down
187 changes: 57 additions & 130 deletions src/miniscript/satisfy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ use core::{cmp, fmt, i64, mem};

use bitcoin::hashes::hash160;
use bitcoin::secp256k1::XOnlyPublicKey;
use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapLeafHash};
use bitcoin::util::taproot::{ControlBlock, LeafVersion, TapBranchHash, TapLeafHash};
use bitcoin::{LockTime, PackedLockTime, Script, Sequence};
use sync::Arc;

use super::context::SigType;
use crate::descriptor::DescriptorType;
use crate::plan::{AssetProvider, Plan, RequiredPreimage, RequiredSig};
use crate::plan::{AssetProvider, Plan};
use crate::prelude::*;
use crate::util::witness_size;
use crate::{
Expand Down Expand Up @@ -530,6 +530,21 @@ impl_tuple_satisfier!(A, B, C, D, E, F);
impl_tuple_satisfier!(A, B, C, D, E, F, G);
impl_tuple_satisfier!(A, B, C, D, E, F, G, H);

#[derive(Debug, Clone, PartialEq, Eq)]
/// Type of schnorr signature to produce
pub enum SchnorrSigType {
/// Key spend signature
KeySpend {
/// Merkle root to tweak the key, if present
merkle_root: Option<TapBranchHash>,
},
/// Script spend signature
ScriptSpend {
/// Leaf hash of the script
leaf_hash: TapLeafHash,
},
}

#[derive(Debug, Clone, PartialEq, Eq)]
/// Placeholder for some data in a [`WitnessTemplate`]
pub enum Placeholder<Pk: MiniscriptKey> {
Expand All @@ -541,10 +556,10 @@ pub enum Placeholder<Pk: MiniscriptKey> {
EcdsaSigPk(Pk),
/// ECDSA signature given the pubkey hash
EcdsaSigHash(hash160::Hash),
/// Schnorr signature
SchnorrSig(Pk, Option<TapLeafHash>),
/// Schnorr signature given the pubkey hash and the leaf hash
SchnorrSigHash(hash160::Hash, TapLeafHash),
/// Schnorr signature and its size
SchnorrSig(Pk, SchnorrSigType, usize),
/// Schnorr signature given the pubkey hash and the leaf hash, plus its size
SchnorrSigHash(hash160::Hash, TapLeafHash, usize),
/// SHA-256 preimage
Sha256Preimage(Pk::Sha256),
/// HASH256 preimage
Expand Down Expand Up @@ -575,12 +590,16 @@ impl<Pk: MiniscriptKey> fmt::Display for Placeholder<Pk> {
PubkeyHash(pkh, size) => write!(f, "PubkeyHash(pkh: {}, size: {})", pkh, size),
EcdsaSigPk(pk) => write!(f, "EcdsaSigPk(pk: {})", pk),
EcdsaSigHash(hash) => write!(f, "EcdsaSigHash(hash: {})", hash),
SchnorrSig(pk, tap_leaf_hash) => write!(
SchnorrSig(pk, tap_leaf_hash, size) => write!(
f,
"SchnorrSig(pk: {}, tap_leaf_hash: {:?})",
pk, tap_leaf_hash
"SchnorrSig(pk: {}, tap_leaf_hash: {:?}, size: {})",
pk, tap_leaf_hash, size
),
SchnorrSigHash(pkh, lh, size) => write!(
f,
"SchnorrSigHash(pkh: {}, leaf_hash: {}, size: {})",
pkh, lh, size
),
SchnorrSigHash(pkh, lh) => write!(f, "SchnorrSigHash(pkh: {}, leaf_hash: {})", pkh, lh),
Sha256Preimage(hash) => write!(f, "Sha256Preimage(hash: {})", hash),
Hash256Preimage(hash) => write!(f, "Hash256Preimage(hash: {})", hash),
Ripemd160Preimage(hash) => write!(f, "Ripemd160Preimage(hash: {})", hash),
Expand Down Expand Up @@ -612,8 +631,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Placeholder<Pk> {
.or(sat.lookup_raw_pkh_ecdsa_sig(pkh).map(|(p, _)| p))
.map(|pk| {
let pk = pk.to_bytes();
// We have to add a 1-byte OP_PUSH
debug_assert!(1 + pk.len() == *size);
// Need to add +1 because the size in the placeholder also accounts for the OP_PUSH
debug_assert!(pk.len() + 1 == *size);
pk
}),
Placeholder::Hash256Preimage(h) => sat.lookup_hash256(h).map(|p| p.to_vec()),
Expand All @@ -624,13 +643,26 @@ impl<Pk: MiniscriptKey + ToPublicKey> Placeholder<Pk> {
Placeholder::EcdsaSigHash(pkh) => {
sat.lookup_raw_pkh_ecdsa_sig(pkh).map(|(_, s)| s.to_vec())
}
Placeholder::SchnorrSig(pk, Some(leaf_hash)) => sat
Placeholder::SchnorrSig(pk, SchnorrSigType::ScriptSpend { leaf_hash }, size) => sat
.lookup_tap_leaf_script_sig(pk, leaf_hash)
.map(|s| s.to_vec()),
Placeholder::SchnorrSigHash(pkh, lh) => sat
.map(|s| s.to_vec())
.map(|s| {
debug_assert!(s.len() == *size);
s
}),
Placeholder::SchnorrSigHash(pkh, lh, size) => sat
.lookup_raw_pkh_tap_leaf_script_sig(&(*pkh, *lh))
.map(|(_, s)| s.to_vec()),
Placeholder::SchnorrSig(_, _) => sat.lookup_tap_key_spend_sig().map(|s| s.to_vec()),
.map(|(_, s)| s.to_vec())
.map(|s| {
debug_assert!(s.len() == *size);
s
}),
Placeholder::SchnorrSig(_, _, size) => {
sat.lookup_tap_key_spend_sig().map(|s| s.to_vec()).map(|s| {
debug_assert!(s.len() == *size);
s
})
}
Placeholder::HashDissatisfaction => Some(vec![0; 32]),
Placeholder::PushZero => Some(vec![]),
Placeholder::PushOne => Some(vec![1]),
Expand All @@ -653,29 +685,6 @@ pub enum Witness<T> {
Impossible,
}

/// Enum for partially satisfied witness templates
pub enum PartialSatisfaction<Pk: MiniscriptKey> {
/// Placeholder item (not yet satisfied)
Placeholder(Placeholder<Pk>),
/// Actual data
Data(Vec<u8>),
}

impl<Pk: MiniscriptKey> PartialSatisfaction<Pk> {
/// Whether the item is a placeholder
pub fn is_placeholder(&self) -> bool {
match &self {
PartialSatisfaction::Placeholder(_) => true,
_ => false,
}
}

/// Whether the item is data
pub fn is_data(&self) -> bool {
!self.is_placeholder()
}
}

/// Template of a witness being constructed interactively
///
/// The generic `I` type determines the available API:
Expand Down Expand Up @@ -710,91 +719,6 @@ impl<Pk: MiniscriptKey + ToPublicKey> WitnessTemplate<Placeholder<Pk>> {

Some(stack)
}

/// Being an interactive satisfaction session
pub fn interactive_satisfaction(self) -> WitnessTemplate<PartialSatisfaction<Pk>> {
WitnessTemplate {
stack: self
.stack
.into_iter()
.map(PartialSatisfaction::Placeholder)
.collect(),
}
}

/// Returns the list of required signatures
pub fn required_signatures(&self) -> Vec<RequiredSig<'_, Pk>> {
self.stack
.iter()
.filter_map(|item| match item {
Placeholder::EcdsaSigPk(pk) => Some(RequiredSig::Ecdsa(pk)),
Placeholder::SchnorrSig(pk, None) => Some(RequiredSig::SchnorrTapKey(pk)),
Placeholder::SchnorrSig(pk, Some(lh)) => {
Some(RequiredSig::SchnorrTapScript(pk, lh))
}
_ => None,
})
.collect()
}

/// Returns the list of required preimages
pub fn required_preimages(&self) -> Vec<RequiredPreimage<'_, Pk>> {
self.stack
.iter()
.filter_map(|item| match item {
Placeholder::Sha256Preimage(h) => Some(RequiredPreimage::Sha256(h)),
Placeholder::Hash256Preimage(h) => Some(RequiredPreimage::Hash256(h)),
Placeholder::Ripemd160Preimage(h) => Some(RequiredPreimage::Ripemd160(h)),
Placeholder::Hash160Preimage(h) => Some(RequiredPreimage::Hash160(h)),
_ => None,
})
.collect()
}
}

impl<Pk: MiniscriptKey + ToPublicKey> WitnessTemplate<PartialSatisfaction<Pk>> {
/// Apply the items needed from a satisfier
///
/// Returns the completed witness if all the placeholders have been filled, or `Err` with itself a list of missing
/// items otherwise.
pub fn apply<Sat: Satisfier<Pk>>(
self,
stfr: &Sat,
) -> Result<Vec<Vec<u8>>, (Self, Vec<Placeholder<Pk>>)> {
let mut unsatisfied = vec![];

let stack = self
.stack
.into_iter()
.map(|ps| {
let placeholder = match &ps {
PartialSatisfaction::Placeholder(p) => p,
PartialSatisfaction::Data(_) => return ps,
};

if let Some(data) = placeholder.satisfy_self(stfr) {
return PartialSatisfaction::Data(data);
}

unsatisfied.push(placeholder.clone());
ps
})
.collect::<Vec<_>>();

if unsatisfied.is_empty() {
Ok(stack
.into_iter()
.map(|ps| match ps {
PartialSatisfaction::Data(d) => d,
PartialSatisfaction::Placeholder(_) => {
unreachable!("there shouldn't be any placeholder left")
}
})
.collect())
} else {
Err((WitnessTemplate { stack }, unsatisfied))
}
}
}

impl<Pk: MiniscriptKey> PartialOrd for Witness<Placeholder<Pk>> {
Expand Down Expand Up @@ -838,10 +762,13 @@ impl<Pk: MiniscriptKey + ToPublicKey> Witness<Placeholder<Pk>> {
}
}
super::context::SigType::Schnorr => {
if sat.lookup_tap_leaf_script_sig(pk, leaf_hash) {
if let Some(size) = sat.lookup_tap_leaf_script_sig(pk, leaf_hash) {
Witness::Stack(vec![Placeholder::SchnorrSig(
pk.clone(),
Some(leaf_hash.clone()),
SchnorrSigType::ScriptSpend {
leaf_hash: *leaf_hash,
},
size,
)])
} else {
// Signatures cannot be forged
Expand Down Expand Up @@ -885,11 +812,11 @@ impl<Pk: MiniscriptKey + ToPublicKey> Witness<Placeholder<Pk>> {
None => Witness::Impossible,
},
SigType::Schnorr => match sat.lookup_raw_pkh_tap_leaf_script_sig(&(*pkh, *leaf_hash)) {
true => Witness::Stack(vec![
Placeholder::SchnorrSigHash(pkh.clone(), *leaf_hash),
Some(size) => Witness::Stack(vec![
Placeholder::SchnorrSigHash(pkh.clone(), *leaf_hash, size),
Placeholder::PubkeyHash(pkh.clone(), 32),
]),
false => Witness::Impossible,
None => Witness::Impossible,
},
}
}
Expand Down
Loading

0 comments on commit 816bf94

Please sign in to comment.