use std::io;
use std::str::FromStr;
use amplify::confinement::Confined;
use baid58::{Baid58ParseError, FromBaid58, ToBaid58};
use bc::{TapCode, TapScript};
use commit_verify::{mpc, strategies, CommitEncode, CommitStrategy, CommitVerify};
use strict_encoding::{StrictDeserialize, StrictSerialize};
use super::Lnpbp12;
use crate::LIB_NAME_BPCORE;
pub const TAPRET_SCRIPT_COMMITMENT_PREFIX: [u8; 31] = [
0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x6a, 0x21,
];
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[display(Self::to_baid58_string)]
#[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 TapretCommitment {
pub mpc: mpc::Commitment,
pub nonce: u8,
}
impl StrictSerialize for TapretCommitment {}
impl StrictDeserialize for TapretCommitment {}
impl From<[u8; 33]> for TapretCommitment {
fn from(value: [u8; 33]) -> Self {
let buf = Confined::try_from_iter(value).expect("exact size match");
Self::from_strict_serialized::<33>(buf).expect("exact size match")
}
}
impl ToBaid58<33> for TapretCommitment {
const HRI: &'static str = "tapret";
fn to_baid58_payload(&self) -> [u8; 33] {
let mut data = io::Cursor::new([0u8; 33]);
self.commit_encode(&mut data);
data.into_inner()
}
}
impl FromBaid58<33> for TapretCommitment {}
impl TapretCommitment {
fn to_baid58_string(&self) -> String { format!("{}", self.to_baid58()) }
}
impl FromStr for TapretCommitment {
type Err = Baid58ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_baid58_str(s) }
}
impl TapretCommitment {
pub fn with(mpc: mpc::Commitment, nonce: u8) -> Self { Self { mpc, nonce } }
}
impl CommitStrategy for TapretCommitment {
type Strategy = strategies::Strict;
}
impl CommitVerify<TapretCommitment, Lnpbp12> for TapScript {
fn commit(commitment: &TapretCommitment) -> Self {
let mut tapret = TapScript::with_capacity(64);
for _ in 0..29 {
tapret.push_opcode(TapCode::Reserved);
}
tapret.push_opcode(TapCode::Return);
let mut data = io::Cursor::new([0u8; 33]);
commitment.commit_encode(&mut data);
tapret.push_slice(&data.into_inner());
tapret
}
}
#[cfg(test)]
mod test {
use strict_encoding::StrictDumb;
use super::*;
pub fn commitment() -> TapretCommitment {
TapretCommitment {
mpc: mpc::Commitment::strict_dumb(),
nonce: 8,
}
}
#[test]
pub fn commitment_prefix() {
let script = TapScript::commit(&commitment());
assert_eq!(TAPRET_SCRIPT_COMMITMENT_PREFIX, script[0..31]);
}
#[test]
pub fn commiment_serialization() {
let commitment = commitment();
let script = TapScript::commit(&commitment);
assert_eq!(script[63], commitment.nonce);
assert_eq!(&script[31..63], commitment.mpc.as_slice());
}
#[test]
pub fn tapret_commitment_baid58() {
let commitment = commitment();
let encoded = commitment.to_baid58();
let decoded = TapretCommitment::from_baid58(encoded).unwrap();
assert_eq!(decoded, commitment);
}
}