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