Browse Source

Calculate CET fee based on tx size.

bdk-0.11
Philipp Hoenisch 3 years ago
committed by Lucas Soriano del Pino
parent
commit
2985f96bea
No known key found for this signature in database GPG Key ID: EE611E973A1530E7
  1. 125
      cfd_protocol/src/lib.rs

125
cfd_protocol/src/lib.rs

@ -22,6 +22,9 @@ use std::collections::HashMap;
/// In satoshi per vbyte.
const MIN_RELAY_FEE: u64 = 1;
// TODO use Amount type
const P2PKH_DUST_LIMIT: u64 = 546;
pub trait WalletExt {
fn build_lock_psbt(&self, amount: Amount, identity_pk: PublicKey) -> PartyParams;
}
@ -106,7 +109,7 @@ pub fn build_cfd_transactions(
&maker.address,
&taker.address,
CET_TIMELOCK,
);
)?;
let encsig = cet.encsign(identity_sk, &oracle_params.pk, &oracle_params.nonce_pk)?;
@ -181,29 +184,57 @@ pub struct Payout {
}
impl Payout {
fn to_txouts(self, maker_address: &Address, taker_address: &Address, fee: u64) -> Vec<TxOut> {
let mut txouts = [
fn to_txouts(&self, maker_address: &Address, taker_address: &Address) -> Vec<TxOut> {
let txouts = [
(self.maker_amount, maker_address),
(self.taker_amount, taker_address),
]
.iter()
.filter_map(|(amount, address)| {
(amount != &Amount::ZERO).then(|| TxOut {
(amount >= &Amount::from_sat(P2PKH_DUST_LIMIT)).then(|| TxOut {
value: amount.as_sat(),
script_pubkey: address.script_pubkey(),
})
})
.collect::<Vec<_>>();
// TODO: Rewrite this
if txouts.len() == 1 {
txouts[0].value -= fee;
} else if txouts.len() == 2 {
txouts[0].value -= fee / 2;
txouts[1].value -= fee / 2;
txouts
}
txouts
/// Subtracts fee fairly from both outputs
///
/// We need to consider a few cases:
/// - If both amounts are >= DUST, they share the fee equally
/// - If one amount is < DUST, it set to 0 and the other output needs to cover for the fee.
fn with_updated_fee(self, fee: Amount) -> Result<Self> {
let mut updated = self;
let dust_limit = Amount::from_sat(P2PKH_DUST_LIMIT);
match (
self.maker_amount
.checked_sub(fee / 2)
.map(|a| a > dust_limit)
.unwrap_or(false),
self.taker_amount
.checked_sub(fee / 2)
.map(|a| a > dust_limit)
.unwrap_or(false),
) {
(true, true) => {
updated.maker_amount -= fee / 2;
updated.taker_amount -= fee / 2;
}
(false, true) => {
updated.maker_amount = Amount::ZERO;
updated.taker_amount = self.taker_amount - (fee + self.maker_amount);
}
(true, false) => {
updated.maker_amount = self.maker_amount - (fee + self.taker_amount);
updated.taker_amount = Amount::ZERO;
}
(false, false) => bail!("Amounts are too small, could not subtract fee."),
}
Ok(updated)
}
}
@ -286,22 +317,24 @@ impl ContractExecutionTransaction {
maker_address: &Address,
taker_address: &Address,
relative_timelock_in_blocks: u32,
) -> Self {
) -> Result<Self> {
let commit_input = TxIn {
previous_output: commit_tx.outpoint(),
sequence: relative_timelock_in_blocks,
..Default::default()
};
let fee = Self::calculate_vbytes() * MIN_RELAY_FEE;
let tx = Transaction {
let mut tx = Transaction {
version: 2,
lock_time: 0,
input: vec![commit_input],
output: payout.to_txouts(maker_address, taker_address, fee),
output: payout.to_txouts(maker_address, taker_address),
};
let fee = tx.get_size() * MIN_RELAY_FEE as usize;
let payout = payout.with_updated_fee(Amount::from_sat(fee as u64))?;
tx.output = payout.to_txouts(maker_address, taker_address);
let sighash = SigHashCache::new(&tx).signature_hash(
0,
&commit_tx.descriptor.script_code(),
@ -309,12 +342,12 @@ impl ContractExecutionTransaction {
SigHashType::All,
);
Self {
Ok(Self {
inner: tx,
message: payout.message,
sighash,
commit_descriptor: commit_tx.descriptor(),
}
})
}
fn encsign(
@ -356,11 +389,6 @@ impl ContractExecutionTransaction {
self.inner.txid()
}
fn calculate_vbytes() -> u64 {
// TODO: Do it properly
100
}
pub fn add_signatures(
self,
(maker_pk, maker_sig): (PublicKey, Signature),
@ -970,15 +998,18 @@ mod tests {
let cets = payouts
.iter()
.map(|payout| {
ContractExecutionTransaction::new(
let cet = ContractExecutionTransaction::new(
&commit_tx,
payout,
&maker_address,
&taker_address,
12,
)
)?;
Ok(cet)
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>>>()
.context("cannot build cets")
.unwrap();
let maker_refund_sig = {
let sighash = secp256k1_zkp::Message::from_slice(&refund_tx.sighash()).unwrap();
@ -1250,6 +1281,48 @@ mod tests {
.expect("valid signed punish transaction");
}
#[test]
fn test_fee_subtraction_bigger_than_dust() {
let orig_maker_amount = 1000;
let orig_taker_amount = 1000;
let payout = Payout {
message: Message::Win,
maker_amount: Amount::from_sat(orig_maker_amount),
taker_amount: Amount::from_sat(orig_taker_amount),
};
let fee = 100;
let updated_payout = payout.with_updated_fee(Amount::from_sat(fee)).unwrap();
assert_eq!(
updated_payout.maker_amount,
Amount::from_sat(orig_maker_amount - fee / 2)
);
assert_eq!(
updated_payout.taker_amount,
Amount::from_sat(orig_taker_amount - fee / 2)
);
}
// TODO add proptest for this
#[test]
fn test_fee_subtraction_smaller_than_dust() {
let orig_maker_amount = P2PKH_DUST_LIMIT - 1;
let orig_taker_amount = 1000;
let payout = Payout {
message: Message::Win,
maker_amount: Amount::from_sat(orig_maker_amount),
taker_amount: Amount::from_sat(orig_taker_amount),
};
let fee = 100;
let updated_payout = payout.with_updated_fee(Amount::from_sat(fee)).unwrap();
assert_eq!(updated_payout.maker_amount, Amount::from_sat(0));
assert_eq!(
updated_payout.taker_amount,
Amount::from_sat(orig_taker_amount - (fee + orig_maker_amount))
);
}
fn build_wallet<R>(
rng: &mut R,
utxo_amount: Amount,

Loading…
Cancel
Save