From 5146baa00bed5823617c3663a7d7f9acff177106 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH] bkpr csvs: koinly + cointracker only accept fees on the same line So we print out invoice fees on the same line for those CSVs! This means we have to do a little bit of gymnastics (but not too bad): - we save the fee amount onto the income event now so we can use it later - we ignore every "invoice_fee" event for the koinly/cointracker Note that since we're not skipping income events in the loops we also move the newline character to the start of every `_entry` function so skipped records dont incur empth lines. Changelog-Added: bkpr: print out invoice fees on the same line for `koinly` and `cointracker` csv types --- plugins/bkpr/incomestmt.c | 48 +++++++++++++++++++++++++++------------ plugins/bkpr/incomestmt.h | 3 +++ tests/test_pay.py | 9 ++++++++ 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index 7e586d3de..49d0121c3 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -43,6 +43,7 @@ static struct income_event *chain_to_income(const tal_t *ctx, inc->tag = tal_strdup(inc, ev->tag); inc->credit = credit; inc->debit = debit; + inc->fees = AMOUNT_MSAT(0); inc->currency = tal_strdup(inc, ev->currency); inc->timestamp = ev->timestamp; inc->outpoint = tal_dup(inc, struct bitcoin_outpoint, &ev->outpoint); @@ -69,6 +70,7 @@ static struct income_event *channel_to_income(const tal_t *ctx, inc->tag = tal_strdup(inc, ev->tag); inc->credit = credit; inc->debit = debit; + inc->fees = ev->fees; inc->currency = tal_strdup(inc, ev->currency); inc->timestamp = ev->timestamp; inc->outpoint = NULL; @@ -92,6 +94,7 @@ static struct income_event *onchainfee_to_income(const tal_t *ctx, /* We swap these, as they're actually opposite */ inc->credit = fee->debit; inc->debit = fee->credit; + inc->fees = AMOUNT_MSAT(0); inc->currency = tal_strdup(inc, fee->currency); inc->timestamp = fee->timestamp; inc->txid = tal_dup(inc, struct bitcoin_txid, &fee->txid); @@ -360,9 +363,10 @@ struct income_event **list_income_events(const tal_t *ctx, if (ev) tal_arr_expand(&evs, ev); - /* Breakout fees on sent payments */ + /* Breakout fees on sent payments, if present */ if (streq(chan->tag, "invoice") - && !amount_msat_zero(chan->debit)) { + && !amount_msat_zero(chan->debit) + && !amount_msat_zero(chan->fees)) { ev = paid_invoice_fee(evs, chan); tal_arr_expand(&evs, ev); } @@ -464,6 +468,13 @@ static void cointrack_entry(const tal_t *ctx, FILE *csvf, struct income_event *e time_t tv; tv = ev->timestamp; char timebuf[sizeof("mm/dd/yyyy HH:MM:SS")]; + + /* Cointrack counts invoice fee events inline */ + if (streq(ev->tag, account_entry_tag_str(INVOICEFEE))) + return; + + fprintf(csvf, "\n"); + strftime(timebuf, sizeof(timebuf), "%m/%d/%Y %T", gmtime(&tv)); fprintf(csvf, "%s", timebuf); fprintf(csvf, ","); @@ -479,8 +490,7 @@ static void cointrack_entry(const tal_t *ctx, FILE *csvf, struct income_event *e fprintf(csvf, ","); /* "Sent Quantity,Sent Currency," */ - if (!amount_msat_zero(ev->debit) - && !streq(ev->tag, ONCHAIN_FEE)) { + if (!amount_msat_zero(ev->debit)) { fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); fprintf(csvf, ","); fprintf(csvf, "%s", convert_asset_type(ev)); @@ -490,9 +500,9 @@ static void cointrack_entry(const tal_t *ctx, FILE *csvf, struct income_event *e fprintf(csvf, ","); /* "Fee Amount,Fee Currency," */ - if (!amount_msat_zero(ev->debit) - && streq(ev->tag, ONCHAIN_FEE)) { - fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); + if (!amount_msat_zero(ev->fees) + && streq(ev->tag, mvt_tag_str(INVOICE))) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->fees, false)); fprintf(csvf, ","); fprintf(csvf, "%s", convert_asset_type(ev)); } else @@ -530,13 +540,19 @@ static void koinly_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) tv = ev->timestamp; /* 2018-01-01 14:25 UTC */ char timebuf[sizeof("yyyy-mm-dd HH:MM UTC")]; + + /* Koinly counts invoice fee events inline */ + if (streq(ev->tag, account_entry_tag_str(INVOICEFEE))) + return; + + fprintf(csvf, "\n"); + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M UTC", gmtime(&tv)); fprintf(csvf, "%s", timebuf); fprintf(csvf, ","); /* "Sent Amount,Sent Currency," */ - if (!amount_msat_zero(ev->debit) - && !streq(ev->tag, ONCHAIN_FEE)) { + if (!amount_msat_zero(ev->debit)) { fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); fprintf(csvf, ","); fprintf(csvf, "%s", convert_asset_type(ev)); @@ -557,9 +573,9 @@ static void koinly_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) /* "Fee Amount,Fee Currency," */ - if (!amount_msat_zero(ev->debit) - && streq(ev->tag, ONCHAIN_FEE)) { - fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); + if (!amount_msat_zero(ev->fees) + && streq(ev->tag, mvt_tag_str(INVOICE))) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->fees, false)); fprintf(csvf, ","); fprintf(csvf, "%s", convert_asset_type(ev)); } else @@ -652,6 +668,10 @@ static void harmony_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) /* datefmt: ISO-8601 */ char timebuf[sizeof("yyyy-mm-ddTHH:MM:SSZ")]; strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%TZ", gmtime(&tv)); + + /* New line! */ + fprintf(csvf, "\n"); + fprintf(csvf, "%s", timebuf); fprintf(csvf, ","); @@ -817,10 +837,8 @@ char *csv_print_income_events(const tal_t *ctx, return tal_fmt(ctx, "Failed to open csv file %s", filename); csvfmt->emit_header(csvf); - for (size_t i = 0; i < tal_count(evs); i++) { - fprintf(csvf, "\n"); + for (size_t i = 0; i < tal_count(evs); i++) csvfmt->emit_entry(ctx, csvf, evs[i]); - } fclose(csvf); return NULL; diff --git a/plugins/bkpr/incomestmt.h b/plugins/bkpr/incomestmt.h index 12f288d59..1919ee8a1 100644 --- a/plugins/bkpr/incomestmt.h +++ b/plugins/bkpr/incomestmt.h @@ -11,6 +11,9 @@ struct income_event { char *desc; struct amount_msat credit; struct amount_msat debit; + /* Some CSVs require us to put fees on the + * same line as another entry */ + struct amount_msat fees; char *currency; u64 timestamp; diff --git a/tests/test_pay.py b/tests/test_pay.py index 64d170447..eb15416ee 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1126,6 +1126,15 @@ def test_forward(node_factory, bitcoind): l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) + # Check that invoice payment and fee are tracked appropriately + l1.daemon.wait_for_log('coin_move .* [(]invoice[)]') + l1.rpc.bkpr_dumpincomecsv('koinly', 'koinly.csv') + + koinly_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'koinly.csv') + koinly_csv = open(koinly_path, 'rb').read() + expected_line = r'0.00100000000,.*,,,0.00000001001,.*,invoice' + assert only_one(re.findall(expected_line, str(koinly_csv))) + @pytest.mark.developer("needs --dev-fast-gossip") def test_forward_different_fees_and_cltv(node_factory, bitcoind):