use std::cmp::Ordering;
use amplify::{Bytes32, Wrapper};
use bc::{ScriptPubkey, Tx, Txid};
use commit_verify::mpc::{self, Message, ProtocolId};
use commit_verify::{CommitmentId, ConvolveCommitProof};
use strict_encoding::{StrictDumb, StrictEncode};
use crate::tapret::{TapretError, TapretProof};
use crate::LIB_NAME_BPCORE;
pub const ANCHOR_MIN_LNPBP4_DEPTH: u8 = 3;
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref, BorrowSlice, Display, FromStr, Hex, Index, RangeOps)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE)]
#[derive(CommitEncode)]
#[commit_encode(strategy = strict)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct AnchorId(
#[from]
#[from([u8; 32])]
Bytes32,
);
#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(inner)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum VerifyError {
#[from]
Tapret(TapretError),
#[from]
Lnpbp4InvalidProof(mpc::InvalidProof),
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE)]
#[derive(CommitEncode)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct Anchor<L: mpc::Proof + StrictDumb> {
pub txid: Txid,
pub mpc_proof: L,
pub dbc_proof: Proof,
}
impl CommitmentId for Anchor<mpc::MerkleBlock> {
const TAG: [u8; 32] = *b"urn:lnpbp:lnpbp0011:anchor:v01#A";
type Id = AnchorId;
}
impl Ord for Anchor<mpc::MerkleBlock> {
fn cmp(&self, other: &Self) -> Ordering { self.anchor_id().cmp(&other.anchor_id()) }
}
impl PartialOrd for Anchor<mpc::MerkleBlock> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum MergeError {
#[display(inner)]
#[from]
Lnpbp4Mismatch(mpc::MergeError),
TxidMismatch,
ProofMismatch,
}
impl Anchor<mpc::MerkleBlock> {
#[inline]
pub fn anchor_id(&self) -> AnchorId { self.commitment_id() }
}
impl Anchor<mpc::MerkleProof> {
#[inline]
pub fn anchor_id(
&self,
protocol_id: impl Into<ProtocolId>,
message: Message,
) -> Result<AnchorId, mpc::InvalidProof> {
Ok(self.to_merkle_block(protocol_id, message)?.anchor_id())
}
pub fn into_merkle_block(
self,
protocol_id: impl Into<ProtocolId>,
message: Message,
) -> Result<Anchor<mpc::MerkleBlock>, mpc::InvalidProof> {
let lnpbp4_proof = mpc::MerkleBlock::with(&self.mpc_proof, protocol_id.into(), message)?;
Ok(Anchor {
txid: self.txid,
mpc_proof: lnpbp4_proof,
dbc_proof: self.dbc_proof,
})
}
pub fn to_merkle_block(
&self,
protocol_id: impl Into<ProtocolId>,
message: Message,
) -> Result<Anchor<mpc::MerkleBlock>, mpc::InvalidProof> {
self.clone().into_merkle_block(protocol_id, message)
}
pub fn verify(
&self,
protocol_id: impl Into<ProtocolId>,
message: Message,
tx: &Tx,
) -> Result<bool, VerifyError> {
self.dbc_proof
.verify(&self.mpc_proof.convolve(protocol_id.into(), message)?, tx)
.map_err(VerifyError::from)
}
pub fn convolve(
&self,
protocol_id: impl Into<ProtocolId>,
message: Message,
) -> Result<mpc::Commitment, mpc::InvalidProof> {
self.mpc_proof.convolve(protocol_id.into(), message)
}
}
impl Anchor<mpc::MerkleBlock> {
pub fn to_merkle_proof(
&self,
protocol: impl Into<ProtocolId>,
) -> Result<Anchor<mpc::MerkleProof>, mpc::LeafNotKnown> {
self.clone().into_merkle_proof(protocol)
}
pub fn into_merkle_proof(
self,
protocol: impl Into<ProtocolId>,
) -> Result<Anchor<mpc::MerkleProof>, mpc::LeafNotKnown> {
let lnpbp4_proof = self.mpc_proof.to_merkle_proof(protocol.into())?;
Ok(Anchor {
txid: self.txid,
mpc_proof: lnpbp4_proof,
dbc_proof: self.dbc_proof,
})
}
pub fn conceal_except(
&mut self,
protocols: impl AsRef<[ProtocolId]>,
) -> Result<usize, mpc::LeafNotKnown> {
self.mpc_proof.conceal_except(protocols)
}
pub fn merge_reveal(mut self, other: Self) -> Result<Self, MergeError> {
if self.txid != other.txid {
return Err(MergeError::TxidMismatch);
}
if self.dbc_proof != other.dbc_proof {
return Err(MergeError::ProofMismatch);
}
self.mpc_proof.merge_reveal(other.mpc_proof)?;
Ok(self)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BPCORE, tags = order)]
#[derive(CommitEncode)]
#[commit_encode(strategy = strict)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
#[non_exhaustive]
pub enum Proof {
#[strict_type(dumb)]
OpretFirst,
TapretFirst(TapretProof),
}
impl Proof {
pub fn verify(&self, msg: &mpc::Commitment, tx: &Tx) -> Result<bool, TapretError> {
match self {
Proof::OpretFirst => {
for txout in &tx.outputs {
if txout.script_pubkey.is_op_return() {
return Ok(txout.script_pubkey == ScriptPubkey::op_return(msg.as_slice()));
}
}
Ok(false)
}
Proof::TapretFirst(proof) => ConvolveCommitProof::<_, Tx, _>::verify(proof, msg, tx),
}
}
}