|
5 | 5 | //! Functionality to parse a Bitcoin Script into a `Miniscript`
|
6 | 6 | //!
|
7 | 7 |
|
8 |
| -use core::fmt; |
| 8 | +use core::{fmt, mem}; |
9 | 9 | #[cfg(feature = "std")]
|
10 | 10 | use std::error;
|
11 | 11 |
|
12 | 12 | use bitcoin::hashes::{hash160, ripemd160, sha256, Hash};
|
13 | 13 | use sync::Arc;
|
14 | 14 |
|
| 15 | +use crate::iter::TreeLike; |
15 | 16 | use crate::miniscript::lex::{Token as Tk, TokenIter};
|
16 | 17 | use crate::miniscript::limits::{MAX_PUBKEYS_IN_CHECKSIGADD, MAX_PUBKEYS_PER_MULTISIG};
|
17 | 18 | use crate::miniscript::ScriptContext;
|
@@ -114,7 +115,7 @@ enum NonTerm {
|
114 | 115 | ///
|
115 | 116 | /// The average user should always use the [`Descriptor`] APIs. Advanced users who want deal
|
116 | 117 | /// with Miniscript ASTs should use the [`Miniscript`] APIs.
|
117 |
| -#[derive(Clone, PartialEq, Eq, Hash)] |
| 118 | +#[derive(Clone)] |
118 | 119 | pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
|
119 | 120 | /// `1`
|
120 | 121 | True,
|
@@ -185,6 +186,60 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
|
185 | 186 | MultiA(Threshold<Pk, MAX_PUBKEYS_IN_CHECKSIGADD>),
|
186 | 187 | }
|
187 | 188 |
|
| 189 | +impl<Pk: MiniscriptKey, Ctx: ScriptContext> PartialEq for Terminal<Pk, Ctx> { |
| 190 | + fn eq(&self, other: &Self) -> bool { |
| 191 | + for (me, you) in self.pre_order_iter().zip(other.pre_order_iter()) { |
| 192 | + match (me, you) { |
| 193 | + (Terminal::PkK(key1), Terminal::PkK(key2)) if key1 != key2 => return false, |
| 194 | + (Terminal::PkH(key1), Terminal::PkH(key2)) if key1 != key2 => return false, |
| 195 | + (Terminal::RawPkH(h1), Terminal::RawPkH(h2)) if h1 != h2 => return false, |
| 196 | + (Terminal::After(t1), Terminal::After(t2)) if t1 != t2 => return false, |
| 197 | + (Terminal::Older(t1), Terminal::Older(t2)) if t1 != t2 => return false, |
| 198 | + (Terminal::Sha256(h1), Terminal::Sha256(h2)) if h1 != h2 => return false, |
| 199 | + (Terminal::Hash256(h1), Terminal::Hash256(h2)) if h1 != h2 => return false, |
| 200 | + (Terminal::Ripemd160(h1), Terminal::Ripemd160(h2)) if h1 != h2 => return false, |
| 201 | + (Terminal::Hash160(h1), Terminal::Hash160(h2)) if h1 != h2 => return false, |
| 202 | + (Terminal::Multi(th1), Terminal::Multi(th2)) if th1 != th2 => return false, |
| 203 | + (Terminal::MultiA(th1), Terminal::MultiA(th2)) if th1 != th2 => return false, |
| 204 | + _ => { |
| 205 | + if mem::discriminant(me) != mem::discriminant(you) { |
| 206 | + return false; |
| 207 | + } |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + true |
| 212 | + } |
| 213 | +} |
| 214 | +impl<Pk: MiniscriptKey, Ctx: ScriptContext> Eq for Terminal<Pk, Ctx> {} |
| 215 | + |
| 216 | +impl<Pk: MiniscriptKey, Ctx: ScriptContext> core::hash::Hash for Terminal<Pk, Ctx> { |
| 217 | + fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) { |
| 218 | + for term in self.pre_order_iter() { |
| 219 | + mem::discriminant(term).hash(hasher); |
| 220 | + match term { |
| 221 | + Terminal::PkK(key) => key.hash(hasher), |
| 222 | + Terminal::PkH(key) => key.hash(hasher), |
| 223 | + Terminal::RawPkH(h) => h.hash(hasher), |
| 224 | + Terminal::After(t) => t.hash(hasher), |
| 225 | + Terminal::Older(t) => t.hash(hasher), |
| 226 | + Terminal::Sha256(h) => h.hash(hasher), |
| 227 | + Terminal::Hash256(h) => h.hash(hasher), |
| 228 | + Terminal::Ripemd160(h) => h.hash(hasher), |
| 229 | + Terminal::Hash160(h) => h.hash(hasher), |
| 230 | + Terminal::Thresh(th) => { |
| 231 | + th.k().hash(hasher); |
| 232 | + th.n().hash(hasher); |
| 233 | + // The actual children will be hashed when we iterate |
| 234 | + } |
| 235 | + Terminal::Multi(th) => th.hash(hasher), |
| 236 | + Terminal::MultiA(th) => th.hash(hasher), |
| 237 | + _ => {} |
| 238 | + } |
| 239 | + } |
| 240 | + } |
| 241 | +} |
| 242 | + |
188 | 243 | macro_rules! match_token {
|
189 | 244 | // Base case
|
190 | 245 | ($tokens:expr => $sub:expr,) => { $sub };
|
|
0 commit comments