use core::fmt;
use core::marker::PhantomData;
use core::str::FromStr;
use bitcoin::script;
use crate::miniscript::context::ScriptContext;
use crate::miniscript::decode::Terminal;
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
use crate::miniscript::satisfy::{Placeholder, Satisfaction};
use crate::plan::AssetProvider;
use crate::prelude::*;
use crate::{
errstr, expression, policy, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey,
Satisfier, ToPublicKey, TranslateErr, Translator,
};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SortedMultiVec<Pk: MiniscriptKey, Ctx: ScriptContext> {
pub k: usize,
pub pks: Vec<Pk>,
pub(crate) phantom: PhantomData<Ctx>,
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
pub fn new(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
return Err(Error::BadDescriptor("Too many public keys".to_string()));
}
let term: Terminal<Pk, Ctx> = Terminal::Multi(k, pks.clone());
let ms = Miniscript::from_ast(term)?;
Ctx::check_local_validity(&ms)?;
Ok(Self { k, pks, phantom: PhantomData })
}
pub fn from_tree(tree: &expression::Tree) -> Result<Self, Error>
where
Pk: FromStr,
<Pk as FromStr>::Err: ToString,
{
if tree.args.is_empty() {
return Err(errstr("no arguments given for sortedmulti"));
}
let k = expression::parse_num(tree.args[0].name)?;
if k > (tree.args.len() - 1) as u32 {
return Err(errstr("higher threshold than there were keys in sortedmulti"));
}
let pks: Result<Vec<Pk>, _> = tree.args[1..]
.iter()
.map(|sub| expression::terminal(sub, Pk::from_str))
.collect();
pks.map(|pks| SortedMultiVec::new(k as usize, pks))?
}
pub fn translate_pk<T, Q, FuncError>(
&self,
t: &mut T,
) -> Result<SortedMultiVec<Q, Ctx>, TranslateErr<FuncError>>
where
T: Translator<Pk, Q, FuncError>,
Q: MiniscriptKey,
{
let pks: Result<Vec<Q>, _> = self.pks.iter().map(|pk| t.pk(pk)).collect();
let res = SortedMultiVec::new(self.k, pks?).map_err(TranslateErr::OuterError)?;
Ok(res)
}
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> ForEachKey<Pk> for SortedMultiVec<Pk, Ctx> {
fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: F) -> bool {
self.pks.iter().all(pred)
}
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
pub fn sanity_check(&self) -> Result<(), Error> {
let ms: Miniscript<Pk, Ctx> =
Miniscript::from_ast(Terminal::Multi(self.k, self.pks.clone()))
.expect("Must typecheck");
ms.sanity_check()?;
Ok(())
}
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
pub fn sorted_node(&self) -> Terminal<Pk, Ctx>
where
Pk: ToPublicKey,
{
let mut pks = self.pks.clone();
pks.sort_by(|a, b| {
a.to_public_key()
.inner
.serialize()
.partial_cmp(&b.to_public_key().inner.serialize())
.unwrap()
});
Terminal::Multi(self.k, pks)
}
pub fn encode(&self) -> script::ScriptBuf
where
Pk: ToPublicKey,
{
self.sorted_node()
.encode(script::Builder::new())
.into_script()
}
pub fn satisfy<S>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error>
where
Pk: ToPublicKey,
S: Satisfier<Pk>,
{
let ms = Miniscript::from_ast(self.sorted_node()).expect("Multi node typecheck");
ms.satisfy(satisfier)
}
pub fn build_template<P>(&self, provider: &P) -> Satisfaction<Placeholder<Pk>>
where
Pk: ToPublicKey,
P: AssetProvider<Pk>,
{
let ms = Miniscript::from_ast(self.sorted_node()).expect("Multi node typecheck");
ms.build_template(provider)
}
pub fn script_size(&self) -> usize {
script_num_size(self.k)
+ 1
+ script_num_size(self.pks.len())
+ self.pks.iter().map(|pk| Ctx::pk_len(pk)).sum::<usize>()
}
pub fn max_satisfaction_witness_elements(&self) -> usize { 2 + self.k }
pub fn max_satisfaction_size(&self) -> usize { 1 + 73 * self.k }
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> policy::Liftable<Pk> for SortedMultiVec<Pk, Ctx> {
fn lift(&self) -> Result<policy::semantic::Policy<Pk>, Error> {
let ret = policy::semantic::Policy::Threshold(
self.k,
self.pks
.iter()
.map(|k| policy::semantic::Policy::Key(k.clone()))
.collect(),
);
Ok(ret)
}
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for SortedMultiVec<Pk, Ctx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) }
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for SortedMultiVec<Pk, Ctx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "sortedmulti({}", self.k)?;
for k in &self.pks {
write!(f, ",{}", k)?;
}
f.write_str(")")
}
}
#[cfg(test)]
mod tests {
use bitcoin::secp256k1::PublicKey;
use super::*;
use crate::miniscript::context::Legacy;
#[test]
fn too_many_pubkeys() {
let pk = PublicKey::from_str(
"02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443",
)
.unwrap();
let over = 1 + MAX_PUBKEYS_PER_MULTISIG;
let mut pks = Vec::new();
for _ in 0..over {
pks.push(pk);
}
let res: Result<SortedMultiVec<PublicKey, Legacy>, Error> = SortedMultiVec::new(0, pks);
let error = res.expect_err("constructor should err");
match error {
Error::BadDescriptor(_) => {} other => panic!("unexpected error: {:?}", other),
}
}
}