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(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::Debug for Note {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 f.debug_struct("Note")
81 .field("nonce", &self.nonce)
82 .finish_non_exhaustive()
83 }
84}
85
86impl fmt::Display for Note {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 self.nonce.fmt(f)
89 }
90}
91
92#[derive(
100 Debug,
101 Copy,
102 Clone,
103 Eq,
104 PartialEq,
105 PartialOrd,
106 Ord,
107 Hash,
108 Deserialize,
109 Serialize,
110 Encodable,
111 Decodable,
112)]
113pub struct Nonce(pub secp256k1::PublicKey);
114
115impl fmt::Display for Nonce {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 self.0.fmt(f)
118 }
119}
120
121#[derive(Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
130pub struct BlindNonce(pub tbs::BlindedMessage);
131
132impl fmt::Debug for BlindNonce {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 f.write_fmt(format_args!(
135 "BlindNonce({})",
136 self.0.consensus_hash_sha256().as_byte_array()[0..8].as_hex()
137 ))
138 }
139}
140
141#[derive(Debug)]
142pub struct MintCommonInit;
143
144impl CommonModuleInit for MintCommonInit {
145 const CONSENSUS_VERSION: ModuleConsensusVersion = MODULE_CONSENSUS_VERSION;
146 const KIND: ModuleKind = KIND;
147
148 type ClientConfig = MintClientConfig;
149
150 fn decoder() -> Decoder {
151 MintModuleTypes::decoder_builder().build()
152 }
153}
154
155extensible_associated_module_type!(MintInput, MintInputV0, UnknownMintInputVariantError);
156
157impl MintInput {
158 pub fn new_v0(amount: Amount, note: Note) -> MintInput {
159 MintInput::V0(MintInputV0 { amount, note })
160 }
161}
162
163#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
164pub struct MintInputV0 {
165 pub amount: Amount,
166 pub note: Note,
167}
168
169impl std::fmt::Display for MintInputV0 {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 write!(f, "Mint Note {}", self.amount)
172 }
173}
174
175extensible_associated_module_type!(MintOutput, MintOutputV0, UnknownMintOutputVariantError);
176
177impl MintOutput {
178 pub fn new_v0(amount: Amount, blind_nonce: BlindNonce) -> MintOutput {
179 MintOutput::V0(MintOutputV0 {
180 amount,
181 blind_nonce,
182 })
183 }
184}
185
186#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
187pub struct MintOutputV0 {
188 pub amount: Amount,
189 pub blind_nonce: BlindNonce,
190}
191
192impl std::fmt::Display for MintOutputV0 {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 write!(f, "Mint Note {}", self.amount)
195 }
196}
197
198extensible_associated_module_type!(
199 MintOutputOutcome,
200 MintOutputOutcomeV0,
201 UnknownMintOutputOutcomeVariantError
202);
203
204impl MintOutputOutcome {
205 pub fn new_v0(blind_signature_share: BlindedSignatureShare) -> MintOutputOutcome {
206 MintOutputOutcome::V0(MintOutputOutcomeV0(blind_signature_share))
207 }
208}
209
210#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
211pub struct MintOutputOutcomeV0(pub tbs::BlindedSignatureShare);
212
213impl std::fmt::Display for MintOutputOutcomeV0 {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 write!(f, "MintOutputOutcome")
216 }
217}
218
219pub struct MintModuleTypes;
220
221impl Note {
222 pub fn verify(&self, pk: tbs::AggregatePublicKey) -> bool {
224 tbs::verify(self.nonce.to_message(), self.signature, pk)
225 }
226
227 pub fn spend_key(&self) -> &secp256k1::PublicKey {
229 &self.nonce.0
230 }
231}
232
233impl Nonce {
234 pub fn to_message(&self) -> tbs::Message {
235 tbs::Message::from_bytes(&self.0.serialize()[..])
236 }
237}
238
239plugin_types_trait_impl_common!(
240 KIND,
241 MintModuleTypes,
242 MintClientConfig,
243 MintInput,
244 MintOutput,
245 MintOutputOutcome,
246 MintConsensusItem,
247 MintInputError,
248 MintOutputError
249);
250
251#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
252pub enum MintInputError {
253 #[error("The note is already spent")]
254 SpentCoin,
255 #[error("The note has an invalid amount not issued by the mint: {0}")]
256 InvalidAmountTier(Amount),
257 #[error("The note has an invalid signature")]
258 InvalidSignature,
259 #[error("The mint input version is not supported by this federation")]
260 UnknownInputVariant(#[from] UnknownMintInputVariantError),
261}
262
263#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
264pub enum MintOutputError {
265 #[error("The note has an invalid amount not issued by the mint: {0}")]
266 InvalidAmountTier(Amount),
267 #[error("The mint output version is not supported by this federation")]
268 UnknownOutputVariant(#[from] UnknownMintOutputVariantError),
269 #[error("The mint output blind nonce was already used before")]
270 BlindNonceAlreadyUsed,
271}