mod tapscript;
mod tx;
mod txout;
mod spk;
mod xonlypk;
use bitcoin::key::{Secp256k1, UntweakedPublicKey};
use bitcoin::taproot::LeafScript;
use bitcoin::{ScriptBuf, TapNodeHash, Transaction as Tx};
use strict_types::{StrictDeserialize, StrictSerialize};
pub use tapscript::{TapretCommitment, TAPRET_SCRIPT_COMMITMENT_PREFIX};
pub use tx::TapretError;
pub use xonlypk::TapretKeyError;
use crate::commit_verify::mpc::Commitment;
use crate::commit_verify::{CommitmentProtocol, ConvolveCommitProof, ConvolveVerifyError};
use crate::dbc::proof::Method;
use crate::dbc::Proof;
use crate::LIB_NAME_BPCORE;
pub enum TapretFirst {}
impl CommitmentProtocol for TapretFirst {}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display(doc_comments)]
pub enum TapretPathError {
MaxDepthExceeded,
InvalidNodePartner(TapretNodePartner),
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
#[display("{left_node_hash}:{right_node_hash}")]
pub struct TapretRightBranch {
left_node_hash: TapNodeHash,
right_node_hash: TapNodeHash,
}
impl TapretRightBranch {
pub fn with(a: TapNodeHash, b: TapNodeHash) -> TapretRightBranch {
let (left, right) = if a < b { (a, b) } else { (b, a) };
TapretRightBranch {
left_node_hash: left,
right_node_hash: right,
}
}
#[inline]
pub fn left_node_hash(&self) -> TapNodeHash { self.left_node_hash }
#[inline]
pub fn right_node_hash(&self) -> TapNodeHash { self.right_node_hash }
pub fn node_hash(&self) -> TapNodeHash {
TapNodeHash::from_node_hashes(self.left_node_hash, self.right_node_hash)
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, From)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE, tags = order, dumb = Self::RightLeaf(LeafScript::<ScriptBuf>::strict_dumb()))]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
#[display(doc_comments)]
pub enum TapretNodePartner {
LeftNode(TapNodeHash),
#[from]
#[cfg_attr(feature = "serde", serde(with = "leaf_script_serde"))]
RightLeaf(LeafScript<ScriptBuf>),
RightBranch(TapretRightBranch),
}
impl TapretNodePartner {
pub fn right_branch(a: TapNodeHash, b: TapNodeHash) -> TapretNodePartner {
TapretNodePartner::RightBranch(TapretRightBranch::with(a, b))
}
pub fn check_no_commitment(&self) -> bool {
match self {
TapretNodePartner::LeftNode(_) => true,
TapretNodePartner::RightLeaf(LeafScript { script, .. }) if script.len() < 64 => true,
TapretNodePartner::RightLeaf(LeafScript { script, .. }) => {
script[..31].to_bytes() != TAPRET_SCRIPT_COMMITMENT_PREFIX[..]
}
TapretNodePartner::RightBranch(right_branch) => {
right_branch.left_node_hash()[..31] != TAPRET_SCRIPT_COMMITMENT_PREFIX[..]
}
}
}
pub fn check_ordering(&self, other_node: TapNodeHash) -> bool {
match self {
TapretNodePartner::LeftNode(left_node) => *left_node <= other_node,
TapretNodePartner::RightLeaf(leaf_script) => {
let right_node = TapNodeHash::from_script(&leaf_script.script, leaf_script.version);
other_node <= right_node
}
TapretNodePartner::RightBranch(right_branch) => {
let right_node = right_branch.node_hash();
other_node <= right_node
}
}
}
pub fn tap_node_hash(&self) -> TapNodeHash {
match self {
TapretNodePartner::LeftNode(hash) => *hash,
TapretNodePartner::RightLeaf(leaf_script) => {
TapNodeHash::from_script(&leaf_script.script, leaf_script.version)
}
TapretNodePartner::RightBranch(right_branch) => right_branch.node_hash(),
}
}
}
#[cfg(feature = "serde")]
mod leaf_script_serde {
use bitcoin::taproot::TAPROOT_LEAF_TAPSCRIPT;
use hex_conservative::DisplayHex;
use serde_crate::{Deserialize, Deserializer, Serialize, Serializer};
use super::*;
#[derive(Serialize, Deserialize)]
#[serde(crate = "serde_crate", rename_all = "camelCase")]
enum LeafVersionSerde {
TapScript,
Future(u8),
}
#[derive(Serialize, Deserialize)]
#[serde(crate = "serde_crate")]
struct LeafScriptData {
version: LeafVersionSerde,
script: String,
}
pub fn serialize<S>(
leaf_script: &LeafScript<ScriptBuf>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let byte = leaf_script.version.to_consensus();
let version =
if byte == 0xc0 { LeafVersionSerde::TapScript } else { LeafVersionSerde::Future(byte) };
let data = LeafScriptData {
version,
script: leaf_script.script.to_bytes().to_lower_hex_string(),
};
data.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<LeafScript<ScriptBuf>, D::Error>
where D: Deserializer<'de> {
let data = LeafScriptData::deserialize(deserializer)?;
let byte = match data.version {
LeafVersionSerde::TapScript => TAPROOT_LEAF_TAPSCRIPT,
LeafVersionSerde::Future(b) => b,
};
let version = bitcoin::taproot::LeafVersion::from_consensus(byte)
.map_err(serde_crate::de::Error::custom)?;
let script_bytes = hex_conservative::decode_to_vec(&data.script)
.map_err(|e| serde_crate::de::Error::custom(format!("invalid hex in script: {}", e)))?;
let script = ScriptBuf::from_bytes(script_bytes);
Ok(LeafScript { version, script })
}
}
#[derive(Getters, Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct TapretPathProof {
partner_node: Option<TapretNodePartner>,
#[getter(as_copy)]
nonce: u8,
}
impl StrictSerialize for TapretPathProof {}
impl StrictDeserialize for TapretPathProof {}
impl TapretPathProof {
#[inline]
pub fn root(nonce: u8) -> TapretPathProof {
TapretPathProof {
partner_node: None,
nonce,
}
}
pub fn with(elem: TapretNodePartner, nonce: u8) -> Result<TapretPathProof, TapretPathError> {
if !elem.check_no_commitment() {
return Err(TapretPathError::InvalidNodePartner(elem));
}
Ok(TapretPathProof {
partner_node: Some(elem),
nonce,
})
}
#[inline]
pub fn check_no_commitment(&self) -> bool {
self.partner_node
.as_ref()
.map(TapretNodePartner::check_no_commitment)
.unwrap_or(true)
}
#[inline]
pub fn original_merkle_root(&self) -> Option<TapNodeHash> {
self.partner_node
.as_ref()
.map(|partner| partner.tap_node_hash())
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct TapretProof {
pub path_proof: TapretPathProof,
pub internal_pk: UntweakedPublicKey,
}
impl StrictSerialize for TapretProof {}
impl StrictDeserialize for TapretProof {}
impl TapretProof {
#[inline]
pub fn original_pubkey_script(&self) -> ScriptBuf {
let merkle_root = self.path_proof.original_merkle_root();
ScriptBuf::new_p2tr(&Secp256k1::new(), self.internal_pk, merkle_root)
}
}
impl Proof<Method> for TapretProof {
type Error = ConvolveVerifyError;
fn method(&self) -> Method { Method::TapretFirst }
fn verify(&self, msg: &Commitment, tx: &Tx) -> Result<(), ConvolveVerifyError> {
ConvolveCommitProof::<_, Tx, _>::verify(self, msg, tx)
}
}