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 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")
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()
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')