Skip to content

Commit be18b01

Browse files
committed
lightningd: separate coin_movement tags array into primary_tag and extra_tags.
Signed-off-by: Rusty Russell <[email protected]> Changelog-Deprecated: JSON-RPC: `coin_movement` notification `tags` array (use `primary_tag` and `extra_tags`). Changelog-Added: JSON-RPC: `coin_movement` notification `primary_tag` and `extra_tags`.
1 parent 0dcbc47 commit be18b01

File tree

5 files changed

+63
-36
lines changed

5 files changed

+63
-36
lines changed

doc/developers-guide/deprecated-features.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ hidden: false
1818
| listpeerchannels.max_total_htlc_in_msat | Field | v25.02 | v26.03 | Use our_max_total_htlc_out_msat |
1919
| wait.details | Field | v25.05 | v26.06 | Use subsystem-specific object instead |
2020
| channel_state_changed.old_state.unknown | Notification Field | v25.05 | v26.03 | Value "unknown" is deprecated: field will be omitted instead |
21+
| coin_movement.tags | Notification Field | v25.09 | v26.09 | Use `primary_tag` (first tag) and `extra_tags` instead |
2122

2223
Inevitably there are features which need to change: either to be generalized, or removed when they can no longer be supported.
2324

doc/developers-guide/plugin-development/event-notifications.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,8 @@ A notification for topic `coin_movement` is sent to record the movement of coins
339339
"output_msat": 2000000000, // ('chain_mvt' only)
340340
"output_count": 2, // ('chain_mvt' only, typically only channel closes)
341341
"fees_msat": 382, // ('channel_mvt' only)
342-
"tags": ["deposit"],
342+
"primary_tag": "deposit",
343+
"extra_tags": [],
343344
"blockheight":102, // 'chain_mvt' only
344345
"timestamp":1585948198,
345346
"coin_type":"bc"
@@ -376,7 +377,7 @@ _Only_ tagged on external events (deposits/withdrawals to an external party).
376377

377378
`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.
378379

379-
`tag` is a movement descriptor. Current tags are as follows:
380+
`primary_tag` is a movement descriptor. Current primary tags are as follows:
380381

381382
- `deposit`: funds deposited
382383
- `withdrawal`: funds withdrawn
@@ -391,15 +392,21 @@ _Only_ tagged on external events (deposits/withdrawals to an external party).
391392
- `htlc_fulfill`: on-chian htlc fulfill output
392393
- `htlc_tx`: on-chain htlc tx has happened
393394
- `to_wallet`: output being spent into our wallet
394-
- `ignored`: output is being ignored
395395
- `anchor`: an anchor output
396396
- `to_them`: output intended to peer's wallet
397397
- `penalized`: output we've 'lost' due to a penalty (failed cheat attempt)
398398
- `stolen`: output we've 'lost' due to peer's cheat
399399
- `to_miner`: output we've burned to miner (OP_RETURN)
400-
- `opener`: tags channel_open, we are the channel opener
401400
- `lease_fee`: amount paid as lease fee
402-
- `leased`: tags channel_open, channel contains leased funds
401+
- `channel_proposed`: a zero-conf channel
402+
403+
`extra_tags` is zero or more additional tags. Current extra tags are as follows:
404+
405+
- `ignored`: output is being ignored
406+
- `opener`: tags `channel_open` or `channel_proposed`, we are the channel opener
407+
- `stealable`: funds can be taken by the other party
408+
- `leased`: tags `channel_open` or `channel_proposed`, channel contains leased funds
409+
- `splice`: a channel close due to splice operation.
403410

404411
`blockheight` is the block the txid is included in. `channel_mvt`s will be null, so will the blockheight for withdrawals to external parties (we issue these events when we send the tx containing them, before they're included in the chain).
405412

lightningd/notification.c

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -455,12 +455,32 @@ static void json_add_mvt_account_id(struct json_stream *stream,
455455
json_add_string(stream, fieldname, account_id->alt_account);
456456
}
457457

458+
static void add_movement_tags(struct json_stream *stream,
459+
struct lightningd *ld,
460+
const struct mvt_tags tags)
461+
{
462+
const char **tagstrs = mvt_tag_strs(tmpctx, tags);
463+
464+
if (lightningd_deprecated_out_ok(ld, ld->deprecated_ok,
465+
"coin_movement", "tags",
466+
"v25.05", "v26.09")) {
467+
json_array_start(stream, "tags");
468+
for (size_t i = 0; i < tal_count(tagstrs); i++)
469+
json_add_string(stream, NULL, tagstrs[i]);
470+
json_array_end(stream);
471+
}
472+
473+
json_add_string(stream, "primary_tag", tagstrs[0]);
474+
json_array_start(stream, "extra_tags");
475+
for (size_t i = 1; i < tal_count(tagstrs); i++)
476+
json_add_string(stream, NULL, tagstrs[i]);
477+
json_array_end(stream);
478+
}
479+
458480
static void chain_movement_notification_serialize(struct json_stream *stream,
459481
struct lightningd *ld,
460482
const struct chain_coin_mvt *chain_mvt)
461483
{
462-
const char **tags;
463-
464484
json_add_num(stream, "version", COIN_MVT_VERSION);
465485
json_add_string(stream, "type", "chain_mvt");
466486
json_add_node_id(stream, "node_id", &ld->our_nodeid);
@@ -497,11 +517,7 @@ static void chain_movement_notification_serialize(struct json_stream *stream,
497517
if (chain_mvt->output_count > 0)
498518
json_add_num(stream, "output_count", chain_mvt->output_count);
499519

500-
json_array_start(stream, "tags");
501-
tags = mvt_tag_strs(tmpctx, chain_mvt->tags);
502-
for (size_t i = 0; i < tal_count(tags); i++)
503-
json_add_string(stream, NULL, tags[i]);
504-
json_array_end(stream);
520+
add_movement_tags(stream, ld, chain_mvt->tags);
505521

506522
json_add_u32(stream, "blockheight", chain_mvt->blockheight);
507523
json_add_u32(stream, "timestamp", time_now().ts.tv_sec);
@@ -512,8 +528,6 @@ static void channel_movement_notification_serialize(struct json_stream *stream,
512528
struct lightningd *ld,
513529
const struct channel_coin_mvt *chan_mvt)
514530
{
515-
const char **tags;
516-
517531
json_add_num(stream, "version", COIN_MVT_VERSION);
518532
json_add_string(stream, "type", "channel_mvt");
519533
json_add_node_id(stream, "node_id", &ld->our_nodeid);
@@ -529,11 +543,7 @@ static void channel_movement_notification_serialize(struct json_stream *stream,
529543
json_add_amount_msat(stream, "debit_msat", chan_mvt->debit);
530544
json_add_amount_msat(stream, "fees_msat", chan_mvt->fees);
531545

532-
json_array_start(stream, "tags");
533-
tags = mvt_tag_strs(tmpctx, chan_mvt->tags);
534-
for (size_t i = 0; i < tal_count(tags); i++)
535-
json_add_string(stream, NULL, tags[i]);
536-
json_array_end(stream);
546+
add_movement_tags(stream, ld, chan_mvt->tags);
537547

538548
json_add_u32(stream, "timestamp", time_now().ts.tv_sec);
539549
json_add_string(stream, "coin_type", chainparams->lightning_hrp);

plugins/bkpr/bookkeeper.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,16 +1772,20 @@ static char *parse_tags(const tal_t *ctx,
17721772
enum mvt_tag **tags)
17731773
{
17741774
size_t i;
1775-
const jsmntok_t *tag_tok,
1776-
*tags_tok = json_get_member(buf, tok, "tags");
1777-
1778-
if (tags_tok == NULL || tags_tok->type != JSMN_ARRAY)
1779-
return "Invalid/missing 'tags' field";
1780-
1781-
*tags = tal_arr(ctx, enum mvt_tag, tags_tok->size);
1782-
json_for_each_arr(i, tag_tok, tags_tok) {
1783-
if (!json_to_coin_mvt_tag(buf, tag_tok, &(*tags)[i]))
1784-
return "Unable to parse 'tags'";
1775+
const jsmntok_t *extras_tok,
1776+
*tag_tok = json_get_member(buf, tok, "primary_tag");
1777+
1778+
if (tag_tok == NULL)
1779+
return "missing 'primary_tag' field";
1780+
*tags = tal_arr(ctx, enum mvt_tag, 1);
1781+
if (!json_to_coin_mvt_tag(buf, tag_tok, &(*tags)[0]))
1782+
return "Unable to parse 'primary_tag'";
1783+
1784+
extras_tok = json_get_member(buf, tok, "extra_tags");
1785+
tal_resize(tags, 1 + extras_tok->size);
1786+
json_for_each_arr(i, tag_tok, extras_tok) {
1787+
if (!json_to_coin_mvt_tag(buf, tag_tok, &(*tags)[i + 1]))
1788+
return "Unable to parse 'extra_tags'";
17851789
}
17861790

17871791
return NULL;

tests/utils.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ def move_matches(exp, mv):
8888
return False
8989
if Millisatoshi(mv['debit_msat']) != Millisatoshi(exp['debit_msat']):
9090
return False
91-
if sorted(mv['tags']) != sorted(exp['tags']):
91+
if mv['primary_tag'] != exp['primary_tag']:
92+
return False
93+
if mv['extra_tags'] != exp['extra_tags']:
9294
return False
9395
if 'fees_msat' in exp:
9496
if 'fees_msat' not in mv:
@@ -144,7 +146,7 @@ def check_coin_moves(n, account_id, expected_moves, chainparams):
144146
.format(mv['type'],
145147
Millisatoshi(mv['credit_msat']).millisatoshis,
146148
Millisatoshi(mv['debit_msat']).millisatoshis,
147-
sorted(mv['tags']),
149+
[mv['primary_tag']] + mv['extra_tags'],
148150
mv['fees_msat'] if 'fees_msat' in mv else ''))
149151
if mv['version'] != 2:
150152
raise ValueError(f'version not 2 {mv}')
@@ -261,6 +263,9 @@ def matchup_events(u_set, evs, chans, tag_list):
261263
if len(u_set) == 0:
262264
raise ValueError(f"utxo-set is empty. exp {evs}, actual {u_set}")
263265

266+
def get_tags(utxo):
267+
return [utxo['primary_tag']] + utxo['extra_tags']
268+
264269
txid = u_set[0][0]['utxo_txid']
265270
# Stash the set for logging at end, if error
266271
_u_set = u_set
@@ -277,7 +282,7 @@ def matchup_events(u_set, evs, chans, tag_list):
277282
else:
278283
acct = ev[0]
279284

280-
if u[0]['account_id'] != acct or sorted(u[0]['tags']) != sorted(ev[1]):
285+
if u[0]['account_id'] != acct or get_tags(u[0]) != ev[1]:
281286
continue
282287

283288
if ev[2] is None:
@@ -289,22 +294,22 @@ def matchup_events(u_set, evs, chans, tag_list):
289294

290295
# ugly hack to annotate two possible futures for a utxo
291296
if type(ev[2]) is tuple:
292-
tag = u[1]['tags'] if u[1] else u[1]
297+
tag = get_tags(u[1]) if u[1] else u[1]
293298
if tag not in [x[0] for x in ev[2]]:
294299
raise ValueError(f"Unable to find {tag} in event set {ev}")
295300
if not u[1]:
296301
found = True
297302
u_set.remove(u)
298303
break
299304
for x in ev[2]:
300-
if x[0] == u[1]['tags'] and 'to_miner' not in u[1]['tags']:
305+
if x[0] == get_tags(u[1]) and 'to_miner' not in get_tags(u[1]):
301306
# Save the 'spent to' txid in the tag-list
302307
tag_list[x[1]] = u[1]['txid']
303308
else:
304-
if sorted(ev[2]) != sorted(u[1]['tags']):
309+
if ev[2] != get_tags(u[1]):
305310
raise ValueError(f"tags dont' match. exp {ev}, actual ({u[1]}) full utxo info: {u}")
306311
# Save the 'spent to' txid in the tag-list
307-
if 'to_miner' not in u[1]['tags']:
312+
if 'to_miner' not in get_tags(u[1]):
308313
tag_list[ev[3]] = u[1]['txid']
309314

310315
found = True

0 commit comments

Comments
 (0)