#![allow(where_clauses_object_safety)] extern crate self as fedimint_core;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Debug;
use std::io::Error;
use std::num::ParseIntError;
use std::str::FromStr;
use anyhow::bail;
use bitcoin::Denomination;
use bitcoin_hashes::hash_newtype;
use bitcoin_hashes::sha256::Hash as Sha256;
pub use bitcoin_hashes::Hash as BitcoinHash;
use fedimint_core::config::PeerUrl;
pub use macro_rules_attribute::apply;
pub use module::ServerModule;
use serde::{Deserialize, Serialize};
use thiserror::Error;
pub use tiered::Tiered;
pub use tiered_multi::*;
pub use crate::core::server;
use crate::encoding::{Decodable, DecodeError, Encodable};
use crate::module::registry::ModuleDecoderRegistry;
#[cfg(not(target_family = "wasm"))]
pub mod admin_client;
pub mod api;
pub mod backup;
pub mod bitcoinrpc;
pub mod cancellable;
pub mod config;
pub mod core;
pub mod db;
pub mod encoding;
pub mod endpoint_constants;
pub mod epoch;
pub mod fmt_utils;
pub mod hex;
#[macro_use]
pub mod macros;
pub mod module;
pub mod net;
pub mod query;
pub mod task;
pub mod tiered;
pub mod tiered_multi;
pub mod time;
pub mod timing;
pub mod transaction;
pub mod txoproof;
pub mod util;
pub mod session_outcome;
hash_newtype!(
TransactionId,
Sha256,
32,
doc = "A transaction id for peg-ins, peg-outs and reissuances"
);
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Serialize,
Deserialize,
Encodable,
Decodable,
)]
pub struct PeerId(u16);
impl FromStr for PeerId {
type Err = <u16 as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(PeerId)
}
}
pub const SATS_PER_BITCOIN: u64 = 100_000_000;
#[derive(
Debug,
Clone,
Copy,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
Deserialize,
Serialize,
Encodable,
Decodable,
)]
#[serde(transparent)]
pub struct Amount {
pub msats: u64,
}
impl Amount {
pub const ZERO: Self = Self { msats: 0 };
pub const fn from_msats(msats: u64) -> Amount {
Amount { msats }
}
pub const fn from_sats(sats: u64) -> Amount {
Amount::from_msats(sats * 1000)
}
pub const fn from_bitcoins(bitcoins: u64) -> Amount {
Amount::from_sats(bitcoins * SATS_PER_BITCOIN)
}
pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountError> {
if let Denomination::MilliSatoshi = denom {
return Ok(Self::from_msats(s.parse()?));
}
let btc_amt = bitcoin::util::amount::Amount::from_str_in(s, denom)?;
Ok(Self::from(btc_amt))
}
pub fn saturating_sub(self, other: Amount) -> Self {
Amount {
msats: self.msats.saturating_sub(other.msats),
}
}
pub fn ensure_sats_precision(&self) -> anyhow::Result<()> {
if self.msats % 1000 != 0 {
bail!("Amount is using a precision smaller than satoshi, cannot convert to satoshis");
}
Ok(())
}
pub fn try_into_sats(&self) -> anyhow::Result<u64> {
self.ensure_sats_precision()?;
Ok(self.msats / 1000)
}
pub const fn sats_round_down(&self) -> u64 {
self.msats / 1000
}
}
pub fn msats(msats: u64) -> Amount {
Amount::from_msats(msats)
}
pub fn sats(amount: u64) -> Amount {
Amount::from_sats(amount)
}
pub mod amount {
pub mod serde {
pub mod as_msat {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S: Serializer>(a: &crate::Amount, s: S) -> Result<S::Ok, S::Error> {
u64::serialize(&a.msats, s)
}
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<crate::Amount, D::Error> {
Ok(crate::Amount::from_msats(u64::deserialize(d)?))
}
}
}
}
#[derive(Debug, Eq, PartialEq, Copy, Hash, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BitcoinAmountOrAll {
All,
#[serde(untagged)]
Amount(#[serde(with = "bitcoin::util::amount::serde::as_sat")] bitcoin::Amount),
}
impl FromStr for BitcoinAmountOrAll {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if s == "all" {
Ok(BitcoinAmountOrAll::All)
} else {
let amount = crate::Amount::from_str(s)?;
Ok(BitcoinAmountOrAll::Amount(amount.try_into()?))
}
}
}
#[derive(
Debug,
Clone,
Copy,
Eq,
PartialEq,
PartialOrd,
Ord,
Hash,
Deserialize,
Serialize,
Encodable,
Decodable,
)]
pub struct OutPoint {
pub txid: TransactionId,
pub out_idx: u64,
}
#[derive(Error, Debug)]
pub enum ParseAmountError {
#[error("Error parsing string as integer: {0}")]
NotANumber(#[from] ParseIntError),
#[error("Error parsing string as a bitcoin amount: {0}")]
WrongBitcoinAmount(#[from] bitcoin::util::amount::ParseAmountError),
}
impl<T> NumPeers for BTreeMap<PeerId, T> {
fn total(&self) -> usize {
self.len()
}
}
impl NumPeers for &[PeerId] {
fn total(&self) -> usize {
self.len()
}
}
impl NumPeers for Vec<PeerId> {
fn total(&self) -> usize {
self.len()
}
}
impl NumPeers for Vec<PeerUrl> {
fn total(&self) -> usize {
self.len()
}
}
impl NumPeers for BTreeSet<PeerId> {
fn total(&self) -> usize {
self.len()
}
}
pub trait NumPeers {
fn total(&self) -> usize;
fn max_evil(&self) -> usize {
(self.total() - 1) / 3
}
fn one_honest(&self) -> usize {
self.max_evil() + 1
}
fn degree(&self) -> usize {
self.threshold() - 1
}
fn threshold(&self) -> usize {
self.total() - self.max_evil()
}
}
impl PeerId {
pub fn to_usize(self) -> usize {
self.0 as usize
}
}
impl std::fmt::Display for PeerId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<u16> for PeerId {
fn from(id: u16) -> Self {
Self(id)
}
}
impl From<PeerId> for u16 {
fn from(peer: PeerId) -> u16 {
peer.0
}
}
impl std::fmt::Display for Amount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} msat", self.msats)
}
}
impl std::fmt::Display for OutPoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.txid, self.out_idx)
}
}
impl std::ops::Rem for Amount {
type Output = Amount;
fn rem(self, rhs: Self) -> Self::Output {
Amount {
msats: self.msats % rhs.msats,
}
}
}
impl std::ops::RemAssign for Amount {
fn rem_assign(&mut self, rhs: Self) {
self.msats %= rhs.msats;
}
}
impl std::ops::Div for Amount {
type Output = u64;
fn div(self, rhs: Self) -> Self::Output {
self.msats / rhs.msats
}
}
impl std::ops::SubAssign for Amount {
fn sub_assign(&mut self, rhs: Self) {
self.msats -= rhs.msats
}
}
impl std::ops::Mul<u64> for Amount {
type Output = Amount;
fn mul(self, rhs: u64) -> Self::Output {
Amount {
msats: self.msats * rhs,
}
}
}
impl std::ops::Mul<Amount> for u64 {
type Output = Amount;
fn mul(self, rhs: Amount) -> Self::Output {
Amount {
msats: self * rhs.msats,
}
}
}
impl std::ops::Add for Amount {
type Output = Amount;
fn add(self, rhs: Self) -> Self::Output {
Amount {
msats: self.msats + rhs.msats,
}
}
}
impl std::ops::AddAssign for Amount {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl std::iter::Sum for Amount {
fn sum<I: Iterator<Item = Amount>>(iter: I) -> Self {
Amount {
msats: iter.map(|amt| amt.msats).sum::<u64>(),
}
}
}
impl std::ops::Sub for Amount {
type Output = Amount;
fn sub(self, rhs: Self) -> Self::Output {
Amount {
msats: self.msats - rhs.msats,
}
}
}
impl FromStr for Amount {
type Err = ParseAmountError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(i) = s.find(char::is_alphabetic) {
let (amt, denom) = s.split_at(i);
Amount::from_str_in(amt.trim(), denom.trim().parse()?)
} else {
Amount::from_str_in(s.trim(), bitcoin::Denomination::MilliSatoshi)
}
}
}
impl From<bitcoin::Amount> for Amount {
fn from(amt: bitcoin::Amount) -> Self {
assert!(amt.to_sat() <= 2_100_000_000_000_000);
Amount {
msats: amt.to_sat() * 1000,
}
}
}
impl TryFrom<Amount> for bitcoin::Amount {
type Error = anyhow::Error;
fn try_from(value: Amount) -> anyhow::Result<Self> {
value.try_into_sats().map(bitcoin::Amount::from_sat)
}
}
impl Encodable for TransactionId {
fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
let bytes = &self[..];
writer.write_all(bytes)?;
Ok(bytes.len())
}
}
impl Decodable for TransactionId {
fn consensus_decode<D: std::io::Read>(
d: &mut D,
_modules: &ModuleDecoderRegistry,
) -> Result<Self, DecodeError> {
let mut bytes = [0u8; 32];
d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
Ok(TransactionId::from_inner(bytes))
}
}
#[derive(
Copy,
Clone,
Debug,
PartialEq,
Ord,
PartialOrd,
Eq,
Hash,
Serialize,
Deserialize,
Encodable,
Decodable,
)]
pub struct Feerate {
pub sats_per_kvb: u64,
}
impl Feerate {
pub fn calculate_fee(&self, weight: u64) -> bitcoin::Amount {
let sats = weight_to_vbytes(weight) * self.sats_per_kvb / 1000;
bitcoin::Amount::from_sat(sats)
}
}
const WITNESS_SCALE_FACTOR: u64 = bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR as u64;
pub fn weight_to_vbytes(weight: u64) -> u64 {
(weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR
}
#[derive(Debug, Error)]
pub enum CoreError {
#[error("Mismatching outcome variant: expected {0}, got {1}")]
MismatchingVariant(&'static str, &'static str),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn amount_multiplication_by_scalar() {
assert_eq!(Amount::from_msats(1000) * 123, Amount::from_msats(123_000));
}
#[test]
fn scalar_multiplication_by_amount() {
assert_eq!(123 * Amount::from_msats(1000), Amount::from_msats(123_000));
}
#[test]
fn converts_weight_to_vbytes() {
assert_eq!(1, weight_to_vbytes(4));
assert_eq!(2, weight_to_vbytes(5));
}
#[test]
fn calculate_fee() {
let feerate = Feerate { sats_per_kvb: 1000 };
assert_eq!(bitcoin::Amount::from_sat(25), feerate.calculate_fee(100));
assert_eq!(bitcoin::Amount::from_sat(26), feerate.calculate_fee(101));
}
#[test]
fn test_amount_parsing() {
assert_eq!(Amount::from_msats(123), Amount::from_str("123").unwrap());
assert_eq!(
Amount::from_msats(123),
Amount::from_str("123msat").unwrap()
);
assert_eq!(
Amount::from_msats(123),
Amount::from_str("123 msat").unwrap()
);
assert_eq!(
Amount::from_msats(123),
Amount::from_str("123 msats").unwrap()
);
assert_eq!(Amount::from_sats(123), Amount::from_str("123sat").unwrap());
assert_eq!(Amount::from_sats(123), Amount::from_str("123 sat").unwrap());
assert_eq!(
Amount::from_sats(123),
Amount::from_str("123satoshi").unwrap()
);
assert_eq!(
Amount::from_sats(123),
Amount::from_str("123satoshis").unwrap()
);
assert_eq!(
Amount::from_bitcoins(123),
Amount::from_str("123btc").unwrap()
);
assert_eq!(
Amount::from_sats(12_345_600_000),
Amount::from_str("123.456btc").unwrap()
);
}
#[test]
fn test_deserialize_amount_or_all() {
let all: BitcoinAmountOrAll = serde_json::from_str("\"all\"").unwrap();
assert_eq!(all, BitcoinAmountOrAll::All);
let all: BitcoinAmountOrAll = serde_json::from_str("12345").unwrap();
assert_eq!(
all,
BitcoinAmountOrAll::Amount(bitcoin::Amount::from_sat(12345))
);
}
}