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;
88#[macro_use]
90pub mod macros;
91pub mod base32;
93pub mod module;
95pub mod net;
97mod peer_id;
99pub mod runtime;
101pub mod task;
103pub mod tiered;
105pub mod tiered_multi;
107pub mod time;
109pub mod timing;
111pub mod transaction;
113pub mod txoproof;
115pub mod util;
117pub mod version;
119
120pub mod session_outcome;
122
123mod txid {
127 use bitcoin::hashes::hash_newtype;
128 use bitcoin::hashes::sha256::Hash as Sha256;
129
130 hash_newtype!(
131 pub struct TransactionId(Sha256);
133 );
134}
135pub use txid::TransactionId;
136
137#[derive(Debug, Eq, PartialEq, Copy, Hash, Clone, Serialize, Deserialize)]
139#[serde(rename_all = "snake_case")]
140pub enum BitcoinAmountOrAll {
141 All,
142 #[serde(untagged)]
143 Amount(#[serde(with = "bitcoin::amount::serde::as_sat")] bitcoin::Amount),
144}
145
146impl std::fmt::Display for BitcoinAmountOrAll {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 match self {
149 Self::All => write!(f, "all"),
150 Self::Amount(amount) => write!(f, "{amount}"),
151 }
152 }
153}
154
155impl FromStr for BitcoinAmountOrAll {
156 type Err = anyhow::Error;
157
158 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
159 if s == "all" {
160 Ok(Self::All)
161 } else {
162 let amount = Amount::from_str(s)?;
163 Ok(Self::Amount(amount.try_into()?))
164 }
165 }
166}
167
168#[derive(
172 Debug,
173 Clone,
174 Copy,
175 Eq,
176 PartialEq,
177 PartialOrd,
178 Ord,
179 Hash,
180 Deserialize,
181 Serialize,
182 Encodable,
183 Decodable,
184)]
185pub struct InPoint {
186 pub txid: TransactionId,
188 pub in_idx: u64,
191}
192
193impl std::fmt::Display for InPoint {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 write!(f, "{}:{}", self.txid, self.in_idx)
196 }
197}
198
199#[derive(
203 Debug,
204 Clone,
205 Copy,
206 Eq,
207 PartialEq,
208 PartialOrd,
209 Ord,
210 Hash,
211 Deserialize,
212 Serialize,
213 Encodable,
214 Decodable,
215)]
216pub struct OutPoint {
217 pub txid: TransactionId,
219 pub out_idx: u64,
222}
223
224impl std::fmt::Display for OutPoint {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 write!(f, "{}:{}", self.txid, self.out_idx)
227 }
228}
229
230impl Encodable for TransactionId {
231 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
232 let bytes = &self[..];
233 writer.write_all(bytes)?;
234 Ok(())
235 }
236}
237
238impl Decodable for TransactionId {
239 fn consensus_decode_partial<D: std::io::Read>(
240 d: &mut D,
241 _modules: &ModuleDecoderRegistry,
242 ) -> Result<Self, DecodeError> {
243 let mut bytes = [0u8; 32];
244 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
245 Ok(Self::from_byte_array(bytes))
246 }
247}
248
249#[derive(
250 Copy,
251 Clone,
252 Debug,
253 PartialEq,
254 Ord,
255 PartialOrd,
256 Eq,
257 Hash,
258 Serialize,
259 Deserialize,
260 Encodable,
261 Decodable,
262)]
263pub struct Feerate {
264 pub sats_per_kvb: u64,
265}
266
267impl fmt::Display for Feerate {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 f.write_fmt(format_args!("{}sat/kvb", self.sats_per_kvb))
270 }
271}
272
273impl Feerate {
274 pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount {
275 let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000;
276 bitcoin::Amount::from_sat(sats)
277 }
278}
279
280const WITNESS_SCALE_FACTOR: u64 = bitcoin::constants::WITNESS_SCALE_FACTOR as u64;
281
282pub fn weight_to_vbytes(weight: u64) -> u64 {
287 weight.div_ceil(WITNESS_SCALE_FACTOR)
288}
289
290#[derive(Debug, Error)]
291pub enum CoreError {
292 #[error("Mismatching outcome variant: expected {0}, got {1}")]
293 MismatchingVariant(&'static str, &'static str),
294}
295
296pub fn encode_bolt11_invoice_features_without_length(features: &Bolt11InvoiceFeatures) -> Vec<u8> {
302 let mut feature_bytes = vec![];
303 for f in features.le_flags().iter().rev() {
304 f.write(&mut feature_bytes)
305 .expect("Writing to byte vec can't fail");
306 }
307 feature_bytes
308}
309
310pub fn format_hex(data: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
315 let prec = f.precision().unwrap_or(2 * data.len());
316 let width = f.width().unwrap_or(2 * data.len());
317 for _ in (2 * data.len())..width {
318 f.write_str("0")?;
319 }
320 for ch in data.iter().take(prec / 2) {
321 write!(f, "{:02x}", *ch)?;
322 }
323 if prec < 2 * data.len() && prec % 2 == 1 {
324 write!(f, "{:x}", data[prec / 2] / 16)?;
325 }
326 Ok(())
327}
328
329pub fn get_network_for_address(address: &Address<NetworkUnchecked>) -> Network {
342 if address.is_valid_for_network(Network::Bitcoin) {
343 Network::Bitcoin
344 } else if address.is_valid_for_network(Network::Testnet) {
345 Network::Testnet
346 } else if address.is_valid_for_network(Network::Regtest) {
347 Network::Regtest
348 } else {
349 panic!("Address is not valid for any network");
350 }
351}
352
353#[cfg(test)]
354mod tests;