def test_update_fee(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, fundchannel=True) chanid = l1.get_channel_scid(l2) # Make l1 send out feechange. l1.set_feerates((14000, 7500, 3750)) l2.daemon.wait_for_log('peer updated fee to 14000') # Now make sure an HTLC works. # (First wait for route propagation.) l1.wait_channel_active(chanid) sync_blockheight(bitcoind, [l1, l2]) # Make payments. l1.pay(l2, 200000000) l2.pay(l1, 100000000) # Now shutdown cleanly. with pytest.raises(RpcError, match=r'Channel close negotiation not finished'): l1.rpc.close(chanid, False, 0) l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE') l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE') # And should put closing into mempool. l1.wait_for_channel_onchain(l2.info['id']) l2.wait_for_channel_onchain(l1.info['id']) bitcoind.generate_block(1) l1.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log(' to ONCHAIN') bitcoind.generate_block(99) l1.daemon.wait_for_log('onchaind complete, forgetting peer') l2.daemon.wait_for_log('onchaind complete, forgetting peer')
def test_multiwithdraw_simple(node_factory, bitcoind): """ Test simple multiwithdraw usage. """ l1, l2, l3 = node_factory.get_nodes(3) l1.fundwallet(10**8) addr2 = l2.rpc.newaddr()['bech32'] amount2 = Millisatoshi(2222 * 1000) addr3 = l3.rpc.newaddr()['bech32'] amount3 = Millisatoshi(3333 * 1000) # Multiwithdraw! txid = l1.rpc.multiwithdraw([{addr2: amount2}, {addr3: amount3}])["txid"] bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1, l2, l3]) # l2 shoulda gotten money. funds2 = l2.rpc.listfunds()['outputs'] assert only_one(funds2)["txid"] == txid assert only_one(funds2)["address"] == addr2 assert only_one(funds2)["status"] == "confirmed" assert only_one(funds2)["amount_msat"] == amount2 # l3 shoulda gotten money. funds3 = l3.rpc.listfunds()['outputs'] assert only_one(funds3)["txid"] == txid assert only_one(funds3)["address"] == addr3 assert only_one(funds3)["status"] == "confirmed" assert only_one(funds3)["amount_msat"] == amount3
def test_addfunds_from_block(node_factory, bitcoind): """Send funds to the daemon without telling it explicitly """ # Previous runs with same bitcoind can leave funds! l1 = node_factory.get_node(random_hsm=True) addr = l1.rpc.newaddr()['bech32'] bitcoind.rpc.sendtoaddress(addr, 0.1) bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1) outputs = l1.db_query('SELECT value FROM outputs WHERE status=0;') assert only_one(outputs)['value'] == 10000000 # The address we detect must match what was paid to. output = only_one(l1.rpc.listfunds()['outputs']) assert output['address'] == addr # Send all our money to a P2WPKH address this time. addr = l1.rpc.newaddr("bech32")['bech32'] l1.rpc.withdraw(addr, "all") bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) # The address we detect must match what was paid to. output = only_one(l1.rpc.listfunds()['outputs']) assert output['address'] == addr
def test_max_channel_id(node_factory, bitcoind): # Create a channel between two peers. # Close the channel and have 100 blocks happen (forget channel) # Restart node, create channel again. Should succeed. l1, l2 = node_factory.line_graph(2, fundchannel=True, wait_for_announce=True) sync_blockheight(bitcoind, [l1, l2]) # Now shutdown cleanly. l1.rpc.close(l2.info['id'], 0) l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE') l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE') # And should put closing into mempool. l1.wait_for_channel_onchain(l2.info['id']) l2.wait_for_channel_onchain(l1.info['id']) bitcoind.generate_block(101) wait_for(lambda: l1.rpc.listpeers()['peers'] == []) wait_for(lambda: l2.rpc.listpeers()['peers'] == []) # Stop l2, and restart l2.stop() l2.start() # Reconnect l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # Fundchannel again, should succeed. l1.rpc.fundchannel(l2.info['id'], 10**5)
def test_bitcoin_failure(node_factory, bitcoind): l1 = node_factory.get_node() # Make sure we're not failing it between getblockhash and getblock. sync_blockheight(bitcoind, [l1]) def crash_bitcoincli(r): return {'error': 'go away'} # This is not a JSON-RPC response by purpose l1.daemon.rpcproxy.mock_rpc('estimatesmartfee', crash_bitcoincli) l1.daemon.rpcproxy.mock_rpc('getblockhash', crash_bitcoincli) # This should cause both estimatefee and getblockhash fail l1.daemon.wait_for_logs(['estimatesmartfee .* exited with status 1', 'getblockhash .* exited with status 1']) # And they should retry! l1.daemon.wait_for_logs(['estimatesmartfee .* exited with status 1', 'getblockhash .* exited with status 1']) # Restore, then it should recover and get blockheight. l1.daemon.rpcproxy.mock_rpc('estimatesmartfee', None) l1.daemon.rpcproxy.mock_rpc('getblockhash', None) bitcoind.generate_block(5) sync_blockheight(bitcoind, [l1])
def test_onchain_all_dust(node_factory, bitcoind, executor): """Onchain handling when we reduce output to all dust""" # HTLC 1->2, 2 fails just after they're both irrevocably committed # We need 2 to drop to chain, because then 1's HTLC timeout tx # is generated on-the-fly, and is thus feerate sensitive. disconnects = ['-WIRE_UPDATE_FAIL_HTLC', 'permfail'] # Feerates identical so we don't get gratuitous commit to update them l1 = node_factory.get_node(options={'dev-no-reconnect': None}, feerates=(7500, 7500, 7500)) l2 = node_factory.get_node(disconnect=disconnects) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fund_channel(l2, 10**6) rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash'] # We underpay, so it fails. routestep = { 'msatoshi': 10**7 - 1, 'id': l2.info['id'], 'delay': 5, 'channel': '1:1:1' } executor.submit(l1.rpc.sendpay, [routestep], rhash) # l2 will drop to chain. l2.daemon.wait_for_log('permfail') l2.daemon.wait_for_log('sendrawtx exit 0') # Make l1's fees really high (and wait for it to exceed 50000) l1.set_feerates((100000, 100000, 100000)) l1.daemon.wait_for_log( 'Feerate estimate for normal set to [56789][0-9]{4}') bitcoind.generate_block(1) l1.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log(' to ONCHAIN') # Wait for timeout. l1.daemon.wait_for_log( 'Propose handling THEIR_UNILATERAL/OUR_HTLC by IGNORING_TINY_PAYMENT .* after 6 blocks' ) bitcoind.generate_block(5) l1.daemon.wait_for_logs([ 'Broadcasting IGNORING_TINY_PAYMENT .* to resolve THEIR_UNILATERAL/OUR_HTLC', 'sendrawtx exit 0', 'Ignoring output 0 of .*: THEIR_UNILATERAL/OUR_HTLC' ]) # 100 deep and l2 forgets. bitcoind.generate_block(93) sync_blockheight(bitcoind, [l1, l2]) assert not l2.daemon.is_in_log('onchaind complete, forgetting peer') assert not l1.daemon.is_in_log('onchaind complete, forgetting peer') bitcoind.generate_block(1) l2.daemon.wait_for_log('onchaind complete, forgetting peer') # l1 does not wait for ignored payment. l1.daemon.wait_for_log('onchaind complete, forgetting peer')
def test_htlcs_cltv_only_difference(node_factory, bitcoind): # l1 -> l2 -> l3 -> l4 # l4 ignores htlcs, so they stay. # l3 will see a reconnect from l4 when l4 restarts. l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True, opts=[{}] * 2 + [{ 'dev-no-reconnect': None, 'may_reconnect': True }] * 2) h = l4.rpc.invoice(msatoshi=10**8, label='x', description='desc')['payment_hash'] l4.rpc.dev_ignore_htlcs(id=l3.info['id'], ignore=True) # L2 tries to pay r = l2.rpc.getroute(l4.info['id'], 10**8, 1)["route"] l2.rpc.sendpay(r, h) # Now increment CLTV bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1, l2, l3, l4]) # L1 tries to pay r = l1.rpc.getroute(l4.info['id'], 10**8, 1)["route"] l1.rpc.sendpay(r, h) # Now increment CLTV bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1, l2, l3, l4]) # L3 tries to pay r = l3.rpc.getroute(l4.info['id'], 10**8, 1)["route"] l3.rpc.sendpay(r, h) # Give them time to go through. time.sleep(5) # Will all be connected OK. assert only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] assert only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['connected'] assert only_one(l3.rpc.listpeers(l4.info['id'])['peers'])['connected'] # Restarting tail node will stop it ignoring HTLCs (it will actually # fail them immediately). l4.restart() l3.rpc.connect(l4.info['id'], 'localhost', l4.port) wait_for(lambda: only_one(l1.rpc.listpayments()['payments'])['status'] == 'failed') wait_for(lambda: only_one(l2.rpc.listpayments()['payments'])['status'] == 'failed') wait_for(lambda: only_one(l3.rpc.listpayments()['payments'])['status'] == 'failed') # Should all still be connected. assert only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] assert only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['connected'] assert only_one(l3.rpc.listpeers(l4.info['id'])['peers'])['connected']
def test_funding_reorg_remote_lags(node_factory, bitcoind): """Nodes may disagree about short_channel_id before channel announcement """ # may_reconnect so channeld will restart opts = {'funding-confirms': 1, 'may_reconnect': True} l1, l2 = node_factory.line_graph(2, fundchannel=False, opts=opts) l1.fundwallet(10000000) sync_blockheight(bitcoind, [l1]) # height 102 l1.rpc.fundchannel(l2.info['id'], "all") bitcoind.generate_block(5) # heights 103 - 107 l1.wait_channel_active('103x1x0') # Make l2 temporary blind for blocks > 107 def no_more_blocks(req): return { "result": None, "error": { "code": -8, "message": "Block height out of range" }, "id": req['id'] } l2.daemon.rpcproxy.mock_rpc('getblockhash', no_more_blocks) # Reorg changes short_channel_id 103x1x0 to 103x2x0, l1 sees it, restarts channeld bitcoind.simple_reorg(102, 1) # heights 102 - 108 l1.daemon.wait_for_log( r'Peer transient failure .* short_channel_id changed to 103x2x0 \(was 103x1x0\)' ) wait_for(lambda: only_one(l2.rpc.listpeers( )['peers'][0]['channels'])['status'] == [ 'CHANNELD_NORMAL:Reconnected, and reestablished.', 'CHANNELD_NORMAL:Funding transaction locked. They need our announcement signatures.' ]) # Unblinding l2 brings it back in sync, restarts channeld and sends its announce sig l2.daemon.rpcproxy.mock_rpc('getblockhash', None) wait_for(lambda: [ c['active'] for c in l2.rpc.listchannels('103x1x0')['channels'] ] == [False, False]) wait_for(lambda: [ c['active'] for c in l2.rpc.listchannels('103x2x0')['channels'] ] == [True, True]) wait_for(lambda: only_one(l2.rpc.listpeers()['peers'][0]['channels'])[ 'status'] == [ 'CHANNELD_NORMAL:Reconnected, and reestablished.', 'CHANNELD_NORMAL:Funding transaction locked. Channel announced.' ]) l1.rpc.close( l2.info['id']) # to ignore `Bad gossip order` error in killall bitcoind.generate_block(1, True) l1.daemon.wait_for_log(r'Deleting channel') l2.daemon.wait_for_log(r'Deleting channel')
def test_htlc_in_timeout(node_factory, bitcoind, executor): """Test that we drop onchain if the peer doesn't accept fulfilled HTLC""" # HTLC 1->2, 1 fails after 2 has sent committed the fulfill disconnects = ['-WIRE_REVOKE_AND_ACK*2'] # Feerates identical so we don't get gratuitous commit to update them l1 = node_factory.get_node(disconnect=disconnects, options={'dev-no-reconnect': None}, feerates=(7500, 7500, 7500)) l2 = node_factory.get_node() l1.rpc.connect(l2.info['id'], 'localhost', l2.port) chanid = l1.fund_channel(l2, 10**6) l1.wait_channel_active(chanid) sync_blockheight(bitcoind, [l1, l2]) amt = 200000000 inv = l2.rpc.invoice(amt, 'test_htlc_in_timeout', 'desc')['bolt11'] assert only_one(l2.rpc.listinvoices('test_htlc_in_timeout') ['invoices'])['status'] == 'unpaid' executor.submit(l1.rpc.pay, inv) # l1 will disconnect and not reconnect. l1.daemon.wait_for_log('dev_disconnect: -WIRE_REVOKE_AND_ACK') # Deadline HTLC expiry minus 1/2 cltv-expiry delta (rounded up) (== cltv - 3). cltv is 5+1. bitcoind.generate_block(2) assert not l2.daemon.is_in_log('hit deadline') bitcoind.generate_block(1) l2.daemon.wait_for_log( 'Fulfilled HTLC 0 SENT_REMOVE_COMMIT cltv .* hit deadline') l2.daemon.wait_for_log('sendrawtx exit 0') l2.bitcoin.generate_block(1) l2.daemon.wait_for_log(' to ONCHAIN') l1.daemon.wait_for_log(' to ONCHAIN') # L2 will collect HTLC l2.daemon.wait_for_log( 'Propose handling OUR_UNILATERAL/THEIR_HTLC by OUR_HTLC_SUCCESS_TX .* after 0 blocks' ) l2.daemon.wait_for_log('sendrawtx exit 0') bitcoind.generate_block(1) l2.daemon.wait_for_log( 'Propose handling OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* after 5 blocks' ) bitcoind.generate_block(4) l2.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') l2.daemon.wait_for_log('sendrawtx exit 0') # Now, 100 blocks it should be both done. bitcoind.generate_block(100) l1.daemon.wait_for_log('onchaind complete, forgetting peer') l2.daemon.wait_for_log('onchaind complete, forgetting peer')
def test_forward(node_factory, bitcoind): # Connect 1 -> 2 -> 3. l1, l2, l3 = node_factory.line_graph(3, fundchannel=True) # Allow announce messages. l1.bitcoin.generate_block(5) # If they're at different block heights we can get spurious errors. sync_blockheight(bitcoind, [l1, l2, l3]) chanid1 = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] chanid2 = only_one(l2.rpc.getpeer(l3.info['id'])['channels'])['short_channel_id'] assert only_one(l2.rpc.getpeer(l1.info['id'])['channels'])['short_channel_id'] == chanid1 assert only_one(l3.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] == chanid2 rhash = l3.rpc.invoice(100000000, 'testpayment1', 'desc')['payment_hash'] assert only_one(l3.rpc.listinvoices('testpayment1')['invoices'])['status'] == 'unpaid' # Fee for node2 is 10 millionths, plus 1. amt = 100000000 fee = amt * 10 // 1000000 + 1 baseroute = [{'msatoshi': amt + fee, 'id': l2.info['id'], 'delay': 12, 'channel': chanid1}, {'msatoshi': amt, 'id': l3.info['id'], 'delay': 6, 'channel': chanid2}] # Unknown other peer route = copy.deepcopy(baseroute) route[1]['id'] = '031a8dc444e41bb989653a4501e11175a488a57439b0c4947704fd6e3de5dca607' l1.rpc.sendpay(route, rhash) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) # Delay too short (we always add one internally anyway, so subtract 2 here). route = copy.deepcopy(baseroute) route[0]['delay'] = 8 l1.rpc.sendpay(route, rhash) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) # Final delay too short route = copy.deepcopy(baseroute) route[1]['delay'] = 3 l1.rpc.sendpay(route, rhash) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) # This one works route = copy.deepcopy(baseroute) l1.rpc.sendpay(route, rhash) l1.rpc.waitsendpay(rhash)
def test_funding_all(node_factory, bitcoind): """Add some funds, fund a channel using all funds, make sure no funds remain """ l1, l2 = node_factory.line_graph(2, fundchannel=False) l1.fundwallet(0.1 * 10**8) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) outputs = l1.db_query('SELECT value FROM outputs WHERE status=0;') assert only_one(outputs)['value'] == 10000000 l1.rpc.fundchannel(l2.info['id'], "all") outputs = l1.db_query('SELECT value FROM outputs WHERE status=0;') assert len(outputs) == 0
def test_onchain_unwatch(node_factory, bitcoind): """Onchaind should not watch random spends""" l1, l2 = node_factory.line_graph(2) l1.pay(l2, 200000000) l1.rpc.dev_fail(l2.info['id']) l1.daemon.wait_for_log('Failing due to dev-fail command') l1.daemon.wait_for_log('sendrawtx exit 0') l1.bitcoin.generate_block(1) l1.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log(' to ONCHAIN') # 10 later, l1 should collect its to-self payment. bitcoind.generate_block(10) l1.daemon.wait_for_log( 'Broadcasting OUR_DELAYED_RETURN_TO_WALLET .* to resolve ' 'OUR_UNILATERAL/DELAYED_OUTPUT_TO_US') l1.daemon.wait_for_log('sendrawtx exit 0') # First time it sees it, onchaind cares. bitcoind.generate_block(1) l1.daemon.wait_for_log( 'Resolved OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by our proposal ' 'OUR_DELAYED_RETURN_TO_WALLET') # Now test unrelated onchain churn. # Daemon gets told about wallet; says it doesn't care. l1.rpc.withdraw(l1.rpc.newaddr()['address'], 'all') bitcoind.generate_block(1) l1.daemon.wait_for_log("but we don't care") # And lightningd should respect that! assert not l1.daemon.is_in_log("Can't unwatch txid") # So these should not generate further messages for i in range(5): l1.rpc.withdraw(l1.rpc.newaddr()['address'], 'all') bitcoind.generate_block(1) # Make sure it digests the block sync_blockheight(bitcoind, [l1]) # We won't see this again. assert not l1.daemon.is_in_log("but we don't care", start=l1.daemon.logsearch_start)
def test_funding_reorg_private(node_factory, bitcoind): """Change funding tx height after lockin, between node restart. """ # Rescan to detect reorg at restart and may_reconnect so channeld # will restart opts = {'funding-confirms': 2, 'rescan': 10, 'may_reconnect': True} l1, l2 = node_factory.line_graph(2, fundchannel=False, opts=opts) l1.fundwallet(10000000) sync_blockheight(bitcoind, [l1]) # height 102 bitcoind.generate_block(3) # heights 103-105 l1.rpc.fundchannel(l2.info['id'], "all", announce=False) bitcoind.generate_block(1) # height 106 wait_for(lambda: only_one(l1.rpc.listpeers( )['peers'][0]['channels'])['status'] == [ 'CHANNELD_AWAITING_LOCKIN:Funding needs 1 more confirmations for lockin.' ]) bitcoind.generate_block(1) # height 107 l1.wait_channel_active('106x1x0') l1.stop() # Create a fork that changes short_channel_id from 106x1x0 to 108x1x0 bitcoind.simple_reorg(106, 2) # heights 106-108 bitcoind.generate_block(1) # height 109 (to reach minimum_depth=2 again) l1.start() # l2 was running, sees last stale block being removed l2.daemon.wait_for_logs([ r'Removing stale block {}'.format(106), r'Got depth change .->{} for .* REORG'.format(0) ]) wait_for(lambda: [ c['active'] for c in l2.rpc.listchannels('106x1x0')['channels'] ] == [False, False]) wait_for(lambda: [ c['active'] for c in l2.rpc.listchannels('108x1x0')['channels'] ] == [True, True]) l1.rpc.close( l2.info['id']) # to ignore `Bad gossip order` error in killall wait_for(lambda: len(bitcoind.rpc.getrawmempool()) > 0) bitcoind.generate_block(1) l1.daemon.wait_for_log(r'Deleting channel') l2.daemon.wait_for_log(r'Deleting channel')
def test_optimistic_locking(node_factory, bitcoind): """Have a node run against a DB, then change it under its feet, crashing it. We start a node, wait for it to settle its write so we have a window where we can interfere, and watch the world burn (safely). """ l1 = node_factory.get_node(may_fail=True, allow_broken_log=True) sync_blockheight(bitcoind, [l1]) l1.rpc.getinfo() time.sleep(1) l1.db.execute("UPDATE vars SET intval = intval + 1 WHERE name = 'data_version';") # Now trigger any DB write and we should be crashing. with pytest.raises(RpcError, match=r'Connection to RPC server lost.'): l1.rpc.newaddr() assert(l1.daemon.is_in_log(r'Optimistic lock on the database failed'))
def test_funding_change(node_factory, bitcoind): """Add some funds, fund a channel, and make sure we remember the change """ l1, l2 = node_factory.line_graph(2, fundchannel=False) l1.fundwallet(10000000) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) outputs = l1.db_query('SELECT value FROM outputs WHERE status=0;') assert only_one(outputs)['value'] == 10000000 l1.rpc.fundchannel(l2.info['id'], 1000000) outputs = {r['status']: r['value'] for r in l1.db_query( 'SELECT status, SUM(value) AS value FROM outputs GROUP BY status;')} # The 10m out is spent and we have a change output of 9m-fee assert outputs[0] > 8990000 assert outputs[2] == 10000000
def test_funding_while_offline(node_factory, bitcoind): l1 = node_factory.get_node() addr = l1.rpc.newaddr()['address'] sync_blockheight(bitcoind, [l1]) # l1 goes down. l1.stop() # We send funds bitcoind.rpc.sendtoaddress(addr, (10**6 + 1000000) / 10**8) # Now 120 blocks go by... bitcoind.generate_block(120) # Restart l1.start() sync_blockheight(bitcoind, [l1]) assert len(l1.rpc.listfunds()['outputs']) == 1
def test_bitcoin_failure(node_factory, bitcoind): l1 = node_factory.get_node() # Make sure we're not failing it between getblockhash and getblock. sync_blockheight(bitcoind, [l1]) l1.bitcoind_cmd_override('exit 1') # This should cause both estimatefee and getblockhash fail l1.daemon.wait_for_logs(['estimatesmartfee .* exited with status 1', 'getblockhash .* exited with status 1']) # And they should retry! l1.daemon.wait_for_logs(['estimatesmartfee .* exited with status 1', 'getblockhash .* exited with status 1']) # Restore, then it should recover and get blockheight. l1.bitcoind_cmd_remove_override() bitcoind.generate_block(5) sync_blockheight(bitcoind, [l1])
def test_deprecated_txprepare(node_factory, bitcoind): """Test the deprecated old-style: txprepare {destination} {satoshi} {feerate} {minconf} """ amount = 10**4 l1 = node_factory.get_node(options={'allow-deprecated-apis': True}) addr = l1.rpc.newaddr()['bech32'] for i in range(7): l1.fundwallet(10**8) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 7) # Array type with pytest.raises(RpcError, match=r'.* should be an amount in satoshis or all, not .*'): l1.rpc.call('txprepare', [addr, 'slow']) with pytest.raises(RpcError, match=r'Need set \'satoshi\' field.'): l1.rpc.call('txprepare', [addr]) with pytest.raises(RpcError, match=r'Could not parse destination address.*'): l1.rpc.call('txprepare', [Millisatoshi(amount * 100), 'slow', 1]) l1.rpc.call('txprepare', [addr, Millisatoshi(amount * 100), 'slow', 1]) l1.rpc.call('txprepare', [addr, Millisatoshi(amount * 100), 'normal']) l1.rpc.call('txprepare', [addr, Millisatoshi(amount * 100), None, 1]) l1.rpc.call('txprepare', [addr, Millisatoshi(amount * 100)]) # Object type with pytest.raises(RpcError, match=r'Need set \'outputs\' field.'): l1.rpc.call('txprepare', {'destination': addr, 'feerate': 'slow'}) with pytest.raises(RpcError, match=r'Need set \'outputs\' field.'): l1.rpc.call('txprepare', {'satoshi': Millisatoshi(amount * 100), 'feerate': '10perkw', 'minconf': 2}) l1.rpc.call('txprepare', {'destination': addr, 'satoshi': Millisatoshi(amount * 100), 'feerate': '2000perkw', 'minconf': 1}) l1.rpc.call('txprepare', {'destination': addr, 'satoshi': Millisatoshi(amount * 100), 'feerate': '2000perkw'}) l1.rpc.call('txprepare', {'destination': addr, 'satoshi': Millisatoshi(amount * 100)})
def test_permfail_htlc_out(node_factory, bitcoind, executor): # Test case where we fail with unsettled outgoing HTLC. disconnects = ['+WIRE_REVOKE_AND_ACK', 'permfail'] l1 = node_factory.get_node(options={'dev-no-reconnect': None}) # Feerates identical so we don't get gratuitous commit to update them l2 = node_factory.get_node(disconnect=disconnects, feerates=(7500, 7500, 7500)) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l2.daemon.wait_for_log( 'openingd-{} chan #1: Handed peer, entering loop'.format( l1.info['id'])) l2.fund_channel(l1, 10**6) # This will fail at l2's end. t = executor.submit(l2.pay, l1, 200000000) l2.daemon.wait_for_log('dev_disconnect permfail') l2.wait_for_channel_onchain(l1.info['id']) bitcoind.generate_block(1) l1.daemon.wait_for_log('Their unilateral tx, old commit point') l1.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_logs([ 'Propose handling OUR_UNILATERAL/OUR_HTLC by OUR_HTLC_TIMEOUT_TX \\(.*\\) after 6 blocks', 'Propose handling OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* after 5 blocks' ]) l1.daemon.wait_for_log( 'Propose handling THEIR_UNILATERAL/THEIR_HTLC by THEIR_HTLC_TIMEOUT_TO_THEM \\(IGNORING\\) after 6 blocks' ) # l1 then gets preimage, uses it instead of ignoring l1.wait_for_onchaind_broadcast('THEIR_HTLC_FULFILL_TO_US', 'THEIR_UNILATERAL/THEIR_HTLC') # l2 sees l1 fulfill tx. bitcoind.generate_block(1) l2.daemon.wait_for_log('OUR_UNILATERAL/OUR_HTLC gave us preimage') t.cancel() # l2 can send OUR_DELAYED_RETURN_TO_WALLET after 3 more blocks. bitcoind.generate_block(3) l2.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', 'OUR_UNILATERAL/DELAYED_OUTPUT_TO_US') # Now, 100 blocks they should be done. bitcoind.generate_block(95) sync_blockheight(bitcoind, [l1, l2]) assert not l1.daemon.is_in_log('onchaind complete, forgetting peer') assert not l2.daemon.is_in_log('onchaind complete, forgetting peer') bitcoind.generate_block(1) l1.daemon.wait_for_log('onchaind complete, forgetting peer') sync_blockheight(bitcoind, [l2]) assert not l2.daemon.is_in_log('onchaind complete, forgetting peer') bitcoind.generate_block(3) sync_blockheight(bitcoind, [l2]) assert not l2.daemon.is_in_log('onchaind complete, forgetting peer') bitcoind.generate_block(1) wait_for(lambda: l2.rpc.listpeers()['peers'] == [])
def test_onchaind_replay(node_factory, bitcoind): disconnects = ['+WIRE_REVOKE_AND_ACK', 'permfail'] options = {'watchtime-blocks': 201, 'cltv-delta': 101} # Feerates identical so we don't get gratuitous commit to update them l1 = node_factory.get_node(options=options, disconnect=disconnects, feerates=(7500, 7500, 7500)) l2 = node_factory.get_node(options=options) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fund_channel(l2, 10**6) rhash = l2.rpc.invoice(10**8, 'onchaind_replay', 'desc')['payment_hash'] routestep = { 'msatoshi': 10**8 - 1, 'id': l2.info['id'], 'delay': 101, 'channel': '1:1:1' } l1.rpc.sendpay([routestep], rhash) l1.daemon.wait_for_log('sendrawtx exit 0') bitcoind.rpc.generate(1) # Wait for nodes to notice the failure, this seach needle is after the # DB commit so we're sure the tx entries in onchaindtxs have been added l1.daemon.wait_for_log( "Deleting channel .* due to the funding outpoint being spent") l2.daemon.wait_for_log( "Deleting channel .* due to the funding outpoint being spent") # We should at least have the init tx now assert len(l1.db_query("SELECT * FROM channeltxs;")) > 0 assert len(l2.db_query("SELECT * FROM channeltxs;")) > 0 # Generate some blocks so we restart the onchaind from DB (we rescan # last_height - 100) bitcoind.rpc.generate(100) sync_blockheight(bitcoind, [l1, l2]) # l1 should still have a running onchaind assert len(l1.db_query("SELECT * FROM channeltxs;")) > 0 l2.rpc.stop() l1.restart() # Can't wait for it, it's after the "Server started" wait in restart() assert l1.daemon.is_in_log(r'Restarting onchaind for channel') # l1 should still notice that the funding was spent and that we should react to it l1.daemon.wait_for_log( "Propose handling OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET" ) sync_blockheight(bitcoind, [l1]) bitcoind.rpc.generate(10) sync_blockheight(bitcoind, [l1])
def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): """ Tests for the sign + send psbt RPCs """ amount = 1000000 total_outs = 12 coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') l1 = node_factory.get_node(options={'plugin': coin_mvt_plugin}, feerates=(7500, 7500, 7500, 7500)) l2 = node_factory.get_node() addr = chainparams['example_addr'] out_total = Millisatoshi(amount * 3 * 1000) # Add a medley of funds to withdraw later, bech32 + p2sh-p2wpkh for i in range(total_outs // 2): bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], amount / 10**8) bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('p2sh-segwit')['p2sh-segwit'], amount / 10**8) bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs) # Make a PSBT out of our inputs funding = l1.rpc.fundpsbt(satoshi=out_total, feerate=7500, startweight=42, reserve=True) assert len([x for x in l1.rpc.listfunds()['outputs'] if x['reserved']]) == 4 psbt = bitcoind.rpc.decodepsbt(funding['psbt']) saved_input = psbt['tx']['vin'][0] # Go ahead and unreserve the UTXOs, we'll use a smaller # set of them to create a second PSBT that we'll attempt to sign # and broadcast (to disastrous results) l1.rpc.unreserveinputs(funding['psbt']) # Re-reserve one of the utxos we just unreserved psbt = bitcoind.rpc.createpsbt([{'txid': saved_input['txid'], 'vout': saved_input['vout']}], []) l1.rpc.reserveinputs(psbt) # We require the utxos be reserved before signing them with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO .* is not reserved"): l1.rpc.signpsbt(funding['psbt'])['signed_psbt'] # Now we unreserve the singleton, so we can reserve it again l1.rpc.unreserveinputs(psbt) # Now add an output. Note, we add the 'excess msat' to the output so # that our feerate is 'correct'. This is of particular importance to elementsd, # who requires that every satoshi be accounted for in a tx. out_1_ms = Millisatoshi(funding['excess_msat']) output_psbt = bitcoind.rpc.createpsbt([], [{addr: float((out_total + out_1_ms).to_btc())}]) fullpsbt = bitcoind.rpc.joinpsbts([funding['psbt'], output_psbt]) # We re-reserve the first set... l1.rpc.reserveinputs(fullpsbt) # Sign + send the PSBT we've created signed_psbt = l1.rpc.signpsbt(fullpsbt)['signed_psbt'] broadcast_tx = l1.rpc.sendpsbt(signed_psbt) # Check that it was broadcast successfully l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx'])) bitcoind.generate_block(1) # We didn't add a change output. expected_outs = total_outs - 4 wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == expected_outs) # Let's try *sending* a PSBT that can't be finalized (it's unsigned) with pytest.raises(RpcError, match=r"PSBT not finalizeable"): l1.rpc.sendpsbt(fullpsbt) # Now we try signing a PSBT with an output that's already been spent with pytest.raises(RpcError, match=r"Aborting PSBT signing. UTXO .* is not reserved"): l1.rpc.signpsbt(fullpsbt) # Queue up another node, to make some PSBTs for us for i in range(total_outs // 2): bitcoind.rpc.sendtoaddress(l2.rpc.newaddr()['bech32'], amount / 10**8) bitcoind.rpc.sendtoaddress(l2.rpc.newaddr('p2sh-segwit')['p2sh-segwit'], amount / 10**8) # Create a PSBT using L2 bitcoind.generate_block(1) wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == total_outs) l2_funding = l2.rpc.fundpsbt(satoshi=out_total, feerate=7500, startweight=42, reserve=True) # Try to get L1 to sign it with pytest.raises(RpcError, match=r"No wallet inputs to sign"): l1.rpc.signpsbt(l2_funding['psbt']) # With signonly it will fail if it can't sign it. with pytest.raises(RpcError, match=r"is unknown"): l1.rpc.signpsbt(l2_funding['psbt'], signonly=[0]) # Add some of our own PSBT inputs to it l1_funding = l1.rpc.fundpsbt(satoshi=out_total, feerate=7500, startweight=42, reserve=True) l1_num_inputs = len(bitcoind.rpc.decodepsbt(l1_funding['psbt'])['tx']['vin']) l2_num_inputs = len(bitcoind.rpc.decodepsbt(l2_funding['psbt'])['tx']['vin']) # Join and add an output (reorders!) out_2_ms = Millisatoshi(l1_funding['excess_msat']) out_amt = out_2_ms + Millisatoshi(l2_funding['excess_msat']) + out_total + out_total output_psbt = bitcoind.rpc.createpsbt([], [{addr: float(out_amt.to_btc())}]) joint_psbt = bitcoind.rpc.joinpsbts([l1_funding['psbt'], l2_funding['psbt'], output_psbt]) # Ask it to sign inputs it doesn't know, it will fail. with pytest.raises(RpcError, match=r"is unknown"): l1.rpc.signpsbt(joint_psbt, signonly=list(range(l1_num_inputs + 1))) # Similarly, it can't sign inputs it doesn't know. sign_success = [] for i in range(l1_num_inputs + l2_num_inputs): try: l1.rpc.signpsbt(joint_psbt, signonly=[i]) except RpcError: continue sign_success.append(i) assert len(sign_success) == l1_num_inputs # But it can sign all the valid ones at once. half_signed_psbt = l1.rpc.signpsbt(joint_psbt, signonly=sign_success)['signed_psbt'] for s in sign_success: assert bitcoind.rpc.decodepsbt(half_signed_psbt)['inputs'][s]['partial_signatures'] is not None totally_signed = l2.rpc.signpsbt(half_signed_psbt)['signed_psbt'] broadcast_tx = l1.rpc.sendpsbt(totally_signed) l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx'])) # Send a PSBT that's not ours l2_funding = l2.rpc.fundpsbt(satoshi=out_total, feerate=7500, startweight=42, reserve=True) out_amt = Millisatoshi(l2_funding['excess_msat']) output_psbt = bitcoind.rpc.createpsbt([], [{addr: float((out_total + out_amt).to_btc())}]) psbt = bitcoind.rpc.joinpsbts([l2_funding['psbt'], output_psbt]) l2_signed_psbt = l2.rpc.signpsbt(psbt)['signed_psbt'] l1.rpc.sendpsbt(l2_signed_psbt) # Re-try sending the same tx? bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) # Expect an error here with pytest.raises(JSONRPCError, match=r"Transaction already in block chain"): bitcoind.rpc.sendrawtransaction(broadcast_tx['tx']) # Try an empty PSBT with pytest.raises(RpcError, match=r"should be a PSBT, not"): l1.rpc.signpsbt('') with pytest.raises(RpcError, match=r"should be a PSBT, not"): l1.rpc.sendpsbt('') # Try an invalid PSBT string invalid_psbt = 'cHNidP8BAM0CAAAABJ9446mTRp/ml8OxSLC1hEvrcxG1L02AG7YZ4syHon2sAQAAAAD9////JFJH/NjKwjwrP9myuU68G7t8Q4VIChH0KUkZ5hSAyqcAAAAAAP3///8Uhrj0XDRhGRno8V7qEe4hHvZcmEjt3LQSIXWc+QU2tAEAAAAA/f///wstLikuBrgZJI83VPaY8aM7aPe5U6TMb06+jvGYzQLEAQAAAAD9////AcDGLQAAAAAAFgAUyQltQ/QI6lJgICYsza18hRa5KoEAAAAAAAEBH0BCDwAAAAAAFgAUqc1Qh7Q5kY1noDksmj7cJmHaIbQAAQEfQEIPAAAAAAAWABS3bdYeQbXvBSryHNoyYIiMBwu5rwABASBAQg8AAAAAABepFD1r0NuqAA+R7zDiXrlP7J+/PcNZhwEEFgAUKvGgVL/ThjWE/P1oORVXh/ObucYAAQEgQEIPAAAAAAAXqRRsrE5ugA1VJnAith5+msRMUTwl8ocBBBYAFMrfGCiLi0ZnOCY83ERKJ1sLYMY8A=' with pytest.raises(RpcError, match=r"should be a PSBT, not"): l1.rpc.signpsbt(invalid_psbt) wallet_coin_mvts = [ {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000 + int(out_1_ms), 'tag': 'withdrawal'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000 - int(out_1_ms), 'tag': 'chain_fees'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 4000000000, 'tag': 'withdrawal'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'chain_fees'}, ] check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams)
def test_transaction_annotations(node_factory, bitcoind): l1, l2, l3 = node_factory.get_nodes(3) l1.fundwallet(10**6) # We should now have a transaction that gave us the funds in the # transactions table... outputs = l1.rpc.listfunds()['outputs'] assert (len(outputs) == 1 and outputs[0]['status'] == 'confirmed') out = outputs[0] idx = out['output'] assert (idx in [0, 1] and out['value'] == 10**6) # ... and it should have an annotation on the output reading 'deposit' txs = l1.rpc.listtransactions()['transactions'] assert (len(txs) == 1) tx = txs[0] output = tx['outputs'][idx] assert (output['type'] == 'deposit' and output['satoshis'] == '1000000000msat') # ... and all other output should be change, and have no annotations types = [] for i, o in enumerate(tx['outputs']): if i == idx: continue if 'type' in o: types.append(o['type']) else: types.append(None) assert (set([None]) == set(types)) ########################################################################## # Let's now open a channel. The opener should get the funding transaction # annotated as channel open and deposit. l1.connect(l2) fundingtx = l1.rpc.fundchannel(l2.info['id'], 10**5) # We should have one output available, and it should be unconfirmed outputs = l1.rpc.listfunds()['outputs'] assert (len(outputs) == 1 and outputs[0]['status'] == 'unconfirmed') # It should also match the funding txid: assert (outputs[0]['txid'] == fundingtx['txid']) # Confirm the channel and check that the output changed to confirmed bitcoind.generate_block(3) sync_blockheight(bitcoind, [l1, l2]) outputs = l1.rpc.listfunds()['outputs'] assert (len(outputs) == 1 and outputs[0]['status'] == 'confirmed') # We should have 2 transactions, the second one should be the funding tx # (we are ordering by blockheight and txindex, so that order should be ok) txs = l1.rpc.listtransactions()['transactions'] assert (len(txs) == 2 and txs[1]['hash'] == fundingtx['txid']) # Check the annotated types types = [o['type'] for o in txs[1]['outputs']] changeidx = 0 if types[0] == 'deposit' else 1 fundidx = 1 - changeidx assert (types[changeidx] == 'deposit' and types[fundidx] == 'channel_funding') # And check the channel annotation on the funding output peers = l1.rpc.listpeers()['peers'] assert (len(peers) == 1 and len(peers[0]['channels']) == 1) scid = peers[0]['channels'][0]['short_channel_id'] assert (txs[1]['outputs'][fundidx]['channel'] == scid)
def test_withdraw(node_factory, bitcoind): amount = 1000000 # Don't get any funds from previous runs. l1 = node_factory.get_node(random_hsm=True) l2 = node_factory.get_node(random_hsm=True) addr = l1.rpc.newaddr()['bech32'] # Add some funds to withdraw later for i in range(10): l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01) bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10) # Reach around into the db to check that outputs were added assert l1.db_query( 'SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 10 waddr = l1.bitcoin.rpc.getnewaddress() # Now attempt to withdraw some (making sure we collect multiple inputs) with pytest.raises(RpcError): l1.rpc.withdraw('not an address', amount) with pytest.raises(RpcError): l1.rpc.withdraw(waddr, 'not an amount') with pytest.raises(RpcError): l1.rpc.withdraw(waddr, -amount) with pytest.raises(RpcError, match=r'Cannot afford transaction'): l1.rpc.withdraw(waddr, amount * 100) out = l1.rpc.withdraw(waddr, 2 * amount) # Make sure bitcoind received the withdrawal unspent = l1.bitcoin.rpc.listunspent(0) withdrawal = [u for u in unspent if u['txid'] == out['txid']] assert (withdrawal[0]['amount'] == Decimal('0.02')) l1.bitcoin.generate_block(1) sync_blockheight(l1.bitcoin, [l1]) # Check that there are no unconfirmed outputs (change should be confirmed) for o in l1.rpc.listfunds()['outputs']: assert o['status'] == 'confirmed' # Now make sure two of them were marked as spent assert l1.db_query( 'SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 2 # Now send some money to l2. # lightningd uses P2SH-P2WPKH waddr = l2.rpc.newaddr('bech32')['bech32'] l1.rpc.withdraw(waddr, 2 * amount) bitcoind.generate_block(1) # Make sure l2 received the withdrawal. wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == 1) outputs = l2.db_query('SELECT value FROM outputs WHERE status=0;') assert only_one(outputs)['value'] == 2 * amount # Now make sure an additional two of them were marked as spent assert l1.db_query( 'SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 4 # Simple test for withdrawal to P2WPKH # Address from: https://bc-2.jp/tools/bech32demo/index.html waddr = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080' with pytest.raises(RpcError): l1.rpc.withdraw('xx1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx', 2 * amount) with pytest.raises(RpcError): l1.rpc.withdraw('tb1pw508d6qejxtdg4y5r3zarvary0c5xw7kdl9fad', 2 * amount) with pytest.raises(RpcError): l1.rpc.withdraw('tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxxxxxx', 2 * amount) l1.rpc.withdraw(waddr, 2 * amount) bitcoind.generate_block(1) # Now make sure additional two of them were marked as spent assert l1.db_query( 'SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 6 # Simple test for withdrawal to P2WSH # Address from: https://bc-2.jp/tools/bech32demo/index.html waddr = 'bcrt1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qzf4jry' with pytest.raises(RpcError): l1.rpc.withdraw( 'xx1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7', 2 * amount) with pytest.raises(RpcError): l1.rpc.withdraw( 'tb1prp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qsm03tq', 2 * amount) with pytest.raises(RpcError): l1.rpc.withdraw( 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qxxxxxx', 2 * amount) l1.rpc.withdraw(waddr, 2 * amount) bitcoind.generate_block(1) # Now make sure additional two of them were marked as spent assert l1.db_query( 'SELECT COUNT(*) as c FROM outputs WHERE status=2')[0]['c'] == 8 # failure testing for invalid SegWit addresses, from BIP173 # HRP character out of range with pytest.raises(RpcError): l1.rpc.withdraw(' 1nwldj5', 2 * amount) # overall max length exceeded with pytest.raises(RpcError): l1.rpc.withdraw( 'an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx', 2 * amount) # No separator character with pytest.raises(RpcError): l1.rpc.withdraw('pzry9x0s0muk', 2 * amount) # Empty HRP with pytest.raises(RpcError): l1.rpc.withdraw('1pzry9x0s0muk', 2 * amount) # Invalid witness version with pytest.raises(RpcError): l1.rpc.withdraw('BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2', 2 * amount) # Invalid program length for witness version 0 (per BIP141) with pytest.raises(RpcError): l1.rpc.withdraw('BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P', 2 * amount) # Mixed case with pytest.raises(RpcError): l1.rpc.withdraw( 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7', 2 * amount) # Non-zero padding in 8-to-5 conversion with pytest.raises(RpcError): l1.rpc.withdraw( 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv', 2 * amount) # Should have 6 outputs available. assert l1.db_query( 'SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 6 # Test withdrawal to self. l1.rpc.withdraw(l1.rpc.newaddr('bech32')['bech32'], 'all', minconf=0) bitcoind.generate_block(1) assert l1.db_query( 'SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 1 l1.rpc.withdraw(waddr, 'all', minconf=0) assert l1.db_query( 'SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 0 # This should fail, can't even afford fee. with pytest.raises(RpcError, match=r'Cannot afford transaction'): l1.rpc.withdraw(waddr, 'all') # Add some funds to withdraw for i in range(10): l1.bitcoin.rpc.sendtoaddress(addr, amount / 10**8 + 0.01) bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10) # Try passing in a utxo set utxos = [ utxo["txid"] + ":" + str(utxo["output"]) for utxo in l1.rpc.listfunds()["outputs"] ][:4] withdrawal = l1.rpc.withdraw(waddr, 2 * amount, utxos=utxos) decode = bitcoind.rpc.decoderawtransaction(withdrawal['tx']) assert decode['txid'] == withdrawal['txid'] # Check that correct utxos are included assert len(decode['vin']) == 4 vins = ["{}:{}".format(v['txid'], v['vout']) for v in decode['vin']] for utxo in utxos: assert utxo in vins
def test_onchain_feechange(node_factory, bitcoind, executor): """Onchain handling when we restart with different fees""" # HTLC 1->2, 2 fails just after they're both irrevocably committed # We need 2 to drop to chain, because then 1's HTLC timeout tx # is generated on-the-fly, and is thus feerate sensitive. disconnects = ['-WIRE_UPDATE_FAIL_HTLC', 'permfail'] l1 = node_factory.get_node(may_reconnect=True) l2 = node_factory.get_node(disconnect=disconnects, may_reconnect=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fund_channel(l2, 10**6) rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash'] # We underpay, so it fails. routestep = { 'msatoshi': 10**8 - 1, 'id': l2.info['id'], 'delay': 5, 'channel': '1:1:1' } executor.submit(l1.rpc.sendpay, [routestep], rhash) # l2 will drop to chain. l2.daemon.wait_for_log('permfail') l2.daemon.wait_for_log('sendrawtx exit 0') bitcoind.generate_block(1) l1.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log(' to ONCHAIN') # Wait for timeout. l1.daemon.wait_for_log( 'Propose handling THEIR_UNILATERAL/OUR_HTLC by OUR_HTLC_TIMEOUT_TO_US .* after 6 blocks' ) bitcoind.generate_block(6) l1.daemon.wait_for_log('sendrawtx exit 0') # Make sure that gets included. bitcoind.generate_block(1) # Now we restart with different feerates. l1.stop() l1.daemon.cmd_line.append('--override-fee-rates=20000/9000/2000') l1.start() # We recognize different proposal as ours. l1.daemon.wait_for_log( 'Resolved THEIR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TO_US' ) # We use 3 blocks for "reasonable depth", so add two more bitcoind.generate_block(2) # Note that the very similar test_onchain_timeout looks for a # different string: that's because it sees the JSONRPC response, # and due to the l1 restart, there is none here. l1.daemon.wait_for_log('WIRE_PERMANENT_CHANNEL_FAILURE') # 90 later, l2 is done bitcoind.generate_block(89) sync_blockheight(bitcoind, [l2]) assert not l2.daemon.is_in_log('onchaind complete, forgetting peer') bitcoind.generate_block(1) l2.daemon.wait_for_log('onchaind complete, forgetting peer') # Now, 7 blocks and l1 should be done. bitcoind.generate_block(6) sync_blockheight(bitcoind, [l1]) assert not l1.daemon.is_in_log('onchaind complete, forgetting peer') bitcoind.generate_block(1) l1.daemon.wait_for_log('onchaind complete, forgetting peer') # Payment failed, BTW assert only_one(l2.rpc.listinvoices('onchain_timeout') ['invoices'])['status'] == 'unpaid'
def test_v2_rbf_single(node_factory, bitcoind, chainparams): l1, l2 = node_factory.get_nodes(2, opts={'wumbo': None}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) amount = 2**24 chan_amount = 100000 bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], amount / 10**8 + 0.01) bitcoind.generate_block(1) # Wait for it to arrive. wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0) res = l1.rpc.fundchannel(l2.info['id'], chan_amount) chan_id = res['channel_id'] vins = bitcoind.rpc.decoderawtransaction(res['tx'])['vin'] assert(only_one(vins)) prev_utxos = ["{}:{}".format(vins[0]['txid'], vins[0]['vout'])] # Check that we're waiting for lockin l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') next_feerate = find_next_feerate(l1, l2) # Check that feerate info is correct info_1 = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) assert info_1['initial_feerate'] == info_1['last_feerate'] rate = int(info_1['last_feerate'][:-5]) assert int(info_1['next_feerate'][:-5]) == rate * 65 // 64 # Initiate an RBF startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) # Do the bump bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] # Check that feerate info has incremented info_2 = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) assert info_1['initial_feerate'] == info_2['initial_feerate'] assert info_1['next_feerate'] == info_2['last_feerate'] rate = int(info_2['last_feerate'][:-5]) assert int(info_2['next_feerate'][:-5]) == rate * 65 // 64 # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] # Fails because we didn't put enough feerate in. with pytest.raises(RpcError, match=r'insufficient fee'): l1.rpc.openchannel_signed(chan_id, signed_psbt) # Do it again, with a higher feerate info_2 = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) assert info_1['initial_feerate'] == info_2['initial_feerate'] assert info_1['next_feerate'] == info_2['last_feerate'] rate = int(info_2['last_feerate'][:-5]) assert int(info_2['next_feerate'][:-5]) == rate * 65 // 64 # We 4x the feerate to beat the min-relay fee next_rate = '{}perkw'.format(rate * 65 // 64 * 4) # Gotta unreserve the psbt and re-reserve with higher feerate l1.rpc.unreserveinputs(initpsbt['psbt']) initpsbt = l1.rpc.utxopsbt(chan_amount, next_rate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) # Do the bump+sign bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt'], funding_feerate=next_rate) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] l1.rpc.openchannel_signed(chan_id, signed_psbt) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log(' to CHANNELD_NORMAL') # Check that feerate info is gone info_1 = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) assert 'initial_feerate' not in info_1 assert 'last_feerate' not in info_1 assert 'next_feerate' not in info_1 # Shut l2 down, force close the channel. l2.stop() resp = l1.rpc.close(l2.info['id'], unilateraltimeout=1) assert resp['type'] == 'unilateral' l1.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN') l1.daemon.wait_for_log('sendrawtx exit 0')
def test_rbf_reconnect_tx_sigs(node_factory, bitcoind, chainparams): disconnects = [ '=WIRE_TX_SIGNATURES', # Initial funding succeeds '-WIRE_TX_SIGNATURES', # When we send tx-sigs, RBF '=WIRE_TX_SIGNATURES', # When we reconnect '@WIRE_TX_SIGNATURES', # When we RBF again '=WIRE_TX_SIGNATURES', # When we reconnect '+WIRE_TX_SIGNATURES' ] # When we RBF again l1, l2 = node_factory.get_nodes( 2, opts=[ { 'dev-force-features': '+223', 'disconnect': disconnects, 'may_reconnect': True, # On reconnects, we dont have cmd # outstanding for the missed sigs # so l1 logs a BROKEN. 'allow_broken_log': True }, { 'dev-force-features': '+223', 'may_reconnect': True } ]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) amount = 2**24 chan_amount = 100000 bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], amount / 10**8 + 0.01) bitcoind.generate_block(1) # Wait for it to arrive. wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0) res = l1.rpc.fundchannel(l2.info['id'], chan_amount) chan_id = res['channel_id'] vins = bitcoind.rpc.decoderawtransaction(res['tx'])['vin'] assert (only_one(vins)) prev_utxos = ["{}:{}".format(vins[0]['txid'], vins[0]['vout'])] # Check that we're waiting for lockin l1.daemon.wait_for_log('Broadcasting funding tx') l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') next_feerate = find_next_feerate(l1, l2) # Initiate an RBF startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] # First time we error when we send our sigs with pytest.raises(RpcError, match='Owning subdaemon dualopend died'): l1.rpc.openchannel_signed(chan_id, signed_psbt) # We reconnect and try again. feerate should have bumped next_feerate = find_next_feerate(l1, l2) # Initiate an RBF startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # l2 gets our sigs and broadcasts them l2.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') l2.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') l2.daemon.wait_for_log('sendrawtx exit 0') # Wait until we've done re-establish, if we try to # RBF again too quickly, it'll fail since they haven't # had time to process our sigs yet l1.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') l1.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') # Now we initiate the RBF bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] # Second time we error after we send our sigs with pytest.raises(RpcError, match='Owning subdaemon dualopend died'): l1.rpc.openchannel_signed(chan_id, signed_psbt) # We reconnect and try again. feerate should have bumped next_feerate = find_next_feerate(l1, l2) startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # l2 gets our sigs and broadcasts them l2.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') l2.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') l2.daemon.wait_for_log('sendrawtx exit 0') # Wait until we've done re-establish, if we try to # RBF again too quickly, it'll fail since they haven't # had time to process our sigs yet l1.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') l1.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') # 3rd RBF bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] # Third time we error after we send our sigs with pytest.raises(RpcError, match='Owning subdaemon dualopend died'): l1.rpc.openchannel_signed(chan_id, signed_psbt) # l2 gets our sigs l2.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') l2.daemon.wait_for_log('sendrawtx exit 0') # mine a block? bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log(' to CHANNELD_NORMAL') # Check that they have matching funding txid l1_funding_txid = only_one( only_one(l1.rpc.listpeers()['peers'])['channels'])['funding_txid'] l2_funding_txid = only_one( only_one(l2.rpc.listpeers()['peers'])['channels'])['funding_txid'] assert l1_funding_txid == l2_funding_txid
def test_v2_rbf(node_factory, bitcoind, chainparams): l1, l2 = node_factory.get_nodes(2, opts=[{ 'dev-force-features': '+223' }, { 'dev-force-features': '+223' }]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) amount = 2**24 chan_amount = 100000 bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], amount / 10**8 + 0.01) bitcoind.generate_block(1) # Wait for it to arrive. wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0) res = l1.rpc.fundchannel(l2.info['id'], chan_amount) chan_id = res['channel_id'] vins = bitcoind.rpc.decoderawtransaction(res['tx'])['vin'] assert (only_one(vins)) prev_utxos = ["{}:{}".format(vins[0]['txid'], vins[0]['vout'])] # Check that we're waiting for lockin l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') next_feerate = find_next_feerate(l1, l2) # Check that feerate info is correct info_1 = only_one( only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) assert info_1['initial_feerate'] == info_1['last_feerate'] rate = int(info_1['last_feerate'][:-5]) assert int(info_1['next_feerate'][:-5]) == rate + rate // 4 assert info_1['next_fee_step'] == 1 # Initiate an RBF startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) # Do the bump bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] # Check that feerate info has incremented info_2 = only_one( only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) assert info_1['initial_feerate'] == info_2['initial_feerate'] assert info_1['next_feerate'] == info_2['last_feerate'] rate = int(info_2['last_feerate'][:-5]) assert int(info_2['next_feerate'][:-5]) == rate + rate // 4 assert info_2['next_fee_step'] == 2 # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] l1.rpc.openchannel_signed(chan_id, signed_psbt) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log(' to CHANNELD_NORMAL') # Check that feerate info is gone info_1 = only_one( only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) assert 'initial_feerate' not in info_1 assert 'last_feerate' not in info_1 assert 'next_feerate' not in info_1 assert 'next_fee_step' not in info_1 # Shut l2 down, force close the channel. l2.stop() resp = l1.rpc.close(l2.info['id'], unilateraltimeout=1) assert resp['type'] == 'unilateral' l1.daemon.wait_for_log(' to CHANNELD_SHUTTING_DOWN') l1.daemon.wait_for_log('sendrawtx exit 0')
def test_v2_rbf_multi(node_factory, bitcoind, chainparams): l1, l2 = node_factory.get_nodes(2, opts=[{ 'dev-force-features': '+223' }, { 'dev-force-features': '+223' }]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) amount = 2**24 chan_amount = 100000 bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], amount / 10**8 + 0.01) bitcoind.generate_block(1) # Wait for it to arrive. wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0) res = l1.rpc.fundchannel(l2.info['id'], chan_amount) chan_id = res['channel_id'] vins = bitcoind.rpc.decoderawtransaction(res['tx'])['vin'] assert (only_one(vins)) prev_utxos = ["{}:{}".format(vins[0]['txid'], vins[0]['vout'])] # Check that we're waiting for lockin l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') next_feerate = find_next_feerate(l1, l2) # Initiate an RBF startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) # Do the bump bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] l1.rpc.openchannel_signed(chan_id, signed_psbt) next_feerate = find_next_feerate(l1, l2) # Initiate an RBF, double the channel amount this time startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount * 2, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) # Do the bump bump = l1.rpc.openchannel_bump(chan_id, chan_amount * 2, initpsbt['psbt']) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] l1.rpc.openchannel_signed(chan_id, signed_psbt) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log(' to CHANNELD_NORMAL')
def test_forward_event_notification(node_factory, bitcoind, executor): """ test 'forward_event' notifications """ amount = 10**8 disconnects = ['-WIRE_UPDATE_FAIL_HTLC', 'permfail'] l1, l2, l3 = node_factory.line_graph( 3, opts=[{}, { 'plugin': os.path.join(os.getcwd(), 'tests/plugins/forward_payment_status.py') }, {}], wait_for_announce=True) l4 = node_factory.get_node() l5 = node_factory.get_node(disconnect=disconnects) l2.openchannel(l4, 10**6, wait_for_announce=False) l2.openchannel(l5, 10**6, wait_for_announce=True) bitcoind.generate_block(5) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 8) payment_hash13 = l3.rpc.invoice(amount, "first", "desc")['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] # status: offered -> settled l1.rpc.sendpay(route, payment_hash13) l1.rpc.waitsendpay(payment_hash13) # status: offered -> failed route = l1.rpc.getroute(l4.info['id'], amount, 1)['route'] payment_hash14 = "f" * 64 with pytest.raises(RpcError): l1.rpc.sendpay(route, payment_hash14) l1.rpc.waitsendpay(payment_hash14) # status: offered -> local_failed payment_hash15 = l5.rpc.invoice(amount, 'onchain_timeout', 'desc')['payment_hash'] fee = amount * 10 // 1000000 + 1 c12 = l1.get_channel_scid(l2) c25 = l2.get_channel_scid(l5) route = [{ 'msatoshi': amount + fee - 1, 'id': l2.info['id'], 'delay': 12, 'channel': c12 }, { 'msatoshi': amount - 1, 'id': l5.info['id'], 'delay': 5, 'channel': c25 }] executor.submit(l1.rpc.sendpay, route, payment_hash15) l5.daemon.wait_for_log('permfail') l5.wait_for_channel_onchain(l2.info['id']) l2.bitcoin.generate_block(1) l2.daemon.wait_for_log(' to ONCHAIN') l5.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log( 'Propose handling THEIR_UNILATERAL/OUR_HTLC by OUR_HTLC_TIMEOUT_TO_US .* after 6 blocks' ) bitcoind.generate_block(6) l2.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', 'THEIR_UNILATERAL/OUR_HTLC') bitcoind.generate_block(1) l2.daemon.wait_for_log( 'Resolved THEIR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TO_US' ) l5.daemon.wait_for_log('Ignoring output.*: OUR_UNILATERAL/THEIR_HTLC') bitcoind.generate_block(100) sync_blockheight(bitcoind, [l2]) stats = l2.rpc.listforwards()['forwards'] assert len(stats) == 3 plugin_stats = l2.rpc.call('listforwards_plugin')['forwards'] assert len(plugin_stats) == 6 # use stats to build what we expect went to plugin. expect = stats[0].copy() # First event won't have conclusion. del expect['resolved_time'] expect['status'] = 'offered' assert plugin_stats[0] == expect expect = stats[0].copy() assert plugin_stats[1] == expect expect = stats[1].copy() del expect['resolved_time'] expect['status'] = 'offered' assert plugin_stats[2] == expect expect = stats[1].copy() assert plugin_stats[3] == expect expect = stats[2].copy() del expect['failcode'] del expect['failreason'] expect['status'] = 'offered' assert plugin_stats[4] == expect expect = stats[2].copy() assert plugin_stats[5] == expect
def test_v2_rbf_multi(node_factory, bitcoind, chainparams): l1, l2 = node_factory.get_nodes(2, opts={'may_reconnect': True, 'allow_warning': True}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) amount = 2**24 chan_amount = 100000 bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], amount / 10**8 + 0.01) bitcoind.generate_block(1) # Wait for it to arrive. wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0) res = l1.rpc.fundchannel(l2.info['id'], chan_amount) chan_id = res['channel_id'] vins = bitcoind.rpc.decoderawtransaction(res['tx'])['vin'] assert(only_one(vins)) prev_utxos = ["{}:{}".format(vins[0]['txid'], vins[0]['vout'])] # Check that we're waiting for lockin l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') # Attempt to do abort, should fail since we've # already gotten an inflight with pytest.raises(RpcError): l1.rpc.openchannel_abort(chan_id) rate = int(find_next_feerate(l1, l2)[:-5]) # We 4x the feerate to beat the min-relay fee next_feerate = '{}perkw'.format(rate * 4) # Initiate an RBF startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) # Do the bump bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt'], funding_feerate=next_feerate) # Abort this open attempt! We will re-try aborted = l1.rpc.openchannel_abort(chan_id) assert not aborted['channel_canceled'] # Do the bump, again, same feerate l1.rpc.connect(l2.info['id'], 'localhost', l2.port) bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt'], funding_feerate=next_feerate) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] l1.rpc.openchannel_signed(chan_id, signed_psbt) # We 2x the feerate to beat the min-relay fee rate = int(find_next_feerate(l1, l2)[:-5]) next_feerate = '{}perkw'.format(rate * 2) # Initiate another RBF, double the channel amount this time startweight = 42 + 172 # base weight, funding output initpsbt = l1.rpc.utxopsbt(chan_amount * 2, next_feerate, startweight, prev_utxos, reservedok=True, min_witness_weight=110, excess_as_change=True) # Do the bump bump = l1.rpc.openchannel_bump(chan_id, chan_amount * 2, initpsbt['psbt'], funding_feerate=next_feerate) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] l1.rpc.openchannel_signed(chan_id, signed_psbt) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log(' to CHANNELD_NORMAL')