Skip to content

Commit

Permalink
Merge #36: Bug fixes from options code
Browse files Browse the repository at this point in the history
d797aec Add parsing from hashed version of scripts (sanket1729)
04aa95d Add support for parsing negative -1 (sanket1729)
cc73b04 Make covenant fragments as non-malleable (sanket1729)
64df92b Pset finalize input mall should have allow_mall true (sanket1729)
1263490 Fix ambiguity while parsing scripts (sanket1729)
a338092 Make TrExt of type Tr (sanket1729)

Pull request description:

  Misc bug fixes/features required for options project

ACKs for top commit:
  apoelstra:
    ACK d797aec except for the comment about malleability.

Tree-SHA512: 3a0b0638ffcc26fa4cb8a8ce4cc367873355a5aa6a37163482917d2826701c77b396b7d6573abe5fb90ec04e33d1b2014f91d978c42d3913ec12e5f600eb61a6
  • Loading branch information
sanket1729 committed Oct 21, 2022
2 parents 08ff93a + d797aec commit 5430285
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ impl<Pk: MiniscriptKey, Ext: Extension> Descriptor<Pk, Ext> {
},
Descriptor::LegacyCSFSCov(ref _cov) => DescriptorType::Cov,
Descriptor::Tr(ref _tr) => DescriptorType::Tr,
Descriptor::TrExt(ref _tr) => DescriptorType::Cov,
Descriptor::TrExt(ref _tr) => DescriptorType::Tr,
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/extensions/arith.rs
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,8 @@ impl Extension for Arith {
Malleability {
dissat: Dissat::Unknown, // many dissatisfactions possible
safe: false, // Unsafe as a top fragment
non_malleable: true,
non_malleable: true, // There can exist multiple satisfactions for expressions. inp_v(0) = out_v(0), but
// we only deal with script satisfactions here.
}
}

Expand Down
86 changes: 69 additions & 17 deletions src/extensions/introspect_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ impl<T: ExtParam> Extension for CovOps<T> {
Malleability {
dissat: Dissat::Unknown, // many dissatisfactions possible
safe: false, // Unsafe as a top fragment
non_malleable: true,
non_malleable: true, // Script satisfaction is non-malleable, whole fragment tx could be malleable
}
}

Expand Down 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,10 +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 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 Expand Up @@ -973,24 +1003,45 @@ impl CovOps<CovExtArgs> {
&[Tk::FromAltStack, Tk::Equal, Tk::ToAltStack, Tk::Equal, Tk::FromAltStack, Tk::BoolAnd],
) = tks.get(e.checked_sub(6)?..e)
{
if let Some((y, e)) = AssetExpr::from_tokens(tks, e - 6) {
let res = if let Some((y, e)) = AssetExpr::from_tokens(tks, e - 6) {
if tks.get(e - 1) != Some(&Tk::ToAltStack) {
return None;
}
let (x, e) = AssetExpr::from_tokens(tks, e - 1)?;
Some((CovOps::AssetEq(x, y), e))
} else if let Some((y, e)) = ValueExpr::from_tokens(tks, e - 6) {
if let Some((x, e)) = AssetExpr::from_tokens(tks, e - 1) {
Some((CovOps::AssetEq(x, y), e))
} else {
None
}
} else {
None
};
if res.is_some() {
return res;
}
let res = if let Some((y, e)) = ValueExpr::from_tokens(tks, e - 6) {
if tks.get(e - 1) != Some(&Tk::ToAltStack) {
return None;
}
let (x, e) = ValueExpr::from_tokens(tks, e - 1)?;
Some((CovOps::ValueEq(x, y), e))
} else if let Some((y, e)) = SpkExpr::from_tokens(tks, e - 6) {
if let Some((x, e)) = ValueExpr::from_tokens(tks, e - 1) {
Some((CovOps::ValueEq(x, y), e))
} else {
None
}
} else {
None
};
if res.is_some() {
return res;
}
if let Some((y, e)) = SpkExpr::from_tokens(tks, e - 6) {
if tks.get(e - 1) != Some(&Tk::ToAltStack) {
return None;
}
let (x, e) = SpkExpr::from_tokens(tks, e - 1)?;
Some((CovOps::SpkEq(x, y), e))
if let Some((x, e)) = SpkExpr::from_tokens(tks, e - 1) {
Some((CovOps::SpkEq(x, y), e))
} else {
None
}
} else {
None
}
Expand Down Expand Up @@ -1174,6 +1225,7 @@ mod tests {
_test_parse("spk_eq(V1Spk,inp_spk(1))");
_test_parse("spk_eq(curr_inp_spk,out_spk(1))");
_test_parse("spk_eq(inp_spk(3),out_spk(1))");
_test_parse("spk_eq(out_spk(2),V1Spk)");

// Testing the current input index
_test_parse("curr_idx_eq(1)");
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
4 changes: 4 additions & 0 deletions src/miniscript/lex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub enum Token<'s> {
OutAsset,
OutSpk,
InpSpk,
NumNeg1,
}

impl<'s> fmt::Display for Token<'s> {
Expand Down Expand Up @@ -453,6 +454,9 @@ pub fn lex(script: &script::Script) -> Result<Vec<Token<'_>>, Error> {
}
}
}
script::Instruction::Op(opcodes::all::OP_PUSHNUM_NEG1) => {
ret.push(Token::NumNeg1);
}
script::Instruction::Op(opcodes::all::OP_PUSHBYTES_0) => {
ret.push(Token::Num(0));
}
Expand Down
3 changes: 2 additions & 1 deletion src/miniscript/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,8 @@ impl Property for Type {
debug_assert!(!self.corr.dissatisfiable || self.mall.dissat != Dissat::None);
debug_assert!(self.mall.dissat == Dissat::None || self.corr.base != Base::V);
debug_assert!(self.mall.safe || self.corr.base != Base::K);
debug_assert!(self.mall.non_malleable || self.corr.input != Input::Zero);
// Not true for covenant scripts. Covenant scripts have zero inputs, but are still malleable
// debug_assert!(self.mall.non_malleable || self.corr.input != Input::Zero);
}

fn from_true() -> Self {
Expand Down
9 changes: 8 additions & 1 deletion src/psbt/mod.rs

Large diffs are not rendered by default.

0 comments on commit 5430285

Please sign in to comment.