miniscript/
util.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use core::convert::TryFrom;
4
5use bitcoin::hashes::Hash;
6use bitcoin::script::{self, PushBytes, ScriptBuf};
7use bitcoin::PubkeyHash;
8
9use crate::miniscript::context;
10use crate::miniscript::satisfy::Placeholder;
11use crate::prelude::*;
12use crate::{MiniscriptKey, ScriptContext, ToPublicKey};
13pub(crate) fn varint_len(n: usize) -> usize { bitcoin::VarInt(n as u64).size() }
14
15pub(crate) trait ItemSize {
16    fn size(&self) -> usize;
17}
18
19impl<Pk: MiniscriptKey> ItemSize for Placeholder<Pk> {
20    fn size(&self) -> usize {
21        match self {
22            Placeholder::Pubkey(_, size) => *size,
23            Placeholder::PubkeyHash(_, size) => *size,
24            Placeholder::EcdsaSigPk(_) | Placeholder::EcdsaSigPkHash(_) => 73,
25            Placeholder::SchnorrSigPk(_, _, size) | Placeholder::SchnorrSigPkHash(_, _, size) => {
26                size + 1
27            } // +1 for the OP_PUSH
28            Placeholder::HashDissatisfaction
29            | Placeholder::Sha256Preimage(_)
30            | Placeholder::Hash256Preimage(_)
31            | Placeholder::Ripemd160Preimage(_)
32            | Placeholder::Hash160Preimage(_) => 33,
33            Placeholder::PushOne => 2, // On legacy this should be 1 ?
34            Placeholder::PushZero => 1,
35            Placeholder::TapScript(s) => s.len(),
36            Placeholder::TapControlBlock(cb) => cb.serialize().len(),
37        }
38    }
39}
40
41impl ItemSize for Vec<u8> {
42    fn size(&self) -> usize { self.len() }
43}
44
45// Helper function to calculate witness size
46pub(crate) fn witness_size<T: ItemSize>(wit: &[T]) -> usize {
47    wit.iter().map(T::size).sum::<usize>() + varint_len(wit.len())
48}
49
50pub(crate) fn witness_to_scriptsig(witness: &[Vec<u8>]) -> ScriptBuf {
51    let mut b = script::Builder::new();
52    for wit in witness {
53        if let Ok(n) = script::read_scriptint(wit) {
54            b = b.push_int(n);
55        } else {
56            let push = <&PushBytes>::try_from(wit.as_slice())
57                .expect("All pushes in miniscript are <73 bytes");
58            b = b.push_slice(push)
59        }
60    }
61    b.into_script()
62}
63
64// trait for pushing key that depend on context
65pub(crate) trait MsKeyBuilder {
66    /// Serialize the key as bytes based on script context. Used when encoding miniscript into bitcoin script
67    fn push_ms_key<Pk, Ctx>(self, key: &Pk) -> Self
68    where
69        Pk: ToPublicKey,
70        Ctx: ScriptContext;
71
72    /// Serialize the key hash as bytes based on script context. Used when encoding miniscript into bitcoin script
73    fn push_ms_key_hash<Pk, Ctx>(self, key: &Pk) -> Self
74    where
75        Pk: ToPublicKey,
76        Ctx: ScriptContext;
77}
78
79impl MsKeyBuilder for script::Builder {
80    fn push_ms_key<Pk, Ctx>(self, key: &Pk) -> Self
81    where
82        Pk: ToPublicKey,
83        Ctx: ScriptContext,
84    {
85        match Ctx::sig_type() {
86            context::SigType::Ecdsa => self.push_key(&key.to_public_key()),
87            context::SigType::Schnorr => self.push_slice(key.to_x_only_pubkey().serialize()),
88        }
89    }
90
91    fn push_ms_key_hash<Pk, Ctx>(self, key: &Pk) -> Self
92    where
93        Pk: ToPublicKey,
94        Ctx: ScriptContext,
95    {
96        match Ctx::sig_type() {
97            context::SigType::Ecdsa => self.push_slice(key.to_public_key().pubkey_hash()),
98            context::SigType::Schnorr => {
99                self.push_slice(PubkeyHash::hash(&key.to_x_only_pubkey().serialize()))
100            }
101        }
102    }
103}