diff --git a/plugins/bkpr/account_entry.c b/plugins/bkpr/account_entry.c index 7179f761fc1f..4aa6d5489e3e 100644 --- a/plugins/bkpr/account_entry.c +++ b/plugins/bkpr/account_entry.c @@ -7,6 +7,7 @@ static const char *tags[] = { "journal_entry", "penalty_adj", + "invoice_fee", }; const char *account_entry_tag_str(enum account_entry_tag tag) diff --git a/plugins/bkpr/account_entry.h b/plugins/bkpr/account_entry.h index fed529847740..c04f4dafabc3 100644 --- a/plugins/bkpr/account_entry.h +++ b/plugins/bkpr/account_entry.h @@ -2,10 +2,11 @@ #define LIGHTNING_PLUGINS_BKPR_ACCOUNT_ENTRY_H #include "config.h" -#define NUM_ACCOUNT_ENTRY_TAGS (JOURNAL_ENTRY + 1) +#define NUM_ACCOUNT_ENTRY_TAGS (INVOICEFEE + 1) enum account_entry_tag { JOURNAL_ENTRY = 0, PENALTY_ADJ = 1, + INVOICEFEE = 2, }; /* Convert an enum into a string */ diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c index 782aacf6b1c9..58ca871d0a5a 100644 --- a/plugins/bkpr/channel_event.c +++ b/plugins/bkpr/channel_event.c @@ -39,6 +39,8 @@ void json_add_channel_event(struct json_stream *out, json_add_string(out, "tag", ev->tag); json_add_amount_msat_only(out, "credit", ev->credit); json_add_amount_msat_only(out, "debit", ev->debit); + if (!amount_msat_zero(ev->fees)) + json_add_amount_msat_only(out, "fees", ev->fees); json_add_string(out, "currency", ev->currency); if (ev->payment_id) json_add_sha256(out, "payment_id", ev->payment_id); diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index d5d02d347fd5..b9b1ecb3d548 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -190,6 +190,16 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, return NULL; } +static struct income_event *paid_invoice_fee(const tal_t *ctx, + struct channel_event *ev) +{ + struct income_event *iev; + iev = channel_to_income(ctx, ev, AMOUNT_MSAT(0), ev->fees); + iev->tag = tal_free(ev->tag); + iev->tag = (char *)account_entry_tag_str(INVOICEFEE); + return iev; +} + static struct income_event *maybe_channel_income(const tal_t *ctx, struct channel_event *ev) { @@ -203,7 +213,17 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, } if (streq(ev->tag, "invoice")) { - /* FIXME: add a sub-category for fees paid */ + /* If it's a payment, we note fees separately */ + if (!amount_msat_zero(ev->debit)) { + struct amount_msat paid; + bool ok; + ok = amount_msat_sub(&paid, ev->debit, ev->fees); + assert(ok); + return channel_to_income(ctx, ev, + ev->credit, + paid); + } + return channel_to_income(ctx, ev, ev->credit, ev->debit); @@ -338,6 +358,13 @@ struct income_event **list_income_events(const tal_t *ctx, if (ev) tal_arr_expand(&evs, ev); + /* Breakout fees on sent payments */ + if (streq(chan->tag, "invoice") + && !amount_msat_zero(chan->debit)) { + ev = paid_invoice_fee(evs, chan); + tal_arr_expand(&evs, ev); + } + j++; continue; } diff --git a/tests/test_pay.py b/tests/test_pay.py index 686faa9dfd92..dc4bd15fe76b 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1303,6 +1303,21 @@ def test_forward_pad_fees_and_cltv(node_factory, bitcoind): l1.rpc.waitsendpay(rhash) assert only_one(l3.rpc.listinvoices('test_forward_pad_fees_and_cltv')['invoices'])['status'] == 'paid' + # Do some checks of the bookkeeper's records + def _income_tagset(node, tagset): + incomes = node.rpc.listincome()['income_events'] + return [e for e in incomes if e['tag'] in tagset] + + tags = ['invoice', 'invoice_fee'] + wait_for(lambda: len(_income_tagset(l1, tags)) == 2) + incomes = _income_tagset(l1, tags) + # the balance on l3 should equal the invoice + bal = only_one(only_one(l3.rpc.listbalances()['accounts'])['balances'])['balance'] + assert incomes[0]['tag'] == 'invoice' + assert bal == incomes[0]['debit'] + inve = only_one([e for e in l1.rpc.listaccountevents()['events'] if e['tag'] == 'invoice']) + assert Millisatoshi(inve['debit']) == Millisatoshi(incomes[0]['debit']) + Millisatoshi(incomes[1]['debit']) + @pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs") def test_forward_stats(node_factory, bitcoind):