Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions contrib/pyln-testing/pyln/testing/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from bitcoin.rpc import RawProxy as BitcoinProxy
from pyln.client import RpcError
from pyln.testing.btcproxy import BitcoinRpcProxy
from collections import OrderedDict
from decimal import Decimal
Expand Down Expand Up @@ -816,34 +817,45 @@ def wait_for_htlcs(self):
if 'htlcs' in channel:
wait_for(lambda: len(self.rpc.listpeers()['peers'][p]['channels'][c]['htlcs']) == 0)

# This sends money to a directly connected peer
def pay(self, dst, amt, label=None):
if not label:
label = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20))

# check we are connected
dst_id = dst.info['id']
assert len(self.rpc.listpeers(dst_id).get('peers')) == 1

# make an invoice
rhash = dst.rpc.invoice(amt, label, label)['payment_hash']
invoices = dst.rpc.listinvoices(label)['invoices']
assert len(invoices) == 1 and invoices[0]['status'] == 'unpaid'

routestep = {
'msatoshi': amt,
'id': dst.info['id'],
'id': dst_id,
'delay': 5,
'channel': '1x1x1'
'channel': '1x1x1' # note: can be bogus for 1-hop direct payments
}

def wait_pay():
# Up to 10 seconds for payment to succeed.
start_time = time.time()
while dst.rpc.listinvoices(label)['invoices'][0]['status'] != 'paid':
if time.time() > start_time + 10:
raise TimeoutError('Payment timed out')
time.sleep(0.1)
# sendpay is async now
self.rpc.sendpay([routestep], rhash)
# wait for sendpay to comply
result = self.rpc.waitsendpay(rhash)
assert(result.get('status') == 'complete')

# This helper sends all money to a peer until even 1 msat can't get through.
def drain(self, peer):
total = 0
msat = 16**9
while msat != 0:
try:
self.pay(peer, msat)
total += msat
except RpcError:
msat //= 2
return total

# Note: this feeds through the smoother in update_feerate, so changing
# it on a running daemon may not give expected result!
def set_feerates(self, feerates, wait_for_effect=True):
Expand Down Expand Up @@ -873,6 +885,15 @@ def mock_estimatesmartfee(r):
if wait_for_effect:
wait_for(lambda: self.daemon.rpcproxy.mock_counts['estimatesmartfee'] >= 3)

# force new feerates by restarting and thus skipping slow smoothed process
# Note: testnode must be created with: opts={'may_reconnect': True}
def force_feerates(self, rate):
assert(self.may_reconnect)
self.set_feerates([rate] * 3, False)
self.restart()
self.daemon.wait_for_log('peer_out WIRE_UPDATE_FEE')
assert(self.rpc.feerates('perkw')['perkw']['normal'] == rate)

def wait_for_onchaind_broadcast(self, name, resolve=None):
"""Wait for onchaind to drop tx name to resolve (if any)"""
if resolve:
Expand Down
10 changes: 5 additions & 5 deletions doc/lightning-listpeers.7

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions doc/lightning-listpeers.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,15 @@ state, or in various circumstances:
via this channel;
a number followed by a string unit.
The peer imposes this on us, default is 1% of the total channel capacity.
* *spendable\_msat*: A string describing an ***estimate*** of how much we
can send out over this channel in a single payment (or payment-part for
multi-part payments);
* *spendable\_msat* and *receivable\_msat*: A string describing an
***estimate*** of how much we can send or receive over this channel in a
single payment (or payment-part for multi-part payments);
Comment thread
m-schmoock marked this conversation as resolved.
a number followed by a string unit.
This is an ***estimate***, which can be wrong because adding HTLCs requires
an increase in fees paid to onchain miners, and onchain fees change
dynamically according to onchain activity.
For a sufficiently-large channel with capacity on your side, this can
be limited by the rules imposed under certain blockchains;
For a sufficiently-large channel, this can be limited by the rules imposed
under certain blockchains;
for example, individual Bitcoin mainnet payment-parts cannot exceed
42.94967295 mBTC.
* *minimum\_htlc\_in\_msat*: A string describing the minimum amount that
Expand Down
107 changes: 80 additions & 27 deletions lightningd/peer_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,62 +509,63 @@ static void json_add_sat_only(struct json_stream *result,
type_to_string(tmpctx, struct amount_msat, &msat));
}

/* This is quite a lot of work to figure out what it would cost us! */
/* Fee a commitment transaction would currently cost */
static struct amount_sat commit_txfee(const struct channel *channel,
struct amount_msat spendable)
struct amount_msat amount,
enum side side)
{
/* FIXME: make per-channel htlc maps! */
const struct htlc_in *hin;
struct htlc_in_map_iter ini;
const struct htlc_out *hout;
struct htlc_out_map_iter outi;
struct lightningd *ld = channel->peer->ld;
u32 local_feerate = get_feerate(channel->channel_info.fee_states,
channel->funder, LOCAL);
size_t num_untrimmed_htlcs = 0;

/* Assume we tried to spend "spendable" */
if (!htlc_is_trimmed(LOCAL, spendable,
local_feerate, channel->our_config.dust_limit,
LOCAL))
u32 feerate = get_feerate(channel->channel_info.fee_states,
channel->funder, side);
struct amount_sat dust_limit;
if (side == LOCAL)
dust_limit = channel->our_config.dust_limit;
if (side == REMOTE)
dust_limit = channel->channel_info.their_config.dust_limit;

/* Assume we tried to add "amount" */
if (!htlc_is_trimmed(side, amount, feerate, dust_limit, side))
num_untrimmed_htlcs++;

for (hin = htlc_in_map_first(&ld->htlcs_in, &ini);
hin;
hin = htlc_in_map_next(&ld->htlcs_in, &ini)) {
if (hin->key.channel != channel)
continue;
if (!htlc_is_trimmed(REMOTE, hin->msat, local_feerate,
channel->our_config.dust_limit,
LOCAL))
if (!htlc_is_trimmed(!side, hin->msat, feerate, dust_limit,
side))
num_untrimmed_htlcs++;
}
for (hout = htlc_out_map_first(&ld->htlcs_out, &outi);
hout;
hout = htlc_out_map_next(&ld->htlcs_out, &outi)) {
if (hout->key.channel != channel)
continue;
if (!htlc_is_trimmed(LOCAL, hout->msat, local_feerate,
channel->our_config.dust_limit,
LOCAL))
if (!htlc_is_trimmed(side, hout->msat, feerate, dust_limit,
side))
num_untrimmed_htlcs++;
}

/*
* BOLT-95c74fef2fe590cb8adbd7b848743a229ffe825a #2:
* BOLT-f5490f17d17ff49dc26ee459432b3c9db4fda8a9 #2:
* Adding an HTLC: update_add_htlc
*
* A sending node:
* - if it is responsible for paying the Bitcoin fee:
* - SHOULD NOT offer `amount_msat` if, after adding that HTLC to
* its commitment transaction, its remaining balance doesn't allow
* it to pay the fee for a future additional non-dust HTLC at
* `N*feerate_per_kw` while maintaining its channel reserve
* ("fee spike buffer"), where `N` is a parameter chosen by the
* implementation (`N = 2` is recommended to ensure
* predictability).
* - SHOULD NOT offer amount_msat if, after adding that HTLC to its
* commitment transaction, its remaining balance doesn't allow it
* to pay the fee for a future additional non-dust HTLC at a
* higher feerate while maintaining its channel reserve
* ("fee spike buffer"). A buffer of 2*feerate_per_kw is
* recommended to ensure predictability.
*/
return commit_tx_base_fee(local_feerate * 2, num_untrimmed_htlcs + 1);
return commit_tx_base_fee(2 * feerate, num_untrimmed_htlcs + 1);
}

static void subtract_offered_htlcs(const struct channel *channel,
Expand All @@ -584,13 +585,30 @@ static void subtract_offered_htlcs(const struct channel *channel,
}
}

static void subtract_received_htlcs(const struct channel *channel,
struct amount_msat *amount)
{
const struct htlc_in *hin;
struct htlc_in_map_iter ini;
struct lightningd *ld = channel->peer->ld;

for (hin = htlc_in_map_first(&ld->htlcs_in, &ini);
hin;
hin = htlc_in_map_next(&ld->htlcs_in, &ini)) {
if (hin->key.channel != channel)
continue;
if (!amount_msat_sub(amount, *amount, hin->msat))
*amount = AMOUNT_MSAT(0);
}
}

static void json_add_channel(struct lightningd *ld,
struct json_stream *response, const char *key,
const struct channel *channel)
{
struct channel_id cid;
struct channel_stats channel_stats;
struct amount_msat spendable, funding_msat;
struct amount_msat spendable, receivable, funding_msat, their_msat;
struct peer *p = channel->peer;

json_object_start(response, key);
Expand Down Expand Up @@ -703,7 +721,8 @@ static void json_add_channel(struct lightningd *ld,
channel->channel_info.their_config.channel_reserve,
"our_channel_reserve_satoshis",
"our_reserve_msat");
/* Compute how much we can send via this channel. */

/* Compute how much we can send via this channel in one payment. */
if (!amount_msat_sub_sat(&spendable,
channel->our_msat,
channel->channel_info.their_config.channel_reserve))
Expand All @@ -715,7 +734,8 @@ static void json_add_channel(struct lightningd *ld,
/* If we're funder, subtract txfees we'll need to spend this */
if (channel->funder == LOCAL) {
if (!amount_msat_sub_sat(&spendable, spendable,
commit_txfee(channel, spendable)))
commit_txfee(channel, spendable,
LOCAL)))
spendable = AMOUNT_MSAT(0);
}

Expand All @@ -728,8 +748,41 @@ static void json_add_channel(struct lightningd *ld,
if (amount_msat_greater(spendable, chainparams->max_payment))
spendable = chainparams->max_payment;

/* append spendable to JSON output */
json_add_amount_msat_compat(response, spendable,
"spendable_msatoshi", "spendable_msat");

/* Compute how much we can receive via this channel in one payment */
if (!amount_sat_sub_msat(&their_msat, channel->funding, channel->our_msat))
their_msat = AMOUNT_MSAT(0);
if (!amount_msat_sub_sat(&receivable,
their_msat,
channel->our_config.channel_reserve))
receivable = AMOUNT_MSAT(0);

/* Take away any currently-offered HTLCs. */
subtract_received_htlcs(channel, &receivable);

/* If they're funder, subtract txfees they'll need to spend this */
if (channel->funder == REMOTE) {
if (!amount_msat_sub_sat(&receivable, receivable,
commit_txfee(channel,
receivable, REMOTE)))
receivable = AMOUNT_MSAT(0);
}

/* They can't offer an HTLC less than what we will accept. */
if (amount_msat_less(receivable, channel->our_config.htlc_minimum))
receivable = AMOUNT_MSAT(0);

/* They can't offer an HTLC over the max payment threshold either. */
if (amount_msat_greater(receivable, chainparams->max_payment))
receivable = chainparams->max_payment;

/* append receivable to JSON output */
json_add_amount_msat_compat(response, receivable,
"receivable_msatoshi", "receivable_msat");

json_add_amount_msat_compat(response,
channel->our_config.htlc_minimum,
"htlc_minimum_msat",
Expand Down
Loading