mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 20:21:35 +00:00
Feat: fix CLN and reactivate cln integration tests (#1336)
* update cln holdinvoice plugin
DecodeBolt11 was removed in:
d51fd03390
* update CLN to v24.05 and holdinvoice to v3.0.0
* reactivate CLN integration tests
---------
Co-authored-by: jerryfletcher21 <jerryfletcher@cock.email>
This commit is contained in:
parent
b3622420f4
commit
62e6258f0f
5
.github/workflows/integration-tests.yml
vendored
5
.github/workflows/integration-tests.yml
vendored
@ -22,8 +22,8 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
python-tag: ['3.12.3-slim-bookworm', '3.13-rc-slim-bookworm']
|
python-tag: ['3.12.3-slim-bookworm', '3.13-rc-slim-bookworm']
|
||||||
lnd-version: ['v0.17.4-beta']
|
lnd-version: ['v0.17.4-beta']
|
||||||
cln-version: ['v23.11.2'] #,'v24.02']
|
cln-version: ['v24.05']
|
||||||
ln-vendor: ['LND'] #, 'CLN']
|
ln-vendor: ['LND', 'CLN']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Checkout'
|
- name: 'Checkout'
|
||||||
@ -56,7 +56,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
LND_VERSION: ${{ matrix.lnd-version }}
|
LND_VERSION: ${{ matrix.lnd-version }}
|
||||||
CLN_VERSION: ${{ matrix.cln-version }}
|
CLN_VERSION: ${{ matrix.cln-version }}
|
||||||
BITCOIND_VERSION: ${{ matrix.bitcoind-version }}
|
|
||||||
ROBOSATS_ENVS_FILE: ".env-sample"
|
ROBOSATS_ENVS_FILE: ".env-sample"
|
||||||
|
|
||||||
- name: Wait for coordinator (django server)
|
- name: Wait for coordinator (django server)
|
||||||
|
@ -90,9 +90,9 @@ class CLNNode:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def decode_payreq(cls, invoice):
|
def decode_payreq(cls, invoice):
|
||||||
"""Decodes a lightning payment request (invoice)"""
|
"""Decodes a lightning payment request (invoice)"""
|
||||||
request = hold_pb2.DecodeBolt11Request(bolt11=invoice)
|
nodestub = node_pb2_grpc.NodeStub(cls.node_channel)
|
||||||
holdstub = hold_pb2_grpc.HoldStub(cls.hold_channel)
|
request = node_pb2.DecodeRequest(string=invoice)
|
||||||
response = holdstub.DecodeBolt11(request)
|
response = nodestub.Decode(request)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -236,7 +236,7 @@ class CLNNode:
|
|||||||
holdstub = hold_pb2_grpc.HoldStub(cls.hold_channel)
|
holdstub = hold_pb2_grpc.HoldStub(cls.hold_channel)
|
||||||
response = holdstub.HoldInvoiceCancel(request)
|
response = holdstub.HoldInvoiceCancel(request)
|
||||||
|
|
||||||
return response.state == hold_pb2.HoldInvoiceCancelResponse.Holdstate.CANCELED
|
return response.state == hold_pb2.Holdstate.CANCELED
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def settle_hold_invoice(cls, preimage):
|
def settle_hold_invoice(cls, preimage):
|
||||||
@ -247,7 +247,7 @@ class CLNNode:
|
|||||||
holdstub = hold_pb2_grpc.HoldStub(cls.hold_channel)
|
holdstub = hold_pb2_grpc.HoldStub(cls.hold_channel)
|
||||||
response = holdstub.HoldInvoiceSettle(request)
|
response = holdstub.HoldInvoiceSettle(request)
|
||||||
|
|
||||||
return response.state == hold_pb2.HoldInvoiceSettleResponse.Holdstate.SETTLED
|
return response.state == hold_pb2.Holdstate.SETTLED
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def gen_hold_invoice(
|
def gen_hold_invoice(
|
||||||
@ -272,7 +272,7 @@ class CLNNode:
|
|||||||
|
|
||||||
request = hold_pb2.HoldInvoiceRequest(
|
request = hold_pb2.HoldInvoiceRequest(
|
||||||
description=description,
|
description=description,
|
||||||
amount_msat=primitives__pb2.Amount(msat=num_satoshis * 1_000),
|
amount_msat=hold_pb2.Amount(msat=num_satoshis * 1_000),
|
||||||
label=f"Order:{order_id}-{lnpayment_concept}-{time}",
|
label=f"Order:{order_id}-{lnpayment_concept}-{time}",
|
||||||
expiry=invoice_expiry,
|
expiry=invoice_expiry,
|
||||||
cltv=cltv_expiry_blocks,
|
cltv=cltv_expiry_blocks,
|
||||||
@ -286,7 +286,7 @@ class CLNNode:
|
|||||||
hold_payment["preimage"] = preimage.hex()
|
hold_payment["preimage"] = preimage.hex()
|
||||||
hold_payment["payment_hash"] = response.payment_hash.hex()
|
hold_payment["payment_hash"] = response.payment_hash.hex()
|
||||||
hold_payment["created_at"] = timezone.make_aware(
|
hold_payment["created_at"] = timezone.make_aware(
|
||||||
datetime.fromtimestamp(payreq_decoded.timestamp)
|
datetime.fromtimestamp(payreq_decoded.created_at)
|
||||||
)
|
)
|
||||||
hold_payment["expires_at"] = timezone.make_aware(
|
hold_payment["expires_at"] = timezone.make_aware(
|
||||||
datetime.fromtimestamp(response.expires_at)
|
datetime.fromtimestamp(response.expires_at)
|
||||||
@ -309,13 +309,13 @@ class CLNNode:
|
|||||||
# Will fail if 'unable to locate invoice'. Happens if invoice expiry
|
# Will fail if 'unable to locate invoice'. Happens if invoice expiry
|
||||||
# time has passed (but these are 15% padded at the moment). Should catch it
|
# time has passed (but these are 15% padded at the moment). Should catch it
|
||||||
# and report back that the invoice has expired (better robustness)
|
# and report back that the invoice has expired (better robustness)
|
||||||
if response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.OPEN:
|
if response.state == hold_pb2.Holdstate.OPEN:
|
||||||
pass
|
pass
|
||||||
if response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.SETTLED:
|
if response.state == hold_pb2.Holdstate.SETTLED:
|
||||||
pass
|
pass
|
||||||
if response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.CANCELED:
|
if response.state == hold_pb2.Holdstate.CANCELED:
|
||||||
pass
|
pass
|
||||||
if response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.ACCEPTED:
|
if response.state == hold_pb2.Holdstate.ACCEPTED:
|
||||||
lnpayment.expiry_height = response.htlc_expiry
|
lnpayment.expiry_height = response.htlc_expiry
|
||||||
lnpayment.status = LNPayment.Status.LOCKED
|
lnpayment.status = LNPayment.Status.LOCKED
|
||||||
lnpayment.save(update_fields=["expiry_height", "status"])
|
lnpayment.save(update_fields=["expiry_height", "status"])
|
||||||
@ -359,7 +359,7 @@ class CLNNode:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
# If it fails at finding the invoice: it has been expired for more than an hour (and could be paid or just expired).
|
# If it fails at finding the invoice: it has been expired for more than an hour (and could be paid or just expired).
|
||||||
# In RoboSats DB we make a distinction between cancelled and returned
|
# In RoboSats DB we make a distinction between cancelled and returned
|
||||||
# (cln-grpc-hodl has separate state for hodl-invoices, which it forgets after an invoice expired more than an hour ago)
|
# (holdinvoice plugin has separate state for hodl-invoices, which it forgets after an invoice expired more than an hour ago)
|
||||||
if "empty result for listdatastore_state" in str(e):
|
if "empty result for listdatastore_state" in str(e):
|
||||||
print(str(e))
|
print(str(e))
|
||||||
request2 = node_pb2.ListinvoicesRequest(
|
request2 = node_pb2.ListinvoicesRequest(
|
||||||
@ -418,7 +418,7 @@ class CLNNode:
|
|||||||
|
|
||||||
# Some wallet providers (e.g. Muun) force routing through a private channel with high fees >1500ppm
|
# Some wallet providers (e.g. Muun) force routing through a private channel with high fees >1500ppm
|
||||||
# These payments will fail. So it is best to let the user know in advance this invoice is not valid.
|
# These payments will fail. So it is best to let the user know in advance this invoice is not valid.
|
||||||
route_hints = payreq_decoded.route_hints.hints
|
route_hints = payreq_decoded.routes.hints
|
||||||
|
|
||||||
# Max amount RoboSats will pay for routing
|
# Max amount RoboSats will pay for routing
|
||||||
if routing_budget_ppm == 0:
|
if routing_budget_ppm == 0:
|
||||||
@ -438,8 +438,10 @@ class CLNNode:
|
|||||||
route_cost = 0
|
route_cost = 0
|
||||||
# ...add up the cost of every hinted hop...
|
# ...add up the cost of every hinted hop...
|
||||||
for hop_hint in hinted_route.hops:
|
for hop_hint in hinted_route.hops:
|
||||||
route_cost += hop_hint.feebase.msat / 1_000
|
route_cost += hop_hint.fee_base_msat.msat / 1_000
|
||||||
route_cost += hop_hint.feeprop * num_satoshis / 1_000_000
|
route_cost += (
|
||||||
|
hop_hint.fee_proportional_millionths * num_satoshis / 1_000_000
|
||||||
|
)
|
||||||
|
|
||||||
# ...and store the cost of the route to the array
|
# ...and store the cost of the route to the array
|
||||||
routes_cost.append(route_cost)
|
routes_cost.append(route_cost)
|
||||||
@ -466,7 +468,7 @@ class CLNNode:
|
|||||||
return payout
|
return payout
|
||||||
|
|
||||||
payout["created_at"] = timezone.make_aware(
|
payout["created_at"] = timezone.make_aware(
|
||||||
datetime.fromtimestamp(payreq_decoded.timestamp)
|
datetime.fromtimestamp(payreq_decoded.created_at)
|
||||||
)
|
)
|
||||||
payout["expires_at"] = payout["created_at"] + timedelta(
|
payout["expires_at"] = payout["created_at"] + timedelta(
|
||||||
seconds=payreq_decoded.expiry
|
seconds=payreq_decoded.expiry
|
||||||
@ -869,4 +871,4 @@ class CLNNode:
|
|||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
return response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.SETTLED
|
return response.state == hold_pb2.Holdstate.SETTLED
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
# Some useful handy commands that hopefully are never needed
|
# Some useful handy commands that hopefully are never needed
|
||||||
|
|
||||||
|
# docker-compose -f docker-tests.yml --env-file tests/compose.env down --volumes
|
||||||
|
|
||||||
# docker exec -it btc bitcoin-cli -chain=regtest -rpcpassword=test -rpcuser=test createwallet default
|
# docker exec -it btc bitcoin-cli -chain=regtest -rpcpassword=test -rpcuser=test createwallet default
|
||||||
# docker exec -it btc bitcoin-cli -chain=regtest -rpcpassword=test -rpcuser=test -generate 101
|
# docker exec -it btc bitcoin-cli -chain=regtest -rpcpassword=test -rpcuser=test -generate 101
|
||||||
|
|
||||||
@ -27,6 +29,8 @@ services:
|
|||||||
- "6379:6379"
|
- "6379:6379"
|
||||||
volumes:
|
volumes:
|
||||||
- bitcoin:/bitcoin/.bitcoin/
|
- bitcoin:/bitcoin/.bitcoin/
|
||||||
|
- ./tests/bitcoind/entrypoint.sh:/entrypoint.sh
|
||||||
|
entrypoint: ["/entrypoint.sh"]
|
||||||
command:
|
command:
|
||||||
--txindex=1
|
--txindex=1
|
||||||
--printtoconsole
|
--printtoconsole
|
||||||
@ -77,16 +81,16 @@ services:
|
|||||||
network_mode: service:bitcoind
|
network_mode: service:bitcoind
|
||||||
|
|
||||||
coordinator-CLN:
|
coordinator-CLN:
|
||||||
image: elementsproject/lightningd:${CLN_VERSION:-v23.08.1}
|
image: elementsproject/lightningd:${CLN_VERSION:-v24.05}
|
||||||
restart: always
|
restart: always
|
||||||
container_name: coordinator-CLN
|
container_name: coordinator-CLN
|
||||||
environment:
|
environment:
|
||||||
LIGHTNINGD_NETWORK: 'regtest'
|
LIGHTNINGD_NETWORK: 'regtest'
|
||||||
volumes:
|
volumes:
|
||||||
- cln:/root/.lightning
|
- cln:/root/.lightning
|
||||||
- ./docker/cln/plugins/cln-grpc-hold:/root/.lightning/plugins/cln-grpc-hold
|
- ./docker/cln/plugins/holdinvoice:/root/.lightning/plugins/holdinvoice
|
||||||
- bitcoin:/root/.bitcoin
|
- bitcoin:/root/.bitcoin
|
||||||
command: --regtest --wumbo --bitcoin-rpcuser=test --bitcoin-rpcpassword=test --log-level=debug --rest-host=0.0.0.0 --rest-port=3010 --bind-addr=127.0.0.1:9737 --max-concurrent-htlcs=483 --grpc-port=9999 --grpc-hold-port=9998 --important-plugin=/root/.lightning/plugins/cln-grpc-hold --database-upgrade=true
|
command: --regtest --bitcoin-rpcuser=test --bitcoin-rpcpassword=test --developer --dev-bitcoind-poll=1 --dev-fast-gossip --log-level=debug --bind-addr=127.0.0.1:9737 --max-concurrent-htlcs=483 --grpc-port=9999 --grpc-hold-port=9998 --important-plugin=/root/.lightning/plugins/holdinvoice --database-upgrade=true
|
||||||
depends_on:
|
depends_on:
|
||||||
- bitcoind
|
- bitcoind
|
||||||
network_mode: service:bitcoind
|
network_mode: service:bitcoind
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
FROM debian:bullseye-slim as builder
|
FROM debian:bullseye-slim as builder
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
ARG LIGHTNINGD_VERSION=v23.08
|
ARG LIGHTNINGD_VERSION=v24.05
|
||||||
RUN apt-get update -qq && \
|
RUN apt-get update -qq && \
|
||||||
apt-get install -qq -y --no-install-recommends \
|
apt-get install -qq -y --no-install-recommends \
|
||||||
autoconf \
|
autoconf \
|
||||||
@ -18,13 +18,13 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|||||||
RUN rustup toolchain install stable --component rustfmt --allow-downgrade
|
RUN rustup toolchain install stable --component rustfmt --allow-downgrade
|
||||||
|
|
||||||
WORKDIR /opt/lightningd
|
WORKDIR /opt/lightningd
|
||||||
RUN git clone --recursive --branch cln-grpc-hold https://github.com/daywalker90/lightning.git /tmp/cln-grpc-hold
|
RUN git clone https://github.com/daywalker90/holdinvoice.git /tmp/holdinvoice
|
||||||
RUN cd /tmp/cln-grpc-hold \
|
RUN cd /tmp/holdinvoice \
|
||||||
&& cargo build --release
|
&& cargo build --release
|
||||||
|
|
||||||
FROM elementsproject/lightningd:v23.08 as final
|
FROM elementsproject/lightningd:v24.05 as final
|
||||||
|
|
||||||
COPY --from=builder /tmp/cln-grpc-hold/target/release/cln-grpc-hold /tmp/cln-grpc-hold
|
COPY --from=builder /tmp/holdinvoice/target/release/holdinvoice /tmp/holdinvoice
|
||||||
COPY config /tmp/config
|
COPY config /tmp/config
|
||||||
COPY entrypoint.sh entrypoint.sh
|
COPY entrypoint.sh entrypoint.sh
|
||||||
RUN chmod +x entrypoint.sh
|
RUN chmod +x entrypoint.sh
|
||||||
|
@ -5,6 +5,6 @@ addr=statictor:127.0.0.1:9051
|
|||||||
grpc-port=9999
|
grpc-port=9999
|
||||||
grpc-hold-port=9998
|
grpc-hold-port=9998
|
||||||
always-use-proxy=true
|
always-use-proxy=true
|
||||||
important-plugin=/root/.lightning/plugins/cln-grpc-hold
|
important-plugin=/root/.lightning/plugins/holdinvoice
|
||||||
# wallet=postgres://user:pass@localhost:5433/cln
|
# wallet=postgres://user:pass@localhost:5433/cln
|
||||||
# bookkeeper-db=postgres://user:pass@localhost:5433/cln
|
# bookkeeper-db=postgres://user:pass@localhost:5433/cln
|
@ -17,9 +17,9 @@ if [ "$EXPOSE_TCP" == "true" ]; then
|
|||||||
socat "TCP4-listen:$LIGHTNINGD_RPC_PORT,fork,reuseaddr" "UNIX-CONNECT:${networkdatadir}/lightning-rpc" &
|
socat "TCP4-listen:$LIGHTNINGD_RPC_PORT,fork,reuseaddr" "UNIX-CONNECT:${networkdatadir}/lightning-rpc" &
|
||||||
fg %-
|
fg %-
|
||||||
else
|
else
|
||||||
# Always copy the cln-grpc-hodl plugin into the plugins directory on start up
|
# Always copy the holdinvoice plugin into the plugins directory on start up
|
||||||
mkdir -p /root/.lightning/plugins
|
mkdir -p /root/.lightning/plugins
|
||||||
cp /tmp/cln-grpc-hold /root/.lightning/plugins/cln-grpc-hold
|
cp /tmp/holdinvoice /root/.lightning/plugins/holdinvoice
|
||||||
if [ ! -f /root/.lightning/config ]; then
|
if [ ! -f /root/.lightning/config ]; then
|
||||||
cp /tmp/config /root/.lightning/config
|
cp /tmp/config /root/.lightning/config
|
||||||
fi
|
fi
|
||||||
|
Binary file not shown.
BIN
docker/cln/plugins/holdinvoice
Executable file
BIN
docker/cln/plugins/holdinvoice
Executable file
Binary file not shown.
@ -10,9 +10,9 @@ curl --parallel -o lightning.proto https://raw.githubusercontent.com/lightningne
|
|||||||
-o router.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/routerrpc/router.proto \
|
-o router.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/routerrpc/router.proto \
|
||||||
-o signer.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/signrpc/signer.proto \
|
-o signer.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/signrpc/signer.proto \
|
||||||
-o verrpc.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/verrpc/verrpc.proto \
|
-o verrpc.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/verrpc/verrpc.proto \
|
||||||
-o hold.proto https://raw.githubusercontent.com/daywalker90/lightning/cln-grpc-hold/proto/hold.proto \
|
-o hold.proto https://raw.githubusercontent.com/daywalker90/holdinvoice/master/proto/hold.proto \
|
||||||
-o primitives.proto https://raw.githubusercontent.com/ElementsProject/lightning/v23.08/cln-grpc/proto/primitives.proto \
|
-o primitives.proto https://raw.githubusercontent.com/ElementsProject/lightning/v24.05/cln-grpc/proto/primitives.proto \
|
||||||
-o node.proto https://raw.githubusercontent.com/ElementsProject/lightning/v23.08/cln-grpc/proto/node.proto
|
-o node.proto https://raw.githubusercontent.com/ElementsProject/lightning/v24.05/cln-grpc/proto/node.proto
|
||||||
|
|
||||||
echo -n "Done\nBuilding api from GRPC specs..."
|
echo -n "Done\nBuilding api from GRPC specs..."
|
||||||
python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. lightning.proto invoices.proto router.proto signer.proto verrpc.proto
|
python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. lightning.proto invoices.proto router.proto signer.proto verrpc.proto
|
||||||
|
17
tests/bitcoind/entrypoint.sh
Executable file
17
tests/bitcoind/entrypoint.sh
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Start bitcoind in the background
|
||||||
|
bitcoind "$@" &
|
||||||
|
|
||||||
|
# Wait for bitcoind to be ready
|
||||||
|
while ! bitcoin-cli --regtest --rpcuser=test --rpcpassword=test getblockchaininfo 2>/dev/null | grep '"verificationprogress":'; do
|
||||||
|
echo "Waiting for bitcoind to be ready..."
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Run initialization commands
|
||||||
|
bitcoin-cli --regtest --rpcuser=test --rpcpassword=test createwallet default
|
||||||
|
bitcoin-cli --regtest --rpcuser=test --rpcpassword=test generatetoaddress 1 $(bitcoin-cli --regtest --rpcuser=test --rpcpassword=test getnewaddress)
|
||||||
|
|
||||||
|
# Bring bitcoind to the foreground
|
||||||
|
wait
|
@ -2,6 +2,6 @@ ROBOSATS_ENVS_FILE=".env-sample"
|
|||||||
|
|
||||||
BITCOIND_VERSION='24.0.1'
|
BITCOIND_VERSION='24.0.1'
|
||||||
LND_VERSION='v0.17.0-beta'
|
LND_VERSION='v0.17.0-beta'
|
||||||
CLN_VERSION='v23.08.1'
|
CLN_VERSION='v24.05'
|
||||||
REDIS_VERSION='7.2.1'
|
REDIS_VERSION='7.2.1'
|
||||||
POSTGRES_VERSION='14.2'
|
POSTGRES_VERSION='14.2'
|
Loading…
Reference in New Issue
Block a user