Skip to content

Commit

Permalink
Merge branch 'grab_coins_reasonable'
Browse files Browse the repository at this point in the history
  • Loading branch information
darosior committed Sep 8, 2021
2 parents c5407cd + d4ba5d0 commit 4f3034a
Showing 1 changed file with 85 additions and 10 deletions.
95 changes: 85 additions & 10 deletions Model/statemachine.py
Expand Up @@ -22,6 +22,13 @@
)


class CfError(RuntimeError):
"""An error arising during the creation of the Consolidate-Fanout tx"""

def __init__(self, message):
self.message = message


class StateMachine:
"""Watchtower state machine."""

Expand Down Expand Up @@ -152,6 +159,7 @@ def _feerate(self, block_height):

return self.hist_df[self.estimate_strat][block_height]

# FIXME: remove tx_type!!
def _feerate_to_fee(self, feerate, tx_type, n_fb_inputs):
"""Convert feerate (satoshi/vByte) into transaction fee (satoshi).
Expand Down Expand Up @@ -273,15 +281,17 @@ def refill(self, amount):
self.fbcoins.append(utxo)

def grab_coins_0(self, block_height):
"""Select coins to consume as inputs for the tx transaction,
"""Select coins to consume as inputs for the CF transaction,
remove them from P and V.
This version grabs all the existing feebump coins.
Return: total amount of consumed inputs, number of inputs
"""
num_inputs = 0
# Take all fbcoins, get their total amount,
# and remove them from self.fbcoins
total = 0

# FIXME: delete the list, don't delete each elem
# loop over copy of the list since the remove() method changes list indexing
for coin in list(self.fbcoins):
total += coin["amount"]
Expand All @@ -291,15 +301,19 @@ def grab_coins_0(self, block_height):
return total, num_inputs

def grab_coins_1(self, block_height):
"""Select coins to consume as inputs for the tx transaction,
"""Select coins to consume as inputs for the CF transaction,
remove them from P and V.
This version grabs all the coins that either haven't been processed yet, are
negligible, or are not allocated and have been processed a long time ago.
Return: total amount of consumed inputs, number of inputs
"""
num_inputs = 0
# Take all fbcoins that haven't been processed, get their total amount,
# and remove them from self.fbcoins
total_unprocessed = 0

# FIXME: don't loop 3 times! Also may be more efficient to re-create the list
# instead of removing that much.
# loop over copy of the list since the remove() method changes list indexing
for coin in list(self.fbcoins):
if coin["processed"] == None:
Expand Down Expand Up @@ -337,17 +351,20 @@ def grab_coins_1(self, block_height):
return total_to_consume, num_inputs

def grab_coins_2(self, block_height):
"""Select coins to consume as inputs for the tx transaction,
"""Select coins to consume as inputs for the CF transaction,
remove them from P and V.
- unprocessed
- unallocated and not in O(t) and old
- negligible
This version grabs all the coins that either haven't been processed yet, are
negligible, or are unallocated and have been processed a long time ago and are
not in the ideal coin distribution.
Return: total amount of consumed inputs, number of inputs
"""
fb_coins = self.fb_coins_dist(block_height)
num_inputs = 0

# FIXME: don't loop 3 times! Also may be more efficient to re-create the list
# instead of removing that much.
# Take all fbcoins that haven't been processed, get their total amount,
# and remove them from self.fbcoins
total_unprocessed = 0
Expand All @@ -364,6 +381,7 @@ def grab_coins_2(self, block_height):
total_unallocated = 0
old_age = 12 * 7 * 144 # 13 weeks worth of blocks
for coin in list(self.fbcoins):
# FIXME: this is *so* unlikely that the amount will be in fb_coins
if coin["allocation"] == None and coin["amount"] not in fb_coins:
if block_height - coin["processed"] > old_age:
assert isinstance(coin["amount"], int)
Expand All @@ -383,6 +401,59 @@ def grab_coins_2(self, block_height):
total_to_consume = total_unprocessed + total_unallocated + total_negligible
return total_to_consume, num_inputs

def grab_coins_3(self, height):
"""Select coins to consume as inputs of the CF transaction.
This version grabs all coins that were just refilled. In addition it grabs
some fb coins for consolidation:
- Allocated ones that it's not safe to keep (those that would not bump the
Cancel tx feerate at the reserve (max) feerate).
- Unallocated ones ones we would not create, if the current feerate is low.
Returns the total amount to consolidate and the number of coins to consolidate.
"""
to_keep = []
n_to_consolidate = 0
total_to_consolidate = 0

reserve_feerate = self._feerate_reserve_per_vault(height)
dust_threshold = reserve_feerate * P2WPKH_INPUT_SIZE + self._feerate_to_fee(
1, "cancel", 0
)
# FIXME: this should use the next 3 blocks feerate
low_feerate = self._feerate(height) <= 5
min_fbcoin_value = self.min_fbcoin_value(height)
for coin in self.fbcoins:
if (
(coin["processed"] is None)
or (coin["allocation"] is not None and coin["amount"] < dust_threshold)
or (
coin["allocation"] is None
and low_feerate
and coin["amount"] < min_fbcoin_value
)
):
n_to_consolidate += 1
total_to_consolidate += coin["amount"]
# FIXME: index!!
for v in self.vaults:
if coin in v["fee_reserve"]:
v["fee_reserve"].remove(coin)
break
else:
to_keep.append(coin)

self.fbcoins = to_keep
return total_to_consolidate, n_to_consolidate

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
Expand Down Expand Up @@ -414,6 +485,10 @@ def consolidate_fanout(self, block_height):
total_to_consume, num_inputs = self.grab_coins_1(block_height)
elif self.I_version == 2:
total_to_consume, num_inputs = self.grab_coins_2(block_height)
elif self.I_version == 3:
total_to_consume, num_inputs = self.grab_coins_3(block_height)
else:
raise CfError("Unknown algorithm version for coin consolidation")

# Counter for number of outputs of the CF Tx
num_outputs = 0
Expand Down

0 comments on commit 4f3034a

Please sign in to comment.