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 std::fmt;
9use std::hash::Hash;
10
11use bitcoin_hashes::hash160;
12use config::MintClientConfig;
13pub use fedimint_core::config::ExcessiveRelativeFeeError;
14use fedimint_core::core::{Decoder, ModuleInstanceId, ModuleKind};
15use fedimint_core::encoding::{Decodable, Encodable};
16use fedimint_core::module::{CommonModuleInit, ModuleCommon, ModuleConsensusVersion};
17use fedimint_core::secp256k1::PublicKey;
18use fedimint_core::{Amount, extensible_associated_module_type, plugin_types_trait_impl_common};
19use serde::{Deserialize, Serialize};
20use tbs::{BlindedMessage, Message};
21use thiserror::Error;
22
23pub mod config;
24pub mod endpoint_constants;
25
26pub const KIND: ModuleKind = ModuleKind::from_static_str("mintv2");
27pub const MODULE_CONSENSUS_VERSION: ModuleConsensusVersion = ModuleConsensusVersion::new(1, 0);
28
29#[derive(
32 Debug,
33 Copy,
34 Clone,
35 Eq,
36 PartialEq,
37 Hash,
38 Ord,
39 PartialOrd,
40 Serialize,
41 Deserialize,
42 Encodable,
43 Decodable,
44)]
45pub struct Denomination(pub u8);
46
47impl Denomination {
48 pub fn amount(self) -> Amount {
50 Amount::from_msats(1 << self.0 as usize)
51 }
52}
53
54impl fmt::Display for Denomination {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 write!(f, "2^{} msats", self.0)
57 }
58}
59
60#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
65pub enum MintConsensusItem {
66 #[encodable_default]
67 Default { variant: u64, bytes: Vec<u8> },
68}
69
70impl std::fmt::Display for MintConsensusItem {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "MintConsensusItem")
73 }
74}
75
76#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
77pub struct MintOutputBlindSignature(pub tbs::BlindedSignature);
78
79#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
89pub struct Note {
90 pub denomination: Denomination,
91 pub nonce: PublicKey,
92 pub signature: tbs::Signature,
93}
94
95impl Note {
96 pub fn amount(&self) -> Amount {
97 self.denomination.amount()
98 }
99}
100
101#[derive(Debug)]
102pub struct MintCommonInit;
103
104impl CommonModuleInit for MintCommonInit {
105 const CONSENSUS_VERSION: ModuleConsensusVersion = MODULE_CONSENSUS_VERSION;
106 const KIND: ModuleKind = KIND;
107
108 type ClientConfig = MintClientConfig;
109
110 fn decoder() -> Decoder {
111 MintModuleTypes::decoder_builder().build()
112 }
113}
114
115extensible_associated_module_type!(MintInput, MintInputV0, UnknownMintInputVariantError);
116
117impl MintInput {
118 pub fn new_v0(note: Note) -> Self {
119 Self::V0(MintInputV0 { note })
120 }
121}
122
123#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
124pub struct MintInputV0 {
125 pub note: Note,
126}
127
128impl std::fmt::Display for MintInputV0 {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 write!(f, "Mint Note {}", self.note.denomination)
131 }
132}
133
134extensible_associated_module_type!(MintOutput, MintOutputV0, UnknownMintOutputVariantError);
135
136impl MintOutput {
137 pub fn new_v0(denomination: Denomination, nonce: BlindedMessage, tweak: [u8; 16]) -> Self {
138 MintOutput::V0(MintOutputV0 {
139 denomination,
140 nonce,
141 tweak,
142 })
143 }
144}
145
146#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
147pub struct MintOutputV0 {
148 pub denomination: Denomination,
149 pub nonce: BlindedMessage,
150 pub tweak: [u8; 16],
151}
152
153impl MintOutputV0 {
154 pub fn amount(&self) -> Amount {
155 self.denomination.amount()
156 }
157}
158
159impl std::fmt::Display for MintOutputV0 {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 write!(f, "Mint Note {}", self.denomination)
162 }
163}
164
165#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Encodable, Decodable)]
167pub enum RecoveryItem {
168 Output {
169 denomination: Denomination,
170 nonce_hash: hash160::Hash,
171 tweak: [u8; 16],
172 },
173 Input {
174 nonce_hash: hash160::Hash,
175 },
176}
177
178#[derive(Debug, Clone, PartialEq, Hash, Encodable, Decodable)]
179pub struct MintOutputOutcome;
180
181impl std::fmt::Display for MintOutputOutcome {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 write!(f, "MintOutputOutcome")
184 }
185}
186
187pub struct MintModuleTypes;
188
189pub fn verify_note(note: Note, pk: tbs::AggregatePublicKey) -> bool {
190 tbs::verify(nonce_message(note.nonce), note.signature, pk)
191}
192
193pub fn nonce_message(nonce: PublicKey) -> Message {
194 tbs::Message::from_bytes_sha256(&nonce.serialize())
195}
196
197plugin_types_trait_impl_common!(
198 KIND,
199 MintModuleTypes,
200 MintClientConfig,
201 MintInput,
202 MintOutput,
203 MintOutputOutcome,
204 MintConsensusItem,
205 MintInputError,
206 MintOutputError
207);
208
209#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
210pub enum MintInputError {
211 #[error("The mint input version is not supported by this federation")]
212 UnknownInputVariant(#[from] UnknownMintInputVariantError),
213 #[error("The note is already spent")]
214 SpentCoin,
215 #[error("The note has an invalid amount not issued by the mint")]
216 InvalidDenomination,
217 #[error("The note has an invalid signature")]
218 InvalidSignature,
219}
220
221#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Encodable, Decodable)]
222pub enum MintOutputError {
223 #[error("The mint output version is not supported by this federation")]
224 UnknownOutputVariant(#[from] UnknownMintOutputVariantError),
225 #[error("The note has an invalid amount not issued by the mint")]
226 InvalidDenomination,
227}