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, Deserializer, Serialize, Serializer};
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 log;
92#[macro_use]
94pub mod macros;
95pub mod base32;
97pub mod module;
99pub mod net;
101mod peer_id;
103pub mod runtime;
105pub mod rustls;
107pub mod setup_code;
109pub mod task;
111pub mod tiered;
113pub mod tiered_multi;
115pub mod time;
117pub mod timing;
119pub mod transaction;
121pub mod txoproof;
123pub mod util;
125pub mod version;
127
128pub mod session_outcome;
130
131mod txid {
135 use bitcoin::hashes::hash_newtype;
136 use bitcoin::hashes::sha256::Hash as Sha256;
137
138 hash_newtype!(
139 pub struct TransactionId(Sha256);
141 );
142}
143pub use txid::TransactionId;
144
145#[derive(Debug, Eq, PartialEq, Copy, Hash, Clone)]
147pub enum BitcoinAmountOrAll {
148 All,
149 Amount(bitcoin::Amount),
150}
151
152impl std::fmt::Display for BitcoinAmountOrAll {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 match self {
155 Self::All => write!(f, "all"),
156 Self::Amount(amount) => write!(f, "{amount}"),
157 }
158 }
159}
160
161impl FromStr for BitcoinAmountOrAll {
162 type Err = anyhow::Error;
163
164 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
165 if s.eq_ignore_ascii_case("all") {
166 Ok(Self::All)
167 } else {
168 let amount = Amount::from_str(s)?;
169 Ok(Self::Amount(amount.try_into()?))
170 }
171 }
172}
173
174impl<'de> Deserialize<'de> for BitcoinAmountOrAll {
176 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177 where
178 D: Deserializer<'de>,
179 {
180 use serde::de::Error;
181
182 struct Visitor;
183
184 impl serde::de::Visitor<'_> for Visitor {
185 type Value = BitcoinAmountOrAll;
186
187 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
188 write!(f, "a bitcoin amount as number or 'all'")
189 }
190
191 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
192 where
193 E: Error,
194 {
195 if v.eq_ignore_ascii_case("all") {
196 Ok(BitcoinAmountOrAll::All)
197 } else {
198 let sat: u64 = v.parse().map_err(E::custom)?;
199 Ok(BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(sat)))
200 }
201 }
202
203 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
204 where
205 E: Error,
206 {
207 Ok(BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(v)))
208 }
209
210 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
211 where
212 E: Error,
213 {
214 if v < 0 {
215 return Err(E::custom("amount cannot be negative"));
216 }
217 Ok(BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(
218 v as u64,
219 )))
220 }
221 }
222
223 deserializer.deserialize_any(Visitor)
224 }
225}
226
227impl Serialize for BitcoinAmountOrAll {
228 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229 where
230 S: Serializer,
231 {
232 match self {
233 Self::All => serializer.serialize_str("all"),
234 Self::Amount(a) => serializer.serialize_u64(a.to_sat()),
235 }
236 }
237}
238
239#[derive(
243 Debug,
244 Clone,
245 Copy,
246 Eq,
247 PartialEq,
248 PartialOrd,
249 Ord,
250 Hash,
251 Deserialize,
252 Serialize,
253 Encodable,
254 Decodable,
255)]
256pub struct InPoint {
257 pub txid: TransactionId,
259 pub in_idx: u64,
262}
263
264impl std::fmt::Display for InPoint {
265 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266 write!(f, "{}:{}", self.txid, self.in_idx)
267 }
268}
269
270#[derive(
274 Debug,
275 Clone,
276 Copy,
277 Eq,
278 PartialEq,
279 PartialOrd,
280 Ord,
281 Hash,
282 Deserialize,
283 Serialize,
284 Encodable,
285 Decodable,
286)]
287pub struct OutPoint {
288 pub txid: TransactionId,
290 pub out_idx: u64,
293}
294
295impl std::fmt::Display for OutPoint {
296 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297 write!(f, "{}:{}", self.txid, self.out_idx)
298 }
299}
300
301#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Encodable, Decodable)]
303pub struct IdxRange {
304 start: u64,
305 end: u64,
306}
307
308impl IdxRange {
309 pub fn new_single(start: u64) -> Option<Self> {
310 start.checked_add(1).map(|end| Self { start, end })
311 }
312
313 pub fn start(self) -> u64 {
314 self.start
315 }
316
317 pub fn count(self) -> usize {
318 self.into_iter().count()
319 }
320
321 pub fn from_inclusive(range: ops::RangeInclusive<u64>) -> Option<Self> {
322 range.end().checked_add(1).map(|end| Self {
323 start: *range.start(),
324 end,
325 })
326 }
327}
328
329impl From<Range<u64>> for IdxRange {
330 fn from(Range { start, end }: Range<u64>) -> Self {
331 Self { start, end }
332 }
333}
334
335impl IntoIterator for IdxRange {
336 type Item = u64;
337 type IntoIter = ops::Range<u64>;
338
339 fn into_iter(self) -> Self::IntoIter {
340 ops::Range {
341 start: self.start,
342 end: self.end,
343 }
344 }
345}
346
347#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Encodable, Decodable)]
349pub struct OutPointRange {
350 pub txid: TransactionId,
351 idx_range: IdxRange,
352}
353
354impl OutPointRange {
355 pub fn new(txid: TransactionId, idx_range: IdxRange) -> Self {
356 Self { txid, idx_range }
357 }
358
359 pub fn new_single(txid: TransactionId, idx: u64) -> Option<Self> {
360 IdxRange::new_single(idx).map(|idx_range| Self { txid, idx_range })
361 }
362
363 pub fn start_idx(self) -> u64 {
364 self.idx_range.start()
365 }
366
367 pub fn out_idx_iter(self) -> impl Iterator<Item = u64> {
368 self.idx_range.into_iter()
369 }
370
371 pub fn count(self) -> usize {
372 self.idx_range.count()
373 }
374
375 pub fn txid(&self) -> TransactionId {
376 self.txid
377 }
378}
379
380impl IntoIterator for OutPointRange {
381 type Item = OutPoint;
382 type IntoIter = OutPointRangeIter;
383
384 fn into_iter(self) -> Self::IntoIter {
385 OutPointRangeIter {
386 txid: self.txid,
387 inner: self.idx_range.into_iter(),
388 }
389 }
390}
391
392pub struct OutPointRangeIter {
393 txid: TransactionId,
394 inner: ops::Range<u64>,
395}
396
397impl Iterator for OutPointRangeIter {
398 type Item = OutPoint;
399
400 fn next(&mut self) -> Option<Self::Item> {
401 self.inner.next().map(|idx| OutPoint {
402 txid: self.txid,
403 out_idx: idx,
404 })
405 }
406}
407
408impl Encodable for TransactionId {
409 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
410 let bytes = &self[..];
411 writer.write_all(bytes)?;
412 Ok(())
413 }
414}
415
416impl Decodable for TransactionId {
417 fn consensus_decode_partial<D: std::io::Read>(
418 d: &mut D,
419 _modules: &ModuleDecoderRegistry,
420 ) -> Result<Self, DecodeError> {
421 let mut bytes = [0u8; 32];
422 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
423 Ok(Self::from_byte_array(bytes))
424 }
425}
426
427#[derive(
428 Copy,
429 Clone,
430 Debug,
431 PartialEq,
432 Ord,
433 PartialOrd,
434 Eq,
435 Hash,
436 Serialize,
437 Deserialize,
438 Encodable,
439 Decodable,
440)]
441pub struct Feerate {
442 pub sats_per_kvb: u64,
443}
444
445impl fmt::Display for Feerate {
446 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447 f.write_fmt(format_args!("{}sat/kvb", self.sats_per_kvb))
448 }
449}
450
451impl Feerate {
452 pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount {
453 let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000;
454 bitcoin::Amount::from_sat(sats)
455 }
456}
457
458const WITNESS_SCALE_FACTOR: u64 = bitcoin::constants::WITNESS_SCALE_FACTOR as u64;
459
460pub fn weight_to_vbytes(weight: u64) -> u64 {
465 weight.div_ceil(WITNESS_SCALE_FACTOR)
466}
467
468#[derive(Debug, Error)]
469pub enum CoreError {
470 #[error("Mismatching outcome variant: expected {0}, got {1}")]
471 MismatchingVariant(&'static str, &'static str),
472}
473
474pub fn encode_bolt11_invoice_features_without_length(features: &Bolt11InvoiceFeatures) -> Vec<u8> {
480 let mut feature_bytes = vec![];
481 for f in features.le_flags().iter().rev() {
482 f.write(&mut feature_bytes)
483 .expect("Writing to byte vec can't fail");
484 }
485 feature_bytes
486}
487
488pub fn format_hex(data: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
493 let prec = f.precision().unwrap_or(2 * data.len());
494 let width = f.width().unwrap_or(2 * data.len());
495 for _ in (2 * data.len())..width {
496 f.write_str("0")?;
497 }
498 for ch in data.iter().take(prec / 2) {
499 write!(f, "{:02x}", *ch)?;
500 }
501 if prec < 2 * data.len() && prec % 2 == 1 {
502 write!(f, "{:x}", data[prec / 2] / 16)?;
503 }
504 Ok(())
505}
506
507pub fn get_network_for_address(address: &Address<NetworkUnchecked>) -> Network {
520 if address.is_valid_for_network(Network::Bitcoin) {
521 Network::Bitcoin
522 } else if address.is_valid_for_network(Network::Testnet) {
523 Network::Testnet
524 } else if address.is_valid_for_network(Network::Regtest) {
525 Network::Regtest
526 } else {
527 panic!("Address is not valid for any network");
528 }
529}
530
531pub fn default_esplora_server(network: Network, port: Option<String>) -> BitcoinRpcConfig {
533 BitcoinRpcConfig {
534 kind: "esplora".to_string(),
535 url: match network {
536 Network::Bitcoin => SafeUrl::parse("https://mempool.space/api/"),
537 Network::Testnet => SafeUrl::parse("https://mempool.space/testnet/api/"),
538 Network::Testnet4 => SafeUrl::parse("https://mempool.space/testnet4/api/"),
539 Network::Signet => SafeUrl::parse("https://mutinynet.com/api/"),
540 Network::Regtest => SafeUrl::parse(&format!(
541 "http://127.0.0.1:{}/",
542 port.unwrap_or_else(|| String::from("50002"))
543 )),
544 _ => panic!("Failed to parse default esplora server"),
545 }
546 .expect("Failed to parse default esplora server"),
547 }
548}
549
550#[cfg(test)]
551mod tests;