def rebalance(self): if self.last_hop_channel: debug(("Sending {:,} satoshis to rebalance to channel with ID %d" % self.last_hop_channel.chan_id).format(self.amount)) else: debug("Sending {:,} satoshis.".format(self.amount)) if self.channel_ratio != 0.5: debug("Channel ratio used is %d%%" % int(self.channel_ratio * 100)) if self.first_hop_channel: debug("Forced first channel has ID %d" % self.first_hop_channel.chan_id) payment_request = self.generate_invoice() routes = Routes(self.lnd, payment_request, self.first_hop_channel, self.last_hop_channel) self.initialize_ignored_channels(routes) tried_routes = [] while routes.has_next(): route = routes.get_next() success = self.try_route(payment_request, route, routes, tried_routes) if success: return True debug("Could not find any suitable route") return False
def rebalance(self): debug("Sending %d satoshis to rebalance, remote pubkey: %s" % (self.amount, self.remote_pubkey)) payment_request = self.generate_invoice() routes = Routes(self.lnd, payment_request, self.remote_pubkey) if not routes.has_next(): debug("Could not find any suitable route") return None counter = 0 while routes.has_next(): counter += 1 debug("Trying route #%d" % counter) route = routes.get_next() response = self.lnd.send_payment(payment_request, [route]) is_successful = response.payment_error == "" if is_successful: fees_msat = response.payment_route.total_fees_msat fees_satoshi = round(float(fees_msat) / 1000.0, 3) debug("Success! Paid %d Satoshi in fees" % fees_satoshi) return response elif "TemporaryChannelFailure" in response.payment_error: debug("TemporaryChannelFailure (not enough funds along the route?)") else: debug("Error: %s" % response.payment_error) return None
def rebalance(self): first_hop_alias_formatted = "" last_hop_alias_formatted = "" first_channel_id = 0 if self.first_hop_channel: first_hop_alias_formatted = format_alias( self.lnd.get_node_alias(self.first_hop_channel.remote_pubkey)) first_channel_id = format_channel_id( self.first_hop_channel.chan_id) if self.last_hop_channel: last_hop_alias_formatted = format_alias( self.lnd.get_node_alias(self.last_hop_channel.remote_pubkey)) amount_formatted = format_amount(self.amount) if self.first_hop_channel and self.last_hop_channel: self.output.print_line( f"Sending {amount_formatted} satoshis from channel {first_channel_id} with {first_hop_alias_formatted} " f"back through {last_hop_alias_formatted}.") elif self.last_hop_channel: self.output.print_line( f"Sending {amount_formatted} satoshis back through {last_hop_alias_formatted}." ) else: self.output.print_line( f"Sending {self.amount:,} satoshis from channel {first_channel_id} with {first_hop_alias_formatted}." ) fee_limit_msat = self.get_fee_limit_msat() payment_request = self.generate_invoice() min_fee_last_hop = None if self.first_hop_channel: fee_rate_first_hop = self.lnd.get_ppm_to( self.first_hop_channel.chan_id) policy_first_hop = self.lnd.get_policy_to( self.first_hop_channel.chan_id) min_fee_last_hop = self.compute_fee(self.amount, fee_rate_first_hop, policy_first_hop) routes = Routes(self.lnd, payment_request, self.first_hop_channel, self.last_hop_channel, fee_limit_msat, self.output) self.initialize_ignored_channels(routes, fee_limit_msat, min_fee_last_hop) tried_routes = [] while routes.has_next(): route = routes.get_next() success = self.try_route(payment_request, route, routes, tried_routes) if success: return True self.output.print_line("Could not find any suitable route") self.lnd.cancel_invoice(payment_request.payment_hash) return False
def rebalance(self): debug(("Sending {:,} satoshis to rebalance to channel with ID %d" % self.last_hop_channel.chan_id).format(self.amount)) if self.channel_ratio != 0.5: debug("Channel ratio used is %d%%" % int(self.channel_ratio * 100)) if self.first_hop_channel_id: debug("Forced first channel has ID %d" % self.first_hop_channel_id) payment_request = self.generate_invoice() routes = Routes(self.lnd, payment_request, self.first_hop_channel_id, self.last_hop_channel) self.initialize_ignored_channels(routes) tried_routes = [] while routes.has_next(): route = routes.get_next() if self.route_is_invalid(route, routes): continue tried_routes.append(route) debug("") debug("Trying route #%d" % len(tried_routes)) debug(Routes.print_route(route)) response = self.lnd.send_payment(payment_request, route) is_successful = response.failure.code == 0 if is_successful: debug("Success! Paid fees: %s sat (%s msat)" % (route.total_fees, route.total_fees_msat)) debug("Successful route:") debug(Routes.print_route(route)) debug("") debug("Tried routes:") debug("\n".join(Routes.print_route(route) for route in tried_routes)) return else: code = response.failure.code failure_source_pubkey = self.bytes_to_hex_string(response.failure.failure_source_pubkey) if code == 15: debugnobreak("Temporary channel failure, ") routes.ignore_edge_on_route(failure_source_pubkey, route) elif code == 18: debugnobreak("Unknown next peer, ") routes.ignore_edge_on_route(failure_source_pubkey, route) elif code == 12: debugnobreak("Fee insufficient, ") routes.ignore_edge_on_route(failure_source_pubkey, route) else: debug(repr(response)) debug("Unknown error code %s" % repr(code)) debug("Could not find any suitable route") return None
def rebalance(self): if self.last_hop_channel: debug("Ⓘ Sending " + fmt.col_hi("{:,}".format(self.amount)) + " satoshis to rebalance to channel with ID %s" % fmt.col_lo(fmt.print_chanid(self.last_hop_channel.chan_id))) else: debug("Ⓘ Sending " + fmt.col_hi("{:,}".format(self.amount)) + " satoshis.") if self.channel_ratio != 0.5: debug("Ⓘ Channel ratio used is " + fmt.col_hi("%d%%" % int(self.channel_ratio * 100))) if self.first_hop_channel: debug("Ⓘ Forced first channel has ID %s" % fmt.col_lo(fmt.print_chanid(self.first_hop_channel.chan_id))) payment_request = self.generate_invoice() if self.path: myroute = self.lnd.build_route(self.path, self.amount, self.first_hop_channel.chan_id) if isinstance(myroute, Exception): debug("") debug(fmt.col_err("✘ " + myroute.details())) return False try: success = self.try_route(payment_request, myroute, [myroute], []) if success: return True except: # since we use --path, myroute isn't a real Routes object # assume fees too high debug(fmt.col_err("✘ fees too high")) return False else: routes = Routes(self.lnd, payment_request, self.first_hop_channel, self.last_hop_channel, self.deep) self.initialize_ignored_channels(routes) tried_routes = [] while routes.has_next(): route = routes.get_next() success = self.try_route(payment_request, route, routes, tried_routes) if success: return True debug("") debug(fmt.col_err("✘ Could not find any suitable route")) return False
def rebalance(self): debug(("Sending {:,} satoshis to rebalance to channel with ID %d" % self.last_hop_channel.chan_id).format(self.amount)) if self.channel_ratio != 0.5: debug("Channel ratio used is %d%%" % int(self.channel_ratio * 100)) if self.first_hop_channel_id: debug("Forced first channel has ID %d" % self.first_hop_channel_id) payment_request = self.generate_invoice() routes = Routes(self.rpc, payment_request, self.first_hop_channel_id, self.last_hop_channel) if not routes.has_next(): debug("Could not find any suitable route") return None tried_routes = [] while routes.has_next(): route = routes.get_next() if self.route_is_invalid(route): continue tried_routes.append(route) debug("Trying route #%d" % len(tried_routes)) debug(Routes.print_route(route)) response = self.rpc.send_payment_sync(payment_request, [route]) is_successful = response.payment_error == "" if is_successful: fees_msat = response.payment_route.total_fees_msat fees_satoshi = round(float(fees_msat) / 1000.0, 3) debug("Success! Paid %d Satoshi in fees" % fees_satoshi) debug("Tried routes:") debug("\n".join( Routes.print_route(route) for route in tried_routes)) debug("Successful route:") debug(Routes.print_route(route)) return response elif "TemporaryChannelFailure" in response.payment_error: debug( "TemporaryChannelFailure (not enough funds along the route?)" ) else: debug("Error: %s" % response.payment_error) return None
def rebalance(self): self.update_channels() if self.channels_balanced(): debug("Done with rabalancing %d and %d" % (self.first_hop_channel_id, self.last_hop_channel_id)) return True if self.amount_too_big(): debug("Amount %d is too big for current local and/or remote balance of first and/or last hop channel." % self.amount) return False debug(("Sending {:,} satoshis to rebalance to channel with ID %d from channel with ID %d" % (self.last_hop_channel.chan_id, self.first_hop_channel.chan_id)).format(self.amount)) payment_request = self.generate_invoice() routes = Routes(self.lnd, payment_request, self.first_hop_channel, self.last_hop_channel, self.max_routes_to_request) tried_routes = [] while routes.has_next(): route = routes.get_next() success = self.try_route(payment_request, route, routes, tried_routes) if success: self.last_hop_channel.local_balance += self.amount self.last_hop_channel.remote_balance -= self.amount self.last_hop_channel.local_balance -= (self.amount+route.total_fees_msat//1000) self.last_hop_channel.remote_balance += self.amount if self.channels_balanced(): debug("Done with rabalancing %d and %d" % (self.first_hop_channel.chan_id, self.last_hop_channel.chan_id)) return True if self.amount_too_big(): debug( "Amount %d is too big for current local and/or remote balance of first and/or last hop channel." % self.amount) return False return Logic(self.lnd, self.first_hop_channel_id, self.last_hop_channel_id, self.from_ratio, self.to_ratio, self.amount, self.max_amount_halvings, self.max_fee_factor, self.max_routes_to_request, self.num_amount_halvings + 1).rebalance() debug("All routes exhausted.") if self.num_amount_halvings < self.max_amount_halvings: return Logic(self.lnd, self.first_hop_channel_id, self.last_hop_channel_id, self.from_ratio, self.to_ratio, self.amount // 2, self.max_amount_halvings, self.max_fee_factor // 2, self.max_routes_to_request, self.num_amount_halvings + 1).rebalance() return False