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::ops::{self, Range};
44use std::str::FromStr;
45
46pub use amount::*;
47pub use anyhow;
49use bitcoin::address::NetworkUnchecked;
50pub use bitcoin::hashes::Hash as BitcoinHash;
51use bitcoin::{Address, Network};
52use envs::BitcoinRpcConfig;
53use lightning::util::ser::Writeable;
54use lightning_types::features::Bolt11InvoiceFeatures;
55pub use macro_rules_attribute::apply;
56pub use peer_id::*;
57use serde::{Deserialize, Serialize};
58use thiserror::Error;
59pub use tiered::Tiered;
60pub use tiered_multi::*;
61use util::SafeUrl;
62pub use {bitcoin, hex, secp256k1};
63
64use crate::encoding::{Decodable, DecodeError, Encodable};
65use crate::module::registry::ModuleDecoderRegistry;
66
67pub mod admin_client;
69mod amount;
71pub mod backup;
73pub mod bls12_381_serde;
75pub mod config;
77pub mod core;
79pub mod db;
81pub mod encoding;
83pub mod endpoint_constants;
84pub mod envs;
86pub mod epoch;
87pub mod fmt_utils;
89pub mod invite_code;
91pub mod iroh_prod;
92pub mod log;
93#[macro_use]
95pub mod macros;
96pub mod base32;
98pub mod module;
100pub mod net;
102mod peer_id;
104pub mod runtime;
106pub mod rustls;
108pub mod setup_code;
110pub mod task;
112pub mod tiered;
114pub mod tiered_multi;
116pub mod time;
118pub mod timing;
120pub mod transaction;
122pub mod txoproof;
124pub mod util;
126pub mod version;
128
129pub mod session_outcome;
131
132mod txid {
136 use bitcoin::hashes::hash_newtype;
137 use bitcoin::hashes::sha256::Hash as Sha256;
138
139 hash_newtype!(
140 pub struct TransactionId(Sha256);
142 );
143}
144pub use txid::TransactionId;
145
146#[derive(Debug, Eq, PartialEq, Copy, Hash, Clone, Serialize, Deserialize)]
148#[serde(rename_all = "snake_case")]
149pub enum BitcoinAmountOrAll {
150 All,
151 #[serde(untagged)]
152 Amount(#[serde(with = "bitcoin::amount::serde::as_sat")] bitcoin::Amount),
153}
154
155impl std::fmt::Display for BitcoinAmountOrAll {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 match self {
158 Self::All => write!(f, "all"),
159 Self::Amount(amount) => write!(f, "{amount}"),
160 }
161 }
162}
163
164impl FromStr for BitcoinAmountOrAll {
165 type Err = anyhow::Error;
166
167 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
168 if s == "all" {
169 Ok(Self::All)
170 } else {
171 let amount = Amount::from_str(s)?;
172 Ok(Self::Amount(amount.try_into()?))
173 }
174 }
175}
176
177#[derive(
181 Debug,
182 Clone,
183 Copy,
184 Eq,
185 PartialEq,
186 PartialOrd,
187 Ord,
188 Hash,
189 Deserialize,
190 Serialize,
191 Encodable,
192 Decodable,
193)]
194pub struct InPoint {
195 pub txid: TransactionId,
197 pub in_idx: u64,
200}
201
202impl std::fmt::Display for InPoint {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 write!(f, "{}:{}", self.txid, self.in_idx)
205 }
206}
207
208#[derive(
212 Debug,
213 Clone,
214 Copy,
215 Eq,
216 PartialEq,
217 PartialOrd,
218 Ord,
219 Hash,
220 Deserialize,
221 Serialize,
222 Encodable,
223 Decodable,
224)]
225pub struct OutPoint {
226 pub txid: TransactionId,
228 pub out_idx: u64,
231}
232
233impl std::fmt::Display for OutPoint {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 write!(f, "{}:{}", self.txid, self.out_idx)
236 }
237}
238
239#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Encodable, Decodable)]
241pub struct IdxRange {
242 start: u64,
243 end: u64,
244}
245
246impl IdxRange {
247 pub fn new_single(start: u64) -> Option<Self> {
248 start.checked_add(1).map(|end| Self { start, end })
249 }
250
251 pub fn start(self) -> u64 {
252 self.start
253 }
254
255 pub fn count(self) -> usize {
256 self.into_iter().count()
257 }
258
259 pub fn from_inclusive(range: ops::RangeInclusive<u64>) -> Option<Self> {
260 range.end().checked_add(1).map(|end| Self {
261 start: *range.start(),
262 end,
263 })
264 }
265}
266
267impl From<Range<u64>> for IdxRange {
268 fn from(Range { start, end }: Range<u64>) -> Self {
269 Self { start, end }
270 }
271}
272
273impl IntoIterator for IdxRange {
274 type Item = u64;
275 type IntoIter = ops::Range<u64>;
276
277 fn into_iter(self) -> Self::IntoIter {
278 ops::Range {
279 start: self.start,
280 end: self.end,
281 }
282 }
283}
284
285#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Encodable, Decodable)]
287pub struct OutPointRange {
288 pub txid: TransactionId,
289 idx_range: IdxRange,
290}
291
292impl OutPointRange {
293 pub fn new(txid: TransactionId, idx_range: IdxRange) -> Self {
294 Self { txid, idx_range }
295 }
296
297 pub fn new_single(txid: TransactionId, idx: u64) -> Option<Self> {
298 IdxRange::new_single(idx).map(|idx_range| Self { txid, idx_range })
299 }
300
301 pub fn start_idx(self) -> u64 {
302 self.idx_range.start()
303 }
304
305 pub fn out_idx_iter(self) -> impl Iterator<Item = u64> {
306 self.idx_range.into_iter()
307 }
308
309 pub fn count(self) -> usize {
310 self.idx_range.count()
311 }
312
313 pub fn txid(&self) -> TransactionId {
314 self.txid
315 }
316}
317
318impl IntoIterator for OutPointRange {
319 type Item = OutPoint;
320 type IntoIter = OutPointRangeIter;
321
322 fn into_iter(self) -> Self::IntoIter {
323 OutPointRangeIter {
324 txid: self.txid,
325 inner: self.idx_range.into_iter(),
326 }
327 }
328}
329
330pub struct OutPointRangeIter {
331 txid: TransactionId,
332 inner: ops::Range<u64>,
333}
334
335impl Iterator for OutPointRangeIter {
336 type Item = OutPoint;
337
338 fn next(&mut self) -> Option<Self::Item> {
339 self.inner.next().map(|idx| OutPoint {
340 txid: self.txid,
341 out_idx: idx,
342 })
343 }
344}
345
346impl Encodable for TransactionId {
347 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
348 let bytes = &self[..];
349 writer.write_all(bytes)?;
350 Ok(())
351 }
352}
353
354impl Decodable for TransactionId {
355 fn consensus_decode_partial<D: std::io::Read>(
356 d: &mut D,
357 _modules: &ModuleDecoderRegistry,
358 ) -> Result<Self, DecodeError> {
359 let mut bytes = [0u8; 32];
360 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
361 Ok(Self::from_byte_array(bytes))
362 }
363}
364
365#[derive(
366 Copy,
367 Clone,
368 Debug,
369 PartialEq,
370 Ord,
371 PartialOrd,
372 Eq,
373 Hash,
374 Serialize,
375 Deserialize,
376 Encodable,
377 Decodable,
378)]
379pub struct Feerate {
380 pub sats_per_kvb: u64,
381}
382
383impl fmt::Display for Feerate {
384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385 f.write_fmt(format_args!("{}sat/kvb", self.sats_per_kvb))
386 }
387}
388
389impl Feerate {
390 pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount {
391 let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000;
392 bitcoin::Amount::from_sat(sats)
393 }
394}
395
396const WITNESS_SCALE_FACTOR: u64 = bitcoin::constants::WITNESS_SCALE_FACTOR as u64;
397
398pub fn weight_to_vbytes(weight: u64) -> u64 {
403 weight.div_ceil(WITNESS_SCALE_FACTOR)
404}
405
406#[derive(Debug, Error)]
407pub enum CoreError {
408 #[error("Mismatching outcome variant: expected {0}, got {1}")]
409 MismatchingVariant(&'static str, &'static str),
410}
411
412pub fn encode_bolt11_invoice_features_without_length(features: &Bolt11InvoiceFeatures) -> Vec<u8> {
418 let mut feature_bytes = vec![];
419 for f in features.le_flags().iter().rev() {
420 f.write(&mut feature_bytes)
421 .expect("Writing to byte vec can't fail");
422 }
423 feature_bytes
424}
425
426pub fn format_hex(data: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
431 let prec = f.precision().unwrap_or(2 * data.len());
432 let width = f.width().unwrap_or(2 * data.len());
433 for _ in (2 * data.len())..width {
434 f.write_str("0")?;
435 }
436 for ch in data.iter().take(prec / 2) {
437 write!(f, "{:02x}", *ch)?;
438 }
439 if prec < 2 * data.len() && prec % 2 == 1 {
440 write!(f, "{:x}", data[prec / 2] / 16)?;
441 }
442 Ok(())
443}
444
445pub fn get_network_for_address(address: &Address<NetworkUnchecked>) -> Network {
458 if address.is_valid_for_network(Network::Bitcoin) {
459 Network::Bitcoin
460 } else if address.is_valid_for_network(Network::Testnet) {
461 Network::Testnet
462 } else if address.is_valid_for_network(Network::Regtest) {
463 Network::Regtest
464 } else {
465 panic!("Address is not valid for any network");
466 }
467}
468
469pub fn default_esplora_server(network: Network, port: Option<String>) -> BitcoinRpcConfig {
471 BitcoinRpcConfig {
472 kind: "esplora".to_string(),
473 url: match network {
474 Network::Bitcoin => SafeUrl::parse("https://mempool.space/api/"),
475 Network::Testnet => SafeUrl::parse("https://mempool.space/testnet/api/"),
476 Network::Testnet4 => SafeUrl::parse("https://mempool.space/testnet4/api/"),
477 Network::Signet => SafeUrl::parse("https://mutinynet.com/api/"),
478 Network::Regtest => SafeUrl::parse(&format!(
479 "http://127.0.0.1:{}/",
480 port.unwrap_or_else(|| String::from("50002"))
481 )),
482 _ => panic!("Failed to parse default esplora server"),
483 }
484 .expect("Failed to parse default esplora server"),
485 }
486}
487
488#[cfg(test)]
489mod tests;