tcoinchooser: better account for fees in penalty_func - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit cb204dd969955e6ac6b23821500c406678a3ce97 DIR parent e3c26d7c7a7e3b0e179b0274c12017f5bb04368d HTML Author: SomberNight <somber.night@protonmail.com> Date: Mon, 17 Jun 2019 19:55:39 +0200 coinchooser: better account for fees in penalty_func Diffstat: M electrum/coinchooser.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) --- DIR diff --git a/electrum/coinchooser.py b/electrum/coinchooser.py t@@ -121,7 +121,7 @@ class CoinChooserBase(Logger): return list(map(make_Bucket, buckets.keys(), buckets.values())) - def penalty_func(self, tx): + def penalty_func(self, tx, *, fee_for_buckets): def penalty(candidate): return 0 return penalty t@@ -251,10 +251,19 @@ class CoinChooserBase(Logger): total_weight = get_tx_weight(buckets) return total_input >= spent_amount + fee_estimator_w(total_weight) + def fee_for_buckets(buckets) -> int: + """Given a list of buckets, return the total fee paid by the + transaction, in satoshis. + Note that the change output(s) are not yet known here, + so fees for those are excluded and hence this is a lower bound. + """ + total_weight = get_tx_weight(buckets) + return fee_estimator_w(total_weight) + # Collect the coins into buckets, choose a subset of the buckets buckets = self.bucketize_coins(coins) buckets = self.choose_buckets(buckets, sufficient_funds, - self.penalty_func(tx)) + self.penalty_func(tx, fee_for_buckets=fee_for_buckets)) tx.add_inputs([coin for b in buckets for coin in b.coins]) tx_weight = get_tx_weight(buckets) t@@ -379,7 +388,7 @@ class CoinChooserPrivacy(CoinChooserRandom): def keys(self, coins): return [coin['address'] for coin in coins] - def penalty_func(self, tx): + def penalty_func(self, tx, *, fee_for_buckets): min_change = min(o.value for o in tx.outputs()) * 0.75 max_change = max(o.value for o in tx.outputs()) * 1.33 spent_amount = sum(o.value for o in tx.outputs()) t@@ -387,8 +396,10 @@ class CoinChooserPrivacy(CoinChooserRandom): def penalty(buckets): badness = len(buckets) - 1 total_input = sum(bucket.value for bucket in buckets) - # FIXME "change" here also includes fees - change = float(total_input - spent_amount) + # FIXME fee_for_buckets does not include fees needed to cover the change output(s) + # so fee here is a lower bound + fee = fee_for_buckets(buckets) + change = float(total_input - spent_amount - fee) # Penalize change not roughly in output range if change < min_change: badness += (min_change - change) / (min_change + 10000)