mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 23:54:22 +01:00
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
This commit is contained in:
committed by
Rusty Russell
parent
5d0390f637
commit
7e1cc2897c
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user