use std::fmt::{self, Display, Formatter};
use std::io::{self, Read, Write};
use amplify::hex::ToHex;
use amplify::{hex, Wrapper};
use bitcoin::blockdata::script;
use bitcoin::blockdata::script::*;
use bitcoin::blockdata::witness::Witness;
use bitcoin::hashes::{sha256, Hash};
use bitcoin::schnorr::TweakedPublicKey;
use bitcoin::util::address::WitnessVersion;
use bitcoin::util::taproot::{
ControlBlock, LeafVersion, TapBranchHash, TapLeafHash, TaprootError, TAPROOT_ANNEX_PREFIX,
};
use bitcoin::{
consensus, Address, Network, PubkeyHash, SchnorrSig, SchnorrSigError, ScriptHash, WPubkeyHash,
WScriptHash,
};
#[derive(
Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Display, From
)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
#[display("{0}", alt = "{0:x}")]
#[wrapper(LowerHex, UpperHex)]
pub struct LockScript(Script);
impl strict_encoding::Strategy for LockScript {
type Strategy = strict_encoding::strategies::Wrapped;
}
#[derive(
Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Display, From
)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
#[display("{0}", alt = "{0:x}")]
#[wrapper(LowerHex, UpperHex)]
pub struct ScriptCode(Script);
#[derive(
Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Display, From
)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
#[display("{0}", alt = "{0:x}")]
#[wrapper(LowerHex, UpperHex)]
pub struct PubkeyScript(Script);
impl strict_encoding::Strategy for PubkeyScript {
type Strategy = strict_encoding::strategies::Wrapped;
}
impl PubkeyScript {
pub fn address(&self, network: Network) -> Option<Address> {
Address::from_script(self.as_inner(), network).ok()
}
pub fn witness_version(&self) -> Option<WitnessVersion> { self.0.witness_version() }
}
impl From<PubkeyHash> for PubkeyScript {
fn from(pkh: PubkeyHash) -> Self { Script::new_p2pkh(&pkh).into() }
}
impl From<WPubkeyHash> for PubkeyScript {
fn from(wpkh: WPubkeyHash) -> Self { Script::new_v0_p2wpkh(&wpkh).into() }
}
#[derive(
Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Display, From
)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
#[display("{0}", alt = "{0:x}")]
#[wrapper(LowerHex, UpperHex)]
pub struct SigScript(Script);
impl strict_encoding::Strategy for SigScript {
type Strategy = strict_encoding::strategies::Wrapped;
}
#[derive(Debug, Display, From)]
#[display(doc_comments)]
pub enum TaprootWitnessError {
EmptyWitnessStack,
#[from]
Bip341SigError(SchnorrSigError),
#[display(inner)]
#[from]
TaprootError(TaprootError),
ScriptError(bitcoin::consensus::encode::Error),
}
impl std::error::Error for TaprootWitnessError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
TaprootWitnessError::EmptyWitnessStack => None,
TaprootWitnessError::Bip341SigError(err) => Some(err),
TaprootWitnessError::TaprootError(_) => None,
TaprootWitnessError::ScriptError(err) => Some(err),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
pub enum TaprootWitness {
PubkeySpending {
sig: SchnorrSig,
annex: Option<Box<[u8]>>,
},
ScriptSpending {
control_block: ControlBlock,
annex: Option<Box<[u8]>>,
script: LeafScript,
script_input: Vec<Box<[u8]>>,
},
}
impl TryFrom<Witness> for TaprootWitness {
type Error = TaprootWitnessError;
fn try_from(witness: Witness) -> Result<Self, Self::Error> {
if witness.is_empty() {
return Err(TaprootWitnessError::EmptyWitnessStack);
}
let mut len = witness.len();
let annex = if len > 1 {
witness
.last()
.filter(|annex| annex[0] == TAPROOT_ANNEX_PREFIX)
.map(Box::from)
} else {
None
};
if annex.is_some() {
len -= 1;
}
Ok(if len == 1 {
TaprootWitness::PubkeySpending {
sig: SchnorrSig::from_slice(
witness
.last()
.expect("witness must have at least 1 element"),
)?,
annex,
}
} else {
let (control_block, script) = if annex.is_some() {
(
witness
.second_to_last()
.expect("witness must have at least 3 elements"),
witness
.iter()
.nth(len - 2)
.expect("witness must have at least 3 elements"),
)
} else {
(
witness
.last()
.expect("witness must have at least 2 elements"),
witness
.second_to_last()
.expect("witness must have at least 2 elements"),
)
};
let control_block = ControlBlock::from_slice(control_block)?;
let script = bitcoin::consensus::deserialize(script)
.map_err(TaprootWitnessError::ScriptError)?;
let script = LeafScript {
version: control_block.leaf_version,
script: LockScript::from_inner(script),
};
TaprootWitness::ScriptSpending {
control_block,
annex,
script,
script_input: witness.iter().take(len - 2).map(Box::from).collect(),
}
})
}
}
impl From<TaprootWitness> for Witness {
#[inline]
fn from(tw: TaprootWitness) -> Self { Witness::from(&tw) }
}
impl From<&TaprootWitness> for Witness {
fn from(tw: &TaprootWitness) -> Self {
let mut witness = Witness::default();
match tw {
TaprootWitness::PubkeySpending { sig, annex } => {
witness.push(&sig.to_vec());
if let Some(annex) = annex {
witness.push(annex);
}
}
TaprootWitness::ScriptSpending {
control_block,
annex,
script,
script_input,
} => {
for item in script_input {
witness.push(item);
}
witness.push(&bitcoin::consensus::serialize(&script.script.0));
witness.push(&control_block.serialize());
if let Some(annex) = annex {
witness.push(annex);
}
}
}
witness
}
}
impl strict_encoding::Strategy for TaprootWitness {
type Strategy = strict_encoding::strategies::BitcoinConsensus;
}
impl consensus::Encodable for TaprootWitness {
fn consensus_encode<W: Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
Witness::from(self).consensus_encode(writer)
}
}
impl consensus::Decodable for TaprootWitness {
fn consensus_decode<D: Read + ?Sized>(d: &mut D) -> Result<Self, consensus::encode::Error> {
TaprootWitness::try_from(Witness::consensus_decode(d)?).map_err(|_| {
consensus::encode::Error::ParseFailed("witness does not conform to taproot")
})
}
}
#[derive(
Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Display, From
)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
#[display("{0}", alt = "{0:x}")]
#[wrapper(LowerHex, UpperHex)]
pub struct RedeemScript(Script);
impl strict_encoding::Strategy for RedeemScript {
type Strategy = strict_encoding::strategies::Wrapped;
}
impl RedeemScript {
#[inline]
pub fn script_hash(&self) -> ScriptHash { self.as_inner().script_hash() }
#[inline]
pub fn to_p2sh(&self) -> PubkeyScript { Script::new_p2sh(&self.script_hash()).into() }
}
impl From<RedeemScript> for SigScript {
#[inline]
fn from(redeem_script: RedeemScript) -> Self {
script::Builder::new()
.push_slice(redeem_script.as_bytes())
.into_script()
.into()
}
}
#[derive(
Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Display, From
)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
#[display("{0}", alt = "{0:x}")]
#[wrapper(LowerHex, UpperHex)]
pub struct WitnessScript(Script);
impl strict_encoding::Strategy for WitnessScript {
type Strategy = strict_encoding::strategies::Wrapped;
}
impl WitnessScript {
#[inline]
pub fn script_hash(&self) -> WScriptHash { self.as_inner().wscript_hash() }
#[inline]
pub fn to_p2wsh(&self) -> PubkeyScript { Script::new_v0_p2wsh(&self.script_hash()).into() }
#[inline]
pub fn to_p2sh_wsh(&self) -> PubkeyScript { RedeemScript::from(self.clone()).to_p2sh() }
}
impl From<WitnessScript> for RedeemScript {
fn from(witness_script: WitnessScript) -> Self {
RedeemScript(Script::new_v0_p2wsh(&witness_script.script_hash()))
}
}
impl From<LockScript> for WitnessScript {
fn from(lock_script: LockScript) -> Self { WitnessScript(lock_script.to_inner()) }
}
impl From<LockScript> for RedeemScript {
fn from(lock_script: LockScript) -> Self { RedeemScript(lock_script.to_inner()) }
}
impl From<WitnessScript> for LockScript {
fn from(witness_script: WitnessScript) -> Self { LockScript(witness_script.to_inner()) }
}
impl From<RedeemScript> for LockScript {
fn from(redeem_script: RedeemScript) -> Self { LockScript(redeem_script.to_inner()) }
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
#[display("{version:02x} {script}", alt = "{version:02x} {script:x}")]
pub struct LeafScript {
pub version: LeafVersion,
pub script: LockScript,
}
impl strict_encoding::StrictEncode for LeafScript {
fn strict_encode<E: Write>(&self, mut e: E) -> Result<usize, strict_encoding::Error> {
self.version.to_consensus().strict_encode(&mut e)?;
self.script.strict_encode(&mut e)
}
}
impl strict_encoding::StrictDecode for LeafScript {
fn strict_decode<D: Read>(mut d: D) -> Result<Self, strict_encoding::Error> {
let version = u8::strict_decode(&mut d)?;
let version = LeafVersion::from_consensus(version)
.map_err(|_| bitcoin::consensus::encode::Error::ParseFailed("invalid leaf version"))?;
let script = LockScript::strict_decode(d)?;
Ok(LeafScript { version, script })
}
}
impl LeafScript {
#[inline]
pub fn tapscript(script: TapScript) -> LeafScript {
LeafScript {
version: LeafVersion::TapScript,
script: LockScript::from_inner(script.into_inner()),
}
}
#[inline]
pub fn with(version: LeafVersion, script: LockScript) -> LeafScript {
LeafScript { version, script }
}
#[inline]
pub fn tap_leaf_hash(&self) -> TapLeafHash {
TapLeafHash::from_script(self.script.as_inner(), self.version)
}
}
#[derive(
Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Display, From
)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
#[display("{0}", alt = "{0:x}")]
#[wrapper(LowerHex, UpperHex)]
pub struct TapScript(Script);
impl strict_encoding::Strategy for TapScript {
type Strategy = strict_encoding::strategies::Wrapped;
}
impl From<LockScript> for TapScript {
fn from(lock_script: LockScript) -> Self { TapScript(lock_script.to_inner()) }
}
impl From<TapScript> for LeafScript {
fn from(tap_script: TapScript) -> Self {
LeafScript {
version: LeafVersion::TapScript,
script: LockScript::from_inner(tap_script.into_inner()),
}
}
}
#[derive(
Wrapper, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, From
)]
pub struct WitnessProgram(Box<[u8]>);
impl strict_encoding::Strategy for WitnessProgram {
type Strategy = strict_encoding::strategies::Wrapped;
}
impl Display for WitnessProgram {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln!(f, "{}", self.0.to_hex()) }
}
impl From<WPubkeyHash> for WitnessProgram {
#[inline]
fn from(wpkh: WPubkeyHash) -> Self { WitnessProgram(Box::from(&wpkh[..])) }
}
impl From<WScriptHash> for WitnessProgram {
#[inline]
fn from(wsh: WScriptHash) -> Self { WitnessProgram(Box::from(&wsh[..])) }
}
impl From<TweakedPublicKey> for WitnessProgram {
#[inline]
fn from(tpk: TweakedPublicKey) -> Self { WitnessProgram(Box::from(&tpk.serialize()[..])) }
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
pub struct ScriptSet {
pub pubkey_script: PubkeyScript,
pub sig_script: SigScript,
pub witness: Option<Witness>,
}
impl Display for ScriptSet {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} ", self.sig_script,)?;
hex::format_hex(
&self
.witness
.as_ref()
.map(consensus::serialize)
.unwrap_or_default(),
f,
)?;
write!(f, " <- {}", self.pubkey_script)
}
}
impl ScriptSet {
#[inline]
pub fn has_witness(&self) -> bool { self.witness.is_some() }
pub fn is_witness_sh(&self) -> bool {
return !self.sig_script.as_inner().is_empty() && self.has_witness();
}
pub fn transmutate(&mut self, use_witness: bool) -> bool {
if self.is_witness_sh() {
return false;
}
if self.has_witness() != use_witness {
if use_witness {
let witness = self
.sig_script
.as_inner()
.instructions_minimal()
.filter_map(|instr| {
if let Ok(Instruction::PushBytes(bytes)) = instr {
Some(bytes.to_vec())
} else {
None
}
})
.collect::<Vec<Vec<u8>>>();
self.witness = Some(Witness::from_vec(witness));
self.sig_script = SigScript::default();
true
} else if let Some(ref witness_script) = self.witness {
self.sig_script = witness_script
.iter()
.fold(Builder::new(), |builder, bytes| builder.push_slice(bytes))
.into_script()
.into();
self.witness = None;
true
} else {
false
}
} else {
false
}
}
}
pub type TapNodeHash = sha256::Hash;
pub trait IntoNodeHash {
fn into_node_hash(self) -> TapNodeHash;
}
impl IntoNodeHash for TapLeafHash {
#[inline]
fn into_node_hash(self) -> TapNodeHash { TapNodeHash::from_inner(self.into_inner()) }
}
impl IntoNodeHash for TapBranchHash {
#[inline]
fn into_node_hash(self) -> TapNodeHash { TapNodeHash::from_inner(self.into_inner()) }
}
impl IntoNodeHash for TapNodeHash {
#[inline]
fn into_node_hash(self) -> TapNodeHash { self }
}