paymod: Compute fee and CLTV delta limits for a payment

So far we got away with not caring about these but since we're implementing
modifiers that impact these limits, we better keep track of them.
This commit is contained in:
Christian Decker
2020-06-03 15:59:12 +02:00
parent e71bdf9ed8
commit 4648d2867f
4 changed files with 107 additions and 24 deletions

View File

@@ -1678,8 +1678,15 @@ def test_pay_routeboost(node_factory, bitcoind):
l1, l2 = node_factory.line_graph(2, announce_channels=True, wait_for_announce=True)
l3, l4, l5 = node_factory.line_graph(3, announce_channels=False, wait_for_announce=False)
# COMPAT_V090 made the return value of pay and paystatus more consistent,
# but broke tests relying on the inconsistent interface. Remove this once
# COMPAT_V090 gets removed.
COMPAT_V090 = env('COMPAT') == '1'
PAY_ROUTE_NOT_FOUND = 205
# This should a "could not find a route" because that's true.
with pytest.raises(RpcError, match=r'Could not find a route'):
error = r'Could not find a route' if COMPAT_V090 else r'Ran out of routes'
with pytest.raises(RpcError, match=error):
l1.rpc.pay(l5.rpc.invoice(10**8, 'test_retry', 'test_retry')['bolt11'])
l2.rpc.connect(l3.info['id'], 'localhost', l3.port)
@@ -1718,25 +1725,33 @@ def test_pay_routeboost(node_factory, bitcoind):
assert 'label' not in only_one(status['pay'])
assert 'routehint_modifications' not in only_one(status['pay'])
assert 'local_exclusions' not in only_one(status['pay'])
# First attempt will fail, then it will try route hint
attempts = only_one(status['pay'])['attempts']
assert len(attempts) == 2
assert attempts[0]['strategy'] == "Initial attempt"
# FIXME!
PAY_ROUTE_NOT_FOUND = 205
assert attempts[0]['failure']['code'] == PAY_ROUTE_NOT_FOUND
assert attempts[1]['strategy'] == "Trying route hint"
assert 'success' in attempts[1]
assert attempts[1]['age_in_seconds'] <= time.time() - start
assert attempts[1]['duration_in_seconds'] <= end - start
assert only_one(attempts[1]['routehint'])
assert only_one(attempts[1]['routehint'])['id'] == l3.info['id']
scid34 = only_one(l3.rpc.listpeers(l4.info['id'])['peers'])['channels'][0]['short_channel_id']
assert only_one(attempts[1]['routehint'])['channel'] == scid34
assert only_one(attempts[1]['routehint'])['fee_base_msat'] == 1
assert only_one(attempts[1]['routehint'])['fee_proportional_millionths'] == 10
assert only_one(attempts[1]['routehint'])['cltv_expiry_delta'] == 6
if COMPAT_V090:
assert len(attempts) == 2
assert attempts[0]['strategy'] == "Initial attempt"
# FIXME!
assert attempts[0]['failure']['code'] == PAY_ROUTE_NOT_FOUND
assert attempts[1]['strategy'] == "Trying route hint"
assert 'success' in attempts[1]
assert attempts[1]['age_in_seconds'] <= time.time() - start
assert attempts[1]['duration_in_seconds'] <= end - start
assert only_one(attempts[1]['routehint'])
assert only_one(attempts[1]['routehint'])['id'] == l3.info['id']
assert only_one(attempts[1]['routehint'])['channel'] == scid34
assert only_one(attempts[1]['routehint'])['fee_base_msat'] == 1
assert only_one(attempts[1]['routehint'])['fee_proportional_millionths'] == 10
assert only_one(attempts[1]['routehint'])['cltv_expiry_delta'] == 6
else:
# The behavior changed to try with the route hint first, so we end up
# with a single successful attempt:
assert(len(attempts) == 1)
a = attempts[0]
assert(a['strategy'] == "Initial attempt")
assert('success' in a)
assert('payment_preimage' in a['success'])
print("XXX longer route developeronly")
# With dev-route option we can test longer routehints.
if DEVELOPER:
scid45 = only_one(l4.rpc.listpeers(l5.info['id'])['peers'])['channels'][0]['short_channel_id']
@@ -1756,11 +1771,18 @@ def test_pay_routeboost(node_factory, bitcoind):
'dev-routes': [routel3l4l5]})
l1.rpc.dev_pay(inv['bolt11'], use_shadow=False)
status = l1.rpc.call('paystatus', [inv['bolt11']])
assert len(only_one(status['pay'])['attempts']) == 2
assert 'failure' in only_one(status['pay'])['attempts'][0]
assert 'success' not in only_one(status['pay'])['attempts'][0]
assert 'failure' not in only_one(status['pay'])['attempts'][1]
assert 'success' in only_one(status['pay'])['attempts'][1]
pay = only_one(status['pay'])
attempts = pay['attempts']
if COMPAT_V090:
assert len(pay['attempts']) == 2
assert 'failure' in attempts[0]
assert 'success' not in attempts[0]
assert 'failure' not in attempts[1]
assert 'success' in attempts[1]
else:
assert(len(attempts) == 1)
assert 'failure' not in attempts[0]
assert 'success' in attempts[0]
# Finally, it should fall back to second routehint if first fails.
# (Note, this is not public because it's not 6 deep)
@@ -3055,9 +3077,15 @@ def test_pay_modifiers(node_factory):
# Make sure that the dummy param is in the help (and therefore assigned to
# the modifier data).
hlp = l1.rpc.help("paymod")['help'][0]
assert(hlp['command'] == 'paymod bolt11')
assert(hlp['command'] == 'paymod bolt11 [maxdelay] [maxfeepercent]')
inv = l2.rpc.invoice(123, 'lbl', 'desc')['bolt11']
r = l1.rpc.paymod(inv)
assert(r['status'] == 'complete')
assert(sha256(unhexlify(r['payment_preimage'])).hexdigest() == r['payment_hash'])
def test_pay_exemptfee(node_factory):
"""Tiny payment, huge fee
"""
l1, l2, l3 = node_factory.line_graph(3)