#![deny(clippy::pedantic)]
#![allow(clippy::doc_markdown)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
use core::fmt;
use std::hash::Hash;
use bitcoin_hashes::hex::DisplayHex;
use bitcoin_hashes::Hash as _;
pub use common::{BackupRequest, SignedBackupRequest};
use config::MintClientConfig;
use fedimint_core::core::{Decoder, ModuleInstanceId, ModuleKind};
use fedimint_core::encoding::{Decodable, Encodable};
use fedimint_core::module::{CommonModuleInit, ModuleCommon, ModuleConsensusVersion};
use fedimint_core::{
extensible_associated_module_type, plugin_types_trait_impl_common, secp256k1, Amount,
};
use serde::{Deserialize, Serialize};
use tbs::BlindedSignatureShare;
use thiserror::Error;
use tracing::error;
pub mod common;
pub mod config;
pub mod endpoint_constants;
pub const KIND: ModuleKind = ModuleKind::from_static_str("mint");
pub const MODULE_CONSENSUS_VERSION: ModuleConsensusVersion = ModuleConsensusVersion::new(2, 0);
pub const DEFAULT_MAX_NOTES_PER_DENOMINATION: u16 = 3;
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
pub enum MintConsensusItem {
#[encodable_default]
Default { variant: u64, bytes: Vec<u8> },
}
impl std::fmt::Display for MintConsensusItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MintConsensusItem")
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct MintOutputBlindSignature(pub tbs::BlindedSignature);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct Note {
pub nonce: Nonce,
pub signature: tbs::Signature,
}
impl fmt::Display for Note {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.nonce.fmt(f)
}
}
#[derive(
Debug,
Copy,
Clone,
Eq,
PartialEq,
PartialOrd,
Ord,
Hash,
Deserialize,
Serialize,
Encodable,
Decodable,
)]
pub struct Nonce(pub secp256k1::PublicKey);
impl fmt::Display for Nonce {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct BlindNonce(pub tbs::BlindedMessage);
impl fmt::Debug for BlindNonce {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"BlindNonce({})",
self.0.consensus_hash_sha256().as_byte_array()[0..8].as_hex()
))
}
}
#[derive(Debug)]
pub struct MintCommonInit;
impl CommonModuleInit for MintCommonInit {
const CONSENSUS_VERSION: ModuleConsensusVersion = MODULE_CONSENSUS_VERSION;
const KIND: ModuleKind = KIND;
type ClientConfig = MintClientConfig;
fn decoder() -> Decoder {
MintModuleTypes::decoder_builder().build()
}
}
extensible_associated_module_type!(MintInput, MintInputV0, UnknownMintInputVariantError);
impl MintInput {
pub fn new_v0(amount: Amount, note: Note) -> MintInput {
MintInput::V0(MintInputV0 { amount, note })
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct MintInputV0 {
pub amount: Amount,
pub note: Note,
}
impl std::fmt::Display for MintInputV0 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Mint Note {}", self.amount)
}
}
extensible_associated_module_type!(MintOutput, MintOutputV0, UnknownMintOutputVariantError);
impl MintOutput {
pub fn new_v0(amount: Amount, blind_nonce: BlindNonce) -> MintOutput {
MintOutput::V0(MintOutputV0 {
amount,
blind_nonce,
})
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct MintOutputV0 {
pub amount: Amount,
pub blind_nonce: BlindNonce,
}
impl std::fmt::Display for MintOutputV0 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Mint Note {}", self.amount)
}
}
extensible_associated_module_type!(
MintOutputOutcome,
MintOutputOutcomeV0,
UnknownMintOutputOutcomeVariantError
);
impl MintOutputOutcome {
pub fn new_v0(blind_signature_share: BlindedSignatureShare) -> MintOutputOutcome {
MintOutputOutcome::V0(MintOutputOutcomeV0(blind_signature_share))
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
pub struct MintOutputOutcomeV0(pub tbs::BlindedSignatureShare);
impl std::fmt::Display for MintOutputOutcomeV0 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MintOutputOutcome")
}
}
pub struct MintModuleTypes;
impl Note {
pub fn verify(&self, pk: tbs::AggregatePublicKey) -> bool {
tbs::verify(self.nonce.to_message(), self.signature, pk)
}
pub fn spend_key(&self) -> &secp256k1::PublicKey {
&self.nonce.0
}
}
impl Nonce {
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = vec![];
bincode::serialize_into(&mut bytes, &self.0).unwrap();
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Self {
bincode::deserialize(bytes).unwrap()
}
pub fn to_message(&self) -> tbs::Message {
tbs::Message::from_bytes(&self.0.serialize()[..])
}
}
plugin_types_trait_impl_common!(
KIND,
MintModuleTypes,
MintClientConfig,
MintInput,
MintOutput,
MintOutputOutcome,
MintConsensusItem,
MintInputError,
MintOutputError
);
#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
pub enum MintInputError {
#[error("The note is already spent")]
SpentCoin,
#[error("The note has an invalid amount not issued by the mint: {0}")]
InvalidAmountTier(Amount),
#[error("The note has an invalid signature")]
InvalidSignature,
#[error("The mint input version is not supported by this federation")]
UnknownInputVariant(#[from] UnknownMintInputVariantError),
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
pub enum MintOutputError {
#[error("The note has an invalid amount not issued by the mint: {0}")]
InvalidAmountTier(Amount),
#[error("The mint output version is not supported by this federation")]
UnknownOutputVariant(#[from] UnknownMintOutputVariantError),
}