lib.rs
1 //! Fedimint Core library 2 //! 3 //! `fedimint-core` contains are commonly used types, utilities and primitives, 4 //! shared between both client and server code. 5 //! 6 //! Things that are server-side only typically live in `fedimint-server`, and 7 //! client-side only in `fedimint-client`. 8 //! 9 //! ### Wasm support 10 //! 11 //! All code in `fedimint-core` needs to compile on Wasm, and `fedimint-core` 12 //! includes helpers and wrappers around non-wasm-safe utitlies. 13 //! 14 //! In particular: 15 //! 16 //! * [`fedimint_core::task`] for task spawning and control 17 //! * [`fedimint_core::time`] for time-related operations 18 19 #![allow(where_clauses_object_safety)] // https://github.com/dtolnay/async-trait/issues/228 20 extern crate self as fedimint_core; 21 22 use std::collections::{BTreeMap, BTreeSet}; 23 use std::fmt::Debug; 24 use std::io::Error; 25 use std::num::ParseIntError; 26 use std::str::FromStr; 27 28 /// Mostly re-exported for [`Decodable`] macros. 29 pub use anyhow; 30 use anyhow::bail; 31 use bitcoin::Denomination; 32 use bitcoin_hashes::hash_newtype; 33 use bitcoin_hashes::sha256::Hash as Sha256; 34 pub use bitcoin_hashes::Hash as BitcoinHash; 35 use fedimint_core::config::PeerUrl; 36 pub use macro_rules_attribute::apply; 37 pub use module::ServerModule; 38 pub use secp256k1; 39 use serde::{Deserialize, Serialize}; 40 use thiserror::Error; 41 pub use tiered::Tiered; 42 pub use tiered_multi::*; 43 44 pub use crate::core::server; 45 use crate::encoding::{Decodable, DecodeError, Encodable}; 46 use crate::module::registry::ModuleDecoderRegistry; 47 48 /// Admin (guardian) client types 49 pub mod admin_client; 50 /// Federation-stored client backups 51 pub mod backup; 52 /// Gradual bitcoin dependency migration helpers 53 pub mod bitcoin_migration; 54 /// Legacy serde encoding for bls12_381 55 pub mod bls12_381_serde; 56 /// Federation configuration 57 pub mod config; 58 /// Fundamental types 59 pub mod core; 60 /// Database handling 61 pub mod db; 62 /// Consensus encoding 63 pub mod encoding; 64 pub mod endpoint_constants; 65 /// Common environment variables 66 pub mod envs; 67 pub mod epoch; 68 /// Formatting helpers 69 pub mod fmt_utils; 70 /// Hex encoding helpers 71 pub mod hex; 72 /// Federation invite code 73 pub mod invite_code; 74 /// Common macros 75 #[macro_use] 76 pub mod macros; 77 /// Extenable module sysystem 78 pub mod module; 79 /// Peer networking 80 pub mod net; 81 /// Runtime (wasm32 vs native) differences handling 82 pub mod runtime; 83 /// Task handling, including wasm safe logic 84 pub mod task; 85 /// Types handling per-denomination values 86 pub mod tiered; 87 /// Types handling multiple per-denomination values 88 pub mod tiered_multi; 89 /// Time handling, wasm safe functionality 90 pub mod time; 91 /// Timing helpers 92 pub mod timing; 93 /// Fedimint transaction (inpus + outputs + signature) types 94 pub mod transaction; 95 /// Peg-in txo proofs 96 pub mod txoproof; 97 /// General purpose utilities 98 pub mod util; 99 100 /// Atomic BFT unit containing consensus items 101 pub mod session_outcome; 102 103 hash_newtype!( 104 /// A transaction id for peg-ins, peg-outs and reissuances 105 pub struct TransactionId(Sha256); 106 ); 107 108 #[derive( 109 Debug, 110 Clone, 111 Copy, 112 PartialEq, 113 Eq, 114 Hash, 115 PartialOrd, 116 Ord, 117 Serialize, 118 Deserialize, 119 Encodable, 120 Decodable, 121 )] 122 pub struct PeerId(u16); 123 124 impl FromStr for PeerId { 125 type Err = <u16 as FromStr>::Err; 126 127 fn from_str(s: &str) -> Result<Self, Self::Err> { 128 s.parse().map(PeerId) 129 } 130 } 131 132 pub const SATS_PER_BITCOIN: u64 = 100_000_000; 133 134 /// Represents an amount of BTC inside the system. The base denomination is 135 /// milli satoshi for now, this is also why the amount type from rust-bitcoin 136 /// isn't used instead. 137 #[derive( 138 Debug, 139 Clone, 140 Copy, 141 Eq, 142 PartialEq, 143 Ord, 144 PartialOrd, 145 Hash, 146 Deserialize, 147 Serialize, 148 Encodable, 149 Decodable, 150 )] 151 #[serde(transparent)] 152 pub struct Amount { 153 pub msats: u64, 154 } 155 156 impl Amount { 157 pub const ZERO: Self = Self { msats: 0 }; 158 159 pub const fn from_msats(msats: u64) -> Amount { 160 Amount { msats } 161 } 162 163 pub const fn from_sats(sats: u64) -> Amount { 164 Amount::from_msats(sats * 1000) 165 } 166 167 pub const fn from_bitcoins(bitcoins: u64) -> Amount { 168 Amount::from_sats(bitcoins * SATS_PER_BITCOIN) 169 } 170 171 pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> { 172 if let Denomination::MilliSatoshi = denom { 173 return Ok(Self::from_msats(s.parse()?)); 174 } 175 let btc_amt = bitcoin::amount::Amount::from_str_in(s, denom)?; 176 Ok(Self::from(btc_amt)) 177 } 178 179 pub fn saturating_sub(self, other: Amount) -> Self { 180 Amount { 181 msats: self.msats.saturating_sub(other.msats), 182 } 183 } 184 185 pub fn mul_u64(self, other: u64) -> Self { 186 Amount { 187 msats: self.msats * other, 188 } 189 } 190 191 // Makes sure we're dealing with a precision of satoshi or higher 192 pub fn ensure_sats_precision(&self) -> anyhow::Result<()> { 193 if self.msats % 1000 != 0 { 194 bail!("Amount is using a precision smaller than satoshi, cannot convert to satoshis"); 195 } 196 Ok(()) 197 } 198 199 pub fn try_into_sats(&self) -> anyhow::Result<u64> { 200 self.ensure_sats_precision()?; 201 Ok(self.msats / 1000) 202 } 203 204 pub const fn sats_round_down(&self) -> u64 { 205 self.msats / 1000 206 } 207 208 pub fn sats_f64(&self) -> f64 { 209 self.msats as f64 / 1000.0 210 } 211 212 pub fn checked_sub(self, other: Amount) -> Option<Self> { 213 Some(Self { 214 msats: self.msats.checked_sub(other.msats)?, 215 }) 216 } 217 } 218 219 /// Shorthand for [`Amount::from_msats`] 220 /// 221 /// Useful only for tests, but it's so common that it makes sense to have 222 /// it in the main `fedimint-api` crate. 223 pub fn msats(msats: u64) -> Amount { 224 Amount::from_msats(msats) 225 } 226 227 /// Shorthand for [`Amount::from_sats`] 228 pub fn sats(amount: u64) -> Amount { 229 Amount::from_sats(amount) 230 } 231 232 pub mod amount { 233 pub mod serde { 234 pub mod as_msat { 235 //! Serialize and deserialize [`Amount`](crate::Amount) as integers 236 //! denominated in milli-satoshi. Use with 237 //! `#[serde(with = "amount::serde::as_msat")]`. 238 239 use serde::{Deserialize, Deserializer, Serialize, Serializer}; 240 241 pub fn serialize<S: Serializer>(a: &crate::Amount, s: S) -> Result<S::Ok, S::Error> { 242 u64::serialize(&a.msats, s) 243 } 244 245 pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<crate::Amount, D::Error> { 246 Ok(crate::Amount::from_msats(u64::deserialize(d)?)) 247 } 248 } 249 } 250 } 251 252 /// Amount of bitcoin to send, or "all" to send all available funds 253 #[derive(Debug, Eq, PartialEq, Copy, Hash, Clone, Serialize, Deserialize)] 254 #[serde(rename_all = "snake_case")] 255 pub enum BitcoinAmountOrAll { 256 All, 257 #[serde(untagged)] 258 Amount(#[serde(with = "bitcoin::amount::serde::as_sat")] bitcoin::Amount), 259 } 260 261 impl FromStr for BitcoinAmountOrAll { 262 type Err = anyhow::Error; 263 264 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { 265 if s == "all" { 266 Ok(BitcoinAmountOrAll::All) 267 } else { 268 let amount = crate::Amount::from_str(s)?; 269 Ok(BitcoinAmountOrAll::Amount(amount.try_into()?)) 270 } 271 } 272 } 273 274 /// `OutPoint` represents a globally unique output in a transaction 275 /// 276 /// Hence, a transaction ID and the output index is required. 277 #[derive( 278 Debug, 279 Clone, 280 Copy, 281 Eq, 282 PartialEq, 283 PartialOrd, 284 Ord, 285 Hash, 286 Deserialize, 287 Serialize, 288 Encodable, 289 Decodable, 290 )] 291 pub struct OutPoint { 292 /// The referenced transaction ID 293 pub txid: TransactionId, 294 /// As a transaction may have multiple outputs, this refers to the index of 295 /// the output in a transaction 296 pub out_idx: u64, 297 } 298 299 #[derive(Error, Debug)] 300 pub enum ParseAmountError { 301 #[error("Error parsing string as integer: {0}")] 302 NotANumber(#[from] ParseIntError), 303 #[error("Error parsing string as a bitcoin amount: {0}")] 304 WrongBitcoinAmount(#[from] bitcoin::amount::ParseAmountError), 305 } 306 307 impl<T> NumPeersExt for BTreeMap<PeerId, T> { 308 fn total(&self) -> usize { 309 self.len() 310 } 311 } 312 313 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 314 pub struct NumPeers(usize); 315 316 impl From<usize> for NumPeers { 317 fn from(value: usize) -> Self { 318 Self(value) 319 } 320 } 321 impl NumPeers { 322 pub fn as_usize(&self) -> usize { 323 self.0 324 } 325 } 326 327 impl NumPeersExt for NumPeers { 328 fn total(&self) -> usize { 329 self.0 330 } 331 } 332 333 impl NumPeersExt for &[PeerId] { 334 fn total(&self) -> usize { 335 self.len() 336 } 337 } 338 339 impl NumPeersExt for Vec<PeerId> { 340 fn total(&self) -> usize { 341 self.len() 342 } 343 } 344 345 impl NumPeersExt for Vec<PeerUrl> { 346 fn total(&self) -> usize { 347 self.len() 348 } 349 } 350 351 impl NumPeersExt for BTreeSet<PeerId> { 352 fn total(&self) -> usize { 353 self.len() 354 } 355 } 356 357 /// for consensus-related calculations given the number of peers 358 pub trait NumPeersExt { 359 fn total(&self) -> usize; 360 361 /// number of peers that can be evil without disrupting the federation 362 fn max_evil(&self) -> usize { 363 (self.total() - 1) / 3 364 } 365 366 /// number of peers to select such that one is honest (under our 367 /// assumptions) 368 fn one_honest(&self) -> usize { 369 self.max_evil() + 1 370 } 371 372 /// Degree of a underlying polynomial to require `threshold` signatures 373 fn degree(&self) -> usize { 374 self.threshold() - 1 375 } 376 377 /// number of peers required for a signature 378 fn threshold(&self) -> usize { 379 self.total() - self.max_evil() 380 } 381 } 382 383 impl PeerId { 384 pub fn to_usize(self) -> usize { 385 self.0 as usize 386 } 387 } 388 389 impl std::fmt::Display for PeerId { 390 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 391 write!(f, "{}", self.0) 392 } 393 } 394 395 impl From<u16> for PeerId { 396 fn from(id: u16) -> Self { 397 Self(id) 398 } 399 } 400 401 impl From<PeerId> for u16 { 402 fn from(peer: PeerId) -> u16 { 403 peer.0 404 } 405 } 406 407 impl std::fmt::Display for Amount { 408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 409 write!(f, "{} msat", self.msats) 410 } 411 } 412 413 impl std::fmt::Display for OutPoint { 414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 415 write!(f, "{}:{}", self.txid, self.out_idx) 416 } 417 } 418 419 impl std::ops::Rem for Amount { 420 type Output = Amount; 421 422 fn rem(self, rhs: Self) -> Self::Output { 423 Amount { 424 msats: self.msats % rhs.msats, 425 } 426 } 427 } 428 429 impl std::ops::RemAssign for Amount { 430 fn rem_assign(&mut self, rhs: Self) { 431 self.msats %= rhs.msats; 432 } 433 } 434 435 impl std::ops::Div for Amount { 436 type Output = u64; 437 438 fn div(self, rhs: Self) -> Self::Output { 439 self.msats / rhs.msats 440 } 441 } 442 443 impl std::ops::SubAssign for Amount { 444 fn sub_assign(&mut self, rhs: Self) { 445 self.msats -= rhs.msats 446 } 447 } 448 449 impl std::ops::Mul<u64> for Amount { 450 type Output = Amount; 451 452 fn mul(self, rhs: u64) -> Self::Output { 453 Amount { 454 msats: self.msats * rhs, 455 } 456 } 457 } 458 459 impl std::ops::Mul<Amount> for u64 { 460 type Output = Amount; 461 462 fn mul(self, rhs: Amount) -> Self::Output { 463 Amount { 464 msats: self * rhs.msats, 465 } 466 } 467 } 468 469 impl std::ops::Add for Amount { 470 type Output = Amount; 471 472 fn add(self, rhs: Self) -> Self::Output { 473 Amount { 474 msats: self.msats + rhs.msats, 475 } 476 } 477 } 478 479 impl std::ops::AddAssign for Amount { 480 fn add_assign(&mut self, rhs: Self) { 481 *self = *self + rhs; 482 } 483 } 484 485 impl std::iter::Sum for Amount { 486 fn sum<I: Iterator<Item = Amount>>(iter: I) -> Self { 487 Amount { 488 msats: iter.map(|amt| amt.msats).sum::<u64>(), 489 } 490 } 491 } 492 493 impl std::ops::Sub for Amount { 494 type Output = Amount; 495 496 fn sub(self, rhs: Self) -> Self::Output { 497 Amount { 498 msats: self.msats - rhs.msats, 499 } 500 } 501 } 502 503 impl FromStr for Amount { 504 type Err = ParseAmountError; 505 506 fn from_str(s: &str) -> Result<Self, Self::Err> { 507 if let Some(i) = s.find(char::is_alphabetic) { 508 let (amt, denom) = s.split_at(i); 509 Amount::from_str_in(amt.trim(), denom.trim().parse()?) 510 } else { 511 // default to millisatoshi 512 Amount::from_str_in(s.trim(), bitcoin::Denomination::MilliSatoshi) 513 } 514 } 515 } 516 517 impl From<bitcoin::Amount> for Amount { 518 fn from(amt: bitcoin::Amount) -> Self { 519 assert!(amt.to_sat() <= 2_100_000_000_000_000); 520 Amount { 521 msats: amt.to_sat() * 1000, 522 } 523 } 524 } 525 526 impl TryFrom<Amount> for bitcoin::Amount { 527 type Error = anyhow::Error; 528 529 fn try_from(value: Amount) -> anyhow::Result<Self> { 530 value.try_into_sats().map(bitcoin::Amount::from_sat) 531 } 532 } 533 534 impl Encodable for TransactionId { 535 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> { 536 let bytes = &self[..]; 537 writer.write_all(bytes)?; 538 Ok(bytes.len()) 539 } 540 } 541 542 impl Decodable for TransactionId { 543 fn consensus_decode<D: std::io::Read>( 544 d: &mut D, 545 _modules: &ModuleDecoderRegistry, 546 ) -> Result<Self, DecodeError> { 547 let mut bytes = [0u8; 32]; 548 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?; 549 Ok(TransactionId::from_byte_array(bytes)) 550 } 551 } 552 553 #[derive( 554 Copy, 555 Clone, 556 Debug, 557 PartialEq, 558 Ord, 559 PartialOrd, 560 Eq, 561 Hash, 562 Serialize, 563 Deserialize, 564 Encodable, 565 Decodable, 566 )] 567 pub struct Feerate { 568 pub sats_per_kvb: u64, 569 } 570 571 impl Feerate { 572 pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount { 573 let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000; 574 bitcoin::Amount::from_sat(sats) 575 } 576 } 577 578 const WITNESS_SCALE_FACTOR: u64 = bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR as u64; 579 580 /// Converts weight to virtual bytes, defined in [BIP-141] as weight / 4 581 /// (rounded up to the next integer). 582 /// 583 /// [BIP-141]: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations 584 pub fn weight_to_vbytes(weight: u64) -> u64 { 585 (weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR 586 } 587 588 #[derive(Debug, Error)] 589 pub enum CoreError { 590 #[error("Mismatching outcome variant: expected {0}, got {1}")] 591 MismatchingVariant(&'static str, &'static str), 592 } 593 594 #[cfg(test)] 595 mod tests { 596 use super::*; 597 598 #[test] 599 fn amount_multiplication_by_scalar() { 600 assert_eq!(Amount::from_msats(1000) * 123, Amount::from_msats(123_000)); 601 } 602 603 #[test] 604 fn scalar_multiplication_by_amount() { 605 assert_eq!(123 * Amount::from_msats(1000), Amount::from_msats(123_000)); 606 } 607 608 #[test] 609 fn converts_weight_to_vbytes() { 610 assert_eq!(1, weight_to_vbytes(4)); 611 assert_eq!(2, weight_to_vbytes(5)); 612 } 613 614 #[test] 615 fn calculate_fee() { 616 let feerate = Feerate { sats_per_kvb: 1000 }; 617 assert_eq!(bitcoin::Amount::from_sat(25), feerate.calculate_fee(100)); 618 assert_eq!(bitcoin::Amount::from_sat(26), feerate.calculate_fee(101)); 619 } 620 621 #[test] 622 fn test_amount_parsing() { 623 // msats 624 assert_eq!(Amount::from_msats(123), Amount::from_str("123").unwrap()); 625 assert_eq!( 626 Amount::from_msats(123), 627 Amount::from_str("123msat").unwrap() 628 ); 629 assert_eq!( 630 Amount::from_msats(123), 631 Amount::from_str("123 msat").unwrap() 632 ); 633 assert_eq!( 634 Amount::from_msats(123), 635 Amount::from_str("123 msats").unwrap() 636 ); 637 // sats 638 assert_eq!(Amount::from_sats(123), Amount::from_str("123sat").unwrap()); 639 assert_eq!(Amount::from_sats(123), Amount::from_str("123 sat").unwrap()); 640 assert_eq!( 641 Amount::from_sats(123), 642 Amount::from_str("123satoshi").unwrap() 643 ); 644 assert_eq!( 645 Amount::from_sats(123), 646 Amount::from_str("123satoshis").unwrap() 647 ); 648 // btc 649 assert_eq!( 650 Amount::from_bitcoins(123), 651 Amount::from_str("123btc").unwrap() 652 ); 653 assert_eq!( 654 Amount::from_sats(12_345_600_000), 655 Amount::from_str("123.456btc").unwrap() 656 ); 657 } 658 659 #[test] 660 fn test_deserialize_amount_or_all() { 661 let all: BitcoinAmountOrAll = serde_json::from_str("\"all\"").unwrap(); 662 assert_eq!(all, BitcoinAmountOrAll::All); 663 664 let all: BitcoinAmountOrAll = serde_json::from_str("12345").unwrap(); 665 assert_eq!( 666 all, 667 BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(12345)) 668 ); 669 } 670 }