1#![deny(clippy::pedantic)]
2#![allow(clippy::doc_markdown)]
3#![allow(clippy::missing_errors_doc)]
4#![allow(clippy::missing_panics_doc)]
5#![allow(clippy::module_name_repetitions)]
6#![allow(clippy::must_use_candidate)]
7
8use core::fmt;
9use std::hash::Hash;
10
11use bitcoin_hashes::Hash as _;
12use bitcoin_hashes::hex::DisplayHex;
13pub use common::{BackupRequest, SignedBackupRequest};
14use config::MintClientConfig;
15use fedimint_core::core::{Decoder, ModuleInstanceId, ModuleKind};
16use fedimint_core::encoding::{Decodable, Encodable};
17use fedimint_core::module::{CommonModuleInit, ModuleCommon, ModuleConsensusVersion};
18use fedimint_core::{
19 Amount, extensible_associated_module_type, plugin_types_trait_impl_common, secp256k1,
20};
21use serde::{Deserialize, Serialize};
22use tbs::BlindedSignatureShare;
23use thiserror::Error;
24use tracing::error;
25
26pub mod common;
27pub mod config;
28pub mod endpoint_constants;
29
30pub const KIND: ModuleKind = ModuleKind::from_static_str("mint");
31pub const MODULE_CONSENSUS_VERSION: ModuleConsensusVersion = ModuleConsensusVersion::new(2, 0);
32
33pub const DEFAULT_MAX_NOTES_PER_DENOMINATION: u16 = 3;
35
36#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
41pub enum MintConsensusItem {
42 #[encodable_default]
43 Default { variant: u64, bytes: Vec<u8> },
44}
45
46impl std::fmt::Display for MintConsensusItem {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 write!(f, "MintConsensusItem")
49 }
50}
51
52#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
57pub struct MintOutputBlindSignature(pub tbs::BlindedSignature);
58
59#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
73pub struct Note {
74 pub nonce: Nonce,
75 pub signature: tbs::Signature,
76}
77
78impl fmt::Display for Note {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 self.nonce.fmt(f)
81 }
82}
83
84#[derive(
92 Debug,
93 Copy,
94 Clone,
95 Eq,
96 PartialEq,
97 PartialOrd,
98 Ord,
99 Hash,
100 Deserialize,
101 Serialize,
102 Encodable,
103 Decodable,
104)]
105pub struct Nonce(pub secp256k1::PublicKey);
106
107impl fmt::Display for Nonce {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 self.0.fmt(f)
110 }
111}
112
113#[derive(Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
122pub struct BlindNonce(pub tbs::BlindedMessage);
123
124impl fmt::Debug for BlindNonce {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 f.write_fmt(format_args!(
127 "BlindNonce({})",
128 self.0.consensus_hash_sha256().as_byte_array()[0..8].as_hex()
129 ))
130 }
131}
132
133#[derive(Debug)]
134pub struct MintCommonInit;
135
136impl CommonModuleInit for MintCommonInit {
137 const CONSENSUS_VERSION: ModuleConsensusVersion = MODULE_CONSENSUS_VERSION;
138 const KIND: ModuleKind = KIND;
139
140 type ClientConfig = MintClientConfig;
141
142 fn decoder() -> Decoder {
143 MintModuleTypes::decoder_builder().build()
144 }
145}
146
147extensible_associated_module_type!(MintInput, MintInputV0, UnknownMintInputVariantError);
148
149impl MintInput {
150 pub fn new_v0(amount: Amount, note: Note) -> MintInput {
151 MintInput::V0(MintInputV0 { amount, note })
152 }
153}
154
155#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
156pub struct MintInputV0 {
157 pub amount: Amount,
158 pub note: Note,
159}
160
161impl std::fmt::Display for MintInputV0 {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 write!(f, "Mint Note {}", self.amount)
164 }
165}
166
167extensible_associated_module_type!(MintOutput, MintOutputV0, UnknownMintOutputVariantError);
168
169impl MintOutput {
170 pub fn new_v0(amount: Amount, blind_nonce: BlindNonce) -> MintOutput {
171 MintOutput::V0(MintOutputV0 {
172 amount,
173 blind_nonce,
174 })
175 }
176}
177
178#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
179pub struct MintOutputV0 {
180 pub amount: Amount,
181 pub blind_nonce: BlindNonce,
182}
183
184impl std::fmt::Display for MintOutputV0 {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 write!(f, "Mint Note {}", self.amount)
187 }
188}
189
190extensible_associated_module_type!(
191 MintOutputOutcome,
192 MintOutputOutcomeV0,
193 UnknownMintOutputOutcomeVariantError
194);
195
196impl MintOutputOutcome {
197 pub fn new_v0(blind_signature_share: BlindedSignatureShare) -> MintOutputOutcome {
198 MintOutputOutcome::V0(MintOutputOutcomeV0(blind_signature_share))
199 }
200}
201
202#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
203pub struct MintOutputOutcomeV0(pub tbs::BlindedSignatureShare);
204
205impl std::fmt::Display for MintOutputOutcomeV0 {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 write!(f, "MintOutputOutcome")
208 }
209}
210
211pub struct MintModuleTypes;
212
213impl Note {
214 pub fn verify(&self, pk: tbs::AggregatePublicKey) -> bool {
216 tbs::verify(self.nonce.to_message(), self.signature, pk)
217 }
218
219 pub fn spend_key(&self) -> &secp256k1::PublicKey {
221 &self.nonce.0
222 }
223}
224
225impl Nonce {
226 pub fn to_message(&self) -> tbs::Message {
227 tbs::Message::from_bytes(&self.0.serialize()[..])
228 }
229}
230
231plugin_types_trait_impl_common!(
232 KIND,
233 MintModuleTypes,
234 MintClientConfig,
235 MintInput,
236 MintOutput,
237 MintOutputOutcome,
238 MintConsensusItem,
239 MintInputError,
240 MintOutputError
241);
242
243#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
244pub enum MintInputError {
245 #[error("The note is already spent")]
246 SpentCoin,
247 #[error("The note has an invalid amount not issued by the mint: {0}")]
248 InvalidAmountTier(Amount),
249 #[error("The note has an invalid signature")]
250 InvalidSignature,
251 #[error("The mint input version is not supported by this federation")]
252 UnknownInputVariant(#[from] UnknownMintInputVariantError),
253}
254
255#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
256pub enum MintOutputError {
257 #[error("The note has an invalid amount not issued by the mint: {0}")]
258 InvalidAmountTier(Amount),
259 #[error("The mint output version is not supported by this federation")]
260 UnknownOutputVariant(#[from] UnknownMintOutputVariantError),
261 #[error("The mint output blind nonce was already used before")]
262 BlindNonceAlreadyUsed,
263}