use crate::{
Commitment, Error, Hash, KeyImage, PublicKey, PublicKeySet, Result, Signature, SignatureShare,
};
use std::cmp::Ordering;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpentProofContent {
pub key_image: KeyImage,
pub transaction_hash: Hash,
pub public_commitments: Vec<Commitment>,
}
impl SpentProofContent {
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = Default::default();
bytes.extend(self.key_image.to_bytes());
bytes.extend(self.transaction_hash.as_ref());
for pc in self.public_commitments.iter() {
bytes.extend(pc.to_compressed());
}
bytes
}
pub fn hash(&self) -> Hash {
Hash::hash(&self.to_bytes())
}
}
impl PartialOrd for SpentProofContent {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SpentProofContent {
fn cmp(&self, other: &Self) -> Ordering {
self.key_image.cmp(&other.key_image)
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct IndexedSignatureShare {
index: u64,
signature_share: SignatureShare,
}
impl IndexedSignatureShare {
pub fn new(index: u64, signature_share: SignatureShare) -> Self {
Self {
index,
signature_share,
}
}
pub fn threshold_crypto(&self) -> (u64, &SignatureShare) {
(self.index, &self.signature_share)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = self.index.to_le_bytes().to_vec();
bytes.extend(self.signature_share.to_bytes());
bytes
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct SpentProofShare {
pub content: SpentProofContent,
pub spentbook_pks: PublicKeySet,
pub spentbook_sig_share: IndexedSignatureShare,
}
impl PartialEq for SpentProofShare {
fn eq(&self, other: &Self) -> bool {
self.content == other.content
&& self.spentbook_pks == other.spentbook_pks
&& self.spentbook_sig_share == other.spentbook_sig_share
}
}
impl Eq for SpentProofShare {}
impl std::hash::Hash for SpentProofShare {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let bytes = self.to_bytes();
bytes.hash(state);
}
}
impl SpentProofShare {
pub fn key_image(&self) -> &KeyImage {
&self.content.key_image
}
pub fn transaction_hash(&self) -> Hash {
self.content.transaction_hash
}
pub fn public_commitments(&self) -> &Vec<Commitment> {
&self.content.public_commitments
}
pub fn spentbook_sig_share(&self) -> &IndexedSignatureShare {
&self.spentbook_sig_share
}
pub fn spentbook_pks(&self) -> &PublicKeySet {
&self.spentbook_pks
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = self.content.to_bytes();
bytes.extend(&self.spentbook_pks.to_bytes());
bytes.extend(self.spentbook_sig_share.to_bytes());
bytes
}
}
pub trait SpentProofKeyVerifier {
type Error: std::error::Error;
fn verify_known_key(&self, key: &PublicKey) -> Result<(), Self::Error>;
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SpentProof {
pub content: SpentProofContent,
pub spentbook_pub_key: PublicKey,
pub spentbook_sig: Signature,
}
impl SpentProof {
pub fn try_from_proof_shares<'a>(
key_image: KeyImage,
transaction_hash: Hash,
shares: impl Iterator<Item = &'a SpentProofShare>,
) -> Result<Self> {
let mut peekable_shares = shares.peekable();
let any_share = peekable_shares
.peek()
.cloned()
.ok_or(Error::MissingSpentProofShare(key_image))?;
let spentbook_pub_key = any_share.spentbook_pks().public_key();
let spentbook_sig = any_share.spentbook_pks.combine_signatures(
peekable_shares
.map(SpentProofShare::spentbook_sig_share)
.map(IndexedSignatureShare::threshold_crypto),
)?;
let public_commitments: Vec<Commitment> = any_share.public_commitments().clone();
Ok(SpentProof {
content: SpentProofContent {
key_image,
transaction_hash,
public_commitments,
},
spentbook_pub_key,
spentbook_sig,
})
}
pub fn key_image(&self) -> &KeyImage {
&self.content.key_image
}
pub fn transaction_hash(&self) -> Hash {
self.content.transaction_hash
}
pub fn public_commitments(&self) -> &Vec<Commitment> {
&self.content.public_commitments
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = Default::default();
bytes.extend(self.content.to_bytes());
bytes.extend(self.spentbook_pub_key.to_bytes());
bytes.extend(self.spentbook_sig.to_bytes());
bytes
}
pub fn verify<K: SpentProofKeyVerifier>(
&self,
tx_hash: Hash,
proof_key_verifier: &K,
) -> Result<()> {
if tx_hash != self.content.transaction_hash {
return Err(Error::InvalidTransactionHash);
}
let pub_key = &self.spentbook_pub_key;
if !pub_key.verify(&self.spentbook_sig, self.content.hash()) {
return Err(Error::InvalidSpentProofSignature(*self.key_image()));
}
proof_key_verifier
.verify_known_key(pub_key)
.map_err(|err| Error::FailedKnownKeyCheck(err.to_string()))
}
}