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;
15pub use fedimint_core::config::ExcessiveRelativeFeeError;
16use fedimint_core::core::{Decoder, ModuleInstanceId, ModuleKind};
17use fedimint_core::encoding::{Decodable, Encodable};
18use fedimint_core::module::{CommonModuleInit, ModuleCommon, ModuleConsensusVersion};
19use fedimint_core::{
20 Amount, extensible_associated_module_type, plugin_types_trait_impl_common, secp256k1,
21};
22use serde::{Deserialize, Serialize};
23use tbs::BlindedSignatureShare;
24use thiserror::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 write!(f, "{}", self.nonce.fmt_short())
89 }
90}
91
92#[derive(
100 Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Deserialize, Serialize, Encodable, Decodable,
101)]
102pub struct Nonce(pub secp256k1::PublicKey);
103
104pub struct NonceShortFmt<'a>(&'a Nonce);
105pub struct NonceFullFmt<'a>(&'a Nonce);
106
107impl Nonce {
108 pub fn fmt_short(&self) -> NonceShortFmt<'_> {
109 NonceShortFmt(self)
110 }
111
112 pub fn fmt_full(&self) -> NonceFullFmt<'_> {
113 NonceFullFmt(self)
114 }
115}
116
117impl fmt::Display for NonceShortFmt<'_> {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let bytes = self.0.0.serialize();
120 fedimint_core::format_hex(&bytes[..4], f)?;
121 f.write_str("_")?;
122 fedimint_core::format_hex(&bytes[29..], f)
123 }
124}
125
126impl fmt::Display for NonceFullFmt<'_> {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 let bytes = self.0.0.serialize();
129 fedimint_core::format_hex(&bytes, f)
130 }
131}
132
133impl fmt::Debug for Nonce {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 write!(f, "Nonce({})", self.fmt_short())
136 }
137}
138
139#[derive(Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
148pub struct BlindNonce(pub tbs::BlindedMessage);
149
150pub struct BlindNonceShortFmt<'a>(&'a BlindNonce);
151pub struct BlindNonceFullFmt<'a>(&'a BlindNonce);
152
153impl BlindNonce {
154 pub fn fmt_short(&self) -> BlindNonceShortFmt<'_> {
155 BlindNonceShortFmt(self)
156 }
157
158 pub fn fmt_full(&self) -> BlindNonceFullFmt<'_> {
159 BlindNonceFullFmt(self)
160 }
161}
162
163impl fmt::Display for BlindNonceShortFmt<'_> {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 let bytes = self.0.0.consensus_hash_sha256().to_byte_array();
166 fedimint_core::format_hex(&bytes[..4], f)?;
167 f.write_str("_")?;
168 fedimint_core::format_hex(&bytes[28..], f)
169 }
170}
171
172impl fmt::Display for BlindNonceFullFmt<'_> {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 write!(
175 f,
176 "{}",
177 self.0.0.consensus_hash_sha256().as_byte_array().as_hex()
178 )
179 }
180}
181
182impl fmt::Debug for BlindNonce {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 write!(f, "BlindNonce({})", self.fmt_short())
185 }
186}
187
188#[derive(Debug)]
189pub struct MintCommonInit;
190
191impl CommonModuleInit for MintCommonInit {
192 const CONSENSUS_VERSION: ModuleConsensusVersion = MODULE_CONSENSUS_VERSION;
193 const KIND: ModuleKind = KIND;
194
195 type ClientConfig = MintClientConfig;
196
197 fn decoder() -> Decoder {
198 MintModuleTypes::decoder_builder().build()
199 }
200}
201
202extensible_associated_module_type!(MintInput, MintInputV0, UnknownMintInputVariantError);
203
204impl MintInput {
205 pub fn new_v0(amount: Amount, note: Note) -> MintInput {
206 MintInput::V0(MintInputV0 { amount, note })
207 }
208}
209
210#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
211pub struct MintInputV0 {
212 pub amount: Amount,
213 pub note: Note,
214}
215
216impl std::fmt::Display for MintInputV0 {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 write!(
219 f,
220 "Mint Note {} nonce={}",
221 self.amount,
222 self.note.nonce.fmt_short()
223 )
224 }
225}
226
227extensible_associated_module_type!(MintOutput, MintOutputV0, UnknownMintOutputVariantError);
228
229impl MintOutput {
230 pub fn new_v0(amount: Amount, blind_nonce: BlindNonce) -> MintOutput {
231 MintOutput::V0(MintOutputV0 {
232 amount,
233 blind_nonce,
234 })
235 }
236}
237
238#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
239pub struct MintOutputV0 {
240 pub amount: Amount,
241 pub blind_nonce: BlindNonce,
242}
243
244impl std::fmt::Display for MintOutputV0 {
245 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246 write!(
247 f,
248 "Mint Note {} blind_nonce={}",
249 self.amount,
250 self.blind_nonce.fmt_short()
251 )
252 }
253}
254
255extensible_associated_module_type!(
256 MintOutputOutcome,
257 MintOutputOutcomeV0,
258 UnknownMintOutputOutcomeVariantError
259);
260
261impl MintOutputOutcome {
262 pub fn new_v0(blind_signature_share: BlindedSignatureShare) -> MintOutputOutcome {
263 MintOutputOutcome::V0(MintOutputOutcomeV0(blind_signature_share))
264 }
265}
266
267#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
268pub struct MintOutputOutcomeV0(pub tbs::BlindedSignatureShare);
269
270impl std::fmt::Display for MintOutputOutcomeV0 {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 write!(f, "MintOutputOutcome")
273 }
274}
275
276pub struct MintModuleTypes;
277
278impl Note {
279 pub fn verify(&self, pk: tbs::AggregatePublicKey) -> bool {
281 tbs::verify(self.nonce.to_message(), self.signature, pk)
282 }
283
284 pub fn spend_key(&self) -> &secp256k1::PublicKey {
286 &self.nonce.0
287 }
288}
289
290impl Nonce {
291 pub fn to_message(&self) -> tbs::Message {
292 tbs::Message::from_bytes(&self.0.serialize()[..])
293 }
294}
295
296plugin_types_trait_impl_common!(
297 KIND,
298 MintModuleTypes,
299 MintClientConfig,
300 MintInput,
301 MintOutput,
302 MintOutputOutcome,
303 MintConsensusItem,
304 MintInputError,
305 MintOutputError
306);
307
308#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
309pub enum RecoveryItem {
310 Output {
312 amount: Amount,
313 nonce: bitcoin_hashes::hash160::Hash,
314 },
315 Input {
317 nonce: bitcoin_hashes::hash160::Hash,
318 },
319}
320
321#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
322pub enum MintInputError {
323 #[error("The note is already spent")]
324 SpentCoin,
325 #[error("The note has an invalid amount not issued by the mint: {0}")]
326 InvalidAmountTier(Amount),
327 #[error("The note has an invalid signature")]
328 InvalidSignature,
329 #[error("The mint input version is not supported by this federation")]
330 UnknownInputVariant(#[from] UnknownMintInputVariantError),
331}
332
333#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
334pub enum MintOutputError {
335 #[error("The note has an invalid amount not issued by the mint: {0}")]
336 InvalidAmountTier(Amount),
337 #[error("The mint output version is not supported by this federation")]
338 UnknownOutputVariant(#[from] UnknownMintOutputVariantError),
339 #[error("The mint output blind nonce was already used before")]
340 BlindNonceAlreadyUsed,
341}