#![allow(unused_braces)] use std::borrow::Borrow;
use std::fmt::{self, Formatter, LowerHex, UpperHex};
use std::{cmp, io};
use amplify::confinement::{Confined, TinyVec, U32};
use amplify::{Bytes32, Wrapper};
use commit_verify::{DigestExt, Sha256};
use secp256k1::{Scalar, XOnlyPublicKey};
use strict_encoding::{
DecodeError, ReadTuple, StrictDecode, StrictEncode, StrictProduct, StrictTuple, StrictType,
TypeName, TypedRead, TypedWrite, WriteTuple,
};
use crate::opcodes::*;
use crate::{ScriptBytes, ScriptPubkey, WitnessVer, LIB_NAME_BITCOIN};
pub const MIDSTATE_TAPLEAF: [u8; 7] = *b"TapLeaf";
pub const MIDSTATE_TAPBRANCH: [u8; 9] = *b"TapBranch";
pub const MIDSTATE_TAPTWEAK: [u8; 8] = *b"TapTweak";
pub const MIDSTATE_TAPSIGHASH: [u8; 10] = *b"TapSighash";
#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref, LowerHex, Display, FromStr)]
#[wrapper_mut(DerefMut)]
#[derive(StrictType, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN, dumb = { Self(XOnlyPublicKey::from_slice(&[1u8; 32]).unwrap()) })]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct InternalPk(XOnlyPublicKey);
impl InternalPk {
pub fn to_output_key(&self, merkle_root: Option<impl IntoTapHash>) -> XOnlyPublicKey {
let mut engine = Sha256::from_tag(MIDSTATE_TAPTWEAK);
engine.input_raw(&self.0.serialize());
if let Some(merkle_root) = merkle_root {
engine.input_raw(merkle_root.into_tap_hash().as_slice());
}
let tweak =
Scalar::from_be_bytes(engine.finish()).expect("hash value greater than curve order");
let (output_key, tweaked_parity) = self
.0
.add_tweak(secp256k1::SECP256K1, &tweak)
.expect("hash collision");
debug_assert!(self.tweak_add_check(
secp256k1::SECP256K1,
&output_key,
tweaked_parity,
tweak
));
output_key
}
}
impl StrictEncode for InternalPk {
fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> {
let bytes = Bytes32::from(self.0.serialize());
writer.write_newtype::<Self>(&bytes)
}
}
impl StrictDecode for InternalPk {
fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
reader.read_tuple(|r| {
let bytes: Bytes32 = r.read_field()?;
XOnlyPublicKey::from_slice(bytes.as_slice())
.map(Self)
.map_err(|_| {
DecodeError::DataIntegrityError(format!(
"invalid x-only public key value '{bytes:x}'"
))
})
})
}
}
pub trait IntoTapHash {
fn into_tap_hash(self) -> TapNodeHash;
}
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Index, RangeOps, BorrowSlice, Hex, Display, FromStr)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct TapLeafHash(
#[from]
#[from([u8; 32])]
Bytes32,
);
impl TapLeafHash {
pub fn with_leaf_script(leaf_script: &LeafScript) -> Self {
let mut engine = Sha256::from_tag(MIDSTATE_TAPLEAF);
engine.input_raw(&[leaf_script.version.to_consensus()]);
engine.input_with_len::<U32>(leaf_script.script.as_slice());
Self(engine.finish().into())
}
pub fn with_tap_script(tap_script: &TapScript) -> Self {
let mut engine = Sha256::from_tag(MIDSTATE_TAPLEAF);
engine.input_raw(&[TAPROOT_LEAF_TAPSCRIPT]);
engine.input_with_len::<U32>(tap_script.as_slice());
Self(engine.finish().into())
}
}
impl IntoTapHash for TapLeafHash {
fn into_tap_hash(self) -> TapNodeHash { TapNodeHash(self.0) }
}
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Index, RangeOps, BorrowSlice, Hex, Display, FromStr)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct TapBranchHash(
#[from]
#[from([u8; 32])]
Bytes32,
);
impl TapBranchHash {
pub fn with_nodes(node1: TapNodeHash, node2: TapNodeHash) -> Self {
let mut engine = Sha256::from_tag(MIDSTATE_TAPBRANCH);
engine.input_raw(cmp::min(&node1, &node2).borrow());
engine.input_raw(cmp::max(&node1, &node2).borrow());
Self(engine.finish().into())
}
}
impl IntoTapHash for TapBranchHash {
fn into_tap_hash(self) -> TapNodeHash { TapNodeHash(self.0) }
}
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref, Index, RangeOps, BorrowSlice, Hex, Display, FromStr)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct TapNodeHash(
#[from]
#[from([u8; 32])]
#[from(TapLeafHash)]
#[from(TapBranchHash)]
Bytes32,
);
impl IntoTapHash for TapNodeHash {
fn into_tap_hash(self) -> TapNodeHash { self }
}
#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct TapMerklePath(TinyVec<TapBranchHash>);
pub const TAPROOT_ANNEX_PREFIX: u8 = 0x50;
pub const TAPROOT_LEAF_TAPSCRIPT: u8 = 0xc0;
pub const TAPROOT_LEAF_MASK: u8 = 0xfe;
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Display, Error)]
#[display(doc_comments)]
pub struct InvalidLeafVer(u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum LeafVer {
#[default]
TapScript,
Future(FutureLeafVer),
}
impl StrictType for LeafVer {
const STRICT_LIB_NAME: &'static str = LIB_NAME_BITCOIN;
fn strict_name() -> Option<TypeName> { Some(tn!("LeafVer")) }
}
impl StrictProduct for LeafVer {}
impl StrictTuple for LeafVer {
const FIELD_COUNT: u8 = 1;
}
impl StrictEncode for LeafVer {
fn strict_encode<W: TypedWrite>(&self, writer: W) -> std::io::Result<W> {
writer.write_tuple::<Self>(|w| Ok(w.write_field(&self.to_consensus())?.complete()))
}
}
impl StrictDecode for LeafVer {
fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
reader.read_tuple(|r| {
let version = r.read_field()?;
Self::from_consensus(version)
.map_err(|err| DecodeError::DataIntegrityError(err.to_string()))
})
}
}
impl LeafVer {
pub fn from_consensus(version: u8) -> Result<Self, InvalidLeafVer> {
match version {
TAPROOT_LEAF_TAPSCRIPT => Ok(LeafVer::TapScript),
TAPROOT_ANNEX_PREFIX => Err(InvalidLeafVer(TAPROOT_ANNEX_PREFIX)),
future => FutureLeafVer::from_consensus(future).map(LeafVer::Future),
}
}
pub fn to_consensus(self) -> u8 {
match self {
LeafVer::TapScript => TAPROOT_LEAF_TAPSCRIPT,
LeafVer::Future(version) => version.to_consensus(),
}
}
}
impl LowerHex for LeafVer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { LowerHex::fmt(&self.to_consensus(), f) }
}
impl UpperHex for LeafVer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { UpperHex::fmt(&self.to_consensus(), f) }
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN, dumb = { Self(0x51) })]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct FutureLeafVer(u8);
impl FutureLeafVer {
pub(self) fn from_consensus(version: u8) -> Result<FutureLeafVer, InvalidLeafVer> {
match version {
TAPROOT_LEAF_TAPSCRIPT => unreachable!(
"FutureLeafVersion::from_consensus should be never called for 0xC0 value"
),
TAPROOT_ANNEX_PREFIX => Err(InvalidLeafVer(TAPROOT_ANNEX_PREFIX)),
odd if odd & 0xFE != odd => Err(InvalidLeafVer(odd)),
even => Ok(FutureLeafVer(even)),
}
}
#[inline]
pub fn to_consensus(self) -> u8 { self.0 }
}
impl LowerHex for FutureLeafVer {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { LowerHex::fmt(&self.0, f) }
}
impl UpperHex for FutureLeafVer {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { UpperHex::fmt(&self.0, f) }
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, Display)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
#[display("{version:04x} {script:x}")]
pub struct LeafScript {
pub version: LeafVer,
pub script: ScriptBytes,
}
impl From<TapScript> for LeafScript {
fn from(tap_script: TapScript) -> Self {
LeafScript {
version: LeafVer::TapScript,
script: tap_script.into_inner(),
}
}
}
impl LeafScript {
pub fn from_tap_script(tap_script: TapScript) -> Self { Self::from(tap_script) }
pub fn tap_leaf_hash(&self) -> TapLeafHash { TapLeafHash::with_leaf_script(self) }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN, tags = repr, into_u8, try_from_u8)]
#[repr(u8)]
#[non_exhaustive]
pub enum TapCode {
#[display("OP_PUSH_BYTES32")]
PushBytes32 = OP_PUSHBYTES_32,
Reserved = OP_RESERVED,
#[display("OP_RETURN")]
#[strict_type(dumb)]
Return = OP_RETURN,
#[display("OP_PUSH_DATA1")]
PushData1 = OP_PUSHDATA1,
#[display("OP_PUSH_DATA2")]
PushData2 = OP_PUSHDATA2,
#[display("OP_PUSH_DATA3")]
PushData4 = OP_PUSHDATA4,
}
#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref, Index, RangeOps, BorrowSlice, LowerHex, UpperHex)]
#[wrapper_mut(DerefMut, IndexMut, RangeMut, BorrowSliceMut)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct TapScript(ScriptBytes);
impl TapScript {
pub fn new() -> Self { Self::default() }
pub fn with_capacity(capacity: usize) -> Self {
Self(ScriptBytes::from(Confined::with_capacity(capacity)))
}
pub fn push_opcode(&mut self, op_code: TapCode) {
self.0.push(op_code as u8).expect("script exceeds 4GB");
}
}
impl ScriptPubkey {
pub fn p2tr(internal_key: InternalPk, merkle_root: Option<impl IntoTapHash>) -> Self {
let output_key = internal_key.to_output_key(merkle_root);
Self::p2tr_tweaked(output_key)
}
pub fn p2tr_key_only(internal_key: InternalPk) -> Self {
let output_key = internal_key.to_output_key(None::<TapNodeHash>);
Self::p2tr_tweaked(output_key)
}
pub fn p2tr_scripted(internal_key: InternalPk, merkle_root: impl IntoTapHash) -> Self {
let output_key = internal_key.to_output_key(Some(merkle_root));
Self::p2tr_tweaked(output_key)
}
pub fn p2tr_tweaked(output_key: XOnlyPublicKey) -> Self {
Self::with_witness_program_unchecked(WitnessVer::V1, &output_key.serialize())
}
pub fn is_p2tr(&self) -> bool {
self.len() == 34 && self[0] == WitnessVer::V1.op_code() as u8 && self[1] == OP_PUSHBYTES_32
}
}