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
11pub use common::{BackupRequest, SignedBackupRequest};
12use config::MintClientConfig;
13use fedimint_core::core::{Decoder, ModuleInstanceId, ModuleKind};
14use fedimint_core::encoding::{Decodable, Encodable};
15use fedimint_core::module::{CommonModuleInit, ModuleCommon, ModuleConsensusVersion};
16use fedimint_core::{
17 extensible_associated_module_type, plugin_types_trait_impl_common, secp256k1, Amount,
18};
19use serde::{Deserialize, Serialize};
20use tbs::BlindedSignatureShare;
21use thiserror::Error;
22use tracing::error;
23
24pub mod common;
25pub mod config;
26pub mod endpoint_constants;
27
28pub const KIND: ModuleKind = ModuleKind::from_static_str("mint");
29pub const MODULE_CONSENSUS_VERSION: ModuleConsensusVersion = ModuleConsensusVersion::new(2, 0);
30
31pub const DEFAULT_MAX_NOTES_PER_DENOMINATION: u16 = 3;
33
34#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
39pub enum MintConsensusItem {
40 #[encodable_default]
41 Default { variant: u64, bytes: Vec<u8> },
42}
43
44impl std::fmt::Display for MintConsensusItem {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(f, "MintConsensusItem")
47 }
48}
49
50#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
55pub struct MintOutputBlindSignature(pub tbs::BlindedSignature);
56
57#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
71pub struct Note {
72 pub nonce: Nonce,
73 pub signature: tbs::Signature,
74}
75
76impl fmt::Display for Note {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 self.nonce.fmt(f)
79 }
80}
81
82#[derive(
90 Debug,
91 Copy,
92 Clone,
93 Eq,
94 PartialEq,
95 PartialOrd,
96 Ord,
97 Hash,
98 Deserialize,
99 Serialize,
100 Encodable,
101 Decodable,
102)]
103pub struct Nonce(pub secp256k1::PublicKey);
104
105impl fmt::Display for Nonce {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 self.0.fmt(f)
108 }
109}
110
111#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
120pub struct BlindNonce(pub tbs::BlindedMessage);
121
122#[derive(Debug)]
123pub struct MintCommonInit;
124
125impl CommonModuleInit for MintCommonInit {
126 const CONSENSUS_VERSION: ModuleConsensusVersion = MODULE_CONSENSUS_VERSION;
127 const KIND: ModuleKind = KIND;
128
129 type ClientConfig = MintClientConfig;
130
131 fn decoder() -> Decoder {
132 MintModuleTypes::decoder_builder().build()
133 }
134}
135
136extensible_associated_module_type!(MintInput, MintInputV0, UnknownMintInputVariantError);
137
138impl MintInput {
139 pub fn new_v0(amount: Amount, note: Note) -> MintInput {
140 MintInput::V0(MintInputV0 { amount, note })
141 }
142}
143
144#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
145pub struct MintInputV0 {
146 pub amount: Amount,
147 pub note: Note,
148}
149
150impl std::fmt::Display for MintInputV0 {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 write!(f, "Mint Note {}", self.amount)
153 }
154}
155
156extensible_associated_module_type!(MintOutput, MintOutputV0, UnknownMintOutputVariantError);
157
158impl MintOutput {
159 pub fn new_v0(amount: Amount, blind_nonce: BlindNonce) -> MintOutput {
160 MintOutput::V0(MintOutputV0 {
161 amount,
162 blind_nonce,
163 })
164 }
165}
166
167#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
168pub struct MintOutputV0 {
169 pub amount: Amount,
170 pub blind_nonce: BlindNonce,
171}
172
173impl std::fmt::Display for MintOutputV0 {
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 write!(f, "Mint Note {}", self.amount)
176 }
177}
178
179extensible_associated_module_type!(
180 MintOutputOutcome,
181 MintOutputOutcomeV0,
182 UnknownMintOutputOutcomeVariantError
183);
184
185impl MintOutputOutcome {
186 pub fn new_v0(blind_signature_share: BlindedSignatureShare) -> MintOutputOutcome {
187 MintOutputOutcome::V0(MintOutputOutcomeV0(blind_signature_share))
188 }
189}
190
191#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
192pub struct MintOutputOutcomeV0(pub tbs::BlindedSignatureShare);
193
194impl std::fmt::Display for MintOutputOutcomeV0 {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 write!(f, "MintOutputOutcome")
197 }
198}
199
200pub struct MintModuleTypes;
201
202impl Note {
203 pub fn verify(&self, pk: tbs::AggregatePublicKey) -> bool {
205 tbs::verify(self.nonce.to_message(), self.signature, pk)
206 }
207
208 pub fn spend_key(&self) -> &secp256k1::PublicKey {
210 &self.nonce.0
211 }
212}
213
214impl Nonce {
215 pub fn to_bytes(&self) -> Vec<u8> {
216 let mut bytes = vec![];
217 bincode::serialize_into(&mut bytes, &self.0).unwrap();
218 bytes
219 }
220
221 pub fn from_bytes(bytes: &[u8]) -> Self {
222 bincode::deserialize(bytes).unwrap()
224 }
225
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}