Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add KeyExpr<Pk> in Taproot descriptor #456

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ edition = "2018"

[features]
default = ["std"]
std = ["bitcoin/std", "bitcoin/secp-recovery"]
std = ["bitcoin/std", "bitcoin/secp-recovery", "secp256k1-zkp"]
no-std = ["hashbrown", "bitcoin/no-std"]
compiler = []
trace = []
Expand All @@ -24,6 +24,7 @@ rand = ["bitcoin/rand"]
bitcoin = { version = "0.28.1", default-features = false }
serde = { version = "1.0", optional = true }
hashbrown = { version = "0.11", optional = true }
secp256k1-zkp = { git = "https://github.com/sanket1729/rust-secp256k1-zkp", branch = "pr29", optional = true}

[dev-dependencies]
bitcoind = {version = "0.26.1", features=["22_0"]}
Expand Down
2 changes: 1 addition & 1 deletion examples/taproot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn main() {
if let Descriptor::Tr(ref p) = desc {
// Check if internal key is correctly inferred as Ca
// assert_eq!(p.internal_key(), &pubkeys[2]);
assert_eq!(p.internal_key(), "Ca");
assert_eq!(p.internal_key().single_key().unwrap(), "Ca");

// Iterate through scripts
let mut iter = p.iter_scripts();
Expand Down
5 changes: 3 additions & 2 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use bitcoin::{self, secp256k1, Address, Network, Script, TxIn};
use sync::Arc;

use self::checksum::verify_checksum;
use crate::miniscript::musig_key::KeyExpr;
use crate::miniscript::{Legacy, Miniscript, Segwitv0};
use crate::prelude::*;
use crate::{
Expand Down Expand Up @@ -180,7 +181,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
// roundabout way to constuct `c:pk_k(pk)`
let ms: Miniscript<Pk, BareCtx> =
Miniscript::from_ast(miniscript::decode::Terminal::Check(Arc::new(
Miniscript::from_ast(miniscript::decode::Terminal::PkK(pk))
Miniscript::from_ast(miniscript::decode::Terminal::PkK(KeyExpr::SingleKey(pk)))
.expect("Type check cannot fail"),
)))
.expect("Type check cannot fail");
Expand Down Expand Up @@ -270,7 +271,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {

/// Create new tr descriptor
/// Errors when miniscript exceeds resource limits under Tap context
pub fn new_tr(key: Pk, script: Option<tr::TapTree<Pk>>) -> Result<Self, Error> {
pub fn new_tr(key: KeyExpr<Pk>, script: Option<tr::TapTree<Pk>>) -> Result<Self, Error> {
Ok(Descriptor::Tr(Tr::new(key, script)?))
}

Expand Down
31 changes: 16 additions & 15 deletions src/descriptor/tr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use sync::Arc;

use super::checksum::{desc_checksum, verify_checksum};
use crate::expression::{self, FromTree};
use crate::miniscript::musig_key::KeyExpr;
use crate::miniscript::Miniscript;
use crate::policy::semantic::Policy;
use crate::policy::Liftable;
Expand All @@ -39,7 +40,7 @@ pub enum TapTree<Pk: MiniscriptKey> {
/// A taproot descriptor
pub struct Tr<Pk: MiniscriptKey> {
/// A taproot internal key
internal_key: Pk,
internal_key: KeyExpr<Pk>,
/// Optional Taproot Tree with spending conditions
tree: Option<TapTree<Pk>>,
/// Optional spending information associated with the descriptor
Expand Down Expand Up @@ -163,9 +164,8 @@ impl<Pk: MiniscriptKey> fmt::Debug for TapTree<Pk> {

impl<Pk: MiniscriptKey> Tr<Pk> {
/// Create a new [`Tr`] descriptor from internal key and [`TapTree`]
pub fn new(internal_key: Pk, tree: Option<TapTree<Pk>>) -> Result<Self, Error> {
pub fn new(internal_key: KeyExpr<Pk>, tree: Option<TapTree<Pk>>) -> Result<Self, Error> {
let nodes = tree.as_ref().map(|t| t.taptree_height()).unwrap_or(0);

if nodes <= TAPROOT_CONTROL_MAX_NODE_COUNT {
Ok(Self {
internal_key,
Expand All @@ -186,7 +186,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
}

/// Obtain the internal key of [`Tr`] descriptor
pub fn internal_key(&self) -> &Pk {
pub fn internal_key(&self) -> &KeyExpr<Pk> {
&self.internal_key
}

Expand Down Expand Up @@ -226,7 +226,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
let secp = secp256k1::Secp256k1::verification_only();
// Key spend path with no merkle root
let data = if self.tree.is_none() {
TaprootSpendInfo::new_key_spend(&secp, self.internal_key.to_x_only_pubkey(), None)
TaprootSpendInfo::new_key_spend(&secp, self.internal_key.key_agg(), None)
} else {
let mut builder = TaprootBuilder::new();
for (depth, ms) in self.iter_scripts() {
Expand All @@ -236,7 +236,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
.expect("Computing spend data on a valid Tree should always succeed");
}
// Assert builder cannot error here because we have a well formed descriptor
match builder.finalize(&secp, self.internal_key.to_x_only_pubkey()) {
match builder.finalize(&secp, self.internal_key.key_agg()) {
Ok(data) => data,
Err(e) => match e {
TaprootBuilderError::InvalidMerkleTreeDepth(_) => {
Expand Down Expand Up @@ -419,7 +419,7 @@ impl_from_tree!(
key.args.len()
)));
}
Tr::new(expression::terminal(key, Pk::from_str)?, None)
Tr::new(expression::terminal(key, KeyExpr::<Pk>::from_str)?, None)
}
2 => {
let key = &top.args[0];
Expand All @@ -431,7 +431,10 @@ impl_from_tree!(
}
let tree = &top.args[1];
let ret = Self::parse_tr_script_spend(tree)?;
Tr::new(expression::terminal(key, Pk::from_str)?, Some(ret))
Tr::new(
expression::terminal(key, KeyExpr::<Pk>::from_str)?,
Some(ret),
)
}
_ => {
return Err(Error::Unexpected(format!(
Expand Down Expand Up @@ -567,12 +570,10 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for Tr<Pk> {
match &self.tree {
Some(root) => Ok(Policy::Threshold(
1,
vec![
Policy::KeyHash(self.internal_key.to_pubkeyhash()),
root.lift()?,
],
vec![self.internal_key.lift()?, root.lift()?],
)),
None => Ok(Policy::KeyHash(self.internal_key.to_pubkeyhash())),
// None => Ok(Policy::KeyHash(self.internal_key.to_pubkeyhash())),
None => self.internal_key.lift(),
}
}
}
Expand All @@ -586,7 +587,7 @@ impl<Pk: MiniscriptKey> ForEachKey<Pk> for Tr<Pk> {
let script_keys_res = self
.iter_scripts()
.all(|(_d, ms)| ms.for_each_key(&mut pred));
script_keys_res && pred(&self.internal_key)
script_keys_res && self.internal_key().for_any_key(pred)
}
}

Expand All @@ -602,7 +603,7 @@ where
T: Translator<P, Q, E>,
{
let translate_desc = Tr {
internal_key: translate.pk(&self.internal_key)?,
internal_key: self.internal_key.translate_pk(translate)?,
tree: match &self.tree {
Some(tree) => Some(tree.translate_helper(translate)?),
None => None,
Expand Down
12 changes: 8 additions & 4 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,9 @@ where
Terminal::PkK(ref pk) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let pk = pk
.single_key()
.expect("Musig keys cannot be parsed from Script");
let res = self.stack.evaluate_pk(&mut self.verify_sig, *pk);
if res.is_some() {
return res;
Expand Down Expand Up @@ -868,10 +871,11 @@ where
// evaluate each key with as a pk
// note that evaluate_pk will error on non-empty incorrect sigs
// push 1 on satisfied sigs and push 0 on empty sigs
match self
.stack
.evaluate_pk(&mut self.verify_sig, subs[node_state.n_evaluated])
{
let pkk = subs[node_state.n_evaluated]
.single_key()
.expect("Musig keys cannot be parsed from Script");
let res = self.stack.evaluate_pk(&mut self.verify_sig, *pkk);
match res {
Some(Ok(x)) => {
self.push_evaluation_state(
node_state.node,
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ extern crate alloc;
#[cfg(not(feature = "std"))]
extern crate hashbrown;

#[cfg(feature = "std")]
extern crate secp256k1_zkp;

#[cfg(any(feature = "std", test))]
extern crate core;

Expand Down Expand Up @@ -775,6 +778,8 @@ pub enum Error {
TrNoScriptCode,
/// No explicit script for Tr descriptors
TrNoExplicitScript,
/// Parsing error for single key
SingleKeyParseError,
}

// https://github.com/sipa/miniscript/pull/5 for discussion on this number
Expand Down Expand Up @@ -848,6 +853,7 @@ impl fmt::Display for Error {
Error::TaprootSpendInfoUnavialable => write!(f, "Taproot Spend Info not computed."),
Error::TrNoScriptCode => write!(f, "No script code for Tr descriptors"),
Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"),
Error::SingleKeyParseError => f.write_str("not able to parse the single key"),
}
}
}
Expand Down Expand Up @@ -888,6 +894,7 @@ impl error::Error for Error {
| BareDescriptorAddr
| TaprootSpendInfoUnavialable
| TrNoScriptCode
| SingleKeyParseError
| TrNoExplicitScript => None,
Script(e) => Some(e),
AddrError(e) => Some(e),
Expand Down
34 changes: 19 additions & 15 deletions src/miniscript/astelem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use bitcoin::blockdata::{opcodes, script};
use sync::Arc;

use crate::miniscript::context::SigType;
use crate::miniscript::musig_key::KeyExpr;
use crate::miniscript::types::{self, Property};
use crate::miniscript::ScriptContext;
use crate::prelude::*;
Expand Down Expand Up @@ -80,7 +81,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
Pk::RawPkHash: 'a,
{
match *self {
Terminal::PkK(ref p) => pred(p),
Terminal::PkK(ref p) => p.for_each_key(pred),
Terminal::PkH(ref p) => pred(p),
Terminal::RawPkH(..)
| Terminal::After(..)
Expand Down Expand Up @@ -112,9 +113,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
&& c.real_for_each_key(pred)
}
Terminal::Thresh(_, ref subs) => subs.iter().all(|sub| sub.real_for_each_key(pred)),
Terminal::Multi(_, ref keys) | Terminal::MultiA(_, ref keys) => {
keys.iter().all(|key| pred(key))
}
Terminal::Multi(_, ref keys) => keys.iter().all(|key| pred(key)),
Terminal::MultiA(_, ref keys) => keys.iter().all(|key| key.for_each_key(&mut *pred)),
}
}

Expand All @@ -125,7 +125,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
T: Translator<Pk, Q, E>,
{
let frag: Terminal<Q, CtxQ> = match *self {
Terminal::PkK(ref p) => Terminal::PkK(t.pk(p)?),
Terminal::PkK(ref p) => Terminal::PkK(p.translate_pk(t)?),
Terminal::PkH(ref p) => Terminal::PkH(t.pk(p)?),
Terminal::RawPkH(ref p) => Terminal::RawPkH(t.pkh(p)?),
Terminal::After(n) => Terminal::After(n),
Expand Down Expand Up @@ -186,7 +186,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
Terminal::Multi(k, keys?)
}
Terminal::MultiA(k, ref keys) => {
let keys: Result<Vec<Q>, _> = keys.iter().map(|k| t.pk(k)).collect();
let keys: Result<Vec<KeyExpr<Q>>, _> =
keys.iter().map(|k| k.translate_pk(t)).collect();
Terminal::MultiA(k, keys?)
}
};
Expand Down Expand Up @@ -455,7 +456,7 @@ impl_from_tree!(
}
let mut unwrapped = match (frag_name, top.args.len()) {
("pk_k", 1) => {
expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkK))
expression::unary(top, Terminal::PkK)
}
("pk_h", 1) => expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkH)),
("after", 1) => expression::terminal(&top.args[0], |x| {
Expand Down Expand Up @@ -522,15 +523,18 @@ impl_from_tree!(
return Err(errstr("higher threshold than there were keys in multi"));
}

let pks: Result<Vec<Pk>, _> = top.args[1..]
.iter()
.map(|sub| expression::terminal(sub, Pk::from_str))
.collect();

if frag_name == "multi" {
let pks: Result<Vec<Pk>, _> = top.args[1..]
.iter()
.map(|sub| expression::terminal(sub, Pk::from_str))
.collect();
pks.map(|pks| Terminal::Multi(k, pks))
} else {
// must be multi_a
let pks: Result<Vec<KeyExpr<Pk>>, _> = top.args[1..]
.iter()
.map(|sub| KeyExpr::<Pk>::from_tree(sub))
.collect();
pks.map(|pks| Terminal::MultiA(k, pks))
}
}
Expand Down Expand Up @@ -734,7 +738,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
builder = builder.push_ms_key::<_, Ctx>(&keys[0]);
builder = builder.push_opcode(opcodes::all::OP_CHECKSIG);
for pk in keys.iter().skip(1) {
builder = builder.push_ms_key::<_, Ctx>(pk);
builder = builder.push_ms_key::<_, Ctx>(&pk);
builder = builder.push_opcode(opcodes::all::OP_CHECKSIGADD);
}
builder
Expand All @@ -753,7 +757,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
/// will handle the segwit/non-segwit technicalities for you.
pub fn script_size(&self) -> usize {
match *self {
Terminal::PkK(ref pk) => Ctx::pk_len(pk),
Terminal::PkK(ref pk) => Ctx::key_expr_len(pk),
Terminal::PkH(..) | Terminal::RawPkH(..) => 24,
Terminal::After(n) => script_num_size(n as usize) + 1,
Terminal::Older(n) => script_num_size(n as usize) + 1,
Expand Down Expand Up @@ -798,7 +802,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
Terminal::MultiA(k, ref pks) => {
script_num_size(k)
+ 1 // NUMEQUAL
+ pks.iter().map(|pk| Ctx::pk_len(pk)).sum::<usize>() // n keys
+ pks.iter().map(|pk| Ctx::key_expr_len(pk)).sum::<usize>() // n keys
+ pks.len() // n times CHECKSIGADD
}
}
Expand Down
Loading