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::redundant_pub_crate)]
15#![allow(clippy::return_self_not_must_use)]
16#![allow(clippy::similar_names)]
17#![allow(clippy::transmute_ptr_to_ptr)]
18#![allow(clippy::unsafe_derive_deserialize)]
19
20extern crate self as fedimint_core;
39
40use std::fmt::Debug;
41use std::io::Error;
42use std::str::FromStr;
43
44pub use amount::*;
45pub use anyhow;
47use bitcoin::address::NetworkUnchecked;
48pub use bitcoin::hashes::Hash as BitcoinHash;
49use bitcoin::{Address, Network};
50use lightning::util::ser::Writeable;
51use lightning_types::features::Bolt11InvoiceFeatures;
52pub use macro_rules_attribute::apply;
53pub use module::ServerModule;
54pub use peer_id::*;
55use serde::{Deserialize, Serialize};
56use thiserror::Error;
57pub use tiered::Tiered;
58pub use tiered_multi::*;
59pub use {hex, secp256k1};
60
61pub use crate::core::server;
62use crate::encoding::{Decodable, DecodeError, Encodable};
63use crate::module::registry::ModuleDecoderRegistry;
64
65pub mod admin_client;
67mod amount;
69pub mod backup;
71pub mod bls12_381_serde;
73pub mod config;
75pub mod core;
77pub mod db;
79pub mod encoding;
81pub mod endpoint_constants;
82pub mod envs;
84pub mod epoch;
85pub mod fmt_utils;
87pub mod invite_code;
89#[macro_use]
91pub mod macros;
92pub mod module;
94pub mod net;
96mod peer_id;
98pub mod runtime;
100pub mod task;
102pub mod tiered;
104pub mod tiered_multi;
106pub mod time;
108pub mod timing;
110pub mod transaction;
112pub mod txoproof;
114pub mod util;
116pub mod version;
118
119pub mod session_outcome;
121
122mod txid {
126 use bitcoin::hashes::hash_newtype;
127 use bitcoin::hashes::sha256::Hash as Sha256;
128
129 hash_newtype!(
130 pub struct TransactionId(Sha256);
132 );
133}
134pub use txid::TransactionId;
135
136#[derive(Debug, Eq, PartialEq, Copy, Hash, Clone, Serialize, Deserialize)]
138#[serde(rename_all = "snake_case")]
139pub enum BitcoinAmountOrAll {
140 All,
141 #[serde(untagged)]
142 Amount(#[serde(with = "bitcoin::amount::serde::as_sat")] bitcoin::Amount),
143}
144
145impl std::fmt::Display for BitcoinAmountOrAll {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 match self {
148 Self::All => write!(f, "all"),
149 Self::Amount(amount) => write!(f, "{amount}"),
150 }
151 }
152}
153
154impl FromStr for BitcoinAmountOrAll {
155 type Err = anyhow::Error;
156
157 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
158 if s == "all" {
159 Ok(Self::All)
160 } else {
161 let amount = Amount::from_str(s)?;
162 Ok(Self::Amount(amount.try_into()?))
163 }
164 }
165}
166
167#[derive(
171 Debug,
172 Clone,
173 Copy,
174 Eq,
175 PartialEq,
176 PartialOrd,
177 Ord,
178 Hash,
179 Deserialize,
180 Serialize,
181 Encodable,
182 Decodable,
183)]
184pub struct OutPoint {
185 pub txid: TransactionId,
187 pub out_idx: u64,
190}
191
192impl std::fmt::Display for OutPoint {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 write!(f, "{}:{}", self.txid, self.out_idx)
195 }
196}
197
198impl Encodable for TransactionId {
199 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
200 let bytes = &self[..];
201 writer.write_all(bytes)?;
202 Ok(bytes.len())
203 }
204}
205
206impl Decodable for TransactionId {
207 fn consensus_decode<D: std::io::Read>(
208 d: &mut D,
209 _modules: &ModuleDecoderRegistry,
210 ) -> Result<Self, DecodeError> {
211 let mut bytes = [0u8; 32];
212 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
213 Ok(Self::from_byte_array(bytes))
214 }
215}
216
217#[derive(
218 Copy,
219 Clone,
220 Debug,
221 PartialEq,
222 Ord,
223 PartialOrd,
224 Eq,
225 Hash,
226 Serialize,
227 Deserialize,
228 Encodable,
229 Decodable,
230)]
231pub struct Feerate {
232 pub sats_per_kvb: u64,
233}
234
235impl Feerate {
236 pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount {
237 let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000;
238 bitcoin::Amount::from_sat(sats)
239 }
240}
241
242const WITNESS_SCALE_FACTOR: u64 = bitcoin::constants::WITNESS_SCALE_FACTOR as u64;
243
244pub fn weight_to_vbytes(weight: u64) -> u64 {
249 (weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR
250}
251
252#[derive(Debug, Error)]
253pub enum CoreError {
254 #[error("Mismatching outcome variant: expected {0}, got {1}")]
255 MismatchingVariant(&'static str, &'static str),
256}
257
258pub fn encode_bolt11_invoice_features_without_length(features: &Bolt11InvoiceFeatures) -> Vec<u8> {
264 let mut feature_bytes = vec![];
265 for f in features.le_flags().iter().rev() {
266 f.write(&mut feature_bytes)
267 .expect("Writing to byte vec can't fail");
268 }
269 feature_bytes
270}
271
272pub fn format_hex(data: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
277 let prec = f.precision().unwrap_or(2 * data.len());
278 let width = f.width().unwrap_or(2 * data.len());
279 for _ in (2 * data.len())..width {
280 f.write_str("0")?;
281 }
282 for ch in data.iter().take(prec / 2) {
283 write!(f, "{:02x}", *ch)?;
284 }
285 if prec < 2 * data.len() && prec % 2 == 1 {
286 write!(f, "{:x}", data[prec / 2] / 16)?;
287 }
288 Ok(())
289}
290
291pub fn get_network_for_address(address: &Address<NetworkUnchecked>) -> Network {
304 if address.is_valid_for_network(Network::Bitcoin) {
305 Network::Bitcoin
306 } else if address.is_valid_for_network(Network::Testnet) {
307 Network::Testnet
308 } else if address.is_valid_for_network(Network::Regtest) {
309 Network::Regtest
310 } else {
311 panic!("Address is not valid for any network");
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn converts_weight_to_vbytes() {
321 assert_eq!(1, weight_to_vbytes(4));
322 assert_eq!(2, weight_to_vbytes(5));
323 }
324
325 #[test]
326 fn calculate_fee() {
327 let feerate = Feerate { sats_per_kvb: 1000 };
328 assert_eq!(bitcoin::Amount::from_sat(25), feerate.calculate_fee(100));
329 assert_eq!(bitcoin::Amount::from_sat(26), feerate.calculate_fee(101));
330 }
331
332 #[test]
333 fn test_deserialize_amount_or_all() {
334 let all: BitcoinAmountOrAll = serde_json::from_str("\"all\"").unwrap();
335 assert_eq!(all, BitcoinAmountOrAll::All);
336
337 let amount: BitcoinAmountOrAll = serde_json::from_str("12345").unwrap();
338 assert_eq!(
339 amount,
340 BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(12345))
341 );
342
343 let all_string = all.to_string();
344 assert_eq!(all_string, "all");
345 let amount_string = amount.to_string();
346 assert_eq!(amount_string, "0.00012345 BTC");
347 let all_parsed = BitcoinAmountOrAll::from_str(&all_string).unwrap();
348 assert_eq!(all, all_parsed);
349 let amount_parsed = BitcoinAmountOrAll::from_str(&amount_string).unwrap();
350 assert_eq!(amount, amount_parsed);
351 }
352}