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