Skip to content

Commit

Permalink
specify channels using ID instead of pubkey
Browse files Browse the repository at this point in the history
there might be more than one channel between nodes

fixes C-Otto#15
  • Loading branch information
C-Otto committed Jan 6, 2019
1 parent 1194d0e commit 0f1296d
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 51 deletions.
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ rebalance:
Rebalance a channel. You need to specify at least the 'to' channel (-t).
-f CHANNEL, --from CHANNEL
public key identifying the outgoing channel (funds
will be taken from this channel)
channel ID of the outgoing channel (funds will be
taken from this channel)
-t CHANNEL, --to CHANNEL
public key identifying the incoming channel (funds
will be sent to this channel). You may also use the
index as shown in the incoming candidate list (-l -i).
channel ID of the incoming channel (funds will be sent
to this channel). You may also use the index as shown
in the incoming candidate list (-l -i).
-a AMOUNT, --amount AMOUNT
Amount of the rebalance, in satoshis. If not
specified, the amount computed for a perfect rebalance
Expand All @@ -59,8 +59,8 @@ You can also see the list of channels where more than 50% of the total funds are
As an example the following indicates a channel with around 17.7% of the funds on the local side:

```
(23) Pubkey: 012345[...]abcdef
Channel ID: 123[...]456
(23) Channel ID: 123[...]456
Pubkey: 012345[...]abcdef
Local ratio: 0.176
Capacity: 5,000,000
Remote balance: 4,110,320
Expand All @@ -77,17 +77,16 @@ The total width is determined by the channel's capacity, where a channel with ma
occupies the full width of your terminal.
The bar (`=`) indicates the funds on the local side of the channel.

The number next to the pubkey (23 in the example) can be used to directly reference this channel.
The number next to the channel ID (23 in the example) can be used to directly reference this channel.

### Rebalancing a channel
To actually rebalance a channel, run the script and specify the channel to send funds to using `-t`.
Optionally you can also specify the channel to take the funds from (using `-f`), and the amount to send (using `-a`).
You specify the channel(s) using the public key (pubkey) of the node at the other side of the channel,
as shown in the output of `rebalance.py`.
You specify the channel(s) using the channel ID, as shown in the output of `rebalance.py`.

`rebalance.py -t 012345[...]abcdef -a 1613478`
`rebalance.py -t 123[...]456 -a 1613478`

It is also possible to indicate the `--to/-t` channel by the number shown next to the Pubkey (23 in the example).
It is also possible to indicate the `--to/-t` channel by the number shown next to the channel ID (23 in the example).

`rebalance.py -t 23 -a 1613478`

Expand Down
16 changes: 8 additions & 8 deletions logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ def debug(message):


class Logic:
def __init__(self, lnd, first_hop_pubkey, remote_pubkey, amount):
def __init__(self, lnd, first_hop_channel_id, last_hop_channel_id, amount):
self.lnd = lnd
self.first_hop_pubkey = first_hop_pubkey
self.remote_pubkey = remote_pubkey
self.first_hop_channel_id = first_hop_channel_id
self.last_hop_channel_id = last_hop_channel_id
self.amount = amount

def rebalance(self):
debug(("Sending {:,} satoshis to rebalance, remote pubkey: %s" % self.remote_pubkey).format(self.amount))
if self.first_hop_pubkey:
debug("Forced first pubkey is: %s" % self.first_hop_pubkey)
debug(("Sending {:,} satoshis to rebalance to channel with ID %d" % self.last_hop_channel_id).format(self.amount))
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_pubkey, self.remote_pubkey)
routes = Routes(self.lnd, payment_request, self.first_hop_channel_id, self.last_hop_channel_id)

if not routes.has_next():
debug("Could not find any suitable route")
Expand Down Expand Up @@ -53,5 +53,5 @@ def rebalance(self):
return None

def generate_invoice(self):
memo = "Rebalance of channel to " + self.remote_pubkey
memo = "Rebalance of channel to %d" % self.last_hop_channel_id
return self.lnd.generate_invoice(memo, self.amount)
41 changes: 22 additions & 19 deletions rebalance.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@

MAX_CHANNEL_CAPACITY = 16777215
MAX_SATOSHIS_PER_TRANSACTION = 4294967
PUBLIC_KEY_LENGTH = 66


def main():
argument_parser = get_argument_parser()
arguments = argument_parser.parse_args()
from_channel = vars(arguments)['from']
first_hop_channel_id = vars(arguments)['from']
to_channel = arguments.to

if arguments.incoming is not None and not arguments.list_candidates:
Expand All @@ -36,35 +35,37 @@ def main():
argument_parser.print_help()
sys.exit()

# the 'to' argument might be an index, or a pubkey
if to_channel and len(to_channel) < PUBLIC_KEY_LENGTH:
# the 'to' argument might be an index, or a channel ID
if to_channel and to_channel < 10000:
# here we are in the "channel index" case
index = int(to_channel) - 1
candidates = get_incoming_rebalance_candidates()
candidate = candidates[index]
remote_pubkey = candidate.remote_pubkey
last_hop_channel_id = candidate.chan_id
else:
# else the channel argument should be the node's pubkey
remote_pubkey = to_channel
# else the channel argument should be the channel ID
last_hop_channel_id = to_channel

amount = get_amount(arguments, from_channel, remote_pubkey)
amount = get_amount(arguments, first_hop_channel_id, last_hop_channel_id)

if amount == 0:
print("Amount is 0, nothing to do")
sys.exit()

response = Logic(lnd, from_channel, remote_pubkey, amount).rebalance()
response = Logic(lnd, first_hop_channel_id, last_hop_channel_id, amount).rebalance()
if response:
print(response)


def get_amount(arguments, from_channel, remote_pubkey):
def get_amount(arguments, first_hop_channel_id, last_hop_channel_id):
if arguments.amount:
amount = int(arguments.amount)
else:
amount = get_rebalance_amount(get_channel_for_pubkey(remote_pubkey))
if from_channel:
rebalance_amount_from_channel = get_rebalance_amount(get_channel_for_pubkey(from_channel))
last_hop_channel = get_channel_for_channel_id(last_hop_channel_id)
amount = get_rebalance_amount(last_hop_channel)
if first_hop_channel_id:
first_hop_channel = get_channel_for_channel_id(first_hop_channel_id)
rebalance_amount_from_channel = get_rebalance_amount(first_hop_channel)
if amount > rebalance_amount_from_channel:
amount = rebalance_amount_from_channel

Expand All @@ -74,9 +75,9 @@ def get_amount(arguments, from_channel, remote_pubkey):
return amount


def get_channel_for_pubkey(remote_pubkey):
def get_channel_for_channel_id(channel_id):
for channel in lnd.get_channels():
if channel.remote_pubkey == remote_pubkey:
if channel.chan_id == channel_id:
return channel
return None

Expand Down Expand Up @@ -104,11 +105,13 @@ def get_argument_parser():
" the 'to' channel (-t).")
rebalance_group.add_argument("-f", "--from",
metavar="CHANNEL",
help="public key identifying the outgoing channel "
type=int,
help="channel ID of the outgoing channel "
"(funds will be taken from this channel)")
rebalance_group.add_argument("-t", "--to",
metavar="CHANNEL",
help="public key identifying the incoming channel "
type=int,
help="channel ID of the incoming channel "
"(funds will be sent to this channel). "
"You may also use the index as shown in the incoming candidate list (-l -i).")
rebalance_group.add_argument("-a", "--amount",
Expand Down Expand Up @@ -138,8 +141,8 @@ def list_candidates(candidates):
if rebalance_amount_int > MAX_SATOSHIS_PER_TRANSACTION:
rebalance_amount += " (max per transaction: {:,})".format(MAX_SATOSHIS_PER_TRANSACTION)

print("(%2d) Pubkey: " % index + candidate.remote_pubkey)
print("Channel ID: " + str(candidate.chan_id))
print("(%2d) Channel ID: " % index + str(candidate.chan_id))
print("Pubkey: " + candidate.remote_pubkey)
print("Local ratio: {:.3f}".format(get_local_ratio(candidate)))
print("Capacity: {:,}".format(candidate.capacity))
print("Remote balance: {:,}".format(candidate.remote_balance))
Expand Down
24 changes: 12 additions & 12 deletions routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ def debug(message):


class Routes:
def __init__(self, lnd, payment_request, first_hop_pubkey, remote_pubkey):
def __init__(self, lnd, payment_request, first_hop_channel_id, last_hop_channel_id):
self.lnd = lnd
self.payment = payment_request
self.first_hop_pubkey = first_hop_pubkey
self.remote_pubkey = remote_pubkey
self.rebalance_channel = self.get_channel(self.remote_pubkey)
self.first_hop_channel_id = first_hop_channel_id
self.last_hop_channel_id = last_hop_channel_id
self.rebalance_channel = self.get_channel_for_channel_id(self.last_hop_channel_id)
self.all_routes = []
self.returned_routes = []
self.num_requested_routes = 0
Expand Down Expand Up @@ -45,7 +45,7 @@ def update_routes(self):
self.request_routes(num_routes_to_request)

def request_routes(self, num_routes_to_request):
routes = self.lnd.get_routes(self.remote_pubkey, self.get_amount(), num_routes_to_request)
routes = self.lnd.get_routes(self.rebalance_channel.remote_pubkey, self.get_amount(), num_routes_to_request)
self.num_requested_routes = num_routes_to_request
for route in routes:
modified_route = self.add_rebalance_channel(route)
Expand Down Expand Up @@ -76,21 +76,21 @@ def route_is_invalid(self, route):
return False

def does_not_have_requested_first_hop(self, first_hop):
if not self.first_hop_pubkey:
if not self.first_hop_channel_id:
return False
return first_hop.pub_key != self.first_hop_pubkey
return first_hop.chan_id != self.first_hop_channel_id

def low_local_ratio_after_sending(self, first_hop):
pub_key = first_hop.pub_key
channel = self.get_channel(pub_key)
channel_id = first_hop.chan_id
channel = self.get_channel_for_channel_id(channel_id)

remote = channel.remote_balance + self.get_amount()
local = channel.local_balance - self.get_amount()
ratio = float(local) / (remote + local)
return ratio < 0.5

def target_is_first_hop(self, first_hop):
return first_hop.pub_key == self.rebalance_channel.remote_pubkey
return first_hop.chan_id == self.rebalance_channel.chan_id

@staticmethod
def print_route(route):
Expand All @@ -100,7 +100,7 @@ def print_route(route):
def get_amount(self):
return self.payment.num_satoshis

def get_channel(self, pubkey):
def get_channel_for_channel_id(self, channel_id):
for channel in self.lnd.get_channels():
if channel.remote_pubkey == pubkey:
if channel.chan_id == channel_id:
return channel

0 comments on commit 0f1296d

Please sign in to comment.