Skip to content

Experimental Inscription Support #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sapio-miniscript"
version = "7.0.1"
version = "7.0.2-alpha.0"
authors = ["Jeremy Rubin <[email protected]>", "Andrew Poelstra <[email protected]>, Sanket Kanjalkar <[email protected]>"]
repository = "https://github.com/sapio-lang/rust-miniscript"
description = "Miniscript: a subset of Bitcoin Script designed for analysis, Sapio extended edition (supports BIP-119 OP_CTV)"
Expand All @@ -25,6 +25,7 @@ optional = true
[dependencies.serde]
version = "1.0"
optional = true
features = ["derive"]

[[example]]
name = "htlc"
Expand Down
20 changes: 10 additions & 10 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ mod tests {
use super::*;
use bitcoin;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use bitcoin::secp256k1::{self, Secp256k1};
use bitcoin::secp256k1::{self, Secp256k1, Parity};
use miniscript::context::NoChecks;
use Miniscript;
use MiniscriptKey;
Expand All @@ -1066,7 +1066,7 @@ mod tests {
Vec<bitcoin::EcdsaSig>,
secp256k1::Message,
Secp256k1<secp256k1::All>,
Vec<bitcoin::XOnlyPublicKey>,
Vec<(bitcoin::XOnlyPublicKey, Parity)>,
Vec<bitcoin::SchnorrSig>,
Vec<Vec<u8>>,
) {
Expand Down Expand Up @@ -1101,7 +1101,7 @@ mod tests {
pks.push(pk);
der_sigs.push(sigser);

let keypair = bitcoin::KeyPair::from_secret_key(&secp, sk);
let keypair = bitcoin::KeyPair::from_secret_key(&secp, &sk);
x_only_pks.push(bitcoin::XOnlyPublicKey::from_keypair(&keypair));
let schnorr_sig = secp.sign_schnorr_with_aux_rand(&msg, &keypair, &[0u8; 32]);
let schnorr_sig = bitcoin::SchnorrSig {
Expand Down Expand Up @@ -1568,7 +1568,7 @@ mod tests {

let elem = x_only_no_checks_ms(&format!(
"multi_a(3,{},{},{},{},{})",
xpks[0], xpks[1], xpks[2], xpks[3], xpks[4],
xpks[0].0, xpks[1].0, xpks[2].0, xpks[3].0, xpks[4].0,
));
let vfyfn = vfyfn_.clone(); // sigh rust 1.29...
let constraints = from_stack(Box::new(vfyfn), txtmpl_hash, stack, &elem);
Expand All @@ -1578,13 +1578,13 @@ mod tests {
multi_a_satisfied.unwrap(),
vec![
SatisfiedConstraint::PublicKey {
key_sig: KeySigPair::Schnorr(xpks[0], schnorr_sigs[0])
key_sig: KeySigPair::Schnorr(xpks[0].0, schnorr_sigs[0])
},
SatisfiedConstraint::PublicKey {
key_sig: KeySigPair::Schnorr(xpks[1], schnorr_sigs[1])
key_sig: KeySigPair::Schnorr(xpks[1].0, schnorr_sigs[1])
},
SatisfiedConstraint::PublicKey {
key_sig: KeySigPair::Schnorr(xpks[2], schnorr_sigs[2])
key_sig: KeySigPair::Schnorr(xpks[2].0, schnorr_sigs[2])
},
]
);
Expand All @@ -1600,7 +1600,7 @@ mod tests {

let elem = x_only_no_checks_ms(&format!(
"multi_a(3,{},{},{},{},{})",
xpks[0], xpks[1], xpks[2], xpks[3], xpks[4],
xpks[0].0, xpks[1].0, xpks[2].0, xpks[3].0, xpks[4].0,
));
let vfyfn = vfyfn_.clone(); // sigh rust 1.29...
let constraints = from_stack(Box::new(vfyfn), txtmpl_hash, stack.clone(), &elem);
Expand All @@ -1611,7 +1611,7 @@ mod tests {
// multi_a wrong thresh: k = 2, but three sigs
let elem = x_only_no_checks_ms(&format!(
"multi_a(2,{},{},{},{},{})",
xpks[0], xpks[1], xpks[2], xpks[3], xpks[4],
xpks[0].0, xpks[1].0, xpks[2].0, xpks[3].0, xpks[4].0,
));
let vfyfn = vfyfn_.clone(); // sigh rust 1.29...
let constraints = from_stack(Box::new(vfyfn), txtmpl_hash, stack.clone(), &elem);
Expand All @@ -1622,7 +1622,7 @@ mod tests {
// multi_a correct thresh, but small stack
let elem = x_only_no_checks_ms(&format!(
"multi_a(3,{},{},{},{},{},{})",
xpks[0], xpks[1], xpks[2], xpks[3], xpks[4], xpks[5]
xpks[0].0, xpks[1].0, xpks[2].0, xpks[3].0, xpks[4].0, xpks[5].0
));
let vfyfn = vfyfn_.clone(); // sigh rust 1.29...
let constraints = from_stack(Box::new(vfyfn), txtmpl_hash, stack, &elem);
Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ pub mod interpreter;
pub mod miniscript;
pub mod policy;
pub mod psbt;
#[allow(missing_docs)]
pub mod ord;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to you if we exported the inscriptions module in ord and made more types public so you can use it as a crate?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I think it's a good idea. but also not really for sapio unfortunately since we have like the entire rust-bitcoin ecosystem forked with patches.

this patch set though should be upstreamable!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to you if we exported the inscriptions module in ord and made more types public so you can use it as a crate?

I would love that.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a look if this is useful: ordinals/ord#3042


mod util;

Expand Down Expand Up @@ -597,6 +599,8 @@ pub enum Error {
TrNoScriptCode,
/// No explicit script for Tr descriptors
TrNoExplicitScript,
/// Inscription System Issue
InscriptionError(String)
}

#[doc(hidden)]
Expand Down Expand Up @@ -737,6 +741,8 @@ impl fmt::Display for Error {
Error::TrNoExplicitScript => {
write!(f, "No script code for Tr descriptors")
}
Error::InscriptionError(ref s) =>
write!(f, "Inscription Error: {}", s)
}
}
}
Expand Down
71 changes: 71 additions & 0 deletions src/miniscript/astelem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ use std::str::FromStr;
use std::sync::Arc;
use std::{fmt, str};

use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::{opcodes, script};
use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use bitcoin::Script;

use errstr;
use expression;
Expand All @@ -35,6 +37,9 @@ use miniscript::ScriptContext;
use script_num_size;

use util::MsKeyBuilder;

use crate::ord::envelope::{Envelope, ParsedEnvelope};
use crate::ord::Inscription;
use {Error, ForEach, ForEachKey, Miniscript, MiniscriptKey, Terminal, ToPublicKey, TranslatePk};

impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
Expand Down Expand Up @@ -124,6 +129,9 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
Terminal::Multi(_, ref keys) | Terminal::MultiA(_, ref keys) => {
keys.iter().all(|key| pred(ForEach::Key(key)))
}
Terminal::InscribePre(_, ref m) | Terminal::InscribePost(_, ref m) => {
m.real_for_each_key(pred)
}
}
}
pub(super) fn real_translate_pk<FPk, FPkh, Q, Error, CtxQ>(
Expand Down Expand Up @@ -219,6 +227,14 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
Terminal::MultiA(k, keys?)
}
Terminal::TxTemplate(x) => Terminal::TxTemplate(x),
Terminal::InscribePre(ref a, ref b) => Terminal::InscribePre(
a.clone(),
Arc::new(b.real_translate_pk(translatefpk, translatefpkh)?),
),
Terminal::InscribePost(ref a, ref b) => Terminal::InscribePost(
a.clone(),
Arc::new(b.real_translate_pk(translatefpk, translatefpkh)?),
),
};
Ok(frag)
}
Expand Down Expand Up @@ -341,6 +357,24 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for Terminal<Pk, Ctx> {
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Terminal<Pk, Ctx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Terminal::InscribePost(ref insc, ref sub) => {
let script = insc
.iter()
.fold(Builder::new(), |builder, i| {
i.append_reveal_script_to_builder(builder)
})
.into_script();
write!(f, "inscribe_post({:x},{})", script, sub)
}
Terminal::InscribePre(ref insc, ref sub) => {
let script = insc
.iter()
.fold(Builder::new(), |builder, i| {
i.append_reveal_script_to_builder(builder)
})
.into_script();
write!(f, "inscribe_pre({:x},{})", script, sub)
}
Terminal::PkK(ref pk) => write!(f, "pk_k({})", pk),
Terminal::PkH(ref pkh) => write!(f, "pk_h({})", pkh),
Terminal::After(t) => write!(f, "after({})", t),
Expand Down Expand Up @@ -594,6 +628,16 @@ where
("txtmpl", 1) => expression::terminal(&top.args[0], |x| {
sha256::Hash::from_hex(x).map(Terminal::TxTemplate)
}),
("inscribe_pre", 2) => {
let inscription = extract_inscriptions(&top.args[0])?;
let expr = expression::FromTree::from_tree(&top.args[1])?;
Ok(Terminal::InscribePre(Arc::new(inscription), expr))
}
("inscribe_post", 2) => {
let expr = expression::FromTree::from_tree(&top.args[0])?;
let inscription = extract_inscriptions(&top.args[1])?;
Ok(Terminal::InscribePost(Arc::new(inscription), expr))
}
_ => Err(Error::Unexpected(format!(
"{}({} args) while parsing Miniscript",
top.name,
Expand Down Expand Up @@ -643,6 +687,18 @@ where
}
}

fn extract_inscriptions(args: &expression::Tree<'_>) -> Result<Vec<Inscription>, Error> {
let inscription = expression::terminal(args, |x| -> Result<Vec<Inscription>, script::Error> {
let script = Script::from_hex(x).map_err(|e| script::Error::SerializationError)?;
let envelopes = Envelope::from_tapscript(&script, 0 /*Garbage Value OK */)?;
let parsed = envelopes.into_iter().map(ParsedEnvelope::from);

Ok(parsed.map(|i| i.payload).collect::<Vec<_>>())
})
.map_err(|e| Error::InscriptionError(e.to_string()))?;
Ok(inscription)
}

/// Helper trait to add a `push_astelem` method to `script::Builder`
trait PushAstElem<Pk: MiniscriptKey, Ctx: ScriptContext> {
fn push_astelem(self, ast: &Miniscript<Pk, Ctx>) -> Self
Expand Down Expand Up @@ -801,6 +857,18 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
.push_slice(&h[..])
.push_opcode(opcodes::all::OP_NOP4)
.push_opcode(opcodes::all::OP_DROP),
Terminal::InscribePre(ref a, ref b) => {
builder = a.iter().fold(builder, |builder, insc| {
insc.append_reveal_script_to_builder(builder)
});
builder.push_astelem(&b)
}
Terminal::InscribePost(ref a, ref b) => {
builder = builder.push_astelem(&b);
a.iter().fold(builder, |builder, insc| {
insc.append_reveal_script_to_builder(builder)
})
}
}
}

Expand Down Expand Up @@ -862,6 +930,9 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
+ pks.len() // n times CHECKSIGADD
}
Terminal::TxTemplate(..) => 33 + 2,
Terminal::InscribePost(ref i, ref rest) | Terminal::InscribePre(ref i, ref rest) => {
rest.script_size() + i.iter().map(|j| j.size_guess()).sum::<usize>()
}
}
}
}
8 changes: 7 additions & 1 deletion src/miniscript/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ use MiniscriptKey;

use ToPublicKey;

use crate::ord::Inscription;

fn return_none<T>(_: usize) -> Option<T> {
None
}
Expand Down Expand Up @@ -115,7 +117,7 @@ enum NonTerm {
OrC,
ThreshW { k: usize, n: usize },
ThreshE { k: usize, n: usize },
// could be or_d, or_c, or_i, d:, n:
// could be or_d, or_c, or_i, d:, n:, or inscribe
EndIf,
// could be or_d, or_c
EndIfNotIf,
Expand Down Expand Up @@ -194,6 +196,10 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
// Other
/// `<hash> OP_CHECKTEMPLATEVERIFY OP_DROP`
TxTemplate(sha256::Hash),
/// FALSE OP_IF <data> ENDIF [various]
InscribePre(Arc<Vec<Inscription>>, Arc<Miniscript<Pk, Ctx>>),
/// [various] FALSE OP_IF <data> ENDIF [various]
InscribePost(Arc<Vec<Inscription>>, Arc<Miniscript<Pk, Ctx>>)
}

macro_rules! match_token {
Expand Down
Loading