#[cfg(feature = "serde")]
use serde_with::{hex::Hex, As, DisplayFromStr};
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use amplify::Wrapper;
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::script::Builder;
use bitcoin::Script;
use miniscript::{policy, Miniscript, MiniscriptKey};
use strict_encoding::{self, StrictDecode, StrictEncode};
use super::SingleSig;
use crate::bip32::{DerivePublicKey, UnhardenedIndex};
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename = "lowercase")
)]
#[derive(
Clone,
Ord,
PartialOrd,
Eq,
PartialEq,
Debug,
Hash,
Display,
StrictEncode,
StrictDecode,
)]
pub enum OpcodeTemplate<Pk>
where
Pk: MiniscriptKey + StrictEncode + StrictDecode + FromStr,
<Pk as FromStr>::Err: Display,
{
#[display("opcode({0})")]
OpCode(u8),
#[display("data({0:#x?})")]
Data(#[cfg_attr(feature = "serde", serde(with = "As::<Hex>"))] Box<[u8]>),
#[display("key({0})")]
Key(
#[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))] Pk,
),
}
impl<Pk> OpcodeTemplate<Pk>
where
Pk: MiniscriptKey + DerivePublicKey + StrictEncode + StrictDecode + FromStr,
<Pk as FromStr>::Err: Display,
{
fn translate_pk(
&self,
child_index: UnhardenedIndex,
) -> OpcodeTemplate<bitcoin::PublicKey> {
match self {
OpcodeTemplate::OpCode(code) => OpcodeTemplate::OpCode(*code),
OpcodeTemplate::Data(data) => OpcodeTemplate::Data(data.clone()),
OpcodeTemplate::Key(key) => {
OpcodeTemplate::Key(key.derive_public_key(child_index))
}
}
}
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
#[derive(
Wrapper,
Clone,
Ord,
PartialOrd,
Eq,
PartialEq,
Hash,
Debug,
From,
StrictEncode,
StrictDecode,
)]
#[wrap(Index, IndexMut, IndexFull, IndexFrom, IndexTo, IndexInclusive)]
pub struct ScriptTemplate<Pk>(Vec<OpcodeTemplate<Pk>>)
where
Pk: MiniscriptKey + StrictEncode + StrictDecode + FromStr,
<Pk as FromStr>::Err: Display;
impl<Pk> Display for ScriptTemplate<Pk>
where
Pk: MiniscriptKey + StrictEncode + StrictDecode + FromStr,
<Pk as FromStr>::Err: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for instruction in &self.0 {
Display::fmt(instruction, f)?;
}
Ok(())
}
}
impl<Pk> ScriptTemplate<Pk>
where
Pk: MiniscriptKey + DerivePublicKey + StrictEncode + StrictDecode + FromStr,
<Pk as FromStr>::Err: Display,
{
pub fn translate_pk(
&self,
child_index: UnhardenedIndex,
) -> ScriptTemplate<bitcoin::PublicKey> {
self.0
.iter()
.map(|op| op.translate_pk(child_index))
.collect::<Vec<_>>()
.into()
}
}
impl From<ScriptTemplate<bitcoin::PublicKey>> for Script {
fn from(template: ScriptTemplate<bitcoin::PublicKey>) -> Self {
let mut builder = Builder::new();
for op in template.into_inner() {
builder = match op {
OpcodeTemplate::OpCode(code) => {
builder.push_opcode(opcodes::All::from(code))
}
OpcodeTemplate::Data(data) => builder.push_slice(&data),
OpcodeTemplate::Key(key) => builder.push_key(&key),
};
}
builder.into_script()
}
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename = "lowercase")
)]
#[derive(
Clone,
Ord,
PartialOrd,
Eq,
PartialEq,
Debug,
Display,
StrictEncode,
StrictDecode,
)]
#[non_exhaustive]
#[display(inner)]
pub enum ScriptConstruction {
#[cfg_attr(feature = "serde", serde(rename = "script"))]
ScriptTemplate(ScriptTemplate<SingleSig>),
Miniscript(
#[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
Miniscript<SingleSig, miniscript::Segwitv0>,
),
#[cfg_attr(feature = "serde", serde(rename = "policy"))]
MiniscriptPolicy(
#[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
policy::Concrete<SingleSig>,
),
}
impl std::hash::Hash for ScriptConstruction {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.to_string().hash(state)
}
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
#[derive(
Clone,
Ord,
PartialOrd,
Eq,
PartialEq,
Hash,
Debug,
StrictEncode,
StrictDecode,
)]
pub struct ScriptSource {
pub script: ScriptConstruction,
pub source: Option<String>,
#[cfg_attr(
feature = "serde",
serde(with = "As::<Option<DisplayFromStr>>")
)]
pub tweak_target: Option<SingleSig>,
}
impl Display for ScriptSource {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let Some(ref source) = self.source {
f.write_str(source)
} else {
Display::fmt(&self.script, f)
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]
#[cfg_attr(feature = "clap", Clap)]
#[cfg_attr(feature = "strict_encoding", derive(StrictEncode, StrictDecode))]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename = "lowercase")
)]
#[non_exhaustive]
pub enum ScriptSourceFormat {
#[display("hex")]
Hex,
#[display("base64")]
Base64,
#[display("miniscript")]
Miniscript,
#[display("policy")]
Policy,
#[display("asm")]
Asm,
}