coin_mvt: record fees for an outbound htlc

If we initialized the payment, the fees are the entire fee-chain
(final hop amount - starting hop amount)

If it's a payment we routed, the fees are the diff between the
inbound htlc and the outbound (net gain by this routing)

Added to database so data persists nicely.
This commit is contained in:
niftynei
2021-12-07 14:09:28 -06:00
committed by Rusty Russell
parent 29c6718297
commit bddd3694fa
9 changed files with 66 additions and 21 deletions

View File

@@ -63,25 +63,16 @@ 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, AMOUNT_MSAT(0));
false, hout->fees);
}
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,
fees_collected);
hout->fees);
}

View File

@@ -279,6 +279,7 @@ struct htlc_out *new_htlc_out(const tal_t *ctx,
const u8 *onion_routing_packet,
const struct pubkey *blinding,
bool am_origin,
struct amount_msat final_msat,
u64 partid,
u64 groupid,
struct htlc_in *in)
@@ -310,10 +311,34 @@ struct htlc_out *new_htlc_out(const tal_t *ctx,
if (am_origin) {
hout->partid = partid;
hout->groupid = groupid;
/* Stash the fees (for accounting) */
if (!amount_msat_sub(&hout->fees, msat, final_msat))
return corrupt("new_htlc_out",
"overflow subtract %s-%s",
type_to_string(tmpctx,
struct amount_msat,
&msat),
type_to_string(tmpctx,
struct amount_msat,
&final_msat));
}
hout->in = NULL;
if (in)
if (in) {
htlc_out_connect_htlc_in(hout, in);
/* Stash the fees (for accounting) */
if (!amount_msat_sub(&hout->fees, in->msat, msat))
return corrupt("new_htlc_out",
"overflow subtract %s-%s",
type_to_string(tmpctx,
struct amount_msat,
&in->msat),
type_to_string(tmpctx,
struct amount_msat,
&msat));
}
return htlc_out_check(hout, "new_htlc_out");
}

View File

@@ -90,6 +90,10 @@ struct htlc_out {
/* Is this a locally-generated payment? Implies ->in is NULL. */
bool am_origin;
/* Amount of fees that this out htlc pays (if am_origin);
* otherwise fees collected by routing this out */
struct amount_msat fees;
/* If am_origin, this is the partid of the payment. */
u64 partid;
@@ -168,6 +172,7 @@ struct htlc_out *new_htlc_out(const tal_t *ctx,
const u8 *onion_routing_packet,
const struct pubkey *blinding,
bool am_origin,
struct amount_msat final_msat,
u64 partid,
u64 groupid,
struct htlc_in *in);

View File

@@ -773,6 +773,7 @@ static bool should_use_tlv(enum route_hop_style style)
static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld,
const struct onionpacket *packet,
const struct route_hop *first_hop,
const struct amount_msat final_amount,
const struct sha256 *payment_hash,
const struct pubkey *blinding,
u64 partid,
@@ -786,7 +787,8 @@ static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld,
base_expiry = get_block_height(ld->topology) + 1;
onion = serialize_onionpacket(tmpctx, packet);
return send_htlc_out(ctx, channel, first_hop->amount,
base_expiry + first_hop->delay, payment_hash,
base_expiry + first_hop->delay,
final_amount, payment_hash,
blinding, partid, groupid, onion, NULL, hout,
&dont_care_about_channel_update);
}
@@ -1047,7 +1049,8 @@ send_payment_core(struct lightningd *ld,
return command_failed(cmd, data);
}
failmsg = send_onion(tmpctx, ld, packet, first_hop, rhash, NULL, partid,
failmsg = send_onion(tmpctx, ld, packet, first_hop, msat,
rhash, NULL, partid,
group, channel, &hout);
if (failmsg) {
@@ -1211,7 +1214,8 @@ send_payment(struct lightningd *ld,
n_hops, type_to_string(tmpctx, struct amount_msat, &msat));
packet = create_onionpacket(tmpctx, path, ROUTING_INFO_SIZE, &path_secrets);
return send_payment_core(ld, cmd, rhash, partid, group, &route[0],
msat, total_msat, label, invstring,
msat, total_msat,
label, invstring,
packet, &ids[n_hops - 1], ids,
channels, path_secrets, local_offer_id);
}

View File

@@ -638,6 +638,7 @@ static void htlc_offer_timeout(struct htlc_out *out)
const u8 *send_htlc_out(const tal_t *ctx,
struct channel *out,
struct amount_msat amount, u32 cltv,
struct amount_msat final_msat,
const struct sha256 *payment_hash,
const struct pubkey *blinding,
u64 partid,
@@ -675,6 +676,7 @@ const u8 *send_htlc_out(const tal_t *ctx,
*houtp = new_htlc_out(out->owner, out, amount, cltv,
payment_hash, onion_routing_packet,
blinding, in == NULL,
final_msat,
partid, groupid, in);
tal_add_destructor(*houtp, destroy_hout_subd_died);
@@ -786,7 +788,8 @@ static void forward_htlc(struct htlc_in *hin,
}
failmsg = send_htlc_out(tmpctx, next, amt_to_forward,
outgoing_cltv_value, &hin->payment_hash,
outgoing_cltv_value, AMOUNT_MSAT(0),
&hin->payment_hash,
next_blinding, 0 /* partid */, 0 /* groupid */,
next_onion, hin, &hout, &needs_update_appended);
if (!failmsg)
@@ -1605,7 +1608,7 @@ static void remove_htlc_out(struct channel *channel, struct htlc_out *hout)
if (!mvt)
log_broken(channel->log,
"Unable to calculate fees collected."
"Unable to calculate fees."
" Not logging an outbound HTLC");
else
notify_channel_mvt(channel->peer->ld, mvt);

View File

@@ -43,6 +43,7 @@ void update_per_commit_point(struct channel *channel,
const u8 *send_htlc_out(const tal_t *ctx,
struct channel *out,
struct amount_msat amount, u32 cltv,
struct amount_msat final_msat,
const struct sha256 *payment_hash,
const struct pubkey *blinding,
u64 partid,

View File

@@ -1902,10 +1902,18 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams):
{'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tags': ['channel_close']},
]
l3_l2_mvts = [
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']},
{'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice'], 'fees': '0msat'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 50000501, 'tags': ['invoice'], 'fees': '501msat'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 49999499, 'tags': ['channel_close']},
]
coin_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')
l1, l2, l3 = node_factory.line_graph(3, opts=[
{'may_reconnect': True},
{'may_reconnect': True, 'plugin': os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')},
{'may_reconnect': True},
{'may_reconnect': True, 'plugin': coin_plugin},
{'may_reconnect': True, 'plugin': coin_plugin},
], wait_for_announce=True)
bitcoind.generate_block(5)
@@ -1977,12 +1985,14 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams):
bitcoind.generate_block(6)
sync_blockheight(bitcoind, [l2])
l2.daemon.wait_for_log('{}.*FUNDING_TRANSACTION/FUNDING_OUTPUT->MUTUAL_CLOSE depth'.format(l3.info['id']))
l3.daemon.wait_for_log('Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by MUTUAL_CLOSE')
# Ending channel balance should be zero
assert account_balance(l2, chanid_1) == 0
assert account_balance(l2, chanid_3) == 0
# Verify we recorded all the movements we expect
check_coin_moves(l3, chanid_3, l3_l2_mvts, chainparams)
check_coin_moves(l2, chanid_1, l1_l2_mvts, chainparams)
check_coin_moves(l2, chanid_3, l2_l3_mvts, chainparams)

View File

@@ -868,6 +868,7 @@ static struct migration dbmigrations[] = {
[BUILD_ASSERT_OR_ZERO( 9 == RCVD_REMOVE_ACK_REVOCATION) +
BUILD_ASSERT_OR_ZERO(19 == SENT_REMOVE_ACK_REVOCATION)],
NULL},
{SQL("ALTER TABLE channel_htlcs ADD fees_msat BIGINT DEFAULT 0"), NULL},
};
/* Leak tracking. */

View File

@@ -2372,8 +2372,9 @@ void wallet_htlc_save_out(struct wallet *wallet,
" malformed_onion,"
" partid,"
" groupid,"
" fees_msat,"
" min_commit_num"
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?);"));
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?);"));
db_bind_u64(stmt, 0, chan->dbid);
db_bind_u64(stmt, 1, out->key.id);
@@ -2403,7 +2404,9 @@ void wallet_htlc_save_out(struct wallet *wallet,
db_bind_u64(stmt, 10, out->partid);
db_bind_u64(stmt, 11, out->groupid);
}
db_bind_u64(stmt, 12, min_u64(chan->next_index[LOCAL]-1,
db_bind_amount_msat(stmt, 12, &out->fees);
db_bind_u64(stmt, 13, min_u64(chan->next_index[LOCAL]-1,
chan->next_index[REMOTE]-1));
db_exec_prepared_v2(stmt);
@@ -2598,6 +2601,7 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet,
out->failmsg = db_col_arr(out, stmt, "localfailmsg", u8);
out->in = NULL;
db_col_amount_msat(stmt, "fees_msat", &out->fees);
if (!db_col_is_null(stmt, "origin_htlc")) {
u64 in_id = db_col_u64(stmt, "origin_htlc");
@@ -2737,6 +2741,7 @@ bool wallet_htlcs_load_out_for_channel(struct wallet *wallet,
", partid"
", localfailmsg"
", groupid"
", fees_msat"
" FROM channel_htlcs"
" WHERE direction = ?"
" AND channel_id = ?"