def test_unbalancedness_formula(self): self.assertAlmostEqual( channel_unbalancedness_and_commit_fee(500000, 1000000, 1000, False)[0], 0.0) self.assertAlmostEqual( channel_unbalancedness_and_commit_fee(500000, 1000000, 1000, True)[0], -0.002) self.assertAlmostEqual( channel_unbalancedness_and_commit_fee(600000, 1000000, 0, False)[0], -0.2)
def rebalance_and_check(self, test_channel_number, target, allow_unbalancing, places=5): """ Test function for rebalancing to a specific target unbalancedness and asserts afterwards that the target was reached. :param test_channel_number: channel id :type test_channel_number: int :param target: unbalancedness target :type target: float :param allow_unbalancing: if unbalancing should be allowed :type allow_unbalancing: bool :param places: accuracy of the comparison between expected and tested values :type places: int """ rebalancer = Rebalancer(self.lndnode, max_effective_fee_rate=50, budget_sat=20) channel_id = self.testnet.channel_mapping[test_channel_number][ 'channel_id'] try: fees_msat = rebalancer.rebalance( channel_id, dry=False, chunksize=1.0, target=target, allow_unbalancing=allow_unbalancing) except Exception as e: raise e # sleep a bit to let LNDs update their balances time.sleep(SLEEP_SEC_AFTER_REBALANCING) # check if graph has the desired channel balances graph = self.testnet.assemble_graph() channel_data = graph['A'][test_channel_number] listchannels = ListChannels(self.lndnode) listchannels.print_all_channels('rev_alias') channel_unbalancedness, _ = channel_unbalancedness_and_commit_fee( channel_data['local_balance'], channel_data['capacity'], channel_data['commit_fee'], channel_data['initiator']) self.assertAlmostEqual(target, channel_unbalancedness, places=places) return fees_msat
def rebalance_and_check(self, test_channel_number, target, allow_unbalancing, places=5): """ Test function for rebalancing to a specific target and assert after- wards that is was reached. :param test_channel_number: int :param target: float: unbalancedness target :param allow_unbalancing: bool: unbalancing should be allowed :param places: int number of digits the result should match to the requested :param should_fail: bool: indicates whether the rebalancing should fail as requested due to maybe unbalancing of other channels """ rebalancer = Rebalancer(self.lndnode, max_effective_fee_rate=50, budget_sat=20) channel_id = self.testnet.channel_mapping[test_channel_number][ 'channel_id'] try: fees_msat = rebalancer.rebalance( channel_id, dry=False, chunksize=1.0, target=target, allow_unbalancing=allow_unbalancing) except Exception as e: raise e time.sleep(SLEEP_SEC_AFTER_REBALANCING) graph = self.testnet.assemble_graph() channel_data = graph['A'][test_channel_number] listchannels = ListChannels(self.lndnode) listchannels.print_all_channels('rev_alias') channel_unbalancedness, _ = channel_unbalancedness_and_commit_fee( channel_data['local_balance'], channel_data['capacity'], channel_data['commit_fee'], channel_data['initiator']) self.assertAlmostEqual(target, channel_unbalancedness, places=places) return fees_msat
def get_open_channels(self, active_only=False, public_only=False): """ Fetches information (fee settings of the counterparty, channel capacity, balancedness) about this node's open channels and saves it into the channels dict attribute. :param active_only: only take active channels into account (off by default) :type active_only: bool :param public_only: only take public channels into account (off by default) :type public_only: bool :return: dict of channels sorted by remote pubkey :rtype: OrderedDict """ raw_channels = self._rpc.ListChannels( lnd.ListChannelsRequest(active_only=active_only, public_only=public_only)) try: channels_data = raw_channels.ListFields()[0][1] except IndexError: # If there are no channels, return. return OrderedDict({}) channels = OrderedDict() for c in channels_data: # calculate age from blockheight blockheight, _, _ = convert_channel_id_to_short_channel_id( c.chan_id) age_days = (self.blockheight - blockheight) * 10 / (60 * 24) try: sent_received_per_week = int( (c.total_satoshis_sent + c.total_satoshis_received) / (age_days / 7)) except ZeroDivisionError: # age could be zero right after channel becomes pending sent_received_per_week = 0 # determine policy try: edge_info = self.network.edges[c.chan_id] # interested in node2 if edge_info['node1_pub'] == self.pub_key: policy_peer = edge_info['node2_policy'] policy_local = edge_info['node1_policy'] else: # interested in node1 policy_peer = edge_info['node1_policy'] policy_local = edge_info['node2_policy'] except KeyError: # if channel is unknown in describegraph # we need to set the fees to some error value policy_peer = { 'fee_base_msat': float(-999), 'fee_rate_milli_msat': float(999) } policy_local = { 'fee_base_msat': float(-999), 'fee_rate_milli_msat': float(999) } # calculate last update (days ago) def convert_to_days_ago(timestamp): return (time.time() - timestamp) / (60 * 60 * 24) try: last_update = convert_to_days_ago( self.network.edges[c.chan_id]['last_update']) last_update_local = convert_to_days_ago( policy_local['last_update']) last_update_peer = convert_to_days_ago( policy_peer['last_update']) except (TypeError, KeyError): last_update = float('nan') last_update_peer = float('nan') last_update_local = float('nan') # define unbalancedness |ub| large means very unbalanced channel_unbalancedness, our_commit_fee = \ channel_unbalancedness_and_commit_fee( c.local_balance, c.capacity, c.commit_fee, c.initiator) try: uptime_lifetime_ratio = c.uptime / c.lifetime except ZeroDivisionError: uptime_lifetime_ratio = 0 channels[c.chan_id] = { 'active': c.active, 'age': age_days, 'alias': self.network.node_alias(c.remote_pubkey), 'amt_to_balanced': int(channel_unbalancedness * c.capacity / 2 - our_commit_fee), 'capacity': c.capacity, 'chan_id': c.chan_id, 'channel_point': c.channel_point, 'commit_fee': c.commit_fee, 'fee_per_kw': c.fee_per_kw, 'peer_base_fee': policy_peer['fee_base_msat'], 'peer_fee_rate': policy_peer['fee_rate_milli_msat'], 'local_base_fee': policy_local['fee_base_msat'], 'local_fee_rate': policy_local['fee_rate_milli_msat'], 'initiator': c.initiator, 'last_update': last_update, 'last_update_local': last_update_local, 'last_update_peer': last_update_peer, 'local_balance': c.local_balance, 'num_updates': c.num_updates, 'private': c.private, 'remote_balance': c.remote_balance, 'remote_pubkey': c.remote_pubkey, 'sent_received_per_week': sent_received_per_week, 'total_satoshis_sent': c.total_satoshis_sent, 'total_satoshis_received': c.total_satoshis_received, 'unbalancedness': channel_unbalancedness, 'uptime': c.uptime, 'lifetime': c.lifetime, 'uptime_lifetime_ratio': uptime_lifetime_ratio, } sorted_dict = OrderedDict( sorted(channels.items(), key=lambda x: x[1]['alias'])) return sorted_dict