bkpr: forward utxo_deposit / utxo_spend notifications to new injectutxodeposit / injectutxospend calls.

And thus we absorb them as normal when they come back as "foreign" entries.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2025-08-19 10:30:50 +09:30
parent 980241fc36
commit 1f7905259b
3 changed files with 88 additions and 121 deletions

View File

@@ -1517,11 +1517,24 @@ parse_and_log_chain_move(struct command *cmd,
e->stealable = false;
e->splice_close = false;
e->foreign = false;
for (size_t i = 0; i < tal_count(tags); i++) {
e->stealable |= tags[i] == MVT_STEALABLE;
e->splice_close |= tags[i] == MVT_SPLICE;
e->foreign |= tags[i] == MVT_FOREIGN;
}
/* For tests, we log these harder! */
if (e->foreign)
plugin_log(cmd->plugin, LOG_DBG,
"Foreign chain event: %s (%s) %s -%s %"PRIu64" %d %s %s",
e->tag, acct_name,
fmt_amount_msat(tmpctx, e->credit),
fmt_amount_msat(tmpctx, e->debit),
e->timestamp, e->blockheight,
fmt_bitcoin_outpoint(tmpctx, &e->outpoint),
e->spending_txid ? fmt_bitcoin_txid(tmpctx, e->spending_txid) : "");
db_begin_transaction(bkpr->db);
/* FIXME: lookup the peer id for this channel! */
acct = find_or_create_account(cmd, bkpr, acct_name);
@@ -1574,9 +1587,10 @@ parse_and_log_chain_move(struct command *cmd,
/* If this is a channel account event, it's possible
* that we *never* got the open event. (This happens
* if you add the plugin *after* you've closed the channel) */
if ((!acct->open_event_db_id && is_channel_account(acct->name))
if (!e->foreign
&& ((!acct->open_event_db_id && is_channel_account(acct->name))
|| (orig_acct && is_channel_account(orig_acct->name)
&& !orig_acct->open_event_db_id)) {
&& !orig_acct->open_event_db_id))) {
/* Find the channel open info for this peer */
struct out_req *req;
struct event_info *info;
@@ -1729,14 +1743,26 @@ static bool json_to_tok(const char *buffer, const jsmntok_t *tok, const jsmntok_
return true;
}
static struct command_result *inject_done(struct command *notif_cmd,
const char *methodname,
const char *buf,
const jsmntok_t *result,
void *unused)
{
return notification_handled(notif_cmd);
}
/* FIXME: Deprecate */
static struct command_result *json_utxo_deposit(struct command *cmd, const char *buf, const jsmntok_t *params)
{
const char *move_tag ="utxo_deposit";
struct chain_event *ev = tal(cmd, struct chain_event);
struct account *acct;
const char *err;
struct bkpr *bkpr = bkpr_of(cmd->plugin);
const char *acct_name, *origin_acct;
struct amount_msat amount;
struct bitcoin_outpoint outpoint;
u64 timestamp;
u32 blockheight;
const jsmntok_t *transfer_from;
const char *err;
struct out_req *req;
transfer_from = NULL;
err = json_scan(tmpctx, buf, params,
@@ -1748,71 +1774,49 @@ static struct command_result *json_utxo_deposit(struct command *cmd, const char
",timestamp:%"
",blockheight:%"
"}}",
JSON_SCAN_TAL(ev, json_strdup, &ev->acct_name),
JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name),
JSON_SCAN(json_to_tok, &transfer_from),
JSON_SCAN(json_to_outpoint, &ev->outpoint),
JSON_SCAN(json_to_msat, &ev->credit),
JSON_SCAN(json_to_u64, &ev->timestamp),
JSON_SCAN(json_to_u32, &ev->blockheight));
JSON_SCAN(json_to_outpoint, &outpoint),
JSON_SCAN(json_to_msat, &amount),
JSON_SCAN(json_to_u64, &timestamp),
JSON_SCAN(json_to_u32, &blockheight));
if (err)
plugin_err(cmd->plugin,
"`%s` parameters did not scan %s: %.*s",
move_tag, err, json_tok_full_len(params),
"`utxo_deposit` parameters did not scan %s: %.*s",
err, json_tok_full_len(params),
json_tok_full(buf, params));
if (!transfer_from || json_tok_is_null(buf, transfer_from))
ev->origin_acct = NULL;
origin_acct = NULL;
else
ev->origin_acct = json_strdup(ev, buf, transfer_from);
origin_acct = json_strdup(tmpctx, buf, transfer_from);
/* Log the thing */
db_begin_transaction(bkpr->db);
acct = find_or_create_account(cmd, bkpr, ev->acct_name);
ev->tag = "deposit";
ev->stealable = false;
ev->splice_close = false;
ev->debit = AMOUNT_MSAT(0);
ev->output_value = ev->credit;
ev->spending_txid = NULL;
ev->payment_id = NULL;
ev->splice_close = false;
plugin_log(cmd->plugin, LOG_DBG, "%s (%s|%s) %s -%s %"PRIu64" %d %s",
move_tag, ev->tag, ev->acct_name,
fmt_amount_msat(tmpctx, ev->credit),
fmt_amount_msat(tmpctx, ev->debit),
ev->timestamp, ev->blockheight,
fmt_bitcoin_outpoint(tmpctx, &ev->outpoint));
if (!log_chain_event(bkpr, acct, ev)) {
db_commit_transaction(bkpr->db);
/* This is not a new event, do nothing */
return notification_handled(cmd);
}
/* Can we calculate any onchain fees now? */
err = maybe_update_onchain_fees(cmd, cmd, bkpr, &ev->outpoint.txid);
db_commit_transaction(bkpr->db);
if (err)
plugin_err(cmd->plugin,
"Unable to update onchain fees %s",
err);
/* FIXME: do account close checks, when allow onchain close to externals? */
return notification_handled(cmd);;
req = jsonrpc_request_start(cmd, "injectutxodeposit",
inject_done,
plugin_broken_cb,
NULL);
json_add_string(req->js, "account", acct_name);
if (origin_acct)
json_add_string(req->js, "transfer_from", origin_acct);
json_add_outpoint(req->js, "outpoint", &outpoint);
json_add_amount_msat(req->js, "amount_msat", amount);
json_add_u64(req->js, "timestamp", timestamp);
json_add_u32(req->js, "blockheight", blockheight);
return send_outreq(req);
}
static struct command_result *json_utxo_spend(struct command *cmd, const char *buf, const jsmntok_t *params)
{
const char *move_tag ="utxo_spend";
struct account *acct;
struct chain_event *ev = tal(cmd, struct chain_event);
const char *err, *acct_name;
struct bkpr *bkpr = bkpr_of(cmd->plugin);
const char *acct_name;
struct amount_msat amount;
struct bitcoin_txid spending_txid;
struct bitcoin_outpoint outpoint;
u64 timestamp;
u32 blockheight;
const char *err;
struct out_req *req;
ev->spending_txid = tal(ev, struct bitcoin_txid);
err = json_scan(tmpctx, buf, params,
"{utxo_spend:{"
"account:%"
@@ -1823,69 +1827,29 @@ static struct command_result *json_utxo_spend(struct command *cmd, const char *b
",blockheight:%"
"}}",
JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name),
JSON_SCAN(json_to_outpoint, &ev->outpoint),
JSON_SCAN(json_to_txid, ev->spending_txid),
JSON_SCAN(json_to_msat, &ev->debit),
JSON_SCAN(json_to_u64, &ev->timestamp),
JSON_SCAN(json_to_u32, &ev->blockheight));
JSON_SCAN(json_to_outpoint, &outpoint),
JSON_SCAN(json_to_txid, &spending_txid),
JSON_SCAN(json_to_msat, &amount),
JSON_SCAN(json_to_u64, &timestamp),
JSON_SCAN(json_to_u32, &blockheight));
if (err)
plugin_err(cmd->plugin,
"`%s` parameters did not scan %s: %.*s",
move_tag, err, json_tok_full_len(params),
"`utxo_spend` parameters did not scan %s: %.*s",
err, json_tok_full_len(params),
json_tok_full(buf, params));
/* Log the thing */
db_begin_transaction(bkpr->db);
acct = find_or_create_account(cmd, bkpr, acct_name);
ev->origin_acct = NULL;
ev->tag = "withdrawal";
ev->stealable = false;
ev->splice_close = false;
ev->credit = AMOUNT_MSAT(0);
ev->output_value = ev->debit;
ev->payment_id = NULL;
plugin_log(cmd->plugin, LOG_DBG, "%s (%s|%s) %s -%s %"PRIu64" %d %s %s",
move_tag, ev->tag, acct_name,
fmt_amount_msat(tmpctx, ev->credit),
fmt_amount_msat(tmpctx, ev->debit),
ev->timestamp, ev->blockheight,
fmt_bitcoin_outpoint(tmpctx, &ev->outpoint),
fmt_bitcoin_txid(tmpctx, ev->spending_txid));
if (!log_chain_event(bkpr, acct, ev)) {
db_commit_transaction(bkpr->db);
/* This is not a new event, do nothing */
return notification_handled(cmd);
}
err = maybe_update_onchain_fees(cmd, cmd, bkpr, ev->spending_txid);
if (err) {
db_commit_transaction(bkpr->db);
plugin_err(cmd->plugin,
"Unable to update onchain fees %s",
err);
}
err = maybe_update_onchain_fees(cmd, cmd, bkpr, &ev->outpoint.txid);
if (err) {
db_commit_transaction(bkpr->db);
plugin_err(cmd->plugin,
"Unable to update onchain fees %s",
err);
}
/* Go see if there's any deposits to an external
* that are now confirmed */
/* FIXME: might need updating when we can splice? */
maybe_closeout_external_deposits(cmd, bkpr, ev->spending_txid,
ev->blockheight);
db_commit_transaction(bkpr->db);
/* FIXME: do account close checks, when allow onchain close to externals? */
return notification_handled(cmd);;
req = jsonrpc_request_start(cmd, "injectutxospend",
inject_done,
plugin_broken_cb,
NULL);
json_add_string(req->js, "account", acct_name);
json_add_outpoint(req->js, "outpoint", &outpoint);
json_add_txid(req->js, "spending_txid", &spending_txid);
json_add_amount_msat(req->js, "amount_msat", amount);
json_add_u64(req->js, "timestamp", timestamp);
json_add_u32(req->js, "blockheight", blockheight);
return send_outreq(req);
}
static struct command_result *json_coin_moved(struct command *cmd,

View File

@@ -33,6 +33,9 @@ struct chain_event {
* confirmation? */
bool splice_close;
/* Injected? */
bool foreign;
/* Amount we received in this event */
struct amount_msat credit;

View File

@@ -909,9 +909,9 @@ def test_bookkeeper_custom_notifs(node_factory, chainparams):
acct = "nifty's secret stash"
l1.rpc.senddeposit(acct, False, outpoint_in, amount)
l1.daemon.wait_for_log(r"Foreign chain event: deposit \(nifty's secret stash\) 180000000msat -0msat 1679955976 111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0")
l1.rpc.sendspend(acct, outpoint_in, spend_txid, amount)
l1.daemon.wait_for_log(r"utxo_deposit \(deposit|nifty's secret stash\) .* -0msat 1679955976 111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0")
l1.daemon.wait_for_log(r"utxo_spend \(withdrawal|nifty's secret stash\) 0msat -12345678000msat 1679955976 111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
l1.daemon.wait_for_log(r"Foreign chain event: withdrawal \(nifty's secret stash\) 0msat -180000000msat 1679955976 111 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
# balance should be zero
bals = l1.rpc.bkpr_listbalances()['accounts']
@@ -921,7 +921,7 @@ def test_bookkeeper_custom_notifs(node_factory, chainparams):
assert only_one(bal['balances'])['balance_msat'] == Millisatoshi(0)
l1.rpc.senddeposit(acct, False, change_deposit, amount - withdraw_amt - fee)
l1.daemon.wait_for_log(r"utxo_deposit \(deposit|nifty's secret stash\) .* -0msat 1679955976 111 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:0")
l1.daemon.wait_for_log(r"Foreign chain event: deposit \(nifty's secret stash\) .* -0msat 1679955976 111 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:0")
# balance should be equal to amount
events = l1.rpc.bkpr_listaccountevents(acct)['events']
@@ -933,7 +933,7 @@ def test_bookkeeper_custom_notifs(node_factory, chainparams):
assert onchain_fee_one == fee + withdraw_amt
l1.rpc.senddeposit(acct, True, external_deposit, withdraw_amt)
l1.daemon.wait_for_log(r"utxo_deposit \(deposit|external\) .* -0msat 1679955976 111 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:1")
l1.daemon.wait_for_log(r"Foreign chain event: deposit \(external\) .* -0msat 1679955976 111 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb:1")
events = l1.rpc.bkpr_listaccountevents(acct)['events']
onchain_fees = [x for x in events if x['type'] == 'onchain_fee']
assert len(onchain_fees) == 2