From 25a4202905e938884c80fd941994d617b1c2a65e Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Tue, 7 Sep 2021 15:44:55 +0200 Subject: [PATCH] statemachine: be smarter with the CF fee remainder Signed-off-by: Antoine Poinsot --- Model/statemachine.py | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/Model/statemachine.py b/Model/statemachine.py index 1b5a783..30a3b5b 100644 --- a/Model/statemachine.py +++ b/Model/statemachine.py @@ -187,9 +187,7 @@ def Vb(self, block_height): reserve = self.fee_reserve_per_vault(block_height) reserve_rate = self._feerate_reserve_per_vault(block_height) t1 = (reserve - self.Vm(block_height)) / self.O_0_factor - t2 = reserve_rate * P2WPKH_INPUT_SIZE + self._feerate_to_fee( - 10, "cancel", 0 - ) + t2 = reserve_rate * P2WPKH_INPUT_SIZE + self._feerate_to_fee(10, "cancel", 0) return int(max(t1, t2)) def fb_coins_dist(self, block_height): @@ -250,9 +248,7 @@ def is_negligible(self, coin, block_height): assert isinstance(coin["amount"], int) # FIXME: What is a reasonable factor of a 'negligible coin'? reserve_rate = self._feerate_reserve_per_vault(block_height) - t1 = reserve_rate * P2WPKH_INPUT_SIZE + self._feerate_to_fee( - 10, "cancel", 0 - ) + t1 = reserve_rate * P2WPKH_INPUT_SIZE + self._feerate_to_fee(10, "cancel", 0) t2 = self.Vm(block_height) minimum = min(t1, t2) if coin["amount"] <= minimum: @@ -383,6 +379,14 @@ def grab_coins_2(self, block_height): total_to_consume = total_unprocessed + total_unallocated + total_negligible return total_to_consume, num_inputs + def min_fbcoin_value(self, height): + """The minimum value for a feebumping coin we create is one that allows + to pay for its inclusion at the maximum feerate AND increase the Cancel + tx fee by at least 5sat/vbyte. + """ + feerate = self._feerate_reserve_per_vault(height) + return int(feerate * P2WPKH_INPUT_SIZE + self._feerate_to_fee(5, "cancel", 0)) + def consolidate_fanout(self, block_height): """Simulate the WT creating a consolidate-fanout (CF) tx which aims to 1) create coins from new re-fills that enable accurate feebumping and 2) to consolidate negligible feebump coins @@ -451,23 +455,38 @@ def consolidate_fanout(self, block_height): feerate = self._estimate_smart_feerate(block_height) except (ValueError, KeyError): feerate = self._feerate(block_height) - cf_size = cf_tx_size(num_inputs, num_outputs) cf_tx_fee = int(cf_size * feerate) # If there is any remainder, use it first to pay the fee for this transaction remainder = total_to_consume - (num_new_reserves * sum(fb_coins)) assert isinstance(remainder, int) - - # Check if remainder would cover the fee for the tx, if so, add the remainder-fee to the final new coin + # If we have more than we need for the CF fee.. if remainder > cf_tx_fee: - # FIXME: i think this can be large - self.fbcoins[-1]["amount"] += remainder - cf_tx_fee + # .. First try to opportunistically add a new fb coin + # FIXME: maybe add more than one? + added_coin_value = self.min_fbcoin_value(block_height) + if remainder > added_coin_value + cf_tx_fee + P2WPKH_OUTPUT_SIZE * feerate: + self.fbcoins.append( + { + "idx": self.fbcoin_count, + "amount": added_coin_value, + "allocation": None, + "processed": block_height, + } + ) + self.fbcoin_count += 1 + return cf_tx_fee + P2WPKH_OUTPUT_SIZE * feerate + + # And fallback to distribute the excess across the created fb coins + increase = (remainder - cf_tx_fee) // num_outputs + for c in self.fbcoins[len(self.fbcoins) - num_outputs :]: + c["amount"] += increase return cf_tx_fee else: if num_new_reserves == 1: logging.debug( - f" CF Tx failed sice num_new_reserves = 0 (accounting for expected fee)" + " CF Tx failed since num_new_reserves < 1 (accounting for expected fee)" ) # Not enough in available coins to fanout to 1 complete fee_reserve, when accounting # for the fee, so return to initial state and return 0 (as in, 0 fee paid)