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::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 module;
93pub mod net;
95mod peer_id;
97pub mod runtime;
99pub mod task;
101pub mod tiered;
103pub mod tiered_multi;
105pub mod time;
107pub mod timing;
109pub mod transaction;
111pub mod txoproof;
113pub mod util;
115pub mod version;
117
118pub mod session_outcome;
120
121mod txid {
125 use bitcoin::hashes::hash_newtype;
126 use bitcoin::hashes::sha256::Hash as Sha256;
127
128 hash_newtype!(
129 pub struct TransactionId(Sha256);
131 );
132}
133pub use txid::TransactionId;
134
135#[derive(Debug, Eq, PartialEq, Copy, Hash, Clone, Serialize, Deserialize)]
137#[serde(rename_all = "snake_case")]
138pub enum BitcoinAmountOrAll {
139 All,
140 #[serde(untagged)]
141 Amount(#[serde(with = "bitcoin::amount::serde::as_sat")] bitcoin::Amount),
142}
143
144impl std::fmt::Display for BitcoinAmountOrAll {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 match self {
147 Self::All => write!(f, "all"),
148 Self::Amount(amount) => write!(f, "{amount}"),
149 }
150 }
151}
152
153impl FromStr for BitcoinAmountOrAll {
154 type Err = anyhow::Error;
155
156 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
157 if s == "all" {
158 Ok(Self::All)
159 } else {
160 let amount = Amount::from_str(s)?;
161 Ok(Self::Amount(amount.try_into()?))
162 }
163 }
164}
165
166#[derive(
170 Debug,
171 Clone,
172 Copy,
173 Eq,
174 PartialEq,
175 PartialOrd,
176 Ord,
177 Hash,
178 Deserialize,
179 Serialize,
180 Encodable,
181 Decodable,
182)]
183pub struct InPoint {
184 pub txid: TransactionId,
186 pub in_idx: u64,
189}
190
191impl std::fmt::Display for InPoint {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 write!(f, "{}:{}", self.txid, self.in_idx)
194 }
195}
196
197#[derive(
201 Debug,
202 Clone,
203 Copy,
204 Eq,
205 PartialEq,
206 PartialOrd,
207 Ord,
208 Hash,
209 Deserialize,
210 Serialize,
211 Encodable,
212 Decodable,
213)]
214pub struct OutPoint {
215 pub txid: TransactionId,
217 pub out_idx: u64,
220}
221
222impl std::fmt::Display for OutPoint {
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224 write!(f, "{}:{}", self.txid, self.out_idx)
225 }
226}
227
228impl Encodable for TransactionId {
229 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
230 let bytes = &self[..];
231 writer.write_all(bytes)?;
232 Ok(bytes.len())
233 }
234}
235
236impl Decodable for TransactionId {
237 fn consensus_decode_partial<D: std::io::Read>(
238 d: &mut D,
239 _modules: &ModuleDecoderRegistry,
240 ) -> Result<Self, DecodeError> {
241 let mut bytes = [0u8; 32];
242 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
243 Ok(Self::from_byte_array(bytes))
244 }
245}
246
247#[derive(
248 Copy,
249 Clone,
250 Debug,
251 PartialEq,
252 Ord,
253 PartialOrd,
254 Eq,
255 Hash,
256 Serialize,
257 Deserialize,
258 Encodable,
259 Decodable,
260)]
261pub struct Feerate {
262 pub sats_per_kvb: u64,
263}
264
265impl Feerate {
266 pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount {
267 let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000;
268 bitcoin::Amount::from_sat(sats)
269 }
270}
271
272const WITNESS_SCALE_FACTOR: u64 = bitcoin::constants::WITNESS_SCALE_FACTOR as u64;
273
274pub fn weight_to_vbytes(weight: u64) -> u64 {
279 weight.div_ceil(WITNESS_SCALE_FACTOR)
280}
281
282#[derive(Debug, Error)]
283pub enum CoreError {
284 #[error("Mismatching outcome variant: expected {0}, got {1}")]
285 MismatchingVariant(&'static str, &'static str),
286}
287
288pub fn encode_bolt11_invoice_features_without_length(features: &Bolt11InvoiceFeatures) -> Vec<u8> {
294 let mut feature_bytes = vec![];
295 for f in features.le_flags().iter().rev() {
296 f.write(&mut feature_bytes)
297 .expect("Writing to byte vec can't fail");
298 }
299 feature_bytes
300}
301
302pub fn format_hex(data: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
307 let prec = f.precision().unwrap_or(2 * data.len());
308 let width = f.width().unwrap_or(2 * data.len());
309 for _ in (2 * data.len())..width {
310 f.write_str("0")?;
311 }
312 for ch in data.iter().take(prec / 2) {
313 write!(f, "{:02x}", *ch)?;
314 }
315 if prec < 2 * data.len() && prec % 2 == 1 {
316 write!(f, "{:x}", data[prec / 2] / 16)?;
317 }
318 Ok(())
319}
320
321pub fn get_network_for_address(address: &Address<NetworkUnchecked>) -> Network {
334 if address.is_valid_for_network(Network::Bitcoin) {
335 Network::Bitcoin
336 } else if address.is_valid_for_network(Network::Testnet) {
337 Network::Testnet
338 } else if address.is_valid_for_network(Network::Regtest) {
339 Network::Regtest
340 } else {
341 panic!("Address is not valid for any network");
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348
349 #[test]
350 fn converts_weight_to_vbytes() {
351 assert_eq!(1, weight_to_vbytes(4));
352 assert_eq!(2, weight_to_vbytes(5));
353 }
354
355 #[test]
356 fn calculate_fee() {
357 let feerate = Feerate { sats_per_kvb: 1000 };
358 assert_eq!(bitcoin::Amount::from_sat(25), feerate.calculate_fee(100));
359 assert_eq!(bitcoin::Amount::from_sat(26), feerate.calculate_fee(101));
360 }
361
362 #[test]
363 fn test_deserialize_amount_or_all() {
364 let all: BitcoinAmountOrAll = serde_json::from_str("\"all\"").unwrap();
365 assert_eq!(all, BitcoinAmountOrAll::All);
366
367 let amount: BitcoinAmountOrAll = serde_json::from_str("12345").unwrap();
368 assert_eq!(
369 amount,
370 BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(12345))
371 );
372
373 let all_string = all.to_string();
374 assert_eq!(all_string, "all");
375 let amount_string = amount.to_string();
376 assert_eq!(amount_string, "0.00012345 BTC");
377 let all_parsed = BitcoinAmountOrAll::from_str(&all_string).unwrap();
378 assert_eq!(all, all_parsed);
379 let amount_parsed = BitcoinAmountOrAll::from_str(&amount_string).unwrap();
380 assert_eq!(amount, amount_parsed);
381 }
382}