#![allow(unused_braces)] use std::borrow::Borrow;
use std::fmt::{self, Formatter, LowerHex, UpperHex};
use std::ops::BitXor;
use std::str::FromStr;
use std::{cmp, io, slice, vec};
use amplify::confinement::Confined;
use amplify::hex::FromHex;
use amplify::{confinement, ByteArray, Bytes32, Wrapper};
use commit_verify::{DigestExt, Sha256};
use secp256k1::{Keypair, PublicKey, Scalar, XOnlyPublicKey};
use strict_encoding::{
DecodeError, ReadTuple, StrictDecode, StrictEncode, StrictProduct, StrictTuple, StrictType,
TypeName, TypedRead, TypedWrite, WriteTuple,
};
use crate::opcodes::*;
use crate::{
CompressedPk, ConsensusEncode, InvalidPubkey, PubkeyParseError, ScriptBytes, ScriptPubkey,
VarInt, VarIntBytes, WitnessVer, LIB_NAME_BITCOIN,
};
const MIDSTATE_TAPLEAF: [u8; 7] = *b"TapLeaf";
const MIDSTATE_TAPBRANCH: [u8; 9] = *b"TapBranch";
const MIDSTATE_TAPTWEAK: [u8; 8] = *b"TapTweak";
pub const MIDSTATE_TAPSIGHASH: [u8; 10] = *b"TapSighash";
impl<const LEN: usize> From<InvalidPubkey<LEN>> for DecodeError {
fn from(e: InvalidPubkey<LEN>) -> Self {
DecodeError::DataIntegrityError(format!("invalid x-only public key value '{e}'"))
}
}
#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref, LowerHex, Display)]
#[wrapper_mut(DerefMut)]
#[derive(StrictType, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN, dumb = Self::dumb())]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
pub struct XOnlyPk(XOnlyPublicKey);
impl XOnlyPk {
fn dumb() -> Self { Self(XOnlyPublicKey::from_slice(&[1u8; 32]).unwrap()) }
pub fn from_byte_array(data: [u8; 32]) -> Result<Self, InvalidPubkey<32>> {
XOnlyPublicKey::from_slice(data.as_ref())
.map(Self)
.map_err(|_| InvalidPubkey::Specified(data.into()))
}
pub fn to_byte_array(&self) -> [u8; 32] { self.0.serialize() }
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, InvalidPubkey<33>> {
Ok(XOnlyPk(XOnlyPublicKey::from_slice(bytes.as_ref())?))
}
}
impl From<CompressedPk> for XOnlyPk {
fn from(pubkey: CompressedPk) -> Self { XOnlyPk(pubkey.x_only_public_key().0) }
}
impl From<PublicKey> for XOnlyPk {
fn from(pubkey: PublicKey) -> Self { XOnlyPk(pubkey.x_only_public_key().0) }
}
impl From<XOnlyPk> for [u8; 32] {
fn from(pk: XOnlyPk) -> [u8; 32] { pk.to_byte_array() }
}
impl StrictEncode for XOnlyPk {
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 XOnlyPk {
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(|_| InvalidPubkey::Specified(bytes).into())
})
}
}
impl FromStr for XOnlyPk {
type Err = PubkeyParseError<32>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let data = <[u8; 32]>::from_hex(s)?;
let pk = Self::from_byte_array(data)?;
Ok(pk)
}
}
#[derive(Eq, PartialEq, From)]
pub struct InternalKeypair(#[from] Keypair);
impl InternalKeypair {
pub fn to_output_keypair(&self, merkle_root: Option<TapNodeHash>) -> (Keypair, Parity) {
let internal_pk = self.0.x_only_public_key().0;
let mut engine = Sha256::from_tag(MIDSTATE_TAPTWEAK);
engine.input_raw(&internal_pk.serialize());
if let Some(merkle_root) = merkle_root {
engine.input_raw(merkle_root.into_tap_hash().as_ref());
}
let tweak =
Scalar::from_be_bytes(engine.finish()).expect("hash value greater than curve order");
let pair = self.0.add_xonly_tweak(secp256k1::SECP256K1, &tweak).expect("hash collision");
let (outpput_key, tweaked_parity) = pair.x_only_public_key();
debug_assert!(internal_pk.tweak_add_check(
secp256k1::SECP256K1,
&outpput_key,
tweaked_parity,
tweak
));
(pair, tweaked_parity.into())
}
}
#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref, LowerHex, Display, FromStr)]
#[wrapper_mut(DerefMut)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
pub struct InternalPk(
#[from]
#[from(XOnlyPublicKey)]
XOnlyPk,
);
impl InternalPk {
#[inline]
pub fn from_unchecked(pk: XOnlyPk) -> Self { Self(pk) }
#[inline]
pub fn from_byte_array(data: [u8; 32]) -> Result<Self, InvalidPubkey<32>> {
XOnlyPk::from_byte_array(data).map(Self)
}
#[inline]
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, InvalidPubkey<33>> {
XOnlyPk::from_bytes(bytes).map(Self)
}
#[inline]
pub fn to_byte_array(&self) -> [u8; 32] { self.0.to_byte_array() }
#[inline]
pub fn to_xonly_pk(&self) -> XOnlyPk { self.0 }
pub fn to_output_pk(&self, merkle_root: Option<TapNodeHash>) -> (OutputPk, Parity) {
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_ref());
}
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
));
(OutputPk(XOnlyPk(output_key)), tweaked_parity.into())
}
}
impl From<InternalPk> for [u8; 32] {
fn from(pk: InternalPk) -> [u8; 32] { pk.to_byte_array() }
}
#[derive(Wrapper, WrapperMut, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref, LowerHex, Display, FromStr)]
#[wrapper_mut(DerefMut)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
pub struct OutputPk(XOnlyPk);
impl OutputPk {
#[inline]
pub fn from_unchecked(pk: XOnlyPk) -> Self { Self(pk) }
#[inline]
pub fn from_byte_array(data: [u8; 32]) -> Result<Self, InvalidPubkey<32>> {
XOnlyPk::from_byte_array(data).map(Self)
}
#[inline]
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, InvalidPubkey<33>> {
XOnlyPk::from_bytes(bytes).map(Self)
}
#[inline]
pub fn to_xonly_pk(&self) -> XOnlyPk { self.0 }
#[inline]
pub fn to_script_pubkey(&self) -> ScriptPubkey { ScriptPubkey::p2tr_tweaked(*self) }
#[inline]
pub fn to_byte_array(&self) -> [u8; 32] { self.0.to_byte_array() }
}
impl From<OutputPk> for [u8; 32] {
fn from(pk: OutputPk) -> [u8; 32] { pk.to_byte_array() }
}
pub trait IntoTapHash {
fn into_tap_hash(self) -> TapNodeHash;
}
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Index, RangeOps, AsSlice, BorrowSlice, Hex, Display, FromStr)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
pub struct TapSighash(
#[from]
#[from([u8; 32])]
pub Bytes32,
);
impl From<TapSighash> for [u8; 32] {
fn from(value: TapSighash) -> Self { value.0.into_inner() }
}
impl From<TapSighash> for secp256k1::Message {
fn from(sighash: TapSighash) -> Self {
secp256k1::Message::from_digest(sighash.to_byte_array())
}
}
impl TapSighash {
pub fn engine() -> Sha256 { Sha256::from_tag(MIDSTATE_TAPSIGHASH) }
pub fn from_engine(engine: Sha256) -> Self { Self(engine.finish().into()) }
}
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Index, RangeOps, BorrowSlice, Hex, Display, FromStr)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
pub struct TapLeafHash(
#[from]
#[from([u8; 32])]
Bytes32,
);
impl TapLeafHash {
pub fn with_leaf_script(leaf_script: &LeafScript) -> Self {
Self::with_raw_script(leaf_script.version, leaf_script.as_script_bytes())
}
pub fn with_tap_script(tap_script: &TapScript) -> Self {
Self::with_raw_script(LeafVer::TapScript, tap_script.as_script_bytes())
}
fn with_raw_script(version: LeafVer, script: &ScriptBytes) -> Self {
let mut engine = Sha256::from_tag(MIDSTATE_TAPLEAF);
engine.input_raw(&[version.to_consensus_u8()]);
script.len_var_int().consensus_encode(&mut engine).ok();
engine.input_raw(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)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(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(Index, RangeOps, AsSlice, BorrowSlice, Hex, Display, FromStr)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(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, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref)]
#[wrapper_mut(DerefMut)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
pub struct TapMerklePath(Confined<Vec<TapBranchHash>, 0, 128>);
impl IntoIterator for TapMerklePath {
type Item = TapBranchHash;
type IntoIter = vec::IntoIter<TapBranchHash>;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
impl<'a> IntoIterator for &'a TapMerklePath {
type Item = &'a TapBranchHash;
type IntoIter = slice::Iter<'a, TapBranchHash>;
fn into_iter(self) -> Self::IntoIter { self.0.iter() }
}
impl TapMerklePath {
#[inline]
pub fn try_from(path: Vec<TapBranchHash>) -> Result<Self, confinement::Error> {
Confined::try_from(path).map(Self::from_inner)
}
#[inline]
pub fn try_from_iter<I: IntoIterator<Item = TapBranchHash>>(
iter: I,
) -> Result<Self, confinement::Error> {
Confined::try_from_iter(iter).map(Self::from_inner)
}
}
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(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_u8())?.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_u8(version)
.map_err(|err| DecodeError::DataIntegrityError(err.to_string()))
})
}
}
impl LeafVer {
#[doc(hidden)]
#[deprecated(since = "0.10.9", note = "use from_consensus_u8")]
pub fn from_consensus(version: u8) -> Result<Self, InvalidLeafVer> {
Self::from_consensus_u8(version)
}
pub fn from_consensus_u8(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),
}
}
#[doc(hidden)]
#[deprecated(since = "0.10.9", note = "use to_consensus_u8")]
pub fn to_consensus(self) -> u8 { self.to_consensus_u8() }
pub fn to_consensus_u8(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_u8(), f) }
}
impl UpperHex for LeafVer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { UpperHex::fmt(&self.to_consensus_u8(), 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(transparent))]
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))]
#[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 {
#[inline]
pub fn new(version: LeafVer, script: ScriptBytes) -> Self { LeafScript { version, script } }
#[inline]
pub fn with_bytes(version: LeafVer, script: Vec<u8>) -> Result<Self, confinement::Error> {
Ok(LeafScript {
version,
script: ScriptBytes::try_from(script)?,
})
}
#[inline]
pub fn from_tap_script(tap_script: TapScript) -> Self { Self::from(tap_script) }
#[inline]
pub fn as_script_bytes(&self) -> &ScriptBytes { &self.script }
#[inline]
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, AsSlice, Hex)]
#[wrapper_mut(DerefMut, AsSliceMut)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
pub struct TapScript(ScriptBytes);
impl TryFrom<Vec<u8>> for TapScript {
type Error = confinement::Error;
fn try_from(script_bytes: Vec<u8>) -> Result<Self, Self::Error> {
ScriptBytes::try_from(script_bytes).map(Self)
}
}
impl TapScript {
#[inline]
pub fn new() -> Self { Self::default() }
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self(ScriptBytes::from(Confined::with_capacity(capacity)))
}
#[inline]
pub fn from_unsafe(script_bytes: Vec<u8>) -> Self {
Self(ScriptBytes::from_unsafe(script_bytes))
}
#[inline]
pub fn tap_leaf_hash(&self) -> TapLeafHash { TapLeafHash::with_tap_script(self) }
#[inline]
pub fn push_opcode(&mut self, op_code: TapCode) { self.0.push(op_code as u8); }
#[inline]
pub fn as_script_bytes(&self) -> &ScriptBytes { &self.0 }
}
impl ScriptPubkey {
pub fn p2tr(internal_key: InternalPk, merkle_root: Option<TapNodeHash>) -> Self {
let (output_key, _) = internal_key.to_output_pk(merkle_root);
Self::p2tr_tweaked(output_key)
}
pub fn p2tr_key_only(internal_key: InternalPk) -> Self {
let (output_key, _) = internal_key.to_output_pk(None);
Self::p2tr_tweaked(output_key)
}
pub fn p2tr_scripted(internal_key: InternalPk, merkle_root: impl IntoTapHash) -> Self {
let (output_key, _) = internal_key.to_output_pk(Some(merkle_root.into_tap_hash()));
Self::p2tr_tweaked(output_key)
}
pub fn p2tr_tweaked(output_key: OutputPk) -> 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
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, Error)]
#[display(doc_comments)]
pub struct InvalidParityValue(pub u8);
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]
#[display(lowercase)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN, tags = repr, into_u8, try_from_u8)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
#[repr(u8)]
pub enum Parity {
#[strict_type(dumb)]
Even = 0,
Odd = 1,
}
impl From<secp256k1::Parity> for Parity {
fn from(parity: secp256k1::Parity) -> Self {
match parity {
secp256k1::Parity::Even => Parity::Even,
secp256k1::Parity::Odd => Parity::Odd,
}
}
}
impl Parity {
pub fn to_consensus_u8(self) -> u8 { self as u8 }
pub fn from_consensus_u8(parity: u8) -> Result<Parity, InvalidParityValue> {
match parity {
0 => Ok(Parity::Even),
1 => Ok(Parity::Odd),
invalid => Err(InvalidParityValue(invalid)),
}
}
}
impl BitXor for Parity {
type Output = Parity;
fn bitxor(self, rhs: Parity) -> Self::Output {
if self == rhs {
Parity::Even } else {
Parity::Odd }
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
pub struct ControlBlock {
pub leaf_version: LeafVer,
pub output_key_parity: Parity,
pub internal_pk: InternalPk,
pub merkle_branch: TapMerklePath,
}
impl ControlBlock {
#[inline]
pub fn with(
leaf_version: LeafVer,
internal_pk: InternalPk,
output_key_parity: Parity,
merkle_branch: TapMerklePath,
) -> Self {
ControlBlock {
leaf_version,
output_key_parity,
internal_pk,
merkle_branch,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum AnnexError {
WrongFirstByte(u8),
#[from]
#[display(inner)]
Size(confinement::Error),
}
#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref, AsSlice, Hex)]
#[wrapper_mut(DerefMut, AsSliceMut)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN, dumb = { Self(VarIntBytes::with(0x50)) })]
pub struct Annex(VarIntBytes<1>);
impl TryFrom<Vec<u8>> for Annex {
type Error = confinement::Error;
fn try_from(script_bytes: Vec<u8>) -> Result<Self, Self::Error> {
Confined::try_from(script_bytes).map(Self)
}
}
impl Annex {
#[inline]
pub fn new(annex_bytes: Vec<u8>) -> Result<Self, AnnexError> {
let annex = Confined::try_from(annex_bytes).map(Self)?;
if annex[0] != TAPROOT_ANNEX_PREFIX {
return Err(AnnexError::WrongFirstByte(annex[0]));
}
Ok(annex)
}
pub fn len_var_int(&self) -> VarInt { VarInt(self.len() as u64) }
pub fn into_vec(self) -> Vec<u8> { self.0.release() }
pub fn as_slice(&self) -> &[u8] { self.0.as_slice() }
pub(crate) fn as_var_int_bytes(&self) -> &VarIntBytes<1> { &self.0 }
}
#[cfg(feature = "serde")]
mod _serde {
use amplify::hex::{FromHex, ToHex};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::*;
impl Serialize for Annex {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_hex())
} else {
serializer.serialize_bytes(self.as_slice())
}
}
}
impl<'de> Deserialize<'de> for Annex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
if deserializer.is_human_readable() {
String::deserialize(deserializer).and_then(|string| {
Self::from_hex(&string).map_err(|_| D::Error::custom("wrong hex data"))
})
} else {
let bytes = Vec::<u8>::deserialize(deserializer)?;
Self::new(bytes).map_err(|_| D::Error::custom("invalid annex data"))
}
}
}
}