-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
32f4c4d
commit 1dff07b
Showing
6 changed files
with
623 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import json | ||
import time | ||
|
||
import bolt11 | ||
import pytest | ||
from bolt11 import MilliSatoshi | ||
from bolt11.models.tags import TagChar | ||
|
||
from hold.protos.hold_pb2 import ( | ||
Invoice, | ||
InvoiceRequest, | ||
InvoiceResponse, | ||
InvoiceState, | ||
ListRequest, | ||
SettleRequest, | ||
) | ||
from hold.protos.hold_pb2_grpc import HoldStub | ||
from hold.utils import ( | ||
LndPay, | ||
hold_client, | ||
lightning, | ||
lnd, | ||
new_preimage, | ||
new_preimage_bytes, | ||
) | ||
|
||
|
||
def assert_failed_payment( | ||
cl: HoldStub, payment_hash: bytes, dec: bolt11.Bolt11 | ||
) -> None: | ||
invoice = lightning("signinvoice", bolt11.encode(dec))["bolt11"] | ||
|
||
pay = LndPay(1, invoice) | ||
pay.start() | ||
pay.join() | ||
|
||
assert pay.res["status"] == "FAILED" | ||
assert pay.res["failure_reason"] == "FAILURE_REASON_INCORRECT_PAYMENT_DETAILS" | ||
|
||
list_invoice: Invoice = cl.List( | ||
ListRequest( | ||
payment_hash=payment_hash, | ||
) | ||
).invoices[0] | ||
assert list_invoice.state == InvoiceState.UNPAID | ||
assert len(list_invoice.htlcs) == 1 | ||
assert list_invoice.htlcs[0].state == InvoiceState.CANCELLED | ||
|
||
# Poor man's way to check if there is a pending HTLC for that hash | ||
assert payment_hash.hex() not in json.dumps(lightning("listpeerchannels")) | ||
|
||
|
||
class TestHtlcs: | ||
@pytest.fixture(scope="class", autouse=True) | ||
def cl(self) -> HoldStub: | ||
(channel, client) = hold_client() | ||
|
||
yield client | ||
|
||
channel.close() | ||
|
||
def test_ignore_non_hold_invoice(self) -> None: | ||
invoice = lightning("invoice", "1000", new_preimage()[0], "invoice-test")[ | ||
"bolt11" | ||
] | ||
|
||
pay = LndPay(1, invoice) | ||
pay.start() | ||
pay.join() | ||
|
||
assert pay.res["status"] == "SUCCEEDED" | ||
|
||
def test_ignore_forward(self) -> None: | ||
hold_node_id = lightning("getinfo")["id"] | ||
outgoing_channel = next( | ||
c | ||
for c in lnd("listchannels")["channels"] | ||
if c["remote_pubkey"] == hold_node_id | ||
)["chan_id"] | ||
|
||
invoice = lnd("addinvoice", "1000", node=2)["payment_request"] | ||
|
||
pay = LndPay(1, invoice, outgoing_chan_id=outgoing_channel) | ||
pay.start() | ||
pay.join() | ||
|
||
assert pay.res["status"] == "SUCCEEDED" | ||
|
||
def test_invalid_payment_secret(self, cl: HoldStub) -> None: | ||
(_, payment_hash) = new_preimage_bytes() | ||
invoice: InvoiceResponse = cl.Invoice( | ||
InvoiceRequest(payment_hash=payment_hash, amount_msat=21_000) | ||
) | ||
|
||
dec = bolt11.decode(invoice.bolt11) | ||
dec.tags.get(TagChar.payment_secret).data = new_preimage()[0] | ||
|
||
assert_failed_payment(cl, payment_hash, dec) | ||
|
||
def test_invalid_final_cltv_expiry(self, cl: HoldStub) -> None: | ||
(_, payment_hash) = new_preimage_bytes() | ||
min_final_cltv_expiry = 80 | ||
invoice: InvoiceResponse = cl.Invoice( | ||
InvoiceRequest( | ||
payment_hash=payment_hash, | ||
amount_msat=21_000, | ||
min_final_cltv_expiry=min_final_cltv_expiry, | ||
) | ||
) | ||
|
||
dec = bolt11.decode(invoice.bolt11) | ||
dec.tags.get(TagChar.min_final_cltv_expiry).data = min_final_cltv_expiry - 21 | ||
|
||
assert_failed_payment(cl, payment_hash, dec) | ||
|
||
def test_acceptable_overpayment(self, cl: HoldStub) -> None: | ||
(preimage, payment_hash) = new_preimage_bytes() | ||
amount = 21_000 | ||
invoice: InvoiceResponse = cl.Invoice( | ||
InvoiceRequest( | ||
payment_hash=payment_hash, | ||
amount_msat=amount, | ||
) | ||
) | ||
|
||
dec = bolt11.decode(invoice.bolt11) | ||
dec.amount_msat = MilliSatoshi(amount * 2) | ||
|
||
invoice_signed = lightning("signinvoice", bolt11.encode(dec))["bolt11"] | ||
pay = LndPay(1, invoice_signed) | ||
pay.start() | ||
|
||
time.sleep(1) | ||
cl.Settle(SettleRequest(payment_preimage=preimage)) | ||
|
||
pay.join() | ||
assert pay.res["status"] == "SUCCEEDED" | ||
|
||
def test_unacceptable_overpayment(self, cl: HoldStub) -> None: | ||
(preimage, payment_hash) = new_preimage_bytes() | ||
amount = 21_000 | ||
invoice: InvoiceResponse = cl.Invoice( | ||
InvoiceRequest( | ||
payment_hash=payment_hash, | ||
amount_msat=amount, | ||
) | ||
) | ||
|
||
dec = bolt11.decode(invoice.bolt11) | ||
dec.amount_msat = MilliSatoshi((amount * 2) + 1) | ||
|
||
assert_failed_payment(cl, payment_hash, dec) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import json | ||
import time | ||
|
||
import bolt11 | ||
import pytest | ||
from bolt11 import MilliSatoshi | ||
|
||
from hold.protos.hold_pb2 import ( | ||
Invoice, | ||
InvoiceRequest, | ||
InvoiceResponse, | ||
InvoiceState, | ||
ListRequest, | ||
SettleRequest, | ||
) | ||
from hold.protos.hold_pb2_grpc import HoldStub | ||
from hold.utils import LndPay, hold_client, lightning, new_preimage_bytes | ||
|
||
|
||
class TestMpp: | ||
@pytest.fixture(scope="class", autouse=True) | ||
def cl(self) -> HoldStub: | ||
(channel, client) = hold_client() | ||
|
||
yield client | ||
|
||
channel.close() | ||
|
||
@pytest.mark.parametrize("parts", [2, 4, 5, 10]) | ||
def test_mpp_payment(self, cl: HoldStub, parts: int) -> None: | ||
(preimage, payment_hash) = new_preimage_bytes() | ||
amount = 20_000 | ||
invoice: InvoiceResponse = cl.Invoice( | ||
InvoiceRequest(payment_hash=payment_hash, amount_msat=amount) | ||
) | ||
|
||
shard_size = int(amount / parts / 1_000) | ||
pay = LndPay(1, invoice.bolt11, max_shard_size=shard_size) | ||
pay.start() | ||
|
||
# 10 parts can take a little longer than a second | ||
time.sleep(2) | ||
info: Invoice = cl.List(ListRequest(payment_hash=payment_hash)).invoices[0] | ||
assert info.state == InvoiceState.ACCEPTED | ||
assert len(info.htlcs) == parts | ||
assert all(htlc.state == InvoiceState.ACCEPTED for htlc in info.htlcs) | ||
assert all(htlc.msat == int(amount / parts) for htlc in info.htlcs) | ||
|
||
cl.Settle(SettleRequest(payment_preimage=preimage)) | ||
|
||
pay.join() | ||
assert pay.res["status"] == "SUCCEEDED" | ||
|
||
def test_mpp_timeout(self, cl: HoldStub) -> None: | ||
(_, payment_hash) = new_preimage_bytes() | ||
amount = 20_000 | ||
invoice: InvoiceResponse = cl.Invoice( | ||
InvoiceRequest(payment_hash=payment_hash, amount_msat=amount) | ||
) | ||
|
||
dec = bolt11.decode(invoice.bolt11) | ||
dec.amount_msat = MilliSatoshi(dec.amount_msat - 1_000) | ||
|
||
pay = LndPay( | ||
1, lightning("signinvoice", bolt11.encode(dec))["bolt11"], timeout=1 | ||
) | ||
pay.start() | ||
pay.join() | ||
|
||
assert pay.res["status"] == "FAILED" | ||
assert pay.res["failure_reason"] == "FAILURE_REASON_TIMEOUT" | ||
assert len(pay.res["htlcs"]) == 1 | ||
|
||
htlc = pay.res["htlcs"][0] | ||
assert htlc["failure"]["code"] == "MPP_TIMEOUT" | ||
|
||
list_invoice: Invoice = cl.List( | ||
ListRequest( | ||
payment_hash=payment_hash, | ||
) | ||
).invoices[0] | ||
assert list_invoice.state == InvoiceState.UNPAID | ||
assert len(list_invoice.htlcs) == 1 | ||
assert list_invoice.htlcs[0].state == InvoiceState.CANCELLED | ||
|
||
# Poor man's way to check if there is a pending HTLC for that hash | ||
assert payment_hash.hex() not in json.dumps(lightning("listpeerchannels")) |
Oops, something went wrong.