Skip to content

Commit

Permalink
feat: routing hints in hold invoice plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Aug 14, 2023
1 parent 97a6fe8 commit 84e01f7
Show file tree
Hide file tree
Showing 19 changed files with 683 additions and 140 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Check out code
uses: actions/checkout@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
with:
install: true

- name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
Expand Down
2 changes: 1 addition & 1 deletion docker/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class Image:
],
),
"regtest": Image(
tags=["4.0.0"],
tags=["4.0.1"],
arguments=[
UBUNTU_VERSION,
BITCOIN_BUILD_ARG,
Expand Down
24 changes: 17 additions & 7 deletions docker/regtest/scripts/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ function openChannel () {

waitForLndToSync "$2"

$2 connect $3@127.0.0.1:$4 > /dev/null
$2 openchannel --node_key $3 --local_amt 100000000 --push_amt 50000000 > /dev/null
$2 connect $3@127.0.0.1:$4 > /dev/null 2> /dev/null

if $5; then
$2 openchannel --node_key $3 --local_amt 100000000 --push_amt 50000000 --private > /dev/null
else
$2 openchannel --node_key $3 --local_amt 100000000 --push_amt 50000000 > /dev/null
fi

$1 generatetoaddress 6 ${nodeAddress} > /dev/null

Expand Down Expand Up @@ -54,8 +59,8 @@ function waitForClnChannel () {
sleep 25
}

echo "/tools/.venv/bin/python3 /tools/hold/plugin.py" > /root/hold-start.sh
chmod +x /root/hold-start.sh
echo "/tools/.venv/bin/python3 /tools/hold/plugin.py" > /root/hold.sh
chmod +x /root/hold.sh

startNodes

Expand All @@ -80,18 +85,23 @@ echo "Opening BTC channels"
openChannel bitcoin-cli \
"lncli --lnddir=/root/.lnd-btc --rpcserver=127.0.0.1:10009 --network=regtest" \
$(lncli --lnddir=/root/.lnd-btc --rpcserver=127.0.0.1:10011 --network=regtest getinfo | jq -r '.identity_pubkey') \
9736
9736 false
echo "Opened channel to LND"

openChannel bitcoin-cli \
"lncli --lnddir=/root/.lnd-btc --rpcserver=127.0.0.1:10009 --network=regtest" \
$(lightning-cli getinfo | jq -r .id) \
9737
9737 false

openChannel bitcoin-cli \
"lncli --lnddir=/root/.lnd-btc --rpcserver=127.0.0.1:10009 --network=regtest" \
$(lightning-cli getinfo | jq -r .id) \
9737 true

openChannel bitcoin-cli \
"lncli --lnddir=/root/.lnd-btc --rpcserver=127.0.0.1:10011 --network=regtest" \
$(lightning-cli getinfo | jq -r .id) \
9737
9737 false

echo "Opened channels to CLN"

Expand Down
2 changes: 1 addition & 1 deletion docker/regtest/startRegtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ docker run \
-p 31000:31000 \
-p 31001:31001 \
-p 31002:31002 \
boltz/regtest:4.0.0
boltz/regtest:4.0.1
31 changes: 17 additions & 14 deletions tools/hold/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@
from enum import Enum

from bolt11 import Bolt11, Feature, Features, FeatureState, encode
from bolt11.types import MilliSatoshi, Tag, TagChar, Tags
from bolt11.types import MilliSatoshi, RouteHint, Tag, TagChar, Tags
from consts import Network
from pyln.client import Plugin
from secp256k1 import PrivateKey
from utils import time_now

# TODO: routing hints


NETWORK_PREFIXES = {
Network.Mainnet: "bc",
Network.Testnet: "tb",
Expand Down Expand Up @@ -71,21 +68,27 @@ def encode(
expiry: int = Defaults.Expiry,
min_final_cltv_expiry: int = Defaults.MinFinalCltvExpiry,
payment_secret: str | None = None,
route_hints: list[RouteHint] | None = None,
) -> str:
tags = Tags(
[
Tag(TagChar.payment_hash, payment_hash),
Tag(TagChar.description, description),
Tag(TagChar.expire_time, expiry),
Tag(TagChar.min_final_cltv_expiry, min_final_cltv_expiry),
Tag(TagChar.payment_secret, get_payment_secret(payment_secret)),
Tag(TagChar.features, self._features),
]
)

if route_hints is not None:
tags.tags.extend([Tag(TagChar.route_hint, route) for route in route_hints])

return encode(
Bolt11(
self._prefix,
int(time_now().timestamp()),
Tags(
[
Tag(TagChar.payment_hash, payment_hash),
Tag(TagChar.description, description),
Tag(TagChar.expire_time, expiry),
Tag(TagChar.min_final_cltv_expiry, min_final_cltv_expiry),
Tag(TagChar.payment_secret, get_payment_secret(payment_secret)),
Tag(TagChar.features, self._features),
]
),
tags,
MilliSatoshi(amount_msat),
),
self._key,
Expand Down
9 changes: 9 additions & 0 deletions tools/hold/hold.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import hashlib

from bolt11.types import RouteHint
from datastore import DataErrorCodes, DataStore
from encoder import Encoder
from htlc_handler import HtlcHandler
from invoice import HoldInvoice, InvoiceState
from pyln.client import Plugin, RpcError
from route_hints import RouteHints
from settler import Settler
from tracker import Tracker

Expand All @@ -23,13 +25,15 @@ def __init__(self, plugin: Plugin) -> None:
self.tracker = Tracker()
self._settler = Settler(self.tracker)
self._encoder = Encoder(plugin)
self._route_hints = RouteHints(plugin)

self.ds = DataStore(plugin, self._settler)
self.handler = HtlcHandler(plugin, self.ds, self._settler, self.tracker)

def init(self) -> None:
self.handler.init()
self._encoder.init()
self._route_hints.init()

def invoice(
self,
Expand All @@ -38,6 +42,7 @@ def invoice(
description: str,
expiry: int,
min_final_cltv_expiry: int,
route_hints: list[RouteHint] | None = None,
) -> str:
if (
len(self._plugin.rpc.listinvoices(payment_hash=payment_hash)["invoices"])
Expand All @@ -51,6 +56,7 @@ def invoice(
description,
expiry,
min_final_cltv_expiry,
route_hints=route_hints,
)
signed = self._plugin.rpc.call(
"signinvoice",
Expand Down Expand Up @@ -103,3 +109,6 @@ def wipe(self, payment_hash: str | None) -> int:
return 1

raise NoSuchInvoiceError

def get_private_channels(self, node: str) -> list[RouteHint]:
return self._route_hints.get_private_channels(node)
19 changes: 17 additions & 2 deletions tools/hold/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
from pyln.client.plugin import Request
from server import Server
from settler import HtlcFailureMessage, Settler
from transformers import Transformers

from hold import Hold, InvoiceExistsError, NoSuchInvoiceError

# TODO: restart handling
# TODO: docstrings
# TODO: command to get private channels for <node id>

pl = Plugin()
hold = Hold(pl)
Expand Down Expand Up @@ -50,11 +50,19 @@ def hold_invoice(
description: str = "",
expiry: int = Defaults.Expiry,
min_final_cltv_expiry: int = Defaults.MinFinalCltvExpiry,
routing_hints: list[Any] | None = None,
) -> dict[str, Any]:
try:
return {
"bolt11": hold.invoice(
payment_hash, amount_msat, description, expiry, min_final_cltv_expiry
payment_hash,
amount_msat,
description,
expiry,
min_final_cltv_expiry,
Transformers.routing_hints_from_json(routing_hints)
if routing_hints is not None
else None,
),
}
except InvoiceExistsError:
Expand Down Expand Up @@ -92,6 +100,13 @@ def cancel_hold_invoice(plugin: Plugin, payment_hash: str) -> dict[str, Any]:
return {}


@pl.method("routinghints")
def get_routing_hints(plugin: Plugin, node: str) -> dict[str, Any]:
return {
"hints": Transformers.named_tuples_to_dict(hold.get_private_channels(node)),
}


@pl.method("dev-wipeholdinvoices")
def wipe_hold_invoices(plugin: Plugin, payment_hash: str = "") -> dict[str, Any]:
try:
Expand Down
38 changes: 30 additions & 8 deletions tools/hold/protos/hold.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ package hold;

service Hold {
rpc Invoice (InvoiceRequest) returns (InvoiceResponse) {}
rpc RoutingHints (RoutingHintsRequest) returns (RoutingHintsResponse) {}
rpc List (ListRequest) returns (ListResponse) {}

rpc Settle (SettleRequest) returns (SettleResponse) {}
rpc Cancel (CancelRequest) returns (CancelResponse) {}
rpc List (ListRequest) returns (ListResponse) {}

rpc Track (TrackRequest) returns (stream TrackResponse) {}
rpc TrackAll (TrackAllRequest) returns (stream TrackAllResponse) {}
Expand All @@ -18,20 +20,31 @@ message InvoiceRequest {
optional string description = 3;
optional uint64 expiry = 4;
optional uint64 min_final_cltv_expiry = 5;
repeated RoutingHint routing_hints = 6;
}
message InvoiceResponse {
string bolt11 = 1;
}

message SettleRequest {
string payment_preimage = 1;
message RoutingHintsRequest {
string node = 1;
}
message SettleResponse {}

message CancelRequest {
string payment_hash = 1;
message Hop {
string public_key = 1;
string short_channel_id = 2;
uint64 base_fee = 3;
uint64 ppm_fee = 4;
uint64 cltv_expiry_delta = 5;
}

message RoutingHint {
repeated Hop hops = 1;
}

message RoutingHintsResponse {
repeated RoutingHint hints = 1;
}
message CancelResponse {}

message ListRequest {
optional string payment_hash = 1;
Expand All @@ -55,10 +68,19 @@ message ListResponse {
repeated Invoice invoices = 1;
}

message TrackRequest {
message SettleRequest {
string payment_preimage = 1;
}
message SettleResponse {}

message CancelRequest {
string payment_hash = 1;
}
message CancelResponse {}

message TrackRequest {
string payment_hash = 1;
}
message TrackResponse {
InvoiceState state = 1;
}
Expand Down
Loading

0 comments on commit 84e01f7

Please sign in to comment.