2021-03-03 13:22:05 -08:00
// Bitcoin Dev Kit
// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
2020-08-31 11:26:36 +02:00
//
2021-03-03 13:22:05 -08:00
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
2020-08-31 11:26:36 +02:00
//
2021-03-03 13:22:05 -08:00
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
2020-08-31 11:26:36 +02:00
2020-02-05 11:59:02 +01:00
use std ::convert ::AsRef ;
2021-07-16 15:14:20 +10:00
use std ::ops ::Sub ;
2020-02-05 11:59:02 +01:00
use bitcoin ::blockdata ::transaction ::{ OutPoint , Transaction , TxOut } ;
2021-02-08 15:40:56 +11:00
use bitcoin ::{ hash_types ::Txid , util ::psbt } ;
2020-02-05 11:59:02 +01:00
use serde ::{ Deserialize , Serialize } ;
2020-12-16 11:33:34 -08:00
/// Types of keychains
2020-08-10 10:49:34 +02:00
#[ derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash) ]
2020-12-14 17:14:24 +01:00
pub enum KeychainKind {
2020-12-04 16:13:15 +01:00
/// External
2020-02-05 11:59:02 +01:00
External = 0 ,
2020-12-04 16:13:15 +01:00
/// Internal, usually used for change outputs
2020-02-05 11:59:02 +01:00
Internal = 1 ,
}
2020-12-14 17:14:24 +01:00
impl KeychainKind {
2020-12-16 11:33:34 -08:00
/// Return [`KeychainKind`] as a byte
2020-02-05 11:59:02 +01:00
pub fn as_byte ( & self ) -> u8 {
match self {
2020-12-14 17:14:24 +01:00
KeychainKind ::External = > b 'e' ,
KeychainKind ::Internal = > b 'i' ,
2020-02-05 11:59:02 +01:00
}
}
}
2020-12-14 17:14:24 +01:00
impl AsRef < [ u8 ] > for KeychainKind {
2020-02-05 11:59:02 +01:00
fn as_ref ( & self ) -> & [ u8 ] {
match self {
2020-12-14 17:14:24 +01:00
KeychainKind ::External = > b " e " ,
KeychainKind ::Internal = > b " i " ,
2020-02-05 11:59:02 +01:00
}
}
}
2020-09-04 11:44:49 +02:00
/// Fee rate
2020-08-31 10:49:44 +02:00
#[ derive(Debug, Copy, Clone, PartialEq, PartialOrd) ]
// Internally stored as satoshi/vbyte
pub struct FeeRate ( f32 ) ;
impl FeeRate {
2022-07-30 17:51:43 +02:00
/// Create a new instance checking the value provided
///
/// ## Panics
///
/// Panics if the value is not [normal](https://doc.rust-lang.org/std/primitive.f32.html#method.is_normal) (except if it's a positive zero) or negative.
fn new_checked ( value : f32 ) -> Self {
assert! ( value . is_normal ( ) | | value = = 0.0 ) ;
assert! ( value . is_sign_positive ( ) ) ;
FeeRate ( value )
}
2022-07-21 12:58:30 +01:00
/// Create a new instance of [`FeeRate`] given a float fee rate in sats/kwu
pub fn from_sat_per_kwu ( sat_per_kwu : f32 ) -> Self {
FeeRate ::new_checked ( sat_per_kwu / 250.0_ f32 )
}
/// Create a new instance of [`FeeRate`] given a float fee rate in sats/kvb
pub fn from_sat_per_kvb ( sat_per_kvb : f32 ) -> Self {
FeeRate ::new_checked ( sat_per_kvb / 1000.0_ f32 )
}
2020-09-04 11:44:49 +02:00
/// Create a new instance of [`FeeRate`] given a float fee rate in btc/kvbytes
2022-07-30 17:51:43 +02:00
///
/// ## Panics
///
/// Panics if the value is not [normal](https://doc.rust-lang.org/std/primitive.f32.html#method.is_normal) (except if it's a positive zero) or negative.
2020-08-31 10:49:44 +02:00
pub fn from_btc_per_kvb ( btc_per_kvb : f32 ) -> Self {
2022-07-30 17:51:43 +02:00
FeeRate ::new_checked ( btc_per_kvb * 1e5 )
2020-08-31 10:49:44 +02:00
}
2020-09-04 11:44:49 +02:00
/// Create a new instance of [`FeeRate`] given a float fee rate in satoshi/vbyte
2022-07-30 17:51:43 +02:00
///
/// ## Panics
///
/// Panics if the value is not [normal](https://doc.rust-lang.org/std/primitive.f32.html#method.is_normal) (except if it's a positive zero) or negative.
pub fn from_sat_per_vb ( sat_per_vb : f32 ) -> Self {
FeeRate ::new_checked ( sat_per_vb )
2020-08-31 10:49:44 +02:00
}
2020-09-04 11:44:49 +02:00
/// Create a new [`FeeRate`] with the default min relay fee value
2021-02-26 14:15:46 +11:00
pub const fn default_min_relay_fee ( ) -> Self {
2020-08-31 10:49:44 +02:00
FeeRate ( 1.0 )
}
2021-07-16 15:14:20 +10:00
/// Calculate fee rate from `fee` and weight units (`wu`).
pub fn from_wu ( fee : u64 , wu : usize ) -> FeeRate {
Self ::from_vb ( fee , wu . vbytes ( ) )
}
/// Calculate fee rate from `fee` and `vbytes`.
pub fn from_vb ( fee : u64 , vbytes : usize ) -> FeeRate {
let rate = fee as f32 / vbytes as f32 ;
Self ::from_sat_per_vb ( rate )
}
2020-09-04 11:44:49 +02:00
/// Return the value as satoshi/vbyte
2022-07-21 12:58:30 +01:00
pub fn as_sat_per_vb ( & self ) -> f32 {
2020-08-31 10:49:44 +02:00
self . 0
}
2021-07-16 15:14:20 +10:00
/// Calculate absolute fee in Satoshis using size in weight units.
pub fn fee_wu ( & self , wu : usize ) -> u64 {
self . fee_vb ( wu . vbytes ( ) )
}
/// Calculate absolute fee in Satoshis using size in virtual bytes.
pub fn fee_vb ( & self , vbytes : usize ) -> u64 {
2022-07-21 12:58:30 +01:00
( self . as_sat_per_vb ( ) * vbytes as f32 ) . ceil ( ) as u64
2021-07-16 15:14:20 +10:00
}
2020-08-31 10:49:44 +02:00
}
impl std ::default ::Default for FeeRate {
fn default ( ) -> Self {
FeeRate ::default_min_relay_fee ( )
}
}
2021-07-16 15:14:20 +10:00
impl Sub for FeeRate {
type Output = Self ;
fn sub ( self , other : FeeRate ) -> Self ::Output {
FeeRate ( self . 0 - other . 0 )
}
}
/// Trait implemented by types that can be used to measure weight units.
pub trait Vbytes {
/// Convert weight units to virtual bytes.
fn vbytes ( self ) -> usize ;
}
impl Vbytes for usize {
fn vbytes ( self ) -> usize {
// ref: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations
( self as f32 / 4.0 ) . ceil ( ) as usize
}
}
2021-02-08 15:40:56 +11:00
/// An unspent output owned by a [`Wallet`].
///
/// [`Wallet`]: crate::Wallet
2021-05-17 17:20:32 +02:00
#[ derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash) ]
2021-02-04 12:09:53 +11:00
pub struct LocalUtxo {
2020-12-16 11:33:34 -08:00
/// Reference to a transaction output
2020-02-05 11:59:02 +01:00
pub outpoint : OutPoint ,
2020-12-16 11:33:34 -08:00
/// Transaction output
2020-02-05 11:59:02 +01:00
pub txout : TxOut ,
2020-12-16 11:33:34 -08:00
/// Type of keychain
2020-12-14 17:14:24 +01:00
pub keychain : KeychainKind ,
2022-03-09 16:15:34 +01:00
/// Whether this UTXO is spent or not
pub is_spent : bool ,
2020-02-05 11:59:02 +01:00
}
2021-02-08 15:40:56 +11:00
/// A [`Utxo`] with its `satisfaction_weight`.
2022-10-25 11:15:43 +02:00
#[ derive(Debug, Clone, PartialEq, Eq) ]
2021-02-08 15:40:56 +11:00
pub struct WeightedUtxo {
2021-02-12 10:25:57 +11:00
/// The weight of the witness data and `scriptSig` expressed in [weight units]. This is used to
/// properly maintain the feerate when adding this input to a transaction during coin selection.
///
/// [weight units]: https://en.bitcoin.it/wiki/Weight_units
2021-02-08 15:40:56 +11:00
pub satisfaction_weight : usize ,
/// The UTXO
pub utxo : Utxo ,
}
2022-10-25 11:15:43 +02:00
#[ derive(Debug, Clone, PartialEq, Eq) ]
2021-02-08 15:40:56 +11:00
/// An unspent transaction output (UTXO).
pub enum Utxo {
/// A UTXO owned by the local wallet.
Local ( LocalUtxo ) ,
/// A UTXO owned by another wallet.
Foreign {
/// The location of the output.
outpoint : OutPoint ,
/// The information about the input we require to add it to a PSBT.
// Box it to stop the type being too big.
psbt_input : Box < psbt ::Input > ,
} ,
}
impl Utxo {
/// Get the location of the UTXO
pub fn outpoint ( & self ) -> OutPoint {
match & self {
Utxo ::Local ( local ) = > local . outpoint ,
Utxo ::Foreign { outpoint , .. } = > * outpoint ,
}
}
/// Get the `TxOut` of the UTXO
pub fn txout ( & self ) -> & TxOut {
match & self {
Utxo ::Local ( local ) = > & local . txout ,
Utxo ::Foreign {
outpoint ,
psbt_input ,
} = > {
if let Some ( prev_tx ) = & psbt_input . non_witness_utxo {
return & prev_tx . output [ outpoint . vout as usize ] ;
}
if let Some ( txout ) = & psbt_input . witness_utxo {
2021-06-08 13:57:55 +10:00
return txout ;
2021-02-08 15:40:56 +11:00
}
unreachable! ( " Foreign UTXOs will always have one of these set " )
}
}
}
}
2020-09-04 11:44:49 +02:00
/// A wallet transaction
2022-10-25 11:15:43 +02:00
#[ derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq) ]
2020-02-05 11:59:02 +01:00
pub struct TransactionDetails {
2020-12-16 11:33:34 -08:00
/// Optional transaction
2020-02-05 11:59:02 +01:00
pub transaction : Option < Transaction > ,
2020-12-16 11:33:34 -08:00
/// Transaction id
2020-02-05 11:59:02 +01:00
pub txid : Txid ,
2020-12-16 11:33:34 -08:00
/// Received value (sats)
2022-06-15 22:13:41 +08:00
/// Sum of owned outputs of this transaction.
2020-02-05 11:59:02 +01:00
pub received : u64 ,
2020-12-16 11:33:34 -08:00
/// Sent value (sats)
2022-06-15 22:13:41 +08:00
/// Sum of owned inputs of this transaction.
2020-02-05 11:59:02 +01:00
pub sent : u64 ,
2022-06-22 14:37:29 +05:30
/// Fee value (sats) if confirmed.
2021-07-12 10:06:08 +02:00
/// The availability of the fee depends on the backend. It's never `None` with an Electrum
/// Server backend, but it could be `None` with a Bitcoin RPC node without txindex that receive
/// funds while offline.
2021-06-12 15:01:44 +02:00
pub fee : Option < u64 > ,
2023-01-19 15:03:37 -05:00
/// If the transaction is confirmed, contains height and Unix timestamp of the block containing the
2021-06-12 15:01:44 +02:00
/// transaction, unconfirmed transaction contains `None`.
2021-11-03 16:05:30 +00:00
pub confirmation_time : Option < BlockTime > ,
2021-06-12 15:01:44 +02:00
}
2022-12-06 01:37:20 -06:00
impl PartialOrd for TransactionDetails {
fn partial_cmp ( & self , other : & Self ) -> Option < std ::cmp ::Ordering > {
Some ( self . cmp ( other ) )
}
}
impl Ord for TransactionDetails {
fn cmp ( & self , other : & Self ) -> std ::cmp ::Ordering {
self . confirmation_time
. cmp ( & other . confirmation_time )
. then_with ( | | self . txid . cmp ( & other . txid ) )
}
}
2021-11-03 16:05:30 +00:00
/// Block height and timestamp of a block
2021-06-12 15:01:44 +02:00
#[ derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default) ]
2021-11-03 16:05:30 +00:00
pub struct BlockTime {
2021-06-12 15:01:44 +02:00
/// confirmation block height
pub height : u32 ,
/// confirmation block timestamp
pub timestamp : u64 ,
}
2022-12-06 01:37:20 -06:00
impl PartialOrd for BlockTime {
fn partial_cmp ( & self , other : & Self ) -> Option < std ::cmp ::Ordering > {
Some ( self . cmp ( other ) )
}
}
impl Ord for BlockTime {
fn cmp ( & self , other : & Self ) -> std ::cmp ::Ordering {
self . height
. cmp ( & other . height )
. then_with ( | | self . timestamp . cmp ( & other . timestamp ) )
}
}
2021-11-03 16:05:30 +00:00
/// **DEPRECATED**: Confirmation time of a transaction
///
/// The structure has been renamed to `BlockTime`
#[ deprecated(note = " This structure has been renamed to `BlockTime` " ) ]
pub type ConfirmationTime = BlockTime ;
impl BlockTime {
/// Returns `Some` `BlockTime` if both `height` and `timestamp` are `Some`
2021-06-12 15:01:44 +02:00
pub fn new ( height : Option < u32 > , timestamp : Option < u64 > ) -> Option < Self > {
match ( height , timestamp ) {
2021-11-03 16:05:30 +00:00
( Some ( height ) , Some ( timestamp ) ) = > Some ( BlockTime { height , timestamp } ) ,
2021-06-12 15:01:44 +02:00
_ = > None ,
}
}
2020-02-05 11:59:02 +01:00
}
2021-02-26 14:15:46 +11:00
2022-06-22 14:37:29 +05:30
/// Balance differentiated in various categories
#[ derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default) ]
pub struct Balance {
/// All coinbase outputs not yet matured
pub immature : u64 ,
/// Unconfirmed UTXOs generated by a wallet tx
pub trusted_pending : u64 ,
/// Unconfirmed UTXOs received from an external wallet
pub untrusted_pending : u64 ,
/// Confirmed and immediately spendable balance
pub confirmed : u64 ,
}
impl Balance {
/// Get sum of trusted_pending and confirmed coins
pub fn get_spendable ( & self ) -> u64 {
self . confirmed + self . trusted_pending
}
/// Get the whole balance visible to the wallet
pub fn get_total ( & self ) -> u64 {
self . confirmed + self . trusted_pending + self . untrusted_pending + self . immature
}
}
impl std ::fmt ::Display for Balance {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
write! (
f ,
" {{ immature: {}, trusted_pending: {}, untrusted_pending: {}, confirmed: {} }} " ,
self . immature , self . trusted_pending , self . untrusted_pending , self . confirmed
)
}
}
impl std ::ops ::Add for Balance {
type Output = Self ;
fn add ( self , other : Self ) -> Self {
Self {
immature : self . immature + other . immature ,
trusted_pending : self . trusted_pending + other . trusted_pending ,
untrusted_pending : self . untrusted_pending + other . untrusted_pending ,
confirmed : self . confirmed + other . confirmed ,
}
}
}
impl std ::iter ::Sum for Balance {
fn sum < I : Iterator < Item = Self > > ( iter : I ) -> Self {
iter . fold (
Balance {
.. Default ::default ( )
} ,
| a , b | a + b ,
)
}
}
2021-02-26 14:15:46 +11:00
#[ cfg(test) ]
mod tests {
use super ::* ;
2022-12-06 01:37:20 -06:00
use bitcoin ::hashes ::Hash ;
#[ test ]
fn sort_block_time ( ) {
let block_time_a = BlockTime {
height : 100 ,
timestamp : 100 ,
} ;
let block_time_b = BlockTime {
height : 100 ,
timestamp : 110 ,
} ;
let block_time_c = BlockTime {
height : 0 ,
timestamp : 0 ,
} ;
let mut vec = vec! [
block_time_a . clone ( ) ,
block_time_b . clone ( ) ,
block_time_c . clone ( ) ,
] ;
vec . sort ( ) ;
let expected = vec! [ block_time_c , block_time_a , block_time_b ] ;
assert_eq! ( vec , expected )
}
#[ test ]
fn sort_tx_details ( ) {
let block_time_a = BlockTime {
height : 100 ,
timestamp : 100 ,
} ;
let block_time_b = BlockTime {
height : 0 ,
timestamp : 0 ,
} ;
let tx_details_a = TransactionDetails {
transaction : None ,
txid : Txid ::from_inner ( [ 0 ; 32 ] ) ,
received : 0 ,
sent : 0 ,
fee : None ,
confirmation_time : None ,
} ;
let tx_details_b = TransactionDetails {
transaction : None ,
txid : Txid ::from_inner ( [ 0 ; 32 ] ) ,
received : 0 ,
sent : 0 ,
fee : None ,
confirmation_time : Some ( block_time_a ) ,
} ;
let tx_details_c = TransactionDetails {
transaction : None ,
txid : Txid ::from_inner ( [ 0 ; 32 ] ) ,
received : 0 ,
sent : 0 ,
fee : None ,
confirmation_time : Some ( block_time_b . clone ( ) ) ,
} ;
let tx_details_d = TransactionDetails {
transaction : None ,
txid : Txid ::from_inner ( [ 1 ; 32 ] ) ,
received : 0 ,
sent : 0 ,
fee : None ,
confirmation_time : Some ( block_time_b ) ,
} ;
let mut vec = vec! [
tx_details_a . clone ( ) ,
tx_details_b . clone ( ) ,
tx_details_c . clone ( ) ,
tx_details_d . clone ( ) ,
] ;
vec . sort ( ) ;
let expected = vec! [ tx_details_a , tx_details_c , tx_details_d , tx_details_b ] ;
assert_eq! ( vec , expected )
}
2021-02-26 14:15:46 +11:00
#[ test ]
fn can_store_feerate_in_const ( ) {
const _MIN_RELAY : FeeRate = FeeRate ::default_min_relay_fee ( ) ;
}
2022-07-30 17:51:43 +02:00
#[ test ]
#[ should_panic ]
fn test_invalid_feerate_neg_zero ( ) {
let _ = FeeRate ::from_sat_per_vb ( - 0.0 ) ;
}
#[ test ]
#[ should_panic ]
fn test_invalid_feerate_neg_value ( ) {
let _ = FeeRate ::from_sat_per_vb ( - 5.0 ) ;
}
#[ test ]
#[ should_panic ]
fn test_invalid_feerate_nan ( ) {
let _ = FeeRate ::from_sat_per_vb ( f32 ::NAN ) ;
}
#[ test ]
#[ should_panic ]
fn test_invalid_feerate_inf ( ) {
let _ = FeeRate ::from_sat_per_vb ( f32 ::INFINITY ) ;
}
#[ test ]
fn test_valid_feerate_pos_zero ( ) {
let _ = FeeRate ::from_sat_per_vb ( 0.0 ) ;
}
2022-07-21 12:58:30 +01:00
#[ test ]
fn test_fee_from_btc_per_kvb ( ) {
let fee = FeeRate ::from_btc_per_kvb ( 1e-5 ) ;
assert! ( ( fee . as_sat_per_vb ( ) - 1.0 ) . abs ( ) < f32 ::EPSILON ) ;
}
#[ test ]
fn test_fee_from_sat_per_vbyte ( ) {
let fee = FeeRate ::from_sat_per_vb ( 1.0 ) ;
assert! ( ( fee . as_sat_per_vb ( ) - 1.0 ) . abs ( ) < f32 ::EPSILON ) ;
}
#[ test ]
fn test_fee_default_min_relay_fee ( ) {
let fee = FeeRate ::default_min_relay_fee ( ) ;
assert! ( ( fee . as_sat_per_vb ( ) - 1.0 ) . abs ( ) < f32 ::EPSILON ) ;
}
#[ test ]
fn test_fee_from_sat_per_kvb ( ) {
let fee = FeeRate ::from_sat_per_kvb ( 1000.0 ) ;
assert! ( ( fee . as_sat_per_vb ( ) - 1.0 ) . abs ( ) < f32 ::EPSILON ) ;
}
#[ test ]
fn test_fee_from_sat_per_kwu ( ) {
let fee = FeeRate ::from_sat_per_kwu ( 250.0 ) ;
assert! ( ( fee . as_sat_per_vb ( ) - 1.0 ) . abs ( ) < f32 ::EPSILON ) ;
}
2021-02-26 14:15:46 +11:00
}