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. /// In satoshi per vbyte.
const MIN_RELAY_FEE: u64 = 1; const MIN_RELAY_FEE: u64 = 1;
// TODO use Amount type
const P2PKH_DUST_LIMIT: u64 = 546;
pub trait WalletExt { pub trait WalletExt {
fn build_lock_psbt(&self, amount: Amount, identity_pk: PublicKey) -> PartyParams; fn build_lock_psbt(&self, amount: Amount, identity_pk: PublicKey) -> PartyParams;
} }
@ -106,7 +109,7 @@ pub fn build_cfd_transactions(
&maker.address, &maker.address,
&taker.address, &taker.address,
CET_TIMELOCK, CET_TIMELOCK,
); )?;
let encsig = cet.encsign(identity_sk, &oracle_params.pk, &oracle_params.nonce_pk)?; let encsig = cet.encsign(identity_sk, &oracle_params.pk, &oracle_params.nonce_pk)?;
@ -181,29 +184,57 @@ pub struct Payout {
} }
impl Payout { impl Payout {
fn to_txouts(self, maker_address: &Address, taker_address: &Address, fee: u64) -> Vec<TxOut> { fn to_txouts(&self, maker_address: &Address, taker_address: &Address) -> Vec<TxOut> {
let mut txouts = [ let txouts = [
(self.maker_amount, maker_address), (self.maker_amount, maker_address),
(self.taker_amount, taker_address), (self.taker_amount, taker_address),
] ]
.iter() .iter()
.filter_map(|(amount, address)| { .filter_map(|(amount, address)| {
(amount != &Amount::ZERO).then(|| TxOut { (amount >= &Amount::from_sat(P2PKH_DUST_LIMIT)).then(|| TxOut {
value: amount.as_sat(), value: amount.as_sat(),
script_pubkey: address.script_pubkey(), script_pubkey: address.script_pubkey(),
}) })
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// TODO: Rewrite this txouts
if txouts.len() == 1 {
txouts[0].value -= fee;
} else if txouts.len() == 2 {
txouts[0].value -= fee / 2;
txouts[1].value -= fee / 2;
} }
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, maker_address: &Address,
taker_address: &Address, taker_address: &Address,
relative_timelock_in_blocks: u32, relative_timelock_in_blocks: u32,
) -> Self { ) -> Result<Self> {
let commit_input = TxIn { let commit_input = TxIn {
previous_output: commit_tx.outpoint(), previous_output: commit_tx.outpoint(),
sequence: relative_timelock_in_blocks, sequence: relative_timelock_in_blocks,
..Default::default() ..Default::default()
}; };
let fee = Self::calculate_vbytes() * MIN_RELAY_FEE; let mut tx = Transaction {
let tx = Transaction {
version: 2, version: 2,
lock_time: 0, lock_time: 0,
input: vec![commit_input], 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( let sighash = SigHashCache::new(&tx).signature_hash(
0, 0,
&commit_tx.descriptor.script_code(), &commit_tx.descriptor.script_code(),
@ -309,12 +342,12 @@ impl ContractExecutionTransaction {
SigHashType::All, SigHashType::All,
); );
Self { Ok(Self {
inner: tx, inner: tx,
message: payout.message, message: payout.message,
sighash, sighash,
commit_descriptor: commit_tx.descriptor(), commit_descriptor: commit_tx.descriptor(),
} })
} }
fn encsign( fn encsign(
@ -356,11 +389,6 @@ impl ContractExecutionTransaction {
self.inner.txid() self.inner.txid()
} }
fn calculate_vbytes() -> u64 {
// TODO: Do it properly
100
}
pub fn add_signatures( pub fn add_signatures(
self, self,
(maker_pk, maker_sig): (PublicKey, Signature), (maker_pk, maker_sig): (PublicKey, Signature),
@ -970,15 +998,18 @@ mod tests {
let cets = payouts let cets = payouts
.iter() .iter()
.map(|payout| { .map(|payout| {
ContractExecutionTransaction::new( let cet = ContractExecutionTransaction::new(
&commit_tx, &commit_tx,
payout, payout,
&maker_address, &maker_address,
&taker_address, &taker_address,
12, 12,
) )?;
Ok(cet)
}) })
.collect::<Vec<_>>(); .collect::<Result<Vec<_>>>()
.context("cannot build cets")
.unwrap();
let maker_refund_sig = { let maker_refund_sig = {
let sighash = secp256k1_zkp::Message::from_slice(&refund_tx.sighash()).unwrap(); let sighash = secp256k1_zkp::Message::from_slice(&refund_tx.sighash()).unwrap();
@ -1250,6 +1281,48 @@ mod tests {
.expect("valid signed punish transaction"); .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>( fn build_wallet<R>(
rng: &mut R, rng: &mut R,
utxo_amount: Amount, utxo_amount: Amount,

Loading…
Cancel
Save