Example #1
0
    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
Example #2
0
    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
Example #3
0
    def circle_and_check(self,
                         channel_number_send,
                         channel_number_receive,
                         amount_sat,
                         expected_fees_msat,
                         budget_sat=20,
                         max_effective_fee_rate=50,
                         dry=False):
        """
        Helper function for testing a circular payment.

        :param channel_number_send: channel whose local balance is decreased
        :type channel_number_send: int

        :param channel_number_receive: channel whose local balance is increased
        :type channel_number_receive: int

        :param amount_sat: amount in satoshi to rebalance
        :type amount_sat: int

        :param expected_fees_msat: expected fees in millisatoshi for
            the rebalance
        :type expected_fees_msat: int

        :param budget_sat: budget for rebalancing
        :type budget_sat: int

        :param max_effective_fee_rate: the maximal effective fee rate accepted
        :type max_effective_fee_rate: int

        :param dry: if it should be a dry run
        :type dry: bool
        """

        self.rebalancer = Rebalancer(
            self.lndnode,
            max_effective_fee_rate=max_effective_fee_rate,
            budget_sat=budget_sat,
        )

        graph_before = self.testnet.assemble_graph()
        channel_id_send = self.testnet.channel_mapping[channel_number_send][
            'channel_id']
        channel_id_receive = self.testnet.channel_mapping[
            channel_number_receive]['channel_id']
        invoice_r_hash = self.lndnode.get_invoice(amount_sat, '')

        # exercise
        try:
            fees_msat = self.rebalancer.rebalance_two_channels(
                channel_id_send,
                channel_id_receive,
                amount_sat,
                invoice_r_hash,
                budget_sat,
                dry=dry)
            time.sleep(SLEEP_SEC_AFTER_REBALANCING)
        except Exception as e:
            raise e

        # check
        graph_after = self.testnet.assemble_graph()
        channel_data_send_before = graph_before['A'][channel_number_send]
        channel_data_receive_before = graph_before['A'][channel_number_receive]
        channel_data_send_after = graph_after['A'][channel_number_send]
        channel_data_receive_after = graph_after['A'][channel_number_receive]
        listchannels = ListChannels(self.lndnode)
        listchannels.print_all_channels('rev_alias')

        # test that the fees are correct
        self.assertEqual(fees_msat, expected_fees_msat)
        # test if sending channel's remote balance has increased correctly
        self.assertEqual(
            amount_sat, channel_data_send_after['remote_balance'] -
            channel_data_send_before['remote_balance'] -
            int(expected_fees_msat // 1000), "Sending local balance is wrong")
        # test if receiving channel's local balance has increased correctly
        self.assertEqual(
            amount_sat, channel_data_receive_after['local_balance'] -
            channel_data_receive_before['local_balance'],
            "Receiving local balance is wrong")
Example #4
0
    def run_commands(self, node, args):
        # program execution
        if args.loglevel:
            # update the loglevel of the stdout handler to the user choice
            logger.handlers[0].setLevel(args.loglevel)

        if args.cmd == 'status':
            node.print_status()

        elif args.cmd == 'listchannels':
            listchannels = ListChannels(node)
            if not args.subcmd:
                listchannels.print_all_channels('rev_alias')
            if args.subcmd == 'rebalance':
                listchannels.print_channels_unbalanced(
                    args.unbalancedness, sort_string=args.sort_by)
            elif args.subcmd == 'inactive':
                listchannels.print_channels_inactive(sort_string=args.sort_by)
            elif args.subcmd == 'forwardings':
                # convert time interval into unix timestamp
                time_from = time.time() - args.from_days_ago * 24 * 60 * 60
                time_to = time.time() - args.to_days_ago * 24 * 60 * 60
                logger.info(f"Forwardings from {args.from_days_ago} days ago"
                            f" to {args.to_days_ago} days ago are included.")
                listchannels.print_channels_forwardings(
                    time_interval_start=time_from,
                    time_interval_end=time_to,
                    sort_string=args.sort_by)
            elif args.subcmd == 'hygiene':
                time_from = time.time() - args.from_days_ago * 24 * 60 * 60
                logger.info(f"Channel hygiene stats is over last "
                            f"{args.from_days_ago} days.")
                listchannels.print_channels_hygiene(
                    time_interval_start=time_from, sort_string=args.sort_by)

        elif args.cmd == 'rebalance':
            if args.target:
                logger.warning("Warning: Target is set, this is still an "
                               "experimental feature.")
            rebalancer = Rebalancer(node, args.max_fee_rate, args.max_fee_sat)
            try:
                rebalancer.rebalance(args.channel,
                                     dry=not args.reckless,
                                     chunksize=args.chunksize,
                                     target=args.target,
                                     allow_unbalancing=args.allow_unbalancing,
                                     strategy=args.strategy)
            except TooExpensive as e:
                logger.error(f"Too expensive: {e}")
            except RebalanceFailure as e:
                logger.error(f"Rebalance failure: {e}")

        elif args.cmd == 'circle':
            rebalancer = Rebalancer(node, args.max_fee_rate, args.max_fee_sat)
            invoice_r_hash = node.get_rebalance_invoice(
                memo='circular payment')
            try:
                rebalancer.rebalance_two_channels(args.channel_from,
                                                  args.channel_to,
                                                  args.amt_sat,
                                                  invoice_r_hash,
                                                  args.max_fee_sat,
                                                  dry=not args.reckless)
            except DryRun:
                logger.info("This was just a dry run.")
            except TooExpensive:
                logger.error(
                    "Too expensive: consider to raise --max-fee-sat or "
                    "--max-fee-rate.")
            except RebalancingTrialsExhausted:
                logger.error(
                    f"Rebalancing trials exhausted (number of trials: "
                    f"{settings.REBALANCING_TRIALS}).")
            except PaymentTimeOut:
                logger.error("Payment failed because the payment timed out.")

        elif args.cmd == 'recommend-nodes':
            if not args.subcmd:
                self.parser_recommend_nodes.print_help()
                return 0

            recommend_nodes = RecommendNodes(
                node,
                show_connected=args.show_connected,
                show_addresses=args.show_addresses)

            if args.subcmd == 'good-old':
                recommend_nodes.print_good_old(number_of_nodes=args.nnodes,
                                               sort_by=args.sort_by)
            elif args.subcmd == 'flow-analysis':
                recommend_nodes.print_flow_analysis(
                    out_direction=(not args.inwards),
                    number_of_nodes=args.nnodes,
                    forwarding_events=args.forwarding_events,
                    sort_by=args.sort_by)
            elif args.subcmd == 'external-source':
                recommend_nodes.print_external_source(
                    args.source,
                    distributing_nodes=args.distributing_nodes,
                    number_of_nodes=args.nnodes,
                    sort_by=args.sort_by)
            elif args.subcmd == 'channel-openings':
                recommend_nodes.print_channel_openings(
                    from_days_ago=args.from_days_ago,
                    number_of_nodes=args.nnodes,
                    sort_by=args.sort_by)

        elif args.cmd == 'report':
            time_from = time.time() - args.from_days_ago * 24 * 60 * 60
            time_to = time.time() - args.to_days_ago * 24 * 60 * 60
            report = Report(node, time_from, time_to)
            report.report()
Example #5
0
from lndmanage.lib.listchannels import ListChannels
from lndmanage.lib.node import LndNode
from lndmanage import settings

import logging.config
logging.config.dictConfig(settings.logger_config)

if __name__ == '__main__':
    node = LndNode()
    listchannels = ListChannels(node)
    node.print_status()
    listchannels.print_channels_unbalanced(
        unbalancedness=settings.UNBALANCED_CHANNEL, sort_string='alias')