Merge rust-bitcoin/rust-bitcoin#2230: Add effective value calculation
d69d62822d
Add effective_value method (yancy) Pull request description: Draft PR for adding effective value calculation to TxOut. Adding this method was discussed here: https://github.com/rust-bitcoin/rust-bitcoin/pull/2217 ACKs for top commit: Kixunil: ACKd69d62822d
apoelstra: ACKd69d62822d
Tree-SHA512: e74788fea2f97a10ccf2015406178bdac99a54207fe51f0c830937d2106d564beeebe6f70b49aded572aa17ccb42f47e25905a8f791bd9b442eff223ea59baae
This commit is contained in:
commit
a3d698ac7c
|
@ -19,11 +19,11 @@ use internals::write_err;
|
|||
use io::{Read, Write};
|
||||
|
||||
use super::Weight;
|
||||
use crate::blockdata::fee_rate::FeeRate;
|
||||
use crate::blockdata::locktime::absolute::{self, Height, Time};
|
||||
use crate::blockdata::locktime::relative;
|
||||
use crate::blockdata::script::{Script, ScriptBuf};
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::blockdata::FeeRate;
|
||||
use crate::consensus::{encode, Decodable, Encodable};
|
||||
use crate::internal_macros::{impl_consensus_encoding, impl_hashencode};
|
||||
use crate::parse::impl_parse_str_from_int_infallible;
|
||||
|
@ -31,7 +31,7 @@ use crate::prelude::*;
|
|||
#[cfg(doc)]
|
||||
use crate::sighash::{EcdsaSighashType, TapSighashType};
|
||||
use crate::string::FromHexStr;
|
||||
use crate::{Amount, VarInt};
|
||||
use crate::{Amount, SignedAmount, VarInt};
|
||||
|
||||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
#[cfg(feature = "bitcoinconsensus")]
|
||||
|
@ -226,6 +226,12 @@ pub struct TxIn {
|
|||
}
|
||||
|
||||
impl TxIn {
|
||||
/// Returns the input base weight.
|
||||
///
|
||||
/// Base weight excludes the witness and script.
|
||||
const BASE_WEIGHT: Weight =
|
||||
Weight::from_vb_unwrap(OutPoint::SIZE as u64 + Sequence::SIZE as u64);
|
||||
|
||||
/// Returns true if this input enables the [`absolute::LockTime`] (aka `nLockTime`) of its
|
||||
/// [`Transaction`].
|
||||
///
|
||||
|
@ -1168,6 +1174,29 @@ impl From<&Transaction> for Wtxid {
|
|||
fn from(tx: &Transaction) -> Wtxid { tx.wtxid() }
|
||||
}
|
||||
|
||||
/// Computes the value of an output accounting for the cost of spending it.
|
||||
///
|
||||
/// The effective value is the value of an output value minus the amount to spend it. That is, the
|
||||
/// effective_value can be calculated as: value - (fee_rate * weight).
|
||||
///
|
||||
/// Note: the effective value of a [`Transaction`] may increase less than the effective value of
|
||||
/// a [`TxOut`] when adding another [`TxOut`] to the transaction. This happens when the new
|
||||
/// [`TxOut`] added causes the output length `VarInt` to increase its encoding length.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `fee_rate` - the fee rate of the transaction being created.
|
||||
/// * `satisfaction_weight` - satisfied spending conditions weight.
|
||||
pub fn effective_value(
|
||||
fee_rate: FeeRate,
|
||||
satisfaction_weight: Weight,
|
||||
value: Amount,
|
||||
) -> Option<SignedAmount> {
|
||||
let weight = satisfaction_weight.checked_add(TxIn::BASE_WEIGHT)?;
|
||||
let signed_input_fee = fee_rate.checked_mul_by_weight(weight)?.to_signed().ok()?;
|
||||
value.to_signed().ok()?.checked_sub(signed_input_fee)
|
||||
}
|
||||
|
||||
/// Predicts the weight of a to-be-constructed transaction.
|
||||
///
|
||||
/// This function computes the weight of a transaction which is not fully known. All that is needed
|
||||
|
@ -1989,6 +2018,37 @@ mod tests {
|
|||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_value_happy_path() {
|
||||
let value = Amount::from_str("1 cBTC").unwrap();
|
||||
let fee_rate = FeeRate::from_sat_per_kwu(10);
|
||||
let satisfaction_weight = Weight::from_wu(204);
|
||||
let effective_value = effective_value(fee_rate, satisfaction_weight, value).unwrap();
|
||||
|
||||
// 10 sat/kwu * (204wu + BASE_WEIGHT) = 4 sats
|
||||
let expected_fee = SignedAmount::from_str("4 sats").unwrap();
|
||||
let expected_effective_value = value.to_signed().unwrap() - expected_fee;
|
||||
assert_eq!(effective_value, expected_effective_value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_value_fee_rate_does_not_overflow() {
|
||||
let eff_value = effective_value(FeeRate::MAX, Weight::ZERO, Amount::ZERO);
|
||||
assert!(eff_value.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_value_weight_does_not_overflow() {
|
||||
let eff_value = effective_value(FeeRate::ZERO, Weight::MAX, Amount::ZERO);
|
||||
assert!(eff_value.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_value_value_does_not_overflow() {
|
||||
let eff_value = effective_value(FeeRate::ZERO, Weight::ZERO, Amount::MAX);
|
||||
assert!(eff_value.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn txin_txout_weight() {
|
||||
// [(is_segwit, tx_hex, expected_weight)]
|
||||
|
|
Loading…
Reference in New Issue