Exemplo n.º 1
0
    def initialize_ignored_channels(self, routes, fee_limit_msat,
                                    min_fee_last_hop):
        if self.reckless:
            self.output.print_line(
                format_error(
                    "Also considering economically unviable channels for routes."
                ))
        for chan_id in self.excluded:
            self.output.print_line(
                f"Channel {format_channel_id(chan_id)} is excluded:")
            routes.ignore_channel(chan_id)
        if self.first_hop_channel:
            if min_fee_last_hop and not self.reckless:
                self.ignore_cheap_channels_for_last_hop(
                    min_fee_last_hop, routes)
            if not self.last_hop_channel and not self.reckless:
                self.ignore_last_hops_with_low_inbound(routes)

            # avoid me - X - me via the same channel/peer
            chan_id = self.first_hop_channel.chan_id
            from_pub_key = self.first_hop_channel.remote_pubkey
            to_pub_key = self.lnd.get_own_pubkey()
            routes.ignore_edge_from_to(chan_id,
                                       from_pub_key,
                                       to_pub_key,
                                       show_message=False)
        if self.last_hop_channel:
            if not self.reckless:
                self.ignore_first_hops_with_fee_rate_higher_than_last_hop(
                    routes)
            # avoid me - X - me via the same channel/peer
            chan_id = self.last_hop_channel.chan_id
            from_pub_key = self.lnd.get_own_pubkey()
            to_pub_key = self.last_hop_channel.remote_pubkey
            routes.ignore_edge_from_to(chan_id,
                                       from_pub_key,
                                       to_pub_key,
                                       show_message=False)
        if self.last_hop_channel and fee_limit_msat and not self.reckless:
            # ignore first hops with high fee rate configured by our node (causing high missed future fees)
            max_fee_rate_first_hop = math.ceil(fee_limit_msat * 1_000 /
                                               self.amount)
            for channel in self.lnd.get_channels():
                try:
                    fee_rate = self.lnd.get_ppm_to(channel.chan_id)
                    if fee_rate > max_fee_rate_first_hop and self.first_hop_channel != channel:
                        routes.ignore_first_hop(channel, show_message=False)
                except:
                    pass
        for channel in self.lnd.get_channels():
            if self.low_outbound_liquidity_after_sending(channel, self.amount):
                routes.ignore_first_hop(channel, show_message=False)
Exemplo n.º 2
0
 def handle_error(self, response, route, routes):
     code = response.failure.code
     failure_source_pubkey = Logic.get_failure_source_pubkey(
         response, route)
     if code == 15:
         self.output.print_line(format_warning("Temporary channel failure"))
         routes.ignore_edge_on_route(failure_source_pubkey, route)
     elif code == 18:
         self.output.print_line(format_warning("Unknown next peer"))
         routes.ignore_edge_on_route(failure_source_pubkey, route)
     elif code == 12:
         self.output.print_line(format_warning("Fee insufficient"))
     elif code == 14:
         self.output.print_line(format_warning("Channel disabled"))
         routes.ignore_edge_on_route(failure_source_pubkey, route)
     elif code == 13:
         self.output.print_line(format_warning("Incorrect CLTV expiry"))
         routes.ignore_edge_on_route(failure_source_pubkey, route)
     else:
         self.output.print_line(
             format_error(f"Unknown error code {repr(code)}:"))
         self.output.print_line(format_error(repr(response)))
Exemplo n.º 3
0
    def get_amount(self):
        amount = None
        if self.arguments.amount:
            if self.arguments.reckless and self.arguments.amount > MAX_SATOSHIS_PER_TRANSACTION:
                self.output.print_line(
                    format_error("Trying to send wumbo transaction"))
                return self.arguments.amount
            else:
                amount = min(self.arguments.amount,
                             MAX_SATOSHIS_PER_TRANSACTION)
                if not self.arguments.adjust_amount_to_limits:
                    return amount

        should_send = 0
        can_send = 0
        if self.first_hop_channel:
            should_send = -self.get_rebalance_amount(self.first_hop_channel)
            can_send = self.get_amount_can_send(self.first_hop_channel)

            if can_send < 0:
                from_alias = self.lnd.get_node_alias(
                    self.first_hop_channel.remote_pubkey)
                print(
                    f"Error: source channel {format_channel_id(self.first_hop_channel.chan_id)} to "
                    f"{format_alias(from_alias)} needs to {chalk.green('receive')} funds to be within bounds,"
                    f" you want it to {chalk.red('send')} funds. "
                    "Specify amount manually if this was intended.")
                return 0

        should_receive = 0
        can_receive = 0
        if self.last_hop_channel:
            should_receive = self.get_rebalance_amount(self.last_hop_channel)
            can_receive = self.get_amount_can_receive(self.last_hop_channel)

            if can_receive < 0:
                to_alias = self.lnd.get_node_alias(
                    self.last_hop_channel.remote_pubkey)
                print(
                    f"Error: target channel {format_channel_id(self.last_hop_channel.chan_id)} to "
                    f"{format_alias(to_alias)} needs to {chalk.green('send')} funds to be within bounds, "
                    f"you want it to {chalk.red('receive')} funds."
                    f" Specify amount manually if this was intended.")
                return 0

        if self.first_hop_channel and self.last_hop_channel:
            computed_amount = max(min(can_receive, should_send),
                                  min(can_send, should_receive))
        elif self.first_hop_channel:
            computed_amount = should_send
        else:
            computed_amount = should_receive

        computed_amount = int(computed_amount)
        if computed_amount >= 0:
            computed_amount = min(computed_amount,
                                  MAX_SATOSHIS_PER_TRANSACTION)
        computed_amount = max(computed_amount, -MAX_SATOSHIS_PER_TRANSACTION)
        if amount is not None:
            if computed_amount >= 0:
                computed_amount = min(amount, computed_amount)
            else:
                computed_amount = max(-amount, computed_amount)
        return computed_amount
Exemplo n.º 4
0
    def start(self):
        if self.arguments.list_candidates and self.arguments.show_only:
            channel_id = self.parse_channel_id(self.arguments.show_only)
            channel = self.get_channel_for_channel_id(channel_id)
            self.show_channel(channel)
            sys.exit(0)

        if self.arguments.listcompact:
            self.list_channels_compact()
            sys.exit(0)

        if self.arguments.list_candidates:
            incoming = self.arguments.incoming is None or self.arguments.incoming
            if incoming:
                self.list_channels(reverse=False)
            else:
                self.list_channels(reverse=True)
            sys.exit(0)

        if self.first_hop_channel_id == -1:
            self.first_hop_channel = random.choice(
                self.get_first_hop_candidates())
        else:
            self.first_hop_channel = self.get_channel_for_channel_id(
                self.first_hop_channel_id)

        if self.last_hop_channel_id == -1:
            self.last_hop_channel = random.choice(
                self.get_last_hop_candidates())
        else:
            self.last_hop_channel = self.get_channel_for_channel_id(
                self.last_hop_channel_id)

        amount = self.get_amount()
        if self.arguments.percentage:
            new_amount = int(round(amount * self.arguments.percentage / 100))
            print(
                f"Using {self.arguments.percentage}% of amount {format_amount(amount)}: {format_amount(new_amount)}"
            )
            amount = new_amount

        if amount == 0:
            print(f"Amount is {format_amount(0)} sat, nothing to do")
            sys.exit(1)

        if amount < self.min_amount:
            print(
                f"Amount {format_amount(amount)} sat is below limit of {format_amount(self.min_amount)} sat, "
                f"nothing to do (see --min-amount)")
            sys.exit(1)

        if self.arguments.reckless:
            self.output.print_line(format_error("Reckless mode enabled!"))

        fee_factor = self.arguments.fee_factor
        fee_limit_sat = self.arguments.fee_limit
        fee_ppm_limit = self.arguments.fee_ppm_limit
        excluded = []
        if self.arguments.exclude:
            for chan_id in self.arguments.exclude:
                excluded.append(self.parse_channel_id(chan_id))
        return Logic(self.lnd, self.first_hop_channel, self.last_hop_channel,
                     amount, excluded, fee_factor, fee_limit_sat,
                     fee_ppm_limit, self.min_local, self.min_remote,
                     self.output, self.arguments.reckless).rebalance()
Exemplo n.º 5
0
 def fees_too_high(self, route, routes):
     policy_first_hop = self.lnd.get_policy_to(route.hops[0].chan_id)
     amount_msat = route.total_amt_msat
     missed_fee_msat = self.compute_fee(
         amount_msat / 1_000, policy_first_hop.fee_rate_milli_msat,
         policy_first_hop) * 1_000
     policy_last_hop = self.lnd.get_policy_to(route.hops[-1].chan_id)
     fee_rate_last_hop = policy_last_hop.fee_rate_milli_msat
     original_fee_rate_last_hop = fee_rate_last_hop
     if fee_rate_last_hop > MAX_FEE_RATE:
         fee_rate_last_hop = MAX_FEE_RATE
     expected_income_msat = self.fee_factor * self.compute_fee(
         amount_msat / 1_000, fee_rate_last_hop, policy_last_hop) * 1_000
     rebalance_fee_msat = route.total_fees_msat
     high_fees = rebalance_fee_msat + missed_fee_msat > expected_income_msat
     if high_fees:
         if self.reckless:
             self.output.print_line(
                 format_error("Considering route with high fees"))
             return False
         difference_msat = -rebalance_fee_msat - missed_fee_msat + expected_income_msat
         first_hop_alias = format_alias(
             self.lnd.get_node_alias(route.hops[0].pub_key))
         last_hop_alias = format_alias(
             self.lnd.get_node_alias(route.hops[-2].pub_key))
         self.output.print_line("")
         if fee_rate_last_hop != original_fee_rate_last_hop:
             self.output.print_line(
                 f"Calculating using capped fee rate {MAX_FEE_RATE} for inbound channel "
                 f"(with {last_hop_alias}, original fee rate {original_fee_rate_last_hop})"
             )
         route_ppm = int(route.total_fees_msat * 1_000_000 /
                         route.total_amt_msat)
         route_fee_formatted = format_fee_msat(rebalance_fee_msat)
         route_ppm_formatted = format_ppm(route_ppm)
         self.output.print_line(
             f"Skipping route due to high fees "
             f"(fee {route_fee_formatted}, {route_ppm_formatted})")
         self.output.print_route(route)
         future_income_formatted = format_earning(
             math.floor(expected_income_msat), 8)
         self.output.print_without_linebreak(
             f"  {future_income_formatted}: "
             f"expected future fee income for inbound channel (with {last_hop_alias})"
         )
         if self.fee_factor != 1.0:
             self.output.print_line(f" (factor {self.fee_factor})")
         else:
             self.output.print_line("")
         transaction_fees_formatted = format_fee_msat(
             int(rebalance_fee_msat), 8)
         missed_fee_formatted = format_fee_msat(math.ceil(missed_fee_msat),
                                                8)
         difference_formatted = format_fee_msat_red(
             math.ceil(difference_msat), 8)
         self.output.print_line(
             f"- {transaction_fees_formatted}: rebalance transaction fees")
         self.output.print_line(
             f"- {missed_fee_formatted}: "
             f"missing out on future fees for outbound channel (with {first_hop_alias})"
         )
         self.output.print_line(f"= {difference_formatted}")
         routes.ignore_high_fee_hops(route)
     return high_fees