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