Skip to content

Commit

Permalink
test_pay.py: Add test for blockheight disagreement.
Browse files Browse the repository at this point in the history
  • Loading branch information
ZmnSCPxj committed Jan 4, 2020
1 parent 8c9413c commit f475441
Showing 1 changed file with 54 additions and 0 deletions.
54 changes: 54 additions & 0 deletions tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -2716,3 +2716,57 @@ def test_partial_payment_htlc_loss(node_factory, bitcoind):
with pytest.raises(RpcError,
match=r'WIRE_PERMANENT_CHANNEL_FAILURE \(reply from remote\)'):
l1.rpc.waitsendpay(payment_hash=inv['payment_hash'], timeout=TIMEOUT, partid=1)


@pytest.mark.xfail(strict=True)
@unittest.skipIf(not DEVELOPER, "needs use_shadow")
def test_blockheight_disagreement(node_factory, bitcoind, executor):
"""
While a payment is in-transit from payer to payee, a block
might be mined, so that the blockheight the payer used to
initiate the payment is no longer the blockheight when the
payee receives it.
This leads to a failure which *used* to be
`final_expiry_too_soon`, a non-permanent failure, but
which is *now* `incorrect_or_unknown_payment_details`,
a permanent failure.
`pay` treats permanent failures as, well, permanent, and
gives up on receiving such failure from the payee, but
this particular subcase of blockheight disagreement is
actually a non-permanent failure (the payer only needs
to synchronize to the same blockheight as the payee).
"""
l1, l2 = node_factory.line_graph(2)

sync_blockheight(bitcoind, [l1, l2])

# Arrange l1 to stop getting new blocks.
def no_more_blocks(req):
return {"result": None,
"error": {"code": -8, "message": "Block height out of range"}, "id": req['id']}
l1.daemon.rpcproxy.mock_rpc('getblockhash', no_more_blocks)

# Increase blockheight and make sure l2 knows it.
# Why 2? Because `pay` uses min_final_cltv_expiry + 1.
# But 2 blocks coming in close succession, plus slow
# forwarding nodes and block propagation, are still
# possible on the mainnet, thus this test.
bitcoind.generate_block(2)
sync_blockheight(bitcoind, [l2])

# Have l2 make an invoice.
inv = l2.rpc.invoice(1000, 'l', 'd')['bolt11']

# Have l1 pay l2
def pay(l1, inv):
l1.rpc.dev_pay(inv, use_shadow=False)
fut = executor.submit(pay, l1, inv)

# Make sure l1 sends out the HTLC.
l1.daemon.wait_for_logs([r'NEW:: HTLC LOCAL'])

# Unblock l1 from new blocks.
l1.daemon.rpcproxy.mock_rpc('getblockhash', None)

# pay command should complete without error
fut.result()

0 comments on commit f475441

Please sign in to comment.