lightningd: deprecate setchannelfee, use setchannel in tests.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Deprecated: JSON-RPC: `setchannelfee` (use `setchannel`).
This commit is contained in:
Rusty Russell 2022-03-21 11:28:28 +10:30
parent 66e264d6b3
commit 42f91ff2fa
6 changed files with 136 additions and 117 deletions

View File

@ -370,7 +370,7 @@ SEE ALSO
-------- --------
lightning-connect(7), lightning-fundchannel\_start(7), lightning-connect(7), lightning-fundchannel\_start(7),
lightning-setchannelfee(7) lightning-setchannel(7)
RESOURCES RESOURCES
--------- ---------

View File

@ -4,7 +4,7 @@ lightning-setchannelfee -- Command for setting specific routing fees on a lightn
SYNOPSIS SYNOPSIS
-------- --------
**setchannelfee** *id* [*base*] [*ppm*] [*enforcedelay*] (DEPRECATED) **setchannelfee** *id* [*base*] [*ppm*] [*enforcedelay*]
DESCRIPTION DESCRIPTION
----------- -----------
@ -73,8 +73,7 @@ responsible for the c-lightning project.
SEE ALSO SEE ALSO
-------- --------
lightningd-config(5), lightning-fundchannel(7), lightningd-setchannel(7)
lightning-listchannels(7), lightning-listpeers(7)
RESOURCES RESOURCES
--------- ---------

View File

@ -236,14 +236,14 @@ Default: 1000. The base fee to charge for every payment which passes
through. Note that millisatoshis are a very, very small unit! Changing through. Note that millisatoshis are a very, very small unit! Changing
this value will only affect new channels and not existing ones. If you this value will only affect new channels and not existing ones. If you
want to change fees for existing channels, use the RPC call want to change fees for existing channels, use the RPC call
lightning-setchannelfee(7). lightning-setchannel(7).
**fee-per-satoshi**=*MILLIONTHS* **fee-per-satoshi**=*MILLIONTHS*
Default: 10 (0.001%). This is the proportional fee to charge for every Default: 10 (0.001%). This is the proportional fee to charge for every
payment which passes through. As percentages are too coarse, it's in payment which passes through. As percentages are too coarse, it's in
millionths, so 10000 is 1%, 1000 is 0.1%. Changing this value will only millionths, so 10000 is 1%, 1000 is 0.1%. Changing this value will only
affect new channels and not existing ones. If you want to change fees affect new channels and not existing ones. If you want to change fees
for existing channels, use the RPC call lightning-setchannelfee(7). for existing channels, use the RPC call lightning-setchannel(7).
**min-capacity-sat**=*SATOSHI* **min-capacity-sat**=*SATOSHI*
Default: 10000. This value defines the minimal effective channel Default: 10000. This value defines the minimal effective channel
@ -563,7 +563,7 @@ actually implementing these options.
SEE ALSO SEE ALSO
-------- --------
lightning-listconfigs(7) lightning-setchannelfee(7) lightningd(8) lightning-listconfigs(7) lightning-setchannel(7) lightningd(8)
lightning-hsmtool(8) lightning-hsmtool(8)
RESOURCES RESOURCES

View File

@ -2135,7 +2135,8 @@ static const struct json_command setchannelfee_command = {
"and a {ppm} (proportional per millionth) value. " "and a {ppm} (proportional per millionth) value. "
"If values for {base} or {ppm} are left out, defaults will be used. " "If values for {base} or {ppm} are left out, defaults will be used. "
"{base} can also be defined in other units, for example '1sat'. " "{base} can also be defined in other units, for example '1sat'. "
"If {id} is 'all', the fees will be applied for all channels. " "If {id} is 'all', the fees will be applied for all channels. ",
true /* deprecated */
}; };
AUTODATA(json_command, &setchannelfee_command); AUTODATA(json_command, &setchannelfee_command);

View File

@ -1583,7 +1583,7 @@ def setup_gossip_store_test(node_factory, bitcoind):
wait_for(lambda: ['alias' in n for n in l2.rpc.listnodes()['nodes']] == [True, True]) wait_for(lambda: ['alias' in n for n in l2.rpc.listnodes()['nodes']] == [True, True])
# Now, replace the one channel_update, so it's past the node announcements. # Now, replace the one channel_update, so it's past the node announcements.
l2.rpc.setchannelfee(l3.info['id'], 20, 1000) l2.rpc.setchannel(l3.info['id'], 20, 1000)
# Old base feerate is 1. # Old base feerate is 1.
wait_for(lambda: sum([c['base_fee_millisatoshi'] for c in l2.rpc.listchannels()['channels']]) == 21) wait_for(lambda: sum([c['base_fee_millisatoshi'] for c in l2.rpc.listchannels()['channels']]) == 21)
@ -1592,12 +1592,12 @@ def setup_gossip_store_test(node_factory, bitcoind):
# Now insert channel_update for previous channel; now they're both past the # Now insert channel_update for previous channel; now they're both past the
# node announcements. # node announcements.
l3.rpc.setchannelfee(l2.info['id'], 20, 1000) l3.rpc.setchannel(l2.info['id'], feebase=20, feeppm=1000)
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l2.rpc.listchannels(scid23)['channels']] == [20, 20]) wait_for(lambda: [c['base_fee_millisatoshi'] for c in l2.rpc.listchannels(scid23)['channels']] == [20, 20])
# Replace both (private) updates for scid12. # Replace both (private) updates for scid12.
l1.rpc.setchannelfee(l2.info['id'], 20, 1000) l1.rpc.setchannel(l2.info['id'], feebase=20, feeppm=1000)
l2.rpc.setchannelfee(l1.info['id'], 20, 1000) l2.rpc.setchannel(l1.info['id'], feebase=20, feeppm=1000)
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l2.rpc.listchannels(scid12)['channels']] == [20, 20]) wait_for(lambda: [c['base_fee_millisatoshi'] for c in l2.rpc.listchannels(scid12)['channels']] == [20, 20])
# Records in store now looks (something) like: # Records in store now looks (something) like:

View File

@ -1854,7 +1854,7 @@ def test_pay_routeboost(node_factory, bitcoind, compat):
@pytest.mark.developer("updates are delayed without --dev-fast-gossip") @pytest.mark.developer("updates are delayed without --dev-fast-gossip")
def test_setchannelfee_usage(node_factory, bitcoind): def test_setchannel_usage(node_factory, bitcoind):
# TEST SETUP # TEST SETUP
# #
# [l1] ---> [l2] (channel funded) # [l1] ---> [l2] (channel funded)
@ -1862,7 +1862,7 @@ def test_setchannelfee_usage(node_factory, bitcoind):
# o - - > [l3] (only connected) # o - - > [l3] (only connected)
# #
# - check initial SQL values # - check initial SQL values
# - check setchannelfee can be used # - check setchannel can be used
# - checks command's return object format # - checks command's return object format
# - check custom SQL fee values # - check custom SQL fee values
# - check values in local nodes listchannels output # - check values in local nodes listchannels output
@ -1872,77 +1872,99 @@ def test_setchannelfee_usage(node_factory, bitcoind):
DEF_BASE = 10 DEF_BASE = 10
DEF_BASE_MSAT = Millisatoshi(DEF_BASE) DEF_BASE_MSAT = Millisatoshi(DEF_BASE)
DEF_PPM = 100 DEF_PPM = 100
# Minus reserve
MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99))
l1, l2, l3 = node_factory.get_nodes(3, l1, l2, l3 = node_factory.get_nodes(3,
opts={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM}) opts={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM})
node_factory.join_nodes([l1, l2]) node_factory.join_nodes([l1, l2])
l1.rpc.connect(l3.info['id'], 'localhost', l3.port) l1.rpc.connect(l3.info['id'], 'localhost', l3.port)
def channel_get_fees(scid): def channel_get_config(scid):
return l1.db.query( return l1.db.query(
'SELECT feerate_base, feerate_ppm FROM channels ' 'SELECT feerate_base, feerate_ppm, htlc_maximum_msat FROM channels '
'WHERE short_channel_id=\'{}\';'.format(scid)) 'WHERE short_channel_id=\'{}\';'.format(scid))
# get short channel id # get short channel id
scid = l1.get_channel_scid(l2) scid = l1.get_channel_scid(l2)
# feerates should be init with global config # feerates should be init with global config
db_fees = l1.db_query('SELECT feerate_base, feerate_ppm FROM channels;') db_fees = l1.db_query('SELECT feerate_base, feerate_ppm, htlc_maximum_msat FROM channels;')
assert(db_fees[0]['feerate_base'] == DEF_BASE) assert(db_fees[0]['feerate_base'] == DEF_BASE)
assert(db_fees[0]['feerate_ppm'] == DEF_PPM) assert(db_fees[0]['feerate_ppm'] == DEF_PPM)
# This will be the capacity - reserves:
assert(db_fees[0]['htlc_maximum_msat'] == MAX_HTLC)
# this is also what listpeers should return # this is also what listpeers should return
peers = l1.rpc.listpeers()['peers'] peers = l1.rpc.listpeers()['peers']
assert peers[0]['channels'][0]['fee_base_msat'] == DEF_BASE_MSAT assert peers[0]['channels'][0]['fee_base_msat'] == DEF_BASE_MSAT
assert peers[0]['channels'][0]['fee_proportional_millionths'] == DEF_PPM assert peers[0]['channels'][0]['fee_proportional_millionths'] == DEF_PPM
assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == MAX_HTLC
# custom setchannelfee scid <base> <ppm> # custom setchannel scid <feebase> <feeppm> <htlcmax>
result = l1.rpc.setchannelfee(scid, 1337, 137) result = l1.rpc.setchannel(scid, 1337, 137, 133337)
# check result format # check result format
assert(result['base'] == 1337)
assert(result['ppm'] == 137)
assert(len(result['channels']) == 1) assert(len(result['channels']) == 1)
assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id'])) assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id']))
assert(result['channels'][0]['peer_id'] == l2.info['id']) assert(result['channels'][0]['peer_id'] == l2.info['id'])
assert(result['channels'][0]['short_channel_id'] == scid) assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 1337)
assert(result['channels'][0]['fee_proportional_millionths'] == 137)
assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337)
# check if custom values made it into the database # check if custom values made it into the database
db_fees = channel_get_fees(scid) db_fees = channel_get_config(scid)
assert(db_fees[0]['feerate_base'] == 1337) assert(db_fees[0]['feerate_base'] == 1337)
assert(db_fees[0]['feerate_ppm'] == 137) assert(db_fees[0]['feerate_ppm'] == 137)
assert(db_fees[0]['htlc_maximum_msat'] == 133337)
# also check for updated values in `listpeers` # also check for updated values in `listpeers`
peers = l1.rpc.listpeers()['peers'] peers = l1.rpc.listpeers()['peers']
assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(1337) assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(1337)
assert peers[0]['channels'][0]['fee_proportional_millionths'] == 137 assert peers[0]['channels'][0]['fee_proportional_millionths'] == 137
assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == 133337
# wait for gossip and check if l1 sees new fees in listchannels # 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['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]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_PPM, 137])
wait_for(lambda: [c['htlc_maximum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [MAX_HTLC, 133337])
# also test with named and missing parameters # also test with named and missing parameters
result = l1.rpc.setchannelfee(ppm=42, id=scid) result = l1.rpc.setchannel(feeppm=42, id=scid)
assert(result['base'] == DEF_BASE)
assert(result['ppm'] == 42)
assert(len(result['channels']) == 1) assert(len(result['channels']) == 1)
assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id'])) assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id']))
assert(result['channels'][0]['short_channel_id'] == scid) assert(result['channels'][0]['short_channel_id'] == scid)
result = l1.rpc.setchannelfee(base=42, id=scid) assert(result['channels'][0]['fee_base_msat'] == 1337)
assert(result['base'] == 42) assert(result['channels'][0]['fee_proportional_millionths'] == 42)
assert(result['ppm'] == DEF_PPM) assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337)
result = l1.rpc.setchannel(feebase=43, id=scid)
assert(len(result['channels']) == 1) assert(len(result['channels']) == 1)
assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id'])) assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id']))
assert(result['channels'][0]['short_channel_id'] == scid) assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 43)
assert(result['channels'][0]['fee_proportional_millionths'] == 42)
assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337)
result = l1.rpc.setchannel(htlcmax=43333, id=scid)
assert(len(result['channels']) == 1)
assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id']))
assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 43)
assert(result['channels'][0]['fee_proportional_millionths'] == 42)
assert(result['channels'][0]['maximum_htlc_out_msat'] == 43333)
# check if negative fees raise error and DB keeps values # check if negative fees raise error and DB keeps values
# JSONRPC2_INVALID_PARAMS := -32602 # JSONRPC2_INVALID_PARAMS := -32602
with pytest.raises(RpcError, match=r'-32602'): with pytest.raises(RpcError, match=r'-32602'):
l1.rpc.setchannelfee(scid, -1, -1) l1.rpc.setchannel(scid, -1, -1)
# test if zero fees is possible # test if zero fees is possible
result = l1.rpc.setchannelfee(scid, 0, 0) result = l1.rpc.setchannel(scid, 0, 0)
assert(result['base'] == 0) assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['ppm'] == 0) assert(result['channels'][0]['fee_base_msat'] == 0)
db_fees = channel_get_fees(scid) assert(result['channels'][0]['fee_proportional_millionths'] == 0)
db_fees = channel_get_config(scid)
assert(db_fees[0]['feerate_base'] == 0) assert(db_fees[0]['feerate_base'] == 0)
assert(db_fees[0]['feerate_ppm'] == 0) assert(db_fees[0]['feerate_ppm'] == 0)
# also check for updated values in `listpeers` # also check for updated values in `listpeers`
@ -1950,53 +1972,44 @@ def test_setchannelfee_usage(node_factory, bitcoind):
assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(0) assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(0)
assert peers[0]['channels'][0]['fee_proportional_millionths'] == 0 assert peers[0]['channels'][0]['fee_proportional_millionths'] == 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 = channel_get_fees(scid)
assert(db_fees[0]['feerate_base'] == DEF_BASE)
assert(db_fees[0]['feerate_ppm'] == DEF_PPM)
# also check for updated values in `listpeers`
peers = l1.rpc.listpeers()['peers']
assert peers[0]['channels'][0]['fee_base_msat'] == DEF_BASE_MSAT
assert peers[0]['channels'][0]['fee_proportional_millionths'] == DEF_PPM
# check also peer id can be used # check also peer id can be used
result = l1.rpc.setchannelfee(l2.info['id'], 42, 43) result = l1.rpc.setchannel(l2.info['id'], 142, 143)
assert(result['base'] == 42)
assert(result['ppm'] == 43)
assert(len(result['channels']) == 1) assert(len(result['channels']) == 1)
assert(result['channels'][0]['peer_id'] == l2.info['id']) assert(result['channels'][0]['peer_id'] == l2.info['id'])
assert(result['channels'][0]['short_channel_id'] == scid) assert(result['channels'][0]['short_channel_id'] == scid)
db_fees = channel_get_fees(scid) assert(result['channels'][0]['fee_base_msat'] == 142)
assert(db_fees[0]['feerate_base'] == 42) assert(result['channels'][0]['fee_proportional_millionths'] == 143)
assert(db_fees[0]['feerate_ppm'] == 43)
db_fees = channel_get_config(scid)
assert(db_fees[0]['feerate_base'] == 142)
assert(db_fees[0]['feerate_ppm'] == 143)
# check if invalid scid raises proper error # check if invalid scid raises proper error
with pytest.raises(RpcError, match=r'-1.*Could not find active channel of peer with that id'): 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) result = l1.rpc.setchannel(l3.info['id'], 42, 43)
with pytest.raises(RpcError, match=r'-32602.*id: should be a channel ID or short channel ID: invalid token'): with pytest.raises(RpcError, match=r'-32602.*id: should be a channel ID or short channel ID: invalid token'):
result = l1.rpc.setchannelfee('f42' + scid[3:], 42, 43) result = l1.rpc.setchannel('f42' + scid[3:], 42, 43)
# check if 'base' unit can be modified to satoshi # check if 'base' unit can be modified to satoshi
result = l1.rpc.setchannelfee(scid, '1sat') result = l1.rpc.setchannel(scid, '1sat')
assert(result['base'] == 1000) assert(len(result['channels']) == 1)
db_fees = channel_get_fees(scid) assert(result['channels'][0]['peer_id'] == l2.info['id'])
assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 1000)
db_fees = channel_get_config(scid)
assert(db_fees[0]['feerate_base'] == 1000) assert(db_fees[0]['feerate_base'] == 1000)
# check if 'ppm' values greater than u32_max fail # check if 'ppm' values greater than u32_max fail
with pytest.raises(RpcError, match=r'-32602.*ppm: should be an integer: invalid token'): with pytest.raises(RpcError, match=r'-32602.*ppm: should be an integer: invalid token'):
l1.rpc.setchannelfee(scid, 0, 2**32) l1.rpc.setchannel(scid, 0, 2**32)
# check if 'base' values greater than u32_max fail # check if 'base' values greater than u32_max fail
with pytest.raises(RpcError, match=r'-32602.*base: exceeds u32 max: invalid token'): with pytest.raises(RpcError, match=r'-32602.*base: exceeds u32 max: invalid token'):
l1.rpc.setchannelfee(scid, 2**32) l1.rpc.setchannel(scid, 2**32)
@pytest.mark.developer("gossip without DEVELOPER=1 is slow") @pytest.mark.developer("gossip without DEVELOPER=1 is slow")
def test_setchannelfee_state(node_factory, bitcoind): def test_setchannel_state(node_factory, bitcoind):
# TEST SETUP # TEST SETUP
# #
# [l0] --> [l1] --> [l2] # [l0] --> [l1] --> [l2]
@ -2020,7 +2033,7 @@ def test_setchannelfee_state(node_factory, bitcoind):
# try setting the fee in state AWAITING_LOCKIN should be possible # try setting the fee in state AWAITING_LOCKIN should be possible
# assert(l1.channel_state(l2) == "CHANNELD_AWAITING_LOCKIN") # assert(l1.channel_state(l2) == "CHANNELD_AWAITING_LOCKIN")
result = l1.rpc.setchannelfee(l2.info['id'], 42, 0) result = l1.rpc.setchannel(l2.info['id'], 42, 0)
assert(result['channels'][0]['peer_id'] == l2.info['id']) assert(result['channels'][0]['peer_id'] == l2.info['id'])
# cid = result['channels'][0]['channel_id'] # cid = result['channels'][0]['channel_id']
@ -2028,7 +2041,7 @@ def test_setchannelfee_state(node_factory, bitcoind):
mine_funding_to_announce(bitcoind, [l0, l1, l2]) mine_funding_to_announce(bitcoind, [l0, l1, l2])
l0.wait_for_route(l2) l0.wait_for_route(l2)
inv = l2.rpc.invoice(100000, 'test_setchannelfee_state', 'desc')['bolt11'] inv = l2.rpc.invoice(100000, 'test_setchannel_state', 'desc')['bolt11']
result = l0.rpc.dev_pay(inv, use_shadow=False) result = l0.rpc.dev_pay(inv, use_shadow=False)
assert result['status'] == 'complete' assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 100042 assert result['msatoshi_sent'] == 100042
@ -2044,15 +2057,15 @@ def test_setchannelfee_state(node_factory, bitcoind):
bitcoind.generate_block(1) bitcoind.generate_block(1)
# assert l1.channel_state(l2) == "FUNDING_SPEND_SEEN" # assert l1.channel_state(l2) == "FUNDING_SPEND_SEEN"
# Try to setchannelfee in order to raise expected error. # Try to setchannel in order to raise expected error.
# To reduce false positive flakes, only test if state is not NORMAL anymore. # To reduce false positive flakes, only test if state is not NORMAL anymore.
with pytest.raises(RpcError, match=r'-1.*'): with pytest.raises(RpcError, match=r'-1.*'):
# l1.rpc.setchannelfee(l2.info['id'], 10, 1) # l1.rpc.setchannel(l2.info['id'], 10, 1)
l1.rpc.setchannelfee(l2.info['id'], 10, 1) l1.rpc.setchannel(l2.info['id'], 10, 1)
@pytest.mark.developer("gossip without DEVELOPER=1 is slow") @pytest.mark.developer("gossip without DEVELOPER=1 is slow")
def test_setchannelfee_routing(node_factory, bitcoind): def test_setchannel_routing(node_factory, bitcoind):
# TEST SETUP # TEST SETUP
# #
# [l1] <--default_fees--> [l2] <--specific_fees--> [l3] # [l1] <--default_fees--> [l2] <--specific_fees--> [l3]
@ -2062,8 +2075,10 @@ def test_setchannelfee_routing(node_factory, bitcoind):
# - payment can be done using specific fees # - payment can be done using specific fees
# - channel specific fees can be disabled again # - channel specific fees can be disabled again
# - payment can be done using global fees # - payment can be done using global fees
# - htlc max is honored
DEF_BASE = 1 DEF_BASE = 1
DEF_PPM = 10 DEF_PPM = 10
MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99))
l1, l2, l3 = node_factory.line_graph( l1, l2, l3 = node_factory.line_graph(
3, announce_channels=True, wait_for_announce=True, 3, announce_channels=True, wait_for_announce=True,
@ -2073,11 +2088,12 @@ def test_setchannelfee_routing(node_factory, bitcoind):
scid = l2.get_channel_scid(l3) scid = l2.get_channel_scid(l3)
# TEST CUSTOM VALUES # TEST CUSTOM VALUES
l2.rpc.setchannelfee(scid, 1337, 137) l2.rpc.setchannel(scid, 1337, 137, 4000000, enforcedelay=0)
# wait for l1 to see updated channel via gossip # 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['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]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [137, DEF_PPM])
wait_for(lambda: [c['htlc_maximum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [4000000, MAX_HTLC])
# test fees are applied to HTLC forwards # test fees are applied to HTLC forwards
# #
@ -2085,50 +2101,48 @@ def test_setchannelfee_routing(node_factory, bitcoind):
# If l1 were to send 4,999,999 millisatoshi to l3 via l2, it needs to # 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 # pay l2 the fee it specified in the l2->l3 `channel_update`, calculated as
# per [HTLC Fees](#htlc_fees): base + amt * pm / 10**6 # per [HTLC Fees](#htlc_fees): base + amt * pm / 10**6
#
# 1337 + 4999999 * 137 / 1000000 = 2021.999 (2021) # FIXME!
route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"] # # Should refuse to route this!
assert len(route) == 2 # with pytest.raises(RpcError, match=r'Could not find a route'):
assert route[0]['msatoshi'] == 5002020 # l1.rpc.getroute(l3.info['id'], 4000001, 1, fuzzpercent=0)["route"]
assert route[1]['msatoshi'] == 4999999
# 1337 + 4000000 * 137 / 1000000 = 1885
route_ok = l1.rpc.getroute(l3.info['id'], 4000000, 1)["route"]
assert len(route_ok) == 2
assert route_ok[0]['msatoshi'] == 4001885
assert route_ok[1]['msatoshi'] == 4000000
# Make variant that tries to pay more than allowed htlc!
route_bad = copy.deepcopy(route_ok)
route_bad[0]['msatoshi'] = 4001887
route_bad[1]['msatoshi'] = 4000001
route_bad[0]['amount_msat'] = Millisatoshi(4001887)
route_bad[1]['amount_msat'] = Millisatoshi(4000001)
assert route_bad != route_ok
# In case l3 includes a routehint, we need to make sure they also know # In case l3 includes a routehint, we need to make sure they also know
# about the new fees, otherwise we may end up with the old feerate # about the new fees, otherwise we may end up with the old feerate
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, True), (DEF_BASE, DEF_PPM, True)]) wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, 4000000, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)])
# do and check actual payment # do and check actual payment
inv = l3.rpc.invoice(4999999, 'test_setchannelfee_1', 'desc')['bolt11'] inv = l3.rpc.invoice(4000000, 'test_setchannel_1', 'desc')
result = l1.rpc.dev_pay(inv, use_shadow=False) # Check that routehint from l3 incorporated new feerate!
assert result['status'] == 'complete' decoded = l1.rpc.decodepay(inv['bolt11'])
assert result['msatoshi_sent'] == 5002020 assert decoded['routes'] == [[{'pubkey': l2.info['id'], 'short_channel_id': scid, 'fee_base_msat': 1337, 'fee_proportional_millionths': 137, 'cltv_expiry_delta': 6}]]
# TEST DISABLE and check global fee routing # This will fail.
l2.rpc.setchannelfee(scid) l1.rpc.sendpay(route_bad, inv['payment_hash'], payment_secret=inv['payment_secret'])
with pytest.raises(RpcError, match='WIRE_TEMPORARY_CHANNEL_FAILURE'):
l1.rpc.waitsendpay(inv['payment_hash'])
# wait for l1 to see default values again via gossip # This will succeed
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_BASE, DEF_BASE]) l1.rpc.sendpay(route_ok, inv['payment_hash'], payment_secret=inv['payment_secret'])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_PPM, DEF_PPM]) l1.rpc.waitsendpay(inv['payment_hash'])
# 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
# In case l3 includes a routehint, we need to make sure they also know
# about the new fees, otherwise we may end up with the old feerate
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(DEF_BASE, DEF_PPM, True), (DEF_BASE, DEF_PPM, True)])
# do and check actual payment
inv = l3.rpc.invoice(4999999, 'test_setchannelfee_2', 'desc')['bolt11']
result = l1.rpc.dev_pay(inv, use_shadow=False)
assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 5000049
@pytest.mark.developer("gossip without DEVELOPER=1 is slow") @pytest.mark.developer("gossip without DEVELOPER=1 is slow")
def test_setchannelfee_zero(node_factory, bitcoind): def test_setchannel_zero(node_factory, bitcoind):
# TEST SETUP # TEST SETUP
# #
# [l1] <--default_fees--> [l2] <--specific_fees--> [l3] # [l1] <--default_fees--> [l2] <--specific_fees--> [l3]
@ -2147,7 +2161,7 @@ def test_setchannelfee_zero(node_factory, bitcoind):
scid = l2.get_channel_scid(l3) scid = l2.get_channel_scid(l3)
# TEST ZERO fees possible # TEST ZERO fees possible
l2.rpc.setchannelfee(scid, 0, 0) l2.rpc.setchannel(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['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]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [0, DEF_PPM])
@ -2162,14 +2176,14 @@ def test_setchannelfee_zero(node_factory, bitcoind):
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(0, 0, True), (DEF_BASE, DEF_PPM, True)]) wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(0, 0, True), (DEF_BASE, DEF_PPM, True)])
# do and check actual payment # do and check actual payment
inv = l3.rpc.invoice(4999999, 'test_setchannelfee_3', 'desc')['bolt11'] inv = l3.rpc.invoice(4999999, 'test_setchannel_3', 'desc')['bolt11']
result = l1.rpc.dev_pay(inv, use_shadow=False) result = l1.rpc.dev_pay(inv, use_shadow=False)
assert result['status'] == 'complete' assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 4999999 assert result['msatoshi_sent'] == 4999999
@pytest.mark.developer("gossip without DEVELOPER=1 is slow") @pytest.mark.developer("gossip without DEVELOPER=1 is slow")
def test_setchannelfee_restart(node_factory, bitcoind): def test_setchannel_restart(node_factory, bitcoind):
# TEST SETUP # TEST SETUP
# #
# [l1] <--default_fees--> [l2] <--specific_fees--> [l3] # [l1] <--default_fees--> [l2] <--specific_fees--> [l3]
@ -2180,6 +2194,7 @@ def test_setchannelfee_restart(node_factory, bitcoind):
# - l1 routing can be made to l3 and global (1 10) fees are applied # - l1 routing can be made to l3 and global (1 10) fees are applied
DEF_BASE = 1 DEF_BASE = 1
DEF_PPM = 10 DEF_PPM = 10
MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99))
OPTS = {'may_reconnect': True, 'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM} 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) l1, l2, l3 = node_factory.line_graph(3, announce_channels=True, wait_for_announce=True, opts=OPTS)
@ -2189,7 +2204,7 @@ def test_setchannelfee_restart(node_factory, bitcoind):
scid23 = l2.get_channel_scid(l3) scid23 = l2.get_channel_scid(l3)
# l2 set custom fees # l2 set custom fees
l2.rpc.setchannelfee(scid23, 1337, 137) l2.rpc.setchannel(scid23, 1337, 137, 500001)
# restart l2 and reconnect # restart l2 and reconnect
l2.restart() l2.restart()
@ -2200,22 +2215,22 @@ def test_setchannelfee_restart(node_factory, bitcoind):
wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(scid12)['channels']] == [True, True]) wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(scid12)['channels']] == [True, True])
# l1 wait for channel update from l2 # l1 wait for channel update from l2
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l1.rpc.listchannels(scid23)['channels']] == [(1337, 137, True), (DEF_BASE, DEF_PPM, True)]) wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l1.rpc.listchannels(scid23)['channels']] == [(1337, 137, 500001, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)])
# In case l3 includes a routehint, we need to make sure they also know # In case l3 includes a routehint, we need to make sure they also know
# about the new fees, otherwise we may end up with the old feerate # about the new fees, otherwise we may end up with the old feerate
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid23)['channels']] == [(1337, 137, True), (DEF_BASE, DEF_PPM, True)]) wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid23)['channels']] == [(1337, 137, 500001, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)])
# l1 can make payment to l3 with custom fees being applied # l1 can make payment to l3 with custom fees being applied
# Note: BOLT #7 math works out to 2021 msat fees # Note: BOLT #7 math works out to 1405 msat fees
inv = l3.rpc.invoice(4999999, 'test_setchannelfee_1', 'desc')['bolt11'] inv = l3.rpc.invoice(499999, 'test_setchannel_1', 'desc')['bolt11']
result = l1.rpc.dev_pay(inv, use_shadow=False) result = l1.rpc.dev_pay(inv, use_shadow=False)
assert result['status'] == 'complete' assert result['status'] == 'complete'
assert result['msatoshi_sent'] == 5002020 assert result['msatoshi_sent'] == 501404
@pytest.mark.developer("updates are delayed without --dev-fast-gossip") @pytest.mark.developer("updates are delayed without --dev-fast-gossip")
def test_setchannelfee_all(node_factory, bitcoind): def test_setchannel_all(node_factory, bitcoind):
# TEST SETUP # TEST SETUP
# #
# [l1]----> [l2] # [l1]----> [l2]
@ -2235,7 +2250,7 @@ def test_setchannelfee_all(node_factory, bitcoind):
scid3 = l1.get_channel_scid(l3) scid3 = l1.get_channel_scid(l3)
# now try to set all (two) channels using wildcard syntax # now try to set all (two) channels using wildcard syntax
result = l1.rpc.setchannelfee("all", 0xDEAD, 0xBEEF) result = l1.rpc.setchannel("all", 0xDEAD, 0xBEEF, 0xCAFE)
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_BASE, 0xDEAD]) wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_BASE, 0xDEAD])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_PPM, 0xBEEF]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_PPM, 0xBEEF])
@ -2243,12 +2258,16 @@ def test_setchannelfee_all(node_factory, bitcoind):
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid3)['channels']] == [0xBEEF, DEF_PPM]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid3)['channels']] == [0xBEEF, DEF_PPM])
assert len(result['channels']) == 2 assert len(result['channels']) == 2
assert result['base'] == 0xDEAD
assert result['ppm'] == 0xBEEF
assert result['channels'][0]['peer_id'] == l2.info['id'] assert result['channels'][0]['peer_id'] == l2.info['id']
assert result['channels'][0]['short_channel_id'] == scid2 assert result['channels'][0]['short_channel_id'] == scid2
assert result['channels'][0]['fee_base_msat'] == 0xDEAD
assert result['channels'][0]['fee_proportional_millionths'] == 0xBEEF
assert result['channels'][0]['maximum_htlc_out_msat'] == 0xCAFE
assert result['channels'][1]['peer_id'] == l3.info['id'] assert result['channels'][1]['peer_id'] == l3.info['id']
assert result['channels'][1]['short_channel_id'] == scid3 assert result['channels'][1]['short_channel_id'] == scid3
assert result['channels'][1]['fee_base_msat'] == 0xDEAD
assert result['channels'][1]['fee_proportional_millionths'] == 0xBEEF
assert result['channels'][1]['maximum_htlc_out_msat'] == 0xCAFE
@pytest.mark.developer("gossip without DEVELOPER=1 is slow") @pytest.mark.developer("gossip without DEVELOPER=1 is slow")
@ -4898,7 +4917,7 @@ def test_pay_low_max_htlcs(node_factory):
) )
def test_setchannelfee_enforcement_delay(node_factory, bitcoind): def test_setchannel_enforcement_delay(node_factory, bitcoind):
# Fees start at 1msat + 1% # Fees start at 1msat + 1%
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True,
opts={'fee-base': 1, opts={'fee-base': 1,
@ -4924,7 +4943,7 @@ def test_setchannelfee_enforcement_delay(node_factory, bitcoind):
l1.rpc.waitsendpay(inv['payment_hash']) l1.rpc.waitsendpay(inv['payment_hash'])
# Increase fee immediately; l1 payment rejected. # Increase fee immediately; l1 payment rejected.
l2.rpc.setchannelfee("all", 2, 10000, 0) l2.rpc.setchannel("all", 2, 10000, enforcedelay=0)
inv = l3.rpc.invoice(1000, "test2", "test2") inv = l3.rpc.invoice(1000, "test2", "test2")
l1.rpc.sendpay(route, l1.rpc.sendpay(route,
@ -4942,7 +4961,7 @@ def test_setchannelfee_enforcement_delay(node_factory, bitcoind):
l1.rpc.waitsendpay(inv['payment_hash']) l1.rpc.waitsendpay(inv['payment_hash'])
# Now, give us 30 seconds please. # Now, give us 30 seconds please.
l2.rpc.setchannelfee("all", 3, 10000, 30) l2.rpc.setchannel("all", 3, 10000, enforcedelay=30)
inv = l3.rpc.invoice(1000, "test4", "test4") inv = l3.rpc.invoice(1000, "test4", "test4")
l1.rpc.sendpay(route, l1.rpc.sendpay(route,
payment_hash=inv['payment_hash'], payment_hash=inv['payment_hash'],