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, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
156pub struct ChainId(pub bitcoin::BlockHash);
157
158impl ChainId {
159 pub fn new(block_hash: bitcoin::BlockHash) -> Self {
161 Self(block_hash)
162 }
163
164 pub fn block_hash(&self) -> bitcoin::BlockHash {
166 self.0
167 }
168}
169
170impl From<bitcoin::BlockHash> for ChainId {
171 fn from(block_hash: bitcoin::BlockHash) -> Self {
172 Self(block_hash)
173 }
174}
175
176impl From<ChainId> for bitcoin::BlockHash {
177 fn from(chain_id: ChainId) -> Self {
178 chain_id.0
179 }
180}
181
182impl std::fmt::Display for ChainId {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 write!(f, "{}", self.0)
185 }
186}
187
188impl FromStr for ChainId {
189 type Err = bitcoin::hashes::hex::HexToArrayError;
190
191 fn from_str(s: &str) -> Result<Self, Self::Err> {
192 bitcoin::BlockHash::from_str(s).map(Self)
193 }
194}
195
196impl Serialize for ChainId {
197 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
198 where
199 S: Serializer,
200 {
201 self.0.serialize(serializer)
202 }
203}
204
205impl<'de> Deserialize<'de> for ChainId {
206 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
207 where
208 D: Deserializer<'de>,
209 {
210 bitcoin::BlockHash::deserialize(deserializer).map(Self)
211 }
212}
213
214#[derive(Debug, Eq, PartialEq, Copy, Hash, Clone)]
216pub enum BitcoinAmountOrAll {
217 All,
218 Amount(bitcoin::Amount),
219}
220
221impl std::fmt::Display for BitcoinAmountOrAll {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 match self {
224 Self::All => write!(f, "all"),
225 Self::Amount(amount) => write!(f, "{amount}"),
226 }
227 }
228}
229
230impl FromStr for BitcoinAmountOrAll {
231 type Err = anyhow::Error;
232
233 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
234 if s.eq_ignore_ascii_case("all") {
235 Ok(Self::All)
236 } else {
237 let amount = Amount::from_str(s)?;
238 Ok(Self::Amount(amount.try_into()?))
239 }
240 }
241}
242
243impl<'de> Deserialize<'de> for BitcoinAmountOrAll {
245 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
246 where
247 D: Deserializer<'de>,
248 {
249 use serde::de::Error;
250
251 struct Visitor;
252
253 impl serde::de::Visitor<'_> for Visitor {
254 type Value = BitcoinAmountOrAll;
255
256 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
257 write!(f, "a bitcoin amount as number or 'all'")
258 }
259
260 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
261 where
262 E: Error,
263 {
264 if v.eq_ignore_ascii_case("all") {
265 Ok(BitcoinAmountOrAll::All)
266 } else {
267 let sat: u64 = v.parse().map_err(E::custom)?;
268 Ok(BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(sat)))
269 }
270 }
271
272 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
273 where
274 E: Error,
275 {
276 Ok(BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(v)))
277 }
278
279 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
280 where
281 E: Error,
282 {
283 if v < 0 {
284 return Err(E::custom("amount cannot be negative"));
285 }
286 Ok(BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(
287 v as u64,
288 )))
289 }
290 }
291
292 deserializer.deserialize_any(Visitor)
293 }
294}
295
296impl Serialize for BitcoinAmountOrAll {
297 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
298 where
299 S: Serializer,
300 {
301 match self {
302 Self::All => serializer.serialize_str("all"),
303 Self::Amount(a) => serializer.serialize_u64(a.to_sat()),
304 }
305 }
306}
307
308#[derive(
312 Debug,
313 Clone,
314 Copy,
315 Eq,
316 PartialEq,
317 PartialOrd,
318 Ord,
319 Hash,
320 Deserialize,
321 Serialize,
322 Encodable,
323 Decodable,
324)]
325pub struct InPoint {
326 pub txid: TransactionId,
328 pub in_idx: u64,
331}
332
333impl std::fmt::Display for InPoint {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 write!(f, "{}:{}", self.txid, self.in_idx)
336 }
337}
338
339#[derive(
343 Debug,
344 Clone,
345 Copy,
346 Eq,
347 PartialEq,
348 PartialOrd,
349 Ord,
350 Hash,
351 Deserialize,
352 Serialize,
353 Encodable,
354 Decodable,
355)]
356pub struct OutPoint {
357 pub txid: TransactionId,
359 pub out_idx: u64,
362}
363
364impl std::fmt::Display for OutPoint {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 write!(f, "{}:{}", self.txid, self.out_idx)
367 }
368}
369
370#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Encodable, Decodable)]
372pub struct IdxRange {
373 start: u64,
374 end: u64,
375}
376
377impl IdxRange {
378 pub fn new_single(start: u64) -> Option<Self> {
379 start.checked_add(1).map(|end| Self { start, end })
380 }
381
382 pub fn start(self) -> u64 {
383 self.start
384 }
385
386 pub fn count(self) -> usize {
387 self.into_iter().count()
388 }
389
390 pub fn checked_count(self) -> Option<usize> {
398 usize::try_from(self.end.checked_sub(self.start)?).ok()
399 }
400
401 pub fn from_inclusive(range: ops::RangeInclusive<u64>) -> Option<Self> {
402 range.end().checked_add(1).map(|end| Self {
403 start: *range.start(),
404 end,
405 })
406 }
407}
408
409impl From<Range<u64>> for IdxRange {
410 fn from(Range { start, end }: Range<u64>) -> Self {
411 Self { start, end }
412 }
413}
414
415impl IntoIterator for IdxRange {
416 type Item = u64;
417 type IntoIter = ops::Range<u64>;
418
419 fn into_iter(self) -> Self::IntoIter {
420 ops::Range {
421 start: self.start,
422 end: self.end,
423 }
424 }
425}
426
427#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Encodable, Decodable)]
429pub struct OutPointRange {
430 pub txid: TransactionId,
431 idx_range: IdxRange,
432}
433
434impl OutPointRange {
435 pub fn new(txid: TransactionId, idx_range: IdxRange) -> Self {
436 Self { txid, idx_range }
437 }
438
439 pub fn new_single(txid: TransactionId, idx: u64) -> Option<Self> {
440 IdxRange::new_single(idx).map(|idx_range| Self { txid, idx_range })
441 }
442
443 pub fn start_idx(self) -> u64 {
444 self.idx_range.start()
445 }
446
447 pub fn out_idx_iter(self) -> impl Iterator<Item = u64> {
448 self.idx_range.into_iter()
449 }
450
451 pub fn count(self) -> usize {
452 self.idx_range.count()
453 }
454
455 pub fn checked_count(self) -> Option<usize> {
457 self.idx_range.checked_count()
458 }
459
460 pub fn start_out_point(self) -> OutPoint {
461 OutPoint {
462 txid: self.txid,
463 out_idx: self.idx_range.start(),
464 }
465 }
466
467 pub fn end_out_point(self) -> OutPoint {
468 OutPoint {
469 txid: self.txid,
470 out_idx: self.idx_range.end,
471 }
472 }
473
474 pub fn txid(&self) -> TransactionId {
475 self.txid
476 }
477}
478
479impl IntoIterator for OutPointRange {
480 type Item = OutPoint;
481 type IntoIter = OutPointRangeIter;
482
483 fn into_iter(self) -> Self::IntoIter {
484 OutPointRangeIter {
485 txid: self.txid,
486 inner: self.idx_range.into_iter(),
487 }
488 }
489}
490
491pub struct OutPointRangeIter {
492 txid: TransactionId,
493 inner: ops::Range<u64>,
494}
495
496impl Iterator for OutPointRangeIter {
497 type Item = OutPoint;
498
499 fn next(&mut self) -> Option<Self::Item> {
500 self.inner.next().map(|idx| OutPoint {
501 txid: self.txid,
502 out_idx: idx,
503 })
504 }
505}
506
507impl Encodable for TransactionId {
508 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
509 let bytes = &self[..];
510 writer.write_all(bytes)?;
511 Ok(())
512 }
513}
514
515impl Decodable for TransactionId {
516 fn consensus_decode_partial<D: std::io::Read>(
517 d: &mut D,
518 _modules: &ModuleDecoderRegistry,
519 ) -> Result<Self, DecodeError> {
520 let mut bytes = [0u8; 32];
521 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
522 Ok(Self::from_byte_array(bytes))
523 }
524}
525
526#[derive(
527 Copy,
528 Clone,
529 Debug,
530 PartialEq,
531 Ord,
532 PartialOrd,
533 Eq,
534 Hash,
535 Serialize,
536 Deserialize,
537 Encodable,
538 Decodable,
539)]
540pub struct Feerate {
541 pub sats_per_kvb: u64,
542}
543
544impl fmt::Display for Feerate {
545 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546 f.write_fmt(format_args!("{}sat/kvb", self.sats_per_kvb))
547 }
548}
549
550impl Feerate {
551 pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount {
552 let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000;
553 bitcoin::Amount::from_sat(sats)
554 }
555
556 pub fn wrapping_calculate_fee(&self, weight: u64) -> bitcoin::Amount {
564 let sats = weight_to_vbytes(weight).wrapping_mul(self.sats_per_kvb) / 1000;
565 bitcoin::Amount::from_sat(sats)
566 }
567
568 pub fn checked_calculate_fee(&self, weight: u64) -> Option<bitcoin::Amount> {
575 let sats = weight_to_vbytes(weight).checked_mul(self.sats_per_kvb)? / 1000;
576 (sats <= bitcoin::Amount::MAX_MONEY.to_sat()).then(|| bitcoin::Amount::from_sat(sats))
577 }
578}
579
580const WITNESS_SCALE_FACTOR: u64 = bitcoin::constants::WITNESS_SCALE_FACTOR as u64;
581
582pub fn weight_to_vbytes(weight: u64) -> u64 {
587 weight.div_ceil(WITNESS_SCALE_FACTOR)
588}
589
590#[derive(Debug, Error)]
591pub enum CoreError {
592 #[error("Mismatching outcome variant: expected {0}, got {1}")]
593 MismatchingVariant(&'static str, &'static str),
594}
595
596pub fn encode_bolt11_invoice_features_without_length(features: &Bolt11InvoiceFeatures) -> Vec<u8> {
602 let mut feature_bytes = vec![];
603 for f in features.le_flags().iter().rev() {
604 f.write(&mut feature_bytes)
605 .expect("Writing to byte vec can't fail");
606 }
607 feature_bytes
608}
609
610pub fn format_hex(data: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
615 let prec = f.precision().unwrap_or(2 * data.len());
616 let width = f.width().unwrap_or(2 * data.len());
617 for _ in (2 * data.len())..width {
618 f.write_str("0")?;
619 }
620 for ch in data.iter().take(prec / 2) {
621 write!(f, "{:02x}", *ch)?;
622 }
623 if prec < 2 * data.len() && prec % 2 == 1 {
624 write!(f, "{:x}", data[prec / 2] / 16)?;
625 }
626 Ok(())
627}
628
629pub fn get_network_for_address(address: &Address<NetworkUnchecked>) -> Network {
642 if address.is_valid_for_network(Network::Bitcoin) {
643 Network::Bitcoin
644 } else if address.is_valid_for_network(Network::Testnet) {
645 Network::Testnet
646 } else if address.is_valid_for_network(Network::Regtest) {
647 Network::Regtest
648 } else {
649 panic!("Address is not valid for any network");
650 }
651}
652
653pub fn default_esplora_server(network: Network, port: Option<String>) -> BitcoinRpcConfig {
655 BitcoinRpcConfig {
656 kind: "esplora".to_string(),
657 url: match network {
658 Network::Bitcoin => SafeUrl::parse("https://mempool.space/api/"),
659 Network::Testnet => SafeUrl::parse("https://mempool.space/testnet/api/"),
660 Network::Testnet4 => SafeUrl::parse("https://mempool.space/testnet4/api/"),
661 Network::Signet => SafeUrl::parse("https://mutinynet.com/api/"),
662 Network::Regtest => SafeUrl::parse(&format!(
663 "http://127.0.0.1:{}/",
664 port.unwrap_or_else(|| String::from("50002"))
665 )),
666 }
667 .expect("Failed to parse default esplora server"),
668 }
669}
670
671#[cfg(test)]
672mod tests;