Skip to content

Commit

Permalink
add the fee policy
Browse files Browse the repository at this point in the history
  • Loading branch information
weikengchen committed May 28, 2024
1 parent 40c1d37 commit d8202d4
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 1 deletion.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# bitcoin-simulator
A simulator of a local bitcoin testnet based on Rust and Sqlite.

A simulator of a local Bitcoin ledger based on Rust and Sqlite.

74 changes: 74 additions & 0 deletions src/database.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::policy::Policy;
use crate::spending_requirements::{P2TRChecker, P2WPKHChecker, P2WSHChecker};
use anyhow::{Error, Result};
use bitcoin::consensus::Encodable;
Expand Down Expand Up @@ -117,6 +118,79 @@ impl Database {
Ok(())
}

pub fn check_fees(&self, tx: &Transaction, policy: &Policy) -> Result<()> {
let mut input_sats = 0;
for input in tx.input.iter() {
assert_eq!(
input.script_sig.len(),
0,
"Bitcoin simulator only verifies inputs that support segregated witness."
);

let prev_out = self.get_prev_output(
&input.previous_output.txid.to_string(),
input.previous_output.vout,
)?;
input_sats += prev_out.value.to_sat();
}

let mut output_sats = 0;
for output in tx.output.iter() {
if output.script_pubkey.is_op_return() {
if !policy.allow_data_carrier_via_op_return {
return Err(Error::msg("The policy discourages using OP_RETURN to carry data as a spam filter may reject it."));
}
// otherwise, the dust amount is zero for OP_RETURN.
} else if output.script_pubkey.is_p2wsh() {
if policy.require_dust_amount && output.value.to_sat() < (67 + 8 + 1 + 34) * 3 {
return Err(Error::msg(format!("P2WSH output has a dust amount requirement of 330 sats, but one provided output only has {} sats.", output.value.to_sat())));
}
} else if output.script_pubkey.is_p2wpkh() {
if policy.require_dust_amount && output.value.to_sat() < (67 + 8 + 1 + 22) * 3 {
return Err(Error::msg(format!("P2WPKH output has a dust amount requirement of 294 sats, but one provided output only has {} sats.", output.value.to_sat())));
}
} else if output.script_pubkey.is_p2tr() {
if policy.require_dust_amount && output.value.to_sat() < (67 + 8 + 1 + 34) * 3 {
return Err(Error::msg(format!("P2TR output has a dust amount requirement of 330 sats, but one provided output only has {} sats.", output.value.to_sat())));
}
} else {
return Err(Error::msg(
"Bitcoin simulator only supports P2WSH, P2WPKH, and P2TR outputs.",
));
}

output_sats += output.value.to_sat();
}

if output_sats > input_sats {
return Err(Error::msg(format!(
"The output balance {} sats exceeds the input balance {} sats.",
output_sats, input_sats
)));
}

let fee = output_sats - input_sats;
let weight = tx.weight().to_wu();

if weight > policy.max_tx_weight as u64 {
return Err(Error::msg(format!(
"The transaction weight units {} exceed the standard policy limit {}.",
weight, policy.max_tx_weight
)));
}

let vbytes = tx.vsize() as u64;
if fee < vbytes * policy.sat_per_vbyte as u64 {
return Err(Error::msg(format!(
"The transaction fee is {} sats, but only {} sats are provided",
vbytes * policy.sat_per_vbyte as u64,
fee
)));
}

Ok(())
}

pub fn verify_transaction(&self, tx: &Transaction) -> Result<()> {
let mut prev_outs = vec![];
for input in tx.input.iter() {
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ pub mod database;

pub mod spending_requirements;

pub mod policy;

define_pushable!();
39 changes: 39 additions & 0 deletions src/policy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
pub struct Policy {
pub sat_per_vbyte: u32,
pub allow_data_carrier_via_op_return: bool,
pub require_dust_amount: bool,
pub max_tx_weight: u32,
}

impl Default for Policy {
fn default() -> Self {
Self {
sat_per_vbyte: 1,
allow_data_carrier_via_op_return: false, /* subject to many spam filters */
require_dust_amount: true,
max_tx_weight: 400000,
}
}
}

impl Policy {
pub fn no_spam_filter(mut self) -> Self {
self.allow_data_carrier_via_op_return = true;
self
}

pub fn no_dust_amount_requirement(mut self) -> Self {
self.require_dust_amount = false;
self
}

pub fn set_fee(mut self, sat_per_vbyte: u32) -> Self {
self.sat_per_vbyte = sat_per_vbyte;
self
}

pub fn set_max_tx_weight(mut self, max_tx_weight: u32) -> Self {
self.max_tx_weight = max_tx_weight;
self
}
}

0 comments on commit d8202d4

Please sign in to comment.