use std::io;
use bitcoin_hashes::HashEngine;
use crate::{CommitVerify, PrehashedProtocol};
pub trait CommitEncode {
fn commit_encode<E: io::Write>(&self, e: E) -> usize;
fn commit_serialize(&self) -> Vec<u8> {
let mut vec = Vec::new();
self.commit_encode(&mut vec);
vec
}
}
#[macro_export]
macro_rules! commit_encode_list {
( $encoder:ident; $($item:expr),+ ) => {
{
let mut len = 0usize;
$(
len += $item.commit_encode(&mut $encoder);
)+
len
}
}
}
pub trait Strategy {
type Strategy;
}
pub mod strategies {
use bitcoin_hashes::Hash;
use super::*;
pub struct UsingStrict;
pub struct UsingConceal;
pub struct UsingHash<H>(std::marker::PhantomData<H>)
where
H: Hash + strict_encoding::StrictEncode;
impl<T> CommitEncode for amplify::Holder<T, UsingStrict>
where
T: strict_encoding::StrictEncode,
{
fn commit_encode<E: io::Write>(&self, e: E) -> usize {
self.as_inner().strict_encode(e).expect(
"Strict encoding must not fail for types using \
`strategy::UsingStrict`",
)
}
}
impl<T> CommitEncode for amplify::Holder<T, UsingConceal>
where
T: CommitConceal,
<T as CommitConceal>::ConcealedCommitment: CommitEncode,
{
fn commit_encode<E: io::Write>(&self, e: E) -> usize {
self.as_inner().commit_conceal().commit_encode(e)
}
}
impl<T, H> CommitEncode for amplify::Holder<T, UsingHash<H>>
where
H: Hash + strict_encoding::StrictEncode,
T: strict_encoding::StrictEncode,
{
fn commit_encode<E: io::Write>(&self, e: E) -> usize {
let mut engine = H::engine();
engine.input(
&strict_encoding::strict_serialize(self.as_inner()).expect(
"Strict encoding of hash strategy-based commitment data \
must not fail",
),
);
let hash = H::from_engine(engine);
hash.strict_encode(e).expect(
"Strict encoding must not fail for types using \
`strategy::UsingHash`",
)
}
}
impl<K, V> CommitEncode for (K, V)
where
K: CommitEncode,
V: CommitEncode,
{
fn commit_encode<E: io::Write>(&self, mut e: E) -> usize {
self.0.commit_encode(&mut e) + self.1.commit_encode(&mut e)
}
}
impl<A, B, C> CommitEncode for (A, B, C)
where
A: CommitEncode,
B: CommitEncode,
C: CommitEncode,
{
fn commit_encode<E: io::Write>(&self, mut e: E) -> usize {
self.0.commit_encode(&mut e)
+ self.1.commit_encode(&mut e)
+ self.2.commit_encode(&mut e)
}
}
impl<T> CommitEncode for T
where
T: Strategy + Clone,
amplify::Holder<T, <T as Strategy>::Strategy>: CommitEncode,
{
fn commit_encode<E: io::Write>(&self, e: E) -> usize {
amplify::Holder::new(self.clone()).commit_encode(e)
}
}
impl Strategy for usize {
type Strategy = UsingStrict;
}
impl Strategy for u8 {
type Strategy = UsingStrict;
}
impl Strategy for u16 {
type Strategy = UsingStrict;
}
impl Strategy for u32 {
type Strategy = UsingStrict;
}
impl Strategy for u64 {
type Strategy = UsingStrict;
}
impl Strategy for i8 {
type Strategy = UsingStrict;
}
impl Strategy for i16 {
type Strategy = UsingStrict;
}
impl Strategy for i32 {
type Strategy = UsingStrict;
}
impl Strategy for i64 {
type Strategy = UsingStrict;
}
impl Strategy for String {
type Strategy = UsingStrict;
}
impl Strategy for &str {
type Strategy = UsingStrict;
}
impl Strategy for &[u8] {
type Strategy = UsingStrict;
}
impl Strategy for Vec<u8> {
type Strategy = UsingStrict;
}
#[cfg(feature = "lnpbp_secp256k1zkp")]
impl Strategy for secp256k1zkp::pedersen::Commitment {
type Strategy = strategies::UsingStrict;
}
#[cfg(feature = "lnpbp_secp256k1zkp")]
impl Strategy for secp256k1zkp::pedersen::RangeProof {
type Strategy = strategies::UsingHash<bitcoin_hashes::sha256::Hash>;
}
impl<T> Strategy for &T
where
T: Strategy,
{
type Strategy = T::Strategy;
}
}
pub trait CommitConceal {
type ConcealedCommitment;
fn commit_conceal(&self) -> Self::ConcealedCommitment;
}
pub trait ConsensusCommit: Sized + CommitEncode {
type Commitment: CommitVerify<Vec<u8>, PrehashedProtocol>;
#[inline]
fn consensus_commit(&self) -> Self::Commitment {
let mut encoder = io::Cursor::new(vec![]);
self.commit_encode(&mut encoder);
Self::Commitment::commit(&encoder.into_inner())
}
#[inline]
fn consensus_verify(&self, commitment: &Self::Commitment) -> bool {
let mut encoder = io::Cursor::new(vec![]);
self.commit_encode(&mut encoder);
commitment.verify(&encoder.into_inner())
}
}