mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 07:34:24 +01:00
lightningd: handle case where channeld fails locally-generated HTLC.
jl777 reported a crash when we try to pay past reserve. Fix that (and a whole class of related bugs) and add tests. In test_lightning.py I had to make non-async path for sendpay() non-threaded to get the exception passed through for testing. Closes: #236 Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
committed by
Christian Decker
parent
52db7fd27b
commit
1cf33eefe2
@@ -442,6 +442,11 @@ static enum channel_add_err add_htlc(struct channel *channel,
|
|||||||
* sender's reserve. */
|
* sender's reserve. */
|
||||||
if (enforce_aggregate_limits
|
if (enforce_aggregate_limits
|
||||||
&& balance_msat - fee_msat < (s64)channel_reserve_msat(channel, sender)) {
|
&& balance_msat - fee_msat < (s64)channel_reserve_msat(channel, sender)) {
|
||||||
|
status_trace("balance = %"PRIu64
|
||||||
|
", fee is %"PRIu64
|
||||||
|
", reserve is %"PRIu64,
|
||||||
|
balance_msat, fee_msat,
|
||||||
|
channel_reserve_msat(channel, sender));
|
||||||
e = CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED;
|
e = CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -413,7 +413,14 @@ static bool rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (failure_code) {
|
if (failure_code) {
|
||||||
local_fail_htlc(hout->in, failure_code);
|
if (!hout->in) {
|
||||||
|
char *localfail = tal_fmt(msg, "%s: %.*s",
|
||||||
|
onion_type_name(failure_code),
|
||||||
|
(int)tal_len(failurestr),
|
||||||
|
(const char *)failurestr);
|
||||||
|
payment_failed(hout->key.peer->ld, hout, localfail);
|
||||||
|
} else
|
||||||
|
local_fail_htlc(hout->in, failure_code);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -208,10 +208,6 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
}
|
}
|
||||||
lsrc.rpc.sendpay(to_json([routestep]), rhash, async=False)
|
lsrc.rpc.sendpay(to_json([routestep]), rhash, async=False)
|
||||||
|
|
||||||
t = threading.Thread(target=call_pay)
|
|
||||||
t.daemon = True
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
def wait_pay():
|
def wait_pay():
|
||||||
# Up to 10 seconds for payment to succeed.
|
# Up to 10 seconds for payment to succeed.
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
@@ -221,9 +217,12 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
if async:
|
if async:
|
||||||
|
t = threading.Thread(target=call_pay)
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
return self.executor.submit(wait_pay)
|
return self.executor.submit(wait_pay)
|
||||||
else:
|
else:
|
||||||
return wait_pay()
|
call_pay()
|
||||||
|
|
||||||
def test_connect(self):
|
def test_connect(self):
|
||||||
l1,l2 = self.connect()
|
l1,l2 = self.connect()
|
||||||
@@ -329,6 +328,33 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
l1.rpc.sendpay(to_json([routestep]), rhash)
|
l1.rpc.sendpay(to_json([routestep]), rhash)
|
||||||
assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == True
|
assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == True
|
||||||
|
|
||||||
|
def test_sendpay_cant_afford(self):
|
||||||
|
l1,l2 = self.connect()
|
||||||
|
|
||||||
|
# Note, this is in SATOSHI, rest are in MILLISATOSHI!
|
||||||
|
self.fund_channel(l1, l2, 10**6)
|
||||||
|
|
||||||
|
# Can't pay more than channel capacity.
|
||||||
|
self.assertRaises(ValueError, self.pay, l1, l2, 10**9 + 1)
|
||||||
|
|
||||||
|
# This is the fee, which needs to be taken into account for l1.
|
||||||
|
available = 10**9 - 13440
|
||||||
|
# Reserve is 1%.
|
||||||
|
reserve = 10**7
|
||||||
|
|
||||||
|
# Can't pay past reserve.
|
||||||
|
self.assertRaises(ValueError, self.pay, l1, l2, available)
|
||||||
|
self.assertRaises(ValueError, self.pay, l1, l2, available - reserve + 1)
|
||||||
|
|
||||||
|
# Can pay up to reserve (1%)
|
||||||
|
self.pay(l1, l2, available - reserve)
|
||||||
|
|
||||||
|
# And now it can't pay back, due to its own reserve.
|
||||||
|
self.assertRaises(ValueError, self.pay, l2, l1, available - reserve)
|
||||||
|
|
||||||
|
# But this should work.
|
||||||
|
self.pay(l2, l1, available - reserve*2)
|
||||||
|
|
||||||
def test_closing(self):
|
def test_closing(self):
|
||||||
l1,l2 = self.connect()
|
l1,l2 = self.connect()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user