use crate::transaction::fee_model::FeeModel;
use crate::transaction::transaction::Transaction;
use crate::Result;
fn varint_size(val: usize) -> usize {
if val < 0xFD {
1
} else if val <= 0xFFFF {
3
} else if val <= 0xFFFFFFFF {
5
} else {
9
}
}
#[derive(Debug, Clone, Copy)]
pub struct SatoshisPerKilobyte {
pub value: u64,
}
impl SatoshisPerKilobyte {
pub fn new(value: u64) -> Self {
Self { value }
}
fn estimate_size(&self, tx: &Transaction) -> Result<usize> {
let mut size = 4;
size += varint_size(tx.inputs.len());
for (i, input) in tx.inputs.iter().enumerate() {
size += 40;
let script_len = match (&input.unlocking_script, &input.unlocking_script_template) {
(Some(s), _) => s.to_binary().len(),
(_, Some(t)) => t.estimate_length(),
(None, None) => {
return Err(crate::Error::FeeModelError(format!(
"Input {} must have an unlocking script or template for fee computation",
i
)));
}
};
size += varint_size(script_len); size += script_len; }
size += varint_size(tx.outputs.len());
for output in &tx.outputs {
size += 8; let script_len = output.locking_script.to_binary().len();
size += varint_size(script_len); size += script_len; }
size += 4;
Ok(size)
}
}
impl FeeModel for SatoshisPerKilobyte {
fn compute_fee(&self, tx: &Transaction) -> Result<u64> {
let size = self.estimate_size(tx)?;
let fee = (size as u64 * self.value).div_ceil(1000);
Ok(fee)
}
}
impl Default for SatoshisPerKilobyte {
fn default() -> Self {
Self::new(100)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let fee_model = SatoshisPerKilobyte::new(100);
assert_eq!(fee_model.value, 100);
}
#[test]
fn test_default() {
let fee_model = SatoshisPerKilobyte::default();
assert_eq!(fee_model.value, 100);
}
#[test]
fn test_varint_size() {
assert_eq!(varint_size(0), 1);
assert_eq!(varint_size(0xFC), 1);
assert_eq!(varint_size(0xFD), 3);
assert_eq!(varint_size(0xFFFF), 3);
assert_eq!(varint_size(0x10000), 5);
}
#[test]
fn test_empty_transaction_fee() {
let fee_model = SatoshisPerKilobyte::new(1000); let tx = Transaction::new();
let fee = fee_model.compute_fee(&tx).unwrap();
assert_eq!(fee, 10);
}
}