Browse Source

test: add tests for setchannelfee command

Goissp related tests are disabled for non-developers.
New setchannelfee testscases

* py.test -v tests/test_pay.py -k test_setchannelfee_usage
* py.test -v tests/test_pay.py -k test_setchannelfee_routing
* py.test -v tests/test_pay.py -k test_setchannelfee_state
* py.test -v tests/test_pay.py -k test_setchannelfee_zero
* py.test -v tests/test_pay.py -k test_setchannelfee_restart
issue-2504
Michael Schmoock 6 years ago
committed by Rusty Russell
parent
commit
7e1cc2897c
  1. 331
      tests/test_pay.py

331
tests/test_pay.py

@ -6,6 +6,7 @@ from utils import DEVELOPER, wait_for, only_one, sync_blockheight, SLOW_MACHINE
import copy
import pytest
import random
import re
import string
import time
import unittest
@ -1425,3 +1426,333 @@ def test_pay_direct(node_factory, bitcoind):
# have changed.
l1l2msat = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['msatoshi_to_us']
assert l1l2msat == l1l2msatreference
def test_setchannelfee_usage(node_factory, bitcoind):
# TEST SETUP
#
# [l1] ---> [l2] (channel funded)
# |
# o - - > [l3] (only connected)
#
# - check initial SQL values
# - check setchannelfee can be used
# - checks command's return object format
# - check custom SQL fee values
# - check values in local nodes listchannels output
# - json throws exception on negative values
# - checks if peer id can be used instead of scid
DEF_BASE = 10
DEF_PPM = 100
l1 = node_factory.get_node(options={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
l2 = node_factory.get_node(options={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
l3 = node_factory.get_node(options={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
l1.rpc.connect(l3.info['id'], 'localhost', l3.port)
l1.fund_channel(l2, 1000000)
# get short channel id
scid = l1.get_channel_scid(l2)
scid_hex = scid.encode('utf-8').hex()
# feerates should be init with global config
db_fees = l1.db_query('SELECT feerate_base, feerate_ppm FROM channels;')
assert(db_fees[0]['feerate_base'] == DEF_BASE)
assert(db_fees[0]['feerate_ppm'] == DEF_PPM)
# custom setchannelfee scid <base> <ppm>
result = l1.rpc.setchannelfee(scid, 1337, 137)
# check result format
assert(re.match('^[0-9a-f]{64}$', result['channel_id']))
assert(result['peer_id'] == l2.info['id'])
assert(result['short_channel_id'] == scid)
assert(result['base'] == 1337)
assert(result['ppm'] == 137)
# check if custom values made it into the database
db_fees = l1.db_query(
'SELECT feerate_base, feerate_ppm FROM channels '
'WHERE hex(short_channel_id)="' + scid_hex + '";')
assert(db_fees[0]['feerate_base'] == 1337)
assert(db_fees[0]['feerate_ppm'] == 137)
# wait for gossip and check if l1 sees new fees in listchannels
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_BASE, 1337])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_PPM, 137])
# also test with named and missing paramters
result = l1.rpc.setchannelfee(ppm=42, id=scid)
assert(re.match('^[0-9a-f]{64}$', result['channel_id']))
assert(result['short_channel_id'] == scid)
assert(result['base'] == DEF_BASE)
assert(result['ppm'] == 42)
result = l1.rpc.setchannelfee(base=42, id=scid)
assert(re.match('^[0-9a-f]{64}$', result['channel_id']))
assert(result['short_channel_id'] == scid)
assert(result['base'] == 42)
assert(result['ppm'] == DEF_PPM)
# check if negative fees raise error and DB keeps values
# JSONRPC2_INVALID_PARAMS := -32602
with pytest.raises(RpcError, match=r'-32602'):
l1.rpc.setchannelfee(scid, -1, -1)
# test if zero fees is possible
result = l1.rpc.setchannelfee(scid, 0, 0)
assert(result['base'] == 0)
assert(result['ppm'] == 0)
db_fees = l1.db_query(
'SELECT feerate_base, feerate_ppm FROM channels '
'WHERE hex(short_channel_id)="' + scid_hex + '";')
assert(db_fees[0]['feerate_base'] == 0)
assert(db_fees[0]['feerate_ppm'] == 0)
# disable and check for global values to be returned
result = l1.rpc.setchannelfee(scid)
assert(result['base'] == DEF_BASE)
assert(result['ppm'] == DEF_PPM)
# check default values in DB
db_fees = l1.db_query(
'SELECT feerate_base, feerate_ppm FROM channels '
'WHERE hex(short_channel_id)="' + scid_hex + '";')
assert(db_fees[0]['feerate_base'] == DEF_BASE)
assert(db_fees[0]['feerate_ppm'] == DEF_PPM)
# check also peer id can be used
result = l1.rpc.setchannelfee(l2.info['id'], 42, 43)
assert(result['peer_id'] == l2.info['id'])
assert(result['short_channel_id'] == scid)
assert(result['base'] == 42)
assert(result['ppm'] == 43)
db_fees = l1.db_query(
'SELECT feerate_base, feerate_ppm FROM channels '
'WHERE hex(short_channel_id)="' + scid_hex + '";')
assert(db_fees[0]['feerate_base'] == 42)
assert(db_fees[0]['feerate_ppm'] == 43)
# check if invalid scid raises proper error
with pytest.raises(RpcError, match=r'-1.*Could not find active channel of peer with that id'):
result = l1.rpc.setchannelfee(l3.info['id'], 42, 43)
with pytest.raises(RpcError, match=r'-32602.*Given id is not a channel ID or short channel ID'):
result = l1.rpc.setchannelfee('f42' + scid[3:], 42, 43)
# check if 'base' unit can be modified to satoshi
result = l1.rpc.setchannelfee(scid, '1sat')
assert(result['base'] == 1000)
db_fees = l1.db_query(
'SELECT feerate_base, feerate_ppm FROM channels '
'WHERE hex(short_channel_id)="' + scid_hex + '";')
assert(db_fees[0]['feerate_base'] == 1000)
# check if 'ppm' values greater than u32_max fail
with pytest.raises(RpcError, match=r'-32602.*should be an integer, not'):
l1.rpc.setchannelfee(scid, 0, 2**32)
# check if 'ppm' values greater than u32_max fail
with pytest.raises(RpcError, match=r'-32602.*exceeds u32 max'):
l1.rpc.setchannelfee(scid, 2**32)
@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow")
def test_setchannelfee_state(node_factory, bitcoind):
# TEST SETUP
#
# [l0] --> [l1] --> [l2]
#
# Initiate channel [l1,l2] and try to set feerates other states than
# CHANNELD_NORMAL or CHANNELD_AWAITING_LOCKIN. Should raise error.
# Use l0 to make a forward through l1/l2 for testing.
DEF_BASE = 0
DEF_PPM = 0
l0 = node_factory.get_node(options={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
l1 = node_factory.get_node(options={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
l2 = node_factory.get_node(options={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
# connection and funding
l0.rpc.connect(l1.info['id'], 'localhost', l1.port)
l0.fund_channel(l1, 1000000, wait_for_active=True)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
scid = l1.fund_channel(l2, 1000000, wait_for_active=False)
# try setting the fee in state AWAITING_LOCKIN should be possible
# assert(l1.channel_state(l2) == "CHANNELD_AWAITING_LOCKIN")
result = l1.rpc.setchannelfee(l2.info['id'], 42, 0)
assert(result['peer_id'] == l2.info['id'])
# cid = result['channel_id']
# test routing correct new fees once routing is established
bitcoind.generate_block(6)
l0.wait_for_route(l2)
inv = l2.rpc.invoice(100000, 'test_setchannelfee_state', 'desc')['bolt11']
result = l0.rpc.pay(inv)
assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 100042
# Disconnect and unilaterally close from l2 to l1
l2.rpc.disconnect(l1.info['id'], force=True)
l1.rpc.disconnect(l2.info['id'], force=True)
result = l2.rpc.close(scid, True, 0)
assert result['type'] == 'unilateral'
# wait for l1 to see unilateral close via bitcoin network
while l1.channel_state(l2) == "CHANNELD_NORMAL":
bitcoind.generate_block(1)
# assert l1.channel_state(l2) == "FUNDING_SPEND_SEEN"
# Try to setchannelfee in order to raise expected error.
# To reduce false positive flakes, only test if state is not NORMAL anymore.
with pytest.raises(RpcError, match=r'-1.*'):
# l1.rpc.setchannelfee(l2.info['id'], 10, 1)
l1.rpc.setchannelfee(l2.info['id'], 10, 1)
@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow")
def test_setchannelfee_routing(node_factory, bitcoind):
# TEST SETUP
#
# [l1] <--default_fees--> [l2] <--specific_fees--> [l3]
#
# - json listchannels is able to see the new values in foreign node
# - routing calculates fees correctly
# - payment can be done using specific fees
# - channel specific fees can be disabled again
# - payment can be done using global fees
DEF_BASE = 1
DEF_PPM = 10
l1, l2, l3 = node_factory.line_graph(
3, announce_channels=True, wait_for_announce=True,
opts={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
# get short channel id for 2->3
scid = l2.get_channel_scid(l3)
# TEST CUSTOM VALUES
l2.rpc.setchannelfee(scid, 1337, 137)
# wait for l1 to see updated channel via gossip
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [1337, DEF_BASE])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [137, DEF_PPM])
# test fees are applied to HTLC forwards
#
# BOLT #7:
# If l1 were to send 4,999,999 millisatoshi to l3 via l2, it needs to
# pay l2 the fee it specified in the l2->l3 `channel_update`, calculated as
# per [HTLC Fees](#htlc_fees): base + amt * pm / 10**6
#
# 1337 + 4999999 * 137 / 1000000 = 2021.999 (2021)
route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"]
assert len(route) == 2
assert route[0]['msatoshi'] == 5002020
assert route[1]['msatoshi'] == 4999999
# do and check actual payment
inv = l3.rpc.invoice(4999999, 'test_setchannelfee_1', 'desc')['bolt11']
result = l1.rpc.pay(inv)
assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 5002020
# TEST DISABLE and check global fee routing
l2.rpc.setchannelfee(scid)
# wait for l1 to see default values again via gossip
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_BASE, DEF_BASE])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_PPM, DEF_PPM])
# test if global fees are applied again (base 1 ppm 10)
# 1 + 4999999 * 10 / 1000000 = 50.999 (50)
route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"]
assert len(route) == 2
assert route[0]['msatoshi'] == 5000049
assert route[1]['msatoshi'] == 4999999
# do and check actual payment
inv = l3.rpc.invoice(4999999, 'test_setchannelfee_2', 'desc')['bolt11']
result = l1.rpc.pay(inv)
assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 5000049
@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow")
def test_setchannelfee_zero(node_factory, bitcoind):
# TEST SETUP
#
# [l1] <--default_fees--> [l2] <--specific_fees--> [l3]
#
# - json listchannels is able to see the new values in foreign node
# - routing calculates fees correctly
# - payment can be done using zero fees
DEF_BASE = 1
DEF_PPM = 10
l1, l2, l3 = node_factory.line_graph(
3, announce_channels=True, wait_for_announce=True,
opts={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
# get short channel id for 2->3
scid = l2.get_channel_scid(l3)
# TEST ZERO fees possible
l2.rpc.setchannelfee(scid, 0, 0)
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [0, DEF_BASE])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [0, DEF_PPM])
# test if zero fees are applied
route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"]
assert len(route) == 2
assert route[0]['msatoshi'] == 4999999
assert route[1]['msatoshi'] == 4999999
# do and check actual payment
inv = l3.rpc.invoice(4999999, 'test_setchannelfee_3', 'desc')['bolt11']
result = l1.rpc.pay(inv)
assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 4999999
@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow")
def test_setchannelfee_restart(node_factory, bitcoind):
# TEST SETUP
#
# [l1] <--default_fees--> [l2] <--specific_fees--> [l3]
#
# - l2 sets fees to custom values and restarts
# - l1 routing can be made with the custom fees
# - l2 sets fees to UIN32_MAX (db update default) and restarts
# - l1 routing can be made to l3 and global (1 10) fees are applied
DEF_BASE = 1
DEF_PPM = 10
OPTS = {'may_reconnect': True, 'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM}
l1, l2, l3 = node_factory.line_graph(3, announce_channels=True, wait_for_announce=True, opts=OPTS)
# get short channel id for 2->3
scid = l2.get_channel_scid(l3)
# l2 set custom fees
l2.rpc.setchannelfee(scid, 1337, 137)
# restart l2 and reconnect
l2.restart()
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
l2.rpc.connect(l3.info['id'], 'localhost', l3.port)
# l1 wait for channel update
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [1337, DEF_BASE])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [137, DEF_PPM])
# for slow travis: wait for everyone to see all channels
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 4)
wait_for(lambda: len(l2.rpc.listchannels()['channels']) == 4)
wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 4)
# l1 can make payment to l3 with custom fees being applied
# Note: BOLT #7 math works out to 2021 msat fees
inv = l3.rpc.invoice(4999999, 'test_setchannelfee_1', 'desc')['bolt11']
result = l1.rpc.pay(inv)
assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 5002020

Loading…
Cancel
Save