Skip to content

Commit 8d5b34e

Browse files
committed
Implement a sortedmulti_a descriptor
This is the starting point of implementing a sortedmulti_a descriptor valid for taproot descriptors.
1 parent 47d2cc7 commit 8d5b34e

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-0
lines changed

src/descriptor/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ mod segwitv0;
3434
mod sh;
3535
mod sortedmulti;
3636
mod tr;
37+
mod sortedmulti_a;
3738

3839
// Descriptor Exports
3940
pub use self::bare::{Bare, Pkh};

src/descriptor/sortedmulti_a.rs

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
use core::fmt;
2+
use core::marker::PhantomData;
3+
use core::str::FromStr;
4+
5+
use bitcoin::script;
6+
7+
use crate::miniscript::context::ScriptContext;
8+
use crate::miniscript::decode::Terminal;
9+
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
10+
use crate::miniscript::satisfy::{Placeholder, Satisfaction};
11+
use crate::plan::AssetProvider;
12+
use crate::prelude::*;
13+
use crate::{
14+
errstr, expression, Error, ForEachKey, Miniscript, MiniscriptKey,
15+
Satisfier, ToPublicKey, TranslateErr, Translator,
16+
};
17+
18+
/// Contents of a "sortedmultiA" descriptor
19+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20+
pub struct SortedMultiAVec<Pk: MiniscriptKey, Ctx: ScriptContext> {
21+
/// signatures required
22+
pub k: usize,
23+
/// public keys inside sorted Multi
24+
pub pks: Vec<Pk>,
25+
/// The current ScriptContext for sortedmultiA
26+
pub(crate) phantom: PhantomData<Ctx>,
27+
}
28+
29+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiAVec<Pk, Ctx> {
30+
/// Create a new instance of `SortedMultiVecA` given a list of keys and the threshold
31+
pub fn new(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
32+
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
33+
return Err(Error::BadDescriptor("Too many public keys".to_string()));
34+
}
35+
36+
let term: Terminal<Pk, Ctx> = Terminal::MultiA(k, pks.clone());
37+
let ms = Miniscript::from_ast(term)?;
38+
39+
Ctx::check_local_validity(&ms)?;
40+
41+
Ok(Self { k, pks, phantom: PhantomData })
42+
}
43+
44+
/// Parse an expression tree into a SortedMultiVec
45+
#[allow(dead_code)]
46+
pub fn from_tree(tree: &expression::Tree) -> Result<Self, Error>
47+
where
48+
Pk: FromStr,
49+
<Pk as FromStr>::Err: ToString,
50+
{
51+
if tree.args.is_empty() {
52+
return Err(errstr("no arguments given for sortedmulti_a"));
53+
}
54+
let k = expression::parse_num(tree.args[0].name)?;
55+
if k > (tree.args.len() - 1) as u32 {
56+
return Err(errstr("higher threshold than there were keys in sortedmulti_a"));
57+
}
58+
let pks: Result<Vec<Pk>, _> = tree.args[1..]
59+
.iter()
60+
.map(|sub| expression::terminal(sub, Pk::from_str))
61+
.collect();
62+
63+
pks.map(|pks| SortedMultiAVec::new(k as usize, pks))?
64+
}
65+
66+
#[allow(dead_code)]
67+
pub fn translate_pk<T, Q, FuncError>(
68+
&self,
69+
t: &mut T,
70+
) -> Result<SortedMultiAVec<Q, Ctx>, TranslateErr<FuncError>>
71+
where
72+
T: Translator<Pk, Q, FuncError>,
73+
Q: MiniscriptKey,
74+
{
75+
let pks: Result<Vec<Q>, _> = self.pks.iter().map(|pk| t.pk(pk)).collect();
76+
let res = SortedMultiAVec::new(self.k, pks?).map_err(TranslateErr::OuterError)?;
77+
Ok(res)
78+
}
79+
}
80+
81+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> ForEachKey<Pk> for SortedMultiAVec<Pk, Ctx> {
82+
fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: F) -> bool {
83+
self.pks.iter().all(pred)
84+
}
85+
}
86+
87+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiAVec<Pk, Ctx> {
88+
/// utility function to sanity a sorted multi vec
89+
#[allow(dead_code)]
90+
pub fn sanity_check(&self) -> Result<(), Error> {
91+
let ms: Miniscript<Pk, Ctx> =
92+
Miniscript::from_ast(Terminal::MultiA(self.k, self.pks.clone()))
93+
.expect("Must typecheck");
94+
// '?' for doing From conversion
95+
ms.sanity_check()?;
96+
Ok(())
97+
}
98+
}
99+
100+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiAVec<Pk, Ctx> {
101+
/// Create Terminal::Multi containing sorted pubkeys
102+
#[allow(dead_code)]
103+
pub fn sorted_node(&self) -> Terminal<Pk, Ctx>
104+
where
105+
Pk: ToPublicKey,
106+
{
107+
let mut pks = self.pks.clone();
108+
pks.sort_by(|a, b| {
109+
a.to_public_key()
110+
.inner
111+
.serialize()
112+
.partial_cmp(&b.to_public_key().inner.serialize())
113+
.unwrap()
114+
});
115+
Terminal::MultiA(self.k, pks)
116+
}
117+
118+
/// Encode as a Bitcoin script
119+
#[allow(dead_code)]
120+
pub fn encode(&self) -> script::ScriptBuf
121+
where
122+
Pk: ToPublicKey,
123+
{
124+
self.sorted_node()
125+
.encode(script::Builder::new())
126+
.into_script()
127+
}
128+
129+
#[allow(dead_code)]
130+
pub fn satisfy<S>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error>
131+
where
132+
Pk: ToPublicKey,
133+
S: Satisfier<Pk>,
134+
{
135+
todo!("Unimplemented")
136+
}
137+
138+
#[allow(dead_code)]
139+
pub fn build_template<P>(&self, provider: &P) -> Satisfaction<Placeholder<Pk>>
140+
where
141+
Pk: ToPublicKey,
142+
P: AssetProvider<Pk>,
143+
{
144+
todo!("Unimplemented")
145+
}
146+
147+
#[allow(dead_code)]
148+
pub fn script_size(&self) -> usize {
149+
todo!("Unimplemented")
150+
}
151+
152+
#[allow(dead_code)]
153+
pub fn max_satisfaction_witness_elements(&self) -> usize { self.pks.len() }
154+
155+
#[allow(dead_code)]
156+
pub fn max_satisfaction_size(&self) -> usize { (1 + 65) * self.k + (self.pks.len() - self.k) }
157+
}
158+
159+
160+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for SortedMultiAVec<Pk, Ctx> {
161+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) }
162+
}
163+
164+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for SortedMultiAVec<Pk, Ctx> {
165+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166+
write!(f, "sortedmulti_a({}", self.k)?;
167+
for k in &self.pks {
168+
write!(f, ",{}", k)?;
169+
}
170+
f.write_str(")")
171+
}
172+
}

0 commit comments

Comments
 (0)