1#![deny(clippy::pedantic, clippy::nursery)]
2#![allow(clippy::cast_possible_truncation)]
3#![allow(clippy::cast_possible_wrap)]
4#![allow(clippy::cast_precision_loss)]
5#![allow(clippy::cast_sign_loss)]
6#![allow(clippy::cognitive_complexity)]
7#![allow(clippy::doc_markdown)]
8#![allow(clippy::future_not_send)]
9#![allow(clippy::missing_const_for_fn)]
10#![allow(clippy::missing_errors_doc)]
11#![allow(clippy::missing_panics_doc)]
12#![allow(clippy::module_name_repetitions)]
13#![allow(clippy::must_use_candidate)]
14#![allow(clippy::needless_lifetimes)]
15#![allow(clippy::redundant_pub_crate)]
16#![allow(clippy::return_self_not_must_use)]
17#![allow(clippy::similar_names)]
18#![allow(clippy::transmute_ptr_to_ptr)]
19#![allow(clippy::unsafe_derive_deserialize)]
20
21extern crate self as fedimint_core;
40
41use std::fmt::{self, Debug};
42use std::io::Error;
43use std::str::FromStr;
44
45pub use amount::*;
46pub use anyhow;
48use bitcoin::address::NetworkUnchecked;
49pub use bitcoin::hashes::Hash as BitcoinHash;
50use bitcoin::{Address, Network};
51use lightning::util::ser::Writeable;
52use lightning_types::features::Bolt11InvoiceFeatures;
53pub use macro_rules_attribute::apply;
54pub use peer_id::*;
55use serde::{Deserialize, Serialize};
56use thiserror::Error;
57pub use tiered::Tiered;
58pub use tiered_multi::*;
59pub use {bitcoin, hex, secp256k1};
60
61use crate::encoding::{Decodable, DecodeError, Encodable};
62use crate::module::registry::ModuleDecoderRegistry;
63
64pub mod admin_client;
66mod amount;
68pub mod backup;
70pub mod bls12_381_serde;
72pub mod config;
74pub mod core;
76pub mod db;
78pub mod encoding;
80pub mod endpoint_constants;
81pub mod envs;
83pub mod epoch;
84pub mod fmt_utils;
86pub mod invite_code;
88pub mod log;
89#[macro_use]
91pub mod macros;
92pub mod base32;
94pub mod module;
96pub mod net;
98mod peer_id;
100pub mod runtime;
102pub mod task;
104pub mod tiered;
106pub mod tiered_multi;
108pub mod time;
110pub mod timing;
112pub mod transaction;
114pub mod txoproof;
116pub mod util;
118pub mod version;
120
121pub mod session_outcome;
123
124mod txid {
128 use bitcoin::hashes::hash_newtype;
129 use bitcoin::hashes::sha256::Hash as Sha256;
130
131 hash_newtype!(
132 pub struct TransactionId(Sha256);
134 );
135}
136pub use txid::TransactionId;
137
138#[derive(Debug, Eq, PartialEq, Copy, Hash, Clone, Serialize, Deserialize)]
140#[serde(rename_all = "snake_case")]
141pub enum BitcoinAmountOrAll {
142 All,
143 #[serde(untagged)]
144 Amount(#[serde(with = "bitcoin::amount::serde::as_sat")] bitcoin::Amount),
145}
146
147impl std::fmt::Display for BitcoinAmountOrAll {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 match self {
150 Self::All => write!(f, "all"),
151 Self::Amount(amount) => write!(f, "{amount}"),
152 }
153 }
154}
155
156impl FromStr for BitcoinAmountOrAll {
157 type Err = anyhow::Error;
158
159 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
160 if s == "all" {
161 Ok(Self::All)
162 } else {
163 let amount = Amount::from_str(s)?;
164 Ok(Self::Amount(amount.try_into()?))
165 }
166 }
167}
168
169#[derive(
173 Debug,
174 Clone,
175 Copy,
176 Eq,
177 PartialEq,
178 PartialOrd,
179 Ord,
180 Hash,
181 Deserialize,
182 Serialize,
183 Encodable,
184 Decodable,
185)]
186pub struct InPoint {
187 pub txid: TransactionId,
189 pub in_idx: u64,
192}
193
194impl std::fmt::Display for InPoint {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 write!(f, "{}:{}", self.txid, self.in_idx)
197 }
198}
199
200#[derive(
204 Debug,
205 Clone,
206 Copy,
207 Eq,
208 PartialEq,
209 PartialOrd,
210 Ord,
211 Hash,
212 Deserialize,
213 Serialize,
214 Encodable,
215 Decodable,
216)]
217pub struct OutPoint {
218 pub txid: TransactionId,
220 pub out_idx: u64,
223}
224
225impl std::fmt::Display for OutPoint {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 write!(f, "{}:{}", self.txid, self.out_idx)
228 }
229}
230
231impl Encodable for TransactionId {
232 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
233 let bytes = &self[..];
234 writer.write_all(bytes)?;
235 Ok(())
236 }
237}
238
239impl Decodable for TransactionId {
240 fn consensus_decode_partial<D: std::io::Read>(
241 d: &mut D,
242 _modules: &ModuleDecoderRegistry,
243 ) -> Result<Self, DecodeError> {
244 let mut bytes = [0u8; 32];
245 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
246 Ok(Self::from_byte_array(bytes))
247 }
248}
249
250#[derive(
251 Copy,
252 Clone,
253 Debug,
254 PartialEq,
255 Ord,
256 PartialOrd,
257 Eq,
258 Hash,
259 Serialize,
260 Deserialize,
261 Encodable,
262 Decodable,
263)]
264pub struct Feerate {
265 pub sats_per_kvb: u64,
266}
267
268impl fmt::Display for Feerate {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 f.write_fmt(format_args!("{}sat/kvb", self.sats_per_kvb))
271 }
272}
273
274impl Feerate {
275 pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount {
276 let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000;
277 bitcoin::Amount::from_sat(sats)
278 }
279}
280
281const WITNESS_SCALE_FACTOR: u64 = bitcoin::constants::WITNESS_SCALE_FACTOR as u64;
282
283pub fn weight_to_vbytes(weight: u64) -> u64 {
288 weight.div_ceil(WITNESS_SCALE_FACTOR)
289}
290
291#[derive(Debug, Error)]
292pub enum CoreError {
293 #[error("Mismatching outcome variant: expected {0}, got {1}")]
294 MismatchingVariant(&'static str, &'static str),
295}
296
297pub fn encode_bolt11_invoice_features_without_length(features: &Bolt11InvoiceFeatures) -> Vec<u8> {
303 let mut feature_bytes = vec![];
304 for f in features.le_flags().iter().rev() {
305 f.write(&mut feature_bytes)
306 .expect("Writing to byte vec can't fail");
307 }
308 feature_bytes
309}
310
311pub fn format_hex(data: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
316 let prec = f.precision().unwrap_or(2 * data.len());
317 let width = f.width().unwrap_or(2 * data.len());
318 for _ in (2 * data.len())..width {
319 f.write_str("0")?;
320 }
321 for ch in data.iter().take(prec / 2) {
322 write!(f, "{:02x}", *ch)?;
323 }
324 if prec < 2 * data.len() && prec % 2 == 1 {
325 write!(f, "{:x}", data[prec / 2] / 16)?;
326 }
327 Ok(())
328}
329
330pub fn get_network_for_address(address: &Address<NetworkUnchecked>) -> Network {
343 if address.is_valid_for_network(Network::Bitcoin) {
344 Network::Bitcoin
345 } else if address.is_valid_for_network(Network::Testnet) {
346 Network::Testnet
347 } else if address.is_valid_for_network(Network::Regtest) {
348 Network::Regtest
349 } else {
350 panic!("Address is not valid for any network");
351 }
352}
353
354#[cfg(test)]
355mod tests;