mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-03 10:46:58 +01:00
coin_mvt: record new 'fees' field on htlc channel moves
We record the amount of fees collected for a routed payment. For simplicity's sake on the data agg side, we record the fee payment on *BOTH* the incoming htlc and the outgoing htlc. Note that this results in double counting if you add up the fees from both an in-routed and out-routed payment.
This commit is contained in:
parent
b6463174d6
commit
29c6718297
9 changed files with 105 additions and 38 deletions
|
@ -61,7 +61,8 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx,
|
|||
u64 *part_id,
|
||||
struct amount_msat amount,
|
||||
enum mvt_tag *tags STEALS,
|
||||
bool is_credit)
|
||||
bool is_credit,
|
||||
struct amount_msat fees)
|
||||
{
|
||||
struct channel_coin_mvt *mvt = tal(ctx, struct channel_coin_mvt);
|
||||
|
||||
|
@ -78,6 +79,8 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx,
|
|||
mvt->credit = AMOUNT_MSAT(0);
|
||||
}
|
||||
|
||||
mvt->fees = fees;
|
||||
|
||||
return mvt;
|
||||
}
|
||||
|
||||
|
@ -326,7 +329,8 @@ struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx,
|
|||
|
||||
return new_channel_coin_mvt(ctx, cid, empty_hash,
|
||||
NULL, amount,
|
||||
new_tag_arr(ctx, PUSHED), false);
|
||||
new_tag_arr(ctx, PUSHED), false,
|
||||
AMOUNT_MSAT(0));
|
||||
}
|
||||
|
||||
struct coin_mvt *finalize_chain_mvt(const tal_t *ctx,
|
||||
|
@ -351,6 +355,7 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx,
|
|||
|
||||
mvt->output_val = tal(mvt, struct amount_sat);
|
||||
*mvt->output_val = chain_mvt->output_val;
|
||||
mvt->fees = NULL;
|
||||
|
||||
mvt->timestamp = timestamp;
|
||||
mvt->blockheight = chain_mvt->blockheight;
|
||||
|
@ -379,6 +384,8 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx,
|
|||
mvt->credit = chan_mvt->credit;
|
||||
mvt->debit = chan_mvt->debit;
|
||||
mvt->output_val = NULL;
|
||||
mvt->fees = tal(mvt, struct amount_msat);
|
||||
*mvt->fees = chan_mvt->fees;
|
||||
mvt->timestamp = timestamp;
|
||||
/* channel movements don't have a blockheight */
|
||||
mvt->blockheight = 0;
|
||||
|
|
|
@ -58,6 +58,8 @@ struct channel_coin_mvt {
|
|||
struct amount_msat credit;
|
||||
struct amount_msat debit;
|
||||
|
||||
/* Fees collected (or paid) on this mvt */
|
||||
struct amount_msat fees;
|
||||
};
|
||||
|
||||
struct chain_coin_mvt {
|
||||
|
@ -114,6 +116,9 @@ struct coin_mvt {
|
|||
* our credit/debit amount, eg channel opens */
|
||||
struct amount_sat *output_val;
|
||||
|
||||
/* Amount of fees collected/paid by channel mvt */
|
||||
struct amount_msat *fees;
|
||||
|
||||
u32 timestamp;
|
||||
u32 blockheight;
|
||||
|
||||
|
@ -133,7 +138,8 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx,
|
|||
u64 *part_id,
|
||||
struct amount_msat amount,
|
||||
enum mvt_tag *tags STEALS,
|
||||
bool is_credit);
|
||||
bool is_credit,
|
||||
struct amount_msat fees);
|
||||
|
||||
struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx,
|
||||
const struct bitcoin_outpoint *outpoint,
|
||||
|
|
|
@ -708,6 +708,8 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions.
|
|||
"part_id": 0, // (`channel_mvt` type only, mandatory)
|
||||
"credit":"2000000000msat",
|
||||
"debit":"0msat",
|
||||
"output_value": "2000000000msat", // ('chain_mvt' only)
|
||||
"fees": "382msat", // ('channel_mvt' only, optional)
|
||||
"tags": ["deposit"],
|
||||
"blockheight":102, // (May be null)
|
||||
"timestamp":1585948198,
|
||||
|
@ -747,6 +749,16 @@ multiple times. `channel_mvt` only
|
|||
`credit` and `debit` are millisatoshi denominated amounts of the fund movement. A
|
||||
'credit' is funds deposited into an account; a `debit` is funds withdrawn.
|
||||
|
||||
`output_value` is the total value of the on-chain UTXO. Note that for
|
||||
channel opens/closes the total output value will not necessarily correspond
|
||||
to the amount that's credited/debited.
|
||||
|
||||
`fees` is an HTLC annotation for the amount of fees either paid or
|
||||
earned. For "invoice" tagged events, the fees are the total fees
|
||||
paid to send that payment. The end amount can be found by subtracting
|
||||
the total fees from the `debited` amount. For "routed" tagged events,
|
||||
both the debit/credit contain fees. Technically routed debits are the
|
||||
'fee generating' event, however we include them on routed credits as well.
|
||||
|
||||
`tag` is a movement descriptor. Current tags are as follows:
|
||||
- `deposit`: funds deposited
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "config.h"
|
||||
#include <common/onion.h>
|
||||
#include <lightningd/channel.h>
|
||||
#include <lightningd/coin_mvts.h>
|
||||
#include <lightningd/notification.h>
|
||||
|
@ -33,17 +34,26 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx,
|
|||
return new_channel_coin_mvt(ctx, &channel->cid,
|
||||
hin->payment_hash, NULL,
|
||||
hin->msat, new_tag_arr(ctx, INVOICE),
|
||||
true);
|
||||
true, AMOUNT_MSAT(0));
|
||||
}
|
||||
|
||||
struct channel_coin_mvt *new_channel_mvt_routed_hin(const tal_t *ctx,
|
||||
struct htlc_in *hin,
|
||||
struct channel *channel)
|
||||
{
|
||||
struct amount_msat fees_collected;
|
||||
|
||||
if (!hin->payload)
|
||||
return NULL;
|
||||
|
||||
if (!amount_msat_sub(&fees_collected, hin->msat,
|
||||
hin->payload->amt_to_forward))
|
||||
return NULL;
|
||||
|
||||
return new_channel_coin_mvt(ctx, &channel->cid,
|
||||
hin->payment_hash, NULL,
|
||||
hin->msat, new_tag_arr(ctx, ROUTED),
|
||||
true);
|
||||
true, fees_collected);
|
||||
}
|
||||
|
||||
struct channel_coin_mvt *new_channel_mvt_invoice_hout(const tal_t *ctx,
|
||||
|
@ -53,15 +63,25 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hout(const tal_t *ctx,
|
|||
return new_channel_coin_mvt(ctx, &channel->cid,
|
||||
hout->payment_hash, &hout->partid,
|
||||
hout->msat, new_tag_arr(ctx, INVOICE),
|
||||
false);
|
||||
false, AMOUNT_MSAT(0));
|
||||
}
|
||||
|
||||
struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx,
|
||||
struct htlc_out *hout,
|
||||
struct channel *channel)
|
||||
{
|
||||
struct amount_msat fees_collected;
|
||||
|
||||
if (!hout->in)
|
||||
return NULL;
|
||||
|
||||
if (!amount_msat_sub(&fees_collected, hout->in->msat,
|
||||
hout->msat))
|
||||
return NULL;
|
||||
|
||||
return new_channel_coin_mvt(ctx, &channel->cid,
|
||||
hout->payment_hash, NULL,
|
||||
hout->msat, new_tag_arr(ctx, ROUTED),
|
||||
false);
|
||||
false,
|
||||
fees_collected);
|
||||
}
|
||||
|
|
|
@ -474,6 +474,9 @@ static void coin_movement_notification_serialize(struct json_stream *stream,
|
|||
if (mvt->output_val)
|
||||
json_add_amount_sat_only(stream, "output_value",
|
||||
*mvt->output_val);
|
||||
if (mvt->fees)
|
||||
json_add_amount_msat_only(stream, "fees",
|
||||
*mvt->fees);
|
||||
|
||||
json_array_start(stream, "tags");
|
||||
for (size_t i = 0; i < tal_count(mvt->tags); i++)
|
||||
|
|
|
@ -1544,12 +1544,17 @@ static void remove_htlc_in(struct channel *channel, struct htlc_in *hin)
|
|||
channel->msat_to_us_max = channel->our_msat;
|
||||
|
||||
/* Coins have definitively moved, log a movement */
|
||||
if (hin->we_filled)
|
||||
if (hin->we_filled && *hin->we_filled)
|
||||
mvt = new_channel_mvt_invoice_hin(hin, hin, channel);
|
||||
else
|
||||
mvt = new_channel_mvt_routed_hin(hin, hin, channel);
|
||||
|
||||
notify_channel_mvt(channel->peer->ld, mvt);
|
||||
if (!mvt)
|
||||
log_broken(channel->log,
|
||||
"Unable to calculate fees collected."
|
||||
" Not logging an inbound HTLC");
|
||||
else
|
||||
notify_channel_mvt(channel->peer->ld, mvt);
|
||||
}
|
||||
|
||||
tal_free(hin);
|
||||
|
@ -1597,7 +1602,13 @@ static void remove_htlc_out(struct channel *channel, struct htlc_out *hout)
|
|||
else
|
||||
mvt = new_channel_mvt_routed_hout(hout, hout, channel);
|
||||
|
||||
notify_channel_mvt(channel->peer->ld, mvt);
|
||||
|
||||
if (!mvt)
|
||||
log_broken(channel->log,
|
||||
"Unable to calculate fees collected."
|
||||
" Not logging an outbound HTLC");
|
||||
else
|
||||
notify_channel_mvt(channel->peer->ld, mvt);
|
||||
}
|
||||
|
||||
tal_free(hout);
|
||||
|
|
|
@ -1888,17 +1888,17 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams):
|
|||
|
||||
l1_l2_mvts = [
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']},
|
||||
{'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tags': ['routed']},
|
||||
{'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['routed']},
|
||||
{'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice']},
|
||||
{'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['invoice']},
|
||||
{'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tags': ['routed'], 'fees': '1001msat'},
|
||||
{'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['routed'], 'fees': '501msat'},
|
||||
{'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice'], 'fees': '0msat'},
|
||||
{'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['invoice'], 'fees': '0msat'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 100001001, 'tags': ['channel_close']},
|
||||
]
|
||||
|
||||
l2_l3_mvts = [
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['channel_open', 'opener']},
|
||||
{'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tags': ['routed']},
|
||||
{'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tags': ['routed']},
|
||||
{'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tags': ['routed'], 'fees': '1001msat'},
|
||||
{'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tags': ['routed'], 'fees': '501msat'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tags': ['channel_close']},
|
||||
]
|
||||
|
||||
|
|
|
@ -850,26 +850,26 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
|
|||
l1.rpc.signpsbt(invalid_psbt)
|
||||
|
||||
wallet_coin_mvts = [
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']},
|
||||
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']},
|
||||
]
|
||||
|
||||
check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams)
|
||||
|
|
|
@ -79,6 +79,13 @@ def move_matches(exp, mv):
|
|||
return False
|
||||
if mv['tags'] != exp['tags']:
|
||||
return False
|
||||
if 'fees' in exp:
|
||||
if 'fees' not in mv:
|
||||
return False
|
||||
if mv['fees'] != exp['fees']:
|
||||
return False
|
||||
elif 'fees' in mv:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
@ -94,11 +101,12 @@ def check_coin_moves(n, account_id, expected_moves, chainparams):
|
|||
node_id = n.info['id']
|
||||
acct_moves = [m for m in moves if m['account_id'] == account_id]
|
||||
for mv in acct_moves:
|
||||
print("{{'type': '{}', 'credit': {}, 'debit': {}, 'tags': '{}'}},"
|
||||
print("{{'type': '{}', 'credit': {}, 'debit': {}, 'tags': '{}' , ['fees'?: '{}']}},"
|
||||
.format(mv['type'],
|
||||
Millisatoshi(mv['credit']).millisatoshis,
|
||||
Millisatoshi(mv['debit']).millisatoshis,
|
||||
mv['tags']))
|
||||
mv['tags'],
|
||||
mv['fees'] if 'fees' in mv else ''))
|
||||
assert mv['version'] == 2
|
||||
assert mv['node_id'] == node_id
|
||||
assert mv['timestamp'] > 0
|
||||
|
|
Loading…
Add table
Reference in a new issue