Dynamically chose fee for TxRedeem.

Alice chooses the fee for TxRedeem because she is the one that cares. Note must be taken here because if the fee is too low (e.g. < min tx fee) then she might not be able to publish TxRedeem at all.
pull/466/head
Philipp Hoenisch 3 years ago
parent 002e7b38c3
commit d5c1b6693e
No known key found for this signature in database
GPG Key ID: E5F8E74C672BC666

@ -37,6 +37,8 @@ use serde::{Deserialize, Serialize};
use sha2::Sha256;
use std::str::FromStr;
pub use crate::bitcoin::redeem::ESTIMATED_WEIGHT as ESTIMATED_WEIGHT_TX_REDEEM;
// TODO: Configurable tx-fee (note: parties have to agree prior to swapping)
// Current reasoning:
// tx with largest weight (as determined by get_weight() upon broadcast in e2e

@ -1,6 +1,6 @@
use crate::bitcoin::wallet::Watchable;
use crate::bitcoin::{
build_shared_output_descriptor, Address, Amount, PublicKey, Transaction, Wallet, TX_FEE,
build_shared_output_descriptor, Address, Amount, PublicKey, Transaction, Wallet,
};
use ::bitcoin::util::psbt::PartiallySignedTransaction;
use ::bitcoin::{OutPoint, TxIn, TxOut, Txid};

@ -1,6 +1,6 @@
use crate::bitcoin::wallet::Watchable;
use crate::bitcoin::{
verify_encsig, verify_sig, Address, EmptyWitnessStack, EncryptedSignature, NoInputs,
verify_encsig, verify_sig, Address, Amount, EmptyWitnessStack, EncryptedSignature, NoInputs,
NotThreeWitnesses, PublicKey, SecretKey, TooManyInputs, Transaction, TxLock,
};
use ::bitcoin::util::bip143::SigHashCache;
@ -15,6 +15,9 @@ use miniscript::{Descriptor, DescriptorTrait};
use sha2::Sha256;
use std::collections::HashMap;
// TODO take real tx weight from past transaction
pub const ESTIMATED_WEIGHT: usize = 10_000;
#[derive(Clone, Debug)]
pub struct TxRedeem {
inner: Transaction,
@ -24,10 +27,10 @@ pub struct TxRedeem {
}
impl TxRedeem {
pub fn new(tx_lock: &TxLock, redeem_address: &Address) -> Self {
pub fn new(tx_lock: &TxLock, redeem_address: &Address, spending_fee: Amount) -> Self {
// lock_input is the shared output that is now being used as an input for the
// redeem transaction
let tx_redeem = tx_lock.build_spend_transaction(redeem_address, None);
let tx_redeem = tx_lock.build_spend_transaction(redeem_address, None, spending_fee);
let digest = SigHashCache::new(&tx_redeem).signature_hash(
0, // Only one input: lock_input (lock transaction)

@ -347,6 +347,19 @@ impl<B, D, C> Wallet<B, D, C> {
// TODO: This should obviously not be a const :)
FeeRate::from_sat_per_vb(5.0)
}
pub fn estimate_fee(&self, weight: usize) -> bitcoin::Amount {
// Doing some heavy math here :)
// `usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide
// This fine because such a big transaction cannot exist.
#[allow(clippy::cast_precision_loss)]
let calc_fee_bytes = (weight as f32) * self.select_feerate().as_sat_vb() / 4.0;
// There are no fractional satoshi, hence we just round to the next one and
// truncate. We also do not support negative fees.
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let calc_fee_bytes_rounded = ((calc_fee_bytes * 10.0).round() as u64) / 10;
bitcoin::Amount::from_sat(calc_fee_bytes_rounded)
}
}
#[cfg(test)]

@ -39,6 +39,8 @@ pub struct Message1 {
v_a: monero::PrivateViewKey,
redeem_address: bitcoin::Address,
punish_address: bitcoin::Address,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_redeem_fee: bitcoin::Amount,
}
#[derive(Clone, Debug, Serialize, Deserialize)]

@ -1,5 +1,6 @@
use crate::bitcoin::{
current_epoch, CancelTimelock, ExpiredTimelocks, PunishTimelock, TxCancel, TxPunish, TxRefund,
ESTIMATED_WEIGHT_TX_REDEEM,
};
use crate::env::Config;
use crate::monero::wallet::{TransferRequest, WatchRequest};
@ -108,6 +109,7 @@ pub struct State0 {
punish_timelock: PunishTimelock,
redeem_address: bitcoin::Address,
punish_address: bitcoin::Address,
tx_redeem_fee: bitcoin::Amount,
}
impl State0 {
@ -144,6 +146,7 @@ impl State0 {
xmr,
cancel_timelock: env_config.bitcoin_cancel_timelock,
punish_timelock: env_config.bitcoin_punish_timelock,
tx_redeem_fee: bitcoin_wallet.estimate_fee(ESTIMATED_WEIGHT_TX_REDEEM),
})
}
@ -183,6 +186,7 @@ impl State0 {
refund_address: msg.refund_address,
redeem_address: self.redeem_address,
punish_address: self.punish_address,
tx_redeem_fee: self.tx_redeem_fee,
}))
}
}
@ -206,6 +210,7 @@ pub struct State1 {
refund_address: bitcoin::Address,
redeem_address: bitcoin::Address,
punish_address: bitcoin::Address,
tx_redeem_fee: bitcoin::Amount,
}
impl State1 {
@ -218,6 +223,7 @@ impl State1 {
v_a: self.v_a,
redeem_address: self.redeem_address.clone(),
punish_address: self.punish_address.clone(),
tx_redeem_fee: self.tx_redeem_fee,
}
}
@ -240,6 +246,7 @@ impl State1 {
redeem_address: self.redeem_address,
punish_address: self.punish_address,
tx_lock,
tx_redeem_fee: self.tx_redeem_fee,
})
}
}
@ -260,6 +267,7 @@ pub struct State2 {
redeem_address: bitcoin::Address,
punish_address: bitcoin::Address,
tx_lock: bitcoin::TxLock,
tx_redeem_fee: bitcoin::Amount,
}
impl State2 {
@ -309,6 +317,7 @@ impl State2 {
tx_lock: self.tx_lock,
tx_punish_sig_bob: msg.tx_punish_sig,
tx_cancel_sig_bob: msg.tx_cancel_sig,
tx_redeem_fee: self.tx_redeem_fee,
})
}
}
@ -332,6 +341,8 @@ pub struct State3 {
pub tx_lock: bitcoin::TxLock,
tx_punish_sig_bob: bitcoin::Signature,
tx_cancel_sig_bob: bitcoin::Signature,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_redeem_fee: bitcoin::Amount,
}
impl State3 {
@ -407,7 +418,7 @@ impl State3 {
&self,
sig: bitcoin::EncryptedSignature,
) -> Result<bitcoin::Transaction> {
bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address)
bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address, self.tx_redeem_fee)
.complete(sig, self.a.clone(), self.s_a.to_secpfun_scalar(), self.B)
.context("Failed to complete Bitcoin redeem transaction")
}

@ -169,6 +169,7 @@ impl State0 {
punish_address: msg.punish_address,
tx_lock,
min_monero_confirmations: self.min_monero_confirmations,
tx_redeem_fee: msg.tx_redeem_fee,
})
}
}
@ -189,6 +190,7 @@ pub struct State1 {
punish_address: bitcoin::Address,
tx_lock: bitcoin::TxLock,
min_monero_confirmations: u64,
tx_redeem_fee: bitcoin::Amount,
}
impl State1 {
@ -227,6 +229,7 @@ impl State1 {
tx_cancel_sig_a: msg.tx_cancel_sig,
tx_refund_encsig: msg.tx_refund_encsig,
min_monero_confirmations: self.min_monero_confirmations,
tx_redeem_fee: self.tx_redeem_fee,
})
}
}
@ -249,6 +252,8 @@ pub struct State2 {
tx_cancel_sig_a: Signature,
tx_refund_encsig: bitcoin::EncryptedSignature,
min_monero_confirmations: u64,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_redeem_fee: bitcoin::Amount,
}
impl State2 {
@ -283,6 +288,7 @@ impl State2 {
tx_cancel_sig_a: self.tx_cancel_sig_a,
tx_refund_encsig: self.tx_refund_encsig,
min_monero_confirmations: self.min_monero_confirmations,
tx_redeem_fee: self.tx_redeem_fee,
},
self.tx_lock,
))
@ -306,6 +312,8 @@ pub struct State3 {
tx_cancel_sig_a: Signature,
tx_refund_encsig: bitcoin::EncryptedSignature,
min_monero_confirmations: u64,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_redeem_fee: bitcoin::Amount,
}
impl State3 {
@ -338,6 +346,7 @@ impl State3 {
tx_cancel_sig_a: self.tx_cancel_sig_a,
tx_refund_encsig: self.tx_refund_encsig,
monero_wallet_restore_blockheight,
tx_redeem_fee: self.tx_redeem_fee,
}
}
@ -392,16 +401,20 @@ pub struct State4 {
tx_cancel_sig_a: Signature,
tx_refund_encsig: bitcoin::EncryptedSignature,
monero_wallet_restore_blockheight: BlockHeight,
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
tx_redeem_fee: bitcoin::Amount,
}
impl State4 {
pub fn tx_redeem_encsig(&self) -> bitcoin::EncryptedSignature {
let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address);
let tx_redeem =
bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address, self.tx_redeem_fee);
self.b.encsign(self.S_a_bitcoin, tx_redeem.digest())
}
pub async fn watch_for_redeem_btc(&self, bitcoin_wallet: &bitcoin::Wallet) -> Result<State5> {
let tx_redeem = bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address);
let tx_redeem =
bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address, self.tx_redeem_fee);
let tx_redeem_encsig = self.b.encsign(self.S_a_bitcoin, tx_redeem.digest());
bitcoin_wallet

@ -35,6 +35,11 @@ use tracing_subscriber::util::SubscriberInitExt;
use url::Url;
use uuid::Uuid;
// Note: this needs to be adopted if bitcoin::wallet.select_feerate() or
// bitcoin::ESTIMATED_WEIGHT_TX_REDEEM changes.
// TODO: see if there is a better way.
const TX_REDEEM_FEE: u64 = 12500;
pub async fn setup_test<T, F, C>(_config: C, testfn: T)
where
T: Fn(TestContext) -> F,
@ -678,7 +683,7 @@ impl TestContext {
fn alice_redeemed_btc_balance(&self) -> bitcoin::Amount {
self.alice_starting_balances.btc + self.btc_amount
- bitcoin::Amount::from_sat(bitcoind::TX_FEE)
- bitcoin::Amount::from_sat(TX_REDEEM_FEE)
}
fn bob_redeemed_xmr_balance(&self) -> monero::Amount {

Loading…
Cancel
Save