Skip to content

Commit

Permalink
Add parsing from hashed version of scripts
Browse files Browse the repository at this point in the history
This required in order to deal with legacy scripts while parsing from
scripts. When creating scripts, we normally create them using the
regular Script variant. But when parsing from script, we store the
hashed version for legacy scripts as we cannot compute the pre-image.

This is helpful in psbt workflow where we need to infer miniscript from
Script
  • Loading branch information
sanket1729 committed Oct 21, 2022
1 parent 04aa95d commit d797aec
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 10 deletions.
45 changes: 36 additions & 9 deletions src/extensions/introspect_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,11 +518,30 @@ where
/// Wrapper around [`elements::Script`] for representing script pubkeys
// Required because the fmt::Display of elements::Script does not print hex
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct Spk(pub elements::Script);
pub struct Spk(SpkInner);

impl Spk {
/// Creates a new [`Spk`].
pub fn new(s: elements::Script) -> Self {
Spk(SpkInner::Script(s))
}
}

/// Script pubkey representing either a known script or a hash of legacy script
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum SpkInner {
/// A complete known script
Script(elements::Script),
/// An hashed legacy script pubkey
Hashed(sha256::Hash),
}

impl fmt::Display for Spk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.to_hex())
match &self.0 {
SpkInner::Script(s) => write!(f, "{}", s.to_hex()),
SpkInner::Hashed(_h) => write!(f, "hashed_spk"), // This should never be used
}
}
}

Expand All @@ -534,7 +553,7 @@ impl ArgFromStr for Spk {
));
}
let inner = elements::Script::from_hex(s).map_err(|e| Error::Unexpected(e.to_string()))?;
Ok(Spk(inner))
Ok(Spk::new(inner))
}
}

Expand Down Expand Up @@ -808,7 +827,10 @@ impl SpkExpr<CovExtArgs> {
pub fn push_to_builder(&self, builder: script::Builder) -> script::Builder {
match self {
SpkExpr::Const(CovExtArgs::Script(s)) => {
let (ver, prog) = spk_to_components(&s.0);
let (ver, prog) = match &s.0 {
SpkInner::Script(s) => spk_to_components(s),
SpkInner::Hashed(h) => (-1, h.to_vec()),
};
builder.push_slice(&prog).push_int(ver as i64)
}
SpkExpr::Const(_) => unreachable!(
Expand All @@ -830,7 +852,10 @@ impl SpkExpr<CovExtArgs> {
/// Evaluate this expression
pub fn eval(&self, env: &TxEnv) -> Result<(i8, Vec<u8>), EvalError> {
let res = match self {
SpkExpr::Const(CovExtArgs::Script(s)) => spk_to_components(&s.0),
SpkExpr::Const(CovExtArgs::Script(s)) => match &s.0 {
SpkInner::Script(s) => spk_to_components(s),
SpkInner::Hashed(h) => (-1, h.to_vec()),
},
SpkExpr::Const(_) => unreachable!(
"Both constructors from_str and from_token_iter
check that the correct variant is used in Script pubkey"
Expand Down Expand Up @@ -867,13 +892,15 @@ impl SpkExpr<CovExtArgs> {
let e = end_pos; // short abbreviations for succinct readable code
if let Some(&[Tk::Bytes32(spk_vec), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
let script = spk(i8::try_from(i).ok()?, spk_vec)?;
Some((SpkExpr::Const(CovExtArgs::Script(Spk(script))), e - 2))
Some((SpkExpr::Const(CovExtArgs::Script(Spk::new(script))), e - 2))
} else if let Some(&[Tk::Bytes32(spk_vec), Tk::NumNeg1]) = tks.get(e.checked_sub(2)?..e) {
let script = spk(-1, spk_vec)?;
Some((SpkExpr::Const(CovExtArgs::Script(Spk(script))), e - 2))
let mut inner = [0u8; 32];
inner.copy_from_slice(spk_vec);
let hashed_spk = Spk(SpkInner::Hashed(sha256::Hash::from_inner(inner)));
Some((SpkExpr::Const(CovExtArgs::Script(hashed_spk)), e - 2))
} else if let Some(&[Tk::Push(ref spk_vec), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) {
let script = spk(i8::try_from(i).ok()?, spk_vec)?;
Some((SpkExpr::Const(CovExtArgs::Script(Spk(script))), e - 2))
Some((SpkExpr::Const(CovExtArgs::Script(Spk::new(script))), e - 2))
} else if let Some(&[Tk::CurrInp, Tk::InpSpk]) = tks.get(e.checked_sub(2)?..e) {
Some((SpkExpr::CurrInputSpk, e - 2))
} else if let Some(&[Tk::Num(i), Tk::InpSpk]) = tks.get(e.checked_sub(2)?..e) {
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl CovExtArgs {

/// Creates a new script pubkey of [`CovExtArgs`]
pub fn spk(spk: elements::Script) -> Self {
Self::from(Spk(spk))
Self::from(Spk::new(spk))
}
}

Expand Down

0 comments on commit d797aec

Please sign in to comment.