Skip to content

Commit 7b6597b

Browse files
committed
Refactor sorted multi
1 parent aec42c7 commit 7b6597b

File tree

3 files changed

+245
-225
lines changed

3 files changed

+245
-225
lines changed

src/descriptor/mod.rs

Lines changed: 5 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,17 @@
2626
use std::{collections::HashMap, sync::Arc};
2727
use std::{
2828
fmt,
29-
marker::PhantomData,
3029
str::{self, FromStr},
3130
};
3231

33-
use bitcoin::blockdata::{opcodes, script};
3432
use bitcoin::secp256k1;
3533
use bitcoin::util::bip32;
3634
use bitcoin::{self, Script};
3735

3836
use self::checksum::verify_checksum;
39-
use errstr;
4037
use expression;
4138
use miniscript;
42-
use miniscript::context::{ScriptContext, ScriptContextError};
43-
use miniscript::{decode::Terminal, Legacy, Miniscript, Segwitv0};
44-
use policy;
45-
use push_opcode_size;
46-
use script_num_size;
47-
use util::witness_to_scriptsig;
39+
use miniscript::{Legacy, Miniscript, Segwitv0};
4840
use BareCtx;
4941
use Error;
5042
use MiniscriptKey;
@@ -54,14 +46,15 @@ use ToPublicKey;
5446
mod bare;
5547
mod segwitv0;
5648
mod sh;
49+
mod sortedmulti;
5750
// Descriptor Exports
5851
pub use self::bare::{Bare, Pkh};
5952
pub use self::segwitv0::{Wpkh, Wsh};
6053
pub use self::sh::Sh;
54+
pub use self::sortedmulti::SortedMultiVec;
6155

6256
mod checksum;
6357
mod key;
64-
use self::checksum::desc_checksum;
6558
pub use self::key::{
6659
DescriptorKeyParseError, DescriptorPublicKey, DescriptorPublicKeyCtx, DescriptorSecretKey,
6760
DescriptorSinglePriv, DescriptorSinglePub, DescriptorXKey,
@@ -677,225 +670,13 @@ impl<Pk: MiniscriptKey> fmt::Display for Descriptor<Pk> {
677670
}
678671
}
679672

680-
/// Contents of a "sortedmulti" descriptor
681-
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
682-
pub struct SortedMultiVec<Pk: MiniscriptKey, Ctx: ScriptContext> {
683-
/// signatures required
684-
pub k: usize,
685-
/// public keys inside sorted Multi
686-
pub pks: Vec<Pk>,
687-
/// The current ScriptContext for sortedmulti
688-
pub(crate) phantom: PhantomData<Ctx>,
689-
}
690-
691-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
692-
/// Create a new instance of `SortedMultiVec` given a list of keys and the threshold
693-
///
694-
/// Internally checks all the applicable size limits and pubkey types limitations according to the current `Ctx`.
695-
pub fn new(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
696-
// A sortedmulti() is only defined for <= 20 keys (it maps to CHECKMULTISIG)
697-
if pks.len() > 20 {
698-
Error::BadDescriptor("Too many public keys".to_string());
699-
}
700-
701-
// Check the limits before creating a new SortedMultiVec
702-
// For example, under p2sh context the scriptlen can only be
703-
// upto 520 bytes.
704-
let term: miniscript::decode::Terminal<Pk, Ctx> = Terminal::Multi(k, pks.clone());
705-
let ms = Miniscript::from_ast(term)?;
706-
707-
// This would check all the consensus rules for p2sh/p2wsh and
708-
// even tapscript in future
709-
Ctx::check_local_validity(&ms)?;
710-
711-
Ok(Self {
712-
k,
713-
pks,
714-
phantom: PhantomData,
715-
})
716-
}
717-
/// Parse an expression tree into a SortedMultiVec
718-
fn from_tree(tree: &expression::Tree) -> Result<Self, Error>
719-
where
720-
<Pk as FromStr>::Err: ToString,
721-
{
722-
if tree.args.is_empty() {
723-
return Err(errstr("no arguments given for sortedmulti"));
724-
}
725-
let k = expression::parse_num(tree.args[0].name)?;
726-
if k > (tree.args.len() - 1) as u32 {
727-
return Err(errstr(
728-
"higher threshold than there were keys in sortedmulti",
729-
));
730-
}
731-
let pks: Result<Vec<Pk>, _> = tree.args[1..]
732-
.iter()
733-
.map(|sub| expression::terminal(sub, Pk::from_str))
734-
.collect();
735-
736-
pks.map(|pks| SortedMultiVec::new(k as usize, pks))?
737-
}
738-
}
739-
740-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
741-
/// This will panic if translatefpk returns an uncompressed key when
742-
/// converting to a Segwit descriptor. To prevent this panic, ensure
743-
/// translatefpk returns an error in this case instead.
744-
pub fn translate_pk<FPk, Q, FuncError>(
745-
&self,
746-
translatefpk: &mut FPk,
747-
) -> Result<SortedMultiVec<Q, Ctx>, FuncError>
748-
where
749-
FPk: FnMut(&Pk) -> Result<Q, FuncError>,
750-
Q: MiniscriptKey,
751-
{
752-
let pks: Result<Vec<Q>, _> = self.pks.iter().map(&mut *translatefpk).collect();
753-
Ok(SortedMultiVec {
754-
k: self.k,
755-
pks: pks?,
756-
phantom: PhantomData,
757-
})
758-
}
759-
}
760-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
761-
// utility function to sanity a sorted multi vec
762-
fn sanity_check(&self) -> Result<(), Error> {
763-
let ms: Miniscript<Pk, Ctx> =
764-
Miniscript::from_ast(Terminal::Multi(self.k, self.pks.clone()))
765-
.expect("Must typecheck");
766-
// '?' for doing From conversion
767-
ms.sanity_check()?;
768-
Ok(())
769-
}
770-
}
771-
772-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
773-
/// Create Terminal::Multi containing sorted pubkeys
774-
pub fn sorted_node<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> Terminal<Pk, Ctx>
775-
where
776-
Pk: ToPublicKey<ToPkCtx>,
777-
{
778-
let mut pks = self.pks.clone();
779-
// Sort pubkeys lexicographically according to BIP 67
780-
pks.sort_by(|a, b| {
781-
a.to_public_key(to_pk_ctx)
782-
.key
783-
.serialize()
784-
.partial_cmp(&b.to_public_key(to_pk_ctx).key.serialize())
785-
.unwrap()
786-
});
787-
Terminal::Multi(self.k, pks)
788-
}
789-
790-
/// Encode as a Bitcoin script
791-
pub fn encode<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> script::Script
792-
where
793-
Pk: ToPublicKey<ToPkCtx>,
794-
{
795-
self.sorted_node(to_pk_ctx)
796-
.encode(script::Builder::new(), to_pk_ctx)
797-
.into_script()
798-
}
799-
800-
/// Attempt to produce a satisfying witness for the
801-
/// witness script represented by the parse tree
802-
pub fn satisfy<ToPkCtx, S>(
803-
&self,
804-
satisfier: S,
805-
to_pk_ctx: ToPkCtx,
806-
) -> Result<Vec<Vec<u8>>, Error>
807-
where
808-
ToPkCtx: Copy,
809-
Pk: ToPublicKey<ToPkCtx>,
810-
S: Satisfier<ToPkCtx, Pk>,
811-
{
812-
let ms = Miniscript::from_ast(self.sorted_node(to_pk_ctx)).expect("Multi node typecheck");
813-
ms.satisfy(satisfier, to_pk_ctx)
814-
}
815-
816-
/// Size, in bytes of the script-pubkey. If this Miniscript is used outside
817-
/// of segwit (e.g. in a bare or P2SH descriptor), this quantity should be
818-
/// multiplied by 4 to compute the weight.
819-
///
820-
/// In general, it is not recommended to use this function directly, but
821-
/// to instead call the corresponding function on a `Descriptor`, which
822-
/// will handle the segwit/non-segwit technicalities for you.
823-
pub fn script_size(&self) -> usize {
824-
script_num_size(self.k)
825-
+ 1
826-
+ script_num_size(self.pks.len())
827-
+ self.pks.iter().map(|pk| pk.serialized_len()).sum::<usize>()
828-
}
829-
830-
/// Maximum number of witness elements used to satisfy the Miniscript
831-
/// fragment, including the witness script itself. Used to estimate
832-
/// the weight of the `VarInt` that specifies this number in a serialized
833-
/// transaction.
834-
///
835-
/// This function may panic on malformed `Miniscript` objects which do
836-
/// not correspond to semantically sane Scripts. (Such scripts should be
837-
/// rejected at parse time. Any exceptions are bugs.)
838-
pub fn max_satisfaction_witness_elements(&self) -> usize {
839-
2 + self.k
840-
}
841-
842-
/// Maximum size, in bytes, of a satisfying witness. For Segwit outputs
843-
/// `one_cost` should be set to 2, since the number `1` requires two
844-
/// bytes to encode. For non-segwit outputs `one_cost` should be set to
845-
/// 1, since `OP_1` is available in scriptSigs.
846-
///
847-
/// In general, it is not recommended to use this function directly, but
848-
/// to instead call the corresponding function on a `Descriptor`, which
849-
/// will handle the segwit/non-segwit technicalities for you.
850-
///
851-
/// All signatures are assumed to be 73 bytes in size, including the
852-
/// length prefix (segwit) or push opcode (pre-segwit) and sighash
853-
/// postfix.
854-
///
855-
/// This function may panic on malformed `Miniscript` objects which do not
856-
/// correspond to semantically sane Scripts. (Such scripts should be
857-
/// rejected at parse time. Any exceptions are bugs.)
858-
pub fn max_satisfaction_size(&self, _: usize) -> usize {
859-
1 + 73 * self.k
860-
}
861-
}
862-
863-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> policy::Liftable<Pk> for SortedMultiVec<Pk, Ctx> {
864-
fn lift(&self) -> Result<policy::semantic::Policy<Pk>, Error> {
865-
let ret = policy::semantic::Policy::Threshold(
866-
self.k,
867-
self.pks
868-
.clone()
869-
.into_iter()
870-
.map(|k| policy::semantic::Policy::KeyHash(k.to_pubkeyhash()))
871-
.collect(),
872-
);
873-
Ok(ret)
874-
}
875-
}
876-
877-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for SortedMultiVec<Pk, Ctx> {
878-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
879-
fmt::Display::fmt(self, f)
880-
}
881-
}
882-
883-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for SortedMultiVec<Pk, Ctx> {
884-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
885-
write!(f, "sortedmulti({}", self.k)?;
886-
for k in &self.pks {
887-
write!(f, ",{}", k)?;
888-
}
889-
f.write_str(")")
890-
}
891-
}
892-
893673
serde_string_impl_pk!(Descriptor, "a script descriptor");
894674

895675
#[cfg(test)]
896676
mod tests {
677+
use super::checksum::desc_checksum;
678+
use super::DescriptorPublicKeyCtx;
897679
use super::DescriptorTrait;
898-
use super::{desc_checksum, DescriptorPublicKeyCtx};
899680
use bitcoin::blockdata::opcodes::all::{OP_CLTV, OP_CSV};
900681
use bitcoin::blockdata::script::Instruction;
901682
use bitcoin::blockdata::{opcodes, script};

0 commit comments

Comments
 (0)