Skip to content

Commit

Permalink
coin_mvt/bkpr: add "stealable" tag to stealable outputs
Browse files Browse the repository at this point in the history
If we expect further events for an onchain output (because we can steal
it away from the 'external'/rightful owner), we mark them.

This prevents us from marking a channel as 'onchain-resolved' before
all events that we're interested in have actually hit the chain.

Case that this matters:
Peer publishes a (cheating) unilateral close and a timeout htlc (which
we can steal).
We then steal the timeout htlc.

W/o the stealable flag, we'd have marked the channel as resolved when
the peer published the timeout htlc, which is incorrect as we're still
waiting for the resolution of that timeout htlc (b/c we *can* steal it).
  • Loading branch information
niftynei authored and rustyrussell committed Jul 28, 2022
1 parent c1cef77 commit 0617690
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 34 deletions.
35 changes: 30 additions & 5 deletions common/coin_mvt.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static const char *mvt_tags[] = {
"opener",
"lease_fee",
"leased",
"stealable",
};

const char *mvt_tag_str(enum mvt_tag tag)
Expand Down Expand Up @@ -88,7 +89,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct sha256 *payment_hash TAKES,
u32 blockheight,
enum mvt_tag *tags TAKES,
enum mvt_tag *tags,
struct amount_msat amount,
bool is_credit,
struct amount_sat output_val,
Expand Down Expand Up @@ -252,19 +253,43 @@ struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx,
amount, true);
}

struct chain_coin_mvt *new_coin_external_spend_tags(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *txid,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag *tags TAKES)
{
return new_chain_coin_mvt(ctx, EXTERNAL, txid,
outpoint, NULL, blockheight,
take(tags),
AMOUNT_MSAT(0), true, amount, 0);
}

struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *txid,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag)
{
return new_chain_coin_mvt(ctx, EXTERNAL, txid,
outpoint, NULL, blockheight,
take(new_tag_arr(NULL, tag)),
AMOUNT_MSAT(0), true, amount, 0);
return new_coin_external_spend_tags(ctx, outpoint,
txid, blockheight, amount,
new_tag_arr(NULL, tag));
}

struct chain_coin_mvt *new_coin_external_deposit_tags(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag *tags TAKES)
{
return new_chain_coin_mvt_sat(ctx, EXTERNAL, NULL, outpoint, NULL,
blockheight, take(tags),
amount, true);
}


struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
Expand Down
18 changes: 17 additions & 1 deletion common/coin_mvt.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ enum mvt_type {
CHANNEL_MVT = 1,
};

#define NUM_MVT_TAGS (LEASED + 1)
#define NUM_MVT_TAGS (STEALABLE + 1)
enum mvt_tag {
DEPOSIT = 0,
WITHDRAWAL = 1,
Expand All @@ -38,6 +38,7 @@ enum mvt_tag {
OPENER = 19,
LEASE_FEE = 20,
LEASED = 21,
STEALABLE = 22,
};

struct channel_coin_mvt {
Expand Down Expand Up @@ -234,6 +235,14 @@ struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx,
enum mvt_tag tag)
NON_NULL_ARGS(2, 3);

struct chain_coin_mvt *new_coin_external_spend_tags(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *txid,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag *tags)
NON_NULL_ARGS(2, 3);

struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *txid,
Expand All @@ -242,6 +251,13 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx,
enum mvt_tag tag)
NON_NULL_ARGS(2, 3);

struct chain_coin_mvt *new_coin_external_deposit_tags(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag *tags)
NON_NULL_ARGS(2, 5);

struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
Expand Down
6 changes: 2 additions & 4 deletions common/test/run-bolt12_merkle-json.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED)
char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_strdup called!\n"); abort(); }
/* Generated stub for json_to_u32 */
bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
uint32_t *num UNNEEDED)
bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u32 *num UNNEEDED)
{ fprintf(stderr, "json_to_u32 called!\n"); abort(); }
/* Generated stub for json_to_u64 */
bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
uint64_t *num UNNEEDED)
bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED)
{ fprintf(stderr, "json_to_u64 called!\n"); abort(); }
/* Generated stub for json_tok_full */
const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED)
Expand Down
3 changes: 2 additions & 1 deletion lightningd/onchain_control.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "config.h"
#include <bitcoin/feerate.h>
#include <ccan/tal/str/str.h>
#include <common/key_derive.h>
#include <common/type_to_string.h>
#include <db/exec.h>
Expand Down Expand Up @@ -640,7 +641,7 @@ enum watch_result onchaind_funding_spent(struct channel *channel,
channel->state,
FUNDING_SPEND_SEEN,
reason,
"Onchain funding spend");
tal_fmt(tmpctx, "Onchain funding spend"));

hsmfd = hsm_get_client_fd(ld, &channel->peer->id,
channel->dbid,
Expand Down
75 changes: 58 additions & 17 deletions onchaind/onchaind.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ static void record_external_spend(const struct bitcoin_txid *txid,
out->sat, tag)));
}

static void record_external_spend_tags(const struct bitcoin_txid *txid,
struct tracked_output *out,
u32 blockheight,
enum mvt_tag *tags TAKES)
{
send_coin_mvt(take(new_coin_external_spend_tags(NULL, &out->outpoint,
txid, blockheight,
out->sat, tags)));
}

static void record_external_output(const struct bitcoin_outpoint *out,
struct amount_sat amount,
u32 blockheight,
Expand All @@ -251,6 +261,15 @@ static void record_external_deposit(const struct tracked_output *out,
record_external_output(&out->outpoint, out->sat, blockheight, tag);
}

static void record_external_deposit_tags(const struct tracked_output *out,
u32 blockheight,
enum mvt_tag *tags TAKES)
{
send_coin_mvt(take(new_coin_external_deposit_tags(NULL, &out->outpoint,
blockheight, out->sat,
tags)));
}

static void record_mutual_close(const struct tx_parts *tx,
const u8 *remote_scriptpubkey,
u32 blockheight)
Expand Down Expand Up @@ -358,32 +377,36 @@ static void record_coin_movements(struct tracked_output *out,
* AND so we can accurately calculate our on-chain fee burden */
if (out->tx_type == OUR_HTLC_TIMEOUT_TX
|| out->tx_type == OUR_HTLC_SUCCESS_TX)
record_channel_deposit(out, blockheight, HTLC_TX);
record_channel_deposit(out, out->tx_blockheight, HTLC_TX);

if (out->resolved->tx_type == OUR_HTLC_TIMEOUT_TO_US)
record_channel_deposit(out, blockheight, HTLC_TIMEOUT);
record_channel_deposit(out, out->tx_blockheight, HTLC_TIMEOUT);

/* there is a case where we've fulfilled an htlc onchain,
* in which case we log a deposit to the channel */
if (out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US
|| out->resolved->tx_type == OUR_HTLC_SUCCESS_TX)
record_to_us_htlc_fulfilled(out, blockheight);
record_to_us_htlc_fulfilled(out, out->tx_blockheight);

/* If it's our to-us and our close, we publish *another* tx
* which spends the output when the timeout ends */
if (out->tx_type == OUR_UNILATERAL) {
if (out->output_type == DELAYED_OUTPUT_TO_US)
record_channel_deposit(out, blockheight, CHANNEL_TO_US);
record_channel_deposit(out, out->tx_blockheight,
CHANNEL_TO_US);
else if (out->output_type == OUR_HTLC) {
record_channel_deposit(out, blockheight, HTLC_TIMEOUT);
record_channel_withdrawal(txid, out, blockheight, HTLC_TIMEOUT);
record_channel_deposit(out, out->tx_blockheight,
HTLC_TIMEOUT);
record_channel_withdrawal(txid, out, blockheight,
HTLC_TIMEOUT);
} else if (out->output_type == THEIR_HTLC)
record_channel_withdrawal(txid, out, blockheight, HTLC_FULFILL);
record_channel_withdrawal(txid, out, blockheight,
HTLC_FULFILL);
}

if (out->tx_type == THEIR_REVOKED_UNILATERAL
|| out->resolved->tx_type == OUR_PENALTY_TX)
record_channel_deposit(out, blockheight, PENALTY);
record_channel_deposit(out, out->tx_blockheight, PENALTY);

if (out->resolved->tx_type == OUR_DELAYED_RETURN_TO_WALLET
|| out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US
Expand Down Expand Up @@ -1726,15 +1749,25 @@ static void output_spent(struct tracked_output ***outs,
case OUTPUT_TO_US:
case DELAYED_OUTPUT_TO_US:
unknown_spend(out, tx_parts);
record_external_deposit(out, tx_blockheight, PENALIZED);
record_external_deposit(out, out->tx_blockheight,
PENALIZED);
break;

case THEIR_HTLC:
if (out->tx_type == THEIR_REVOKED_UNILATERAL) {
record_external_deposit(out, out->tx_blockheight,
HTLC_TIMEOUT);
record_external_spend(&tx_parts->txid, out,
tx_blockheight, HTLC_TIMEOUT);
enum mvt_tag *tags;
tags = new_tag_arr(NULL, HTLC_TIMEOUT);
tal_arr_expand(&tags, STEALABLE);

record_external_deposit_tags(out, out->tx_blockheight,
/* This takes tags */
tal_dup_talarr(NULL,
enum mvt_tag,
tags));
record_external_spend_tags(&tx_parts->txid,
out,
tx_blockheight,
tags);

/* we've actually got a 'new' output here */
steal_htlc_tx(out, outs, tx_parts,
Expand Down Expand Up @@ -1768,16 +1801,24 @@ static void output_spent(struct tracked_output ***outs,
handle_htlc_onchain_fulfill(out, tx_parts,
&htlc_outpoint);

record_to_them_htlc_fulfilled(out, tx_blockheight);
record_external_spend(&tx_parts->txid, out,
tx_blockheight, HTLC_FULFILL);
record_to_them_htlc_fulfilled(out, out->tx_blockheight);

if (out->tx_type == THEIR_REVOKED_UNILATERAL) {
enum mvt_tag *tags = new_tag_arr(NULL,
HTLC_FULFILL);
tal_arr_expand(&tags, STEALABLE);
record_external_spend_tags(&tx_parts->txid,
out,
tx_blockheight,
tags);
steal_htlc_tx(out, outs, tx_parts,
tx_blockheight,
OUR_HTLC_FULFILL_TO_THEM,
&htlc_outpoint);
} else {
record_external_spend(&tx_parts->txid, out,
tx_blockheight,
HTLC_FULFILL);
/* BOLT #5:
*
* ## HTLC Output Handling: Local Commitment,
Expand Down Expand Up @@ -1806,7 +1847,7 @@ static void output_spent(struct tracked_output ***outs,
resolved_by_other(out, &tx_parts->txid,
THEIR_DELAYED_CHEAT);

record_external_deposit(out, tx_blockheight, STOLEN);
record_external_deposit(out, out->tx_blockheight, STOLEN);
break;
/* Um, we don't track these! */
case OUTPUT_TO_THEM:
Expand Down
17 changes: 17 additions & 0 deletions onchaind/test/run-grind_feerate-bug.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED,
enum mvt_tag tag)

{ fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); }
/* Generated stub for new_coin_external_deposit_tags */
struct chain_coin_mvt *new_coin_external_deposit_tags(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag *tags)

{ fprintf(stderr, "new_coin_external_deposit_tags called!\n"); abort(); }
/* Generated stub for new_coin_external_spend */
struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
Expand All @@ -140,6 +148,15 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED,
enum mvt_tag tag)

{ fprintf(stderr, "new_coin_external_spend called!\n"); abort(); }
/* Generated stub for new_coin_external_spend_tags */
struct chain_coin_mvt *new_coin_external_spend_tags(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag *tags)

{ fprintf(stderr, "new_coin_external_spend_tags called!\n"); abort(); }
/* Generated stub for new_coin_wallet_deposit_tagged */
struct chain_coin_mvt *new_coin_wallet_deposit_tagged(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
Expand Down
17 changes: 17 additions & 0 deletions onchaind/test/run-grind_feerate.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED,
enum mvt_tag tag)

{ fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); }
/* Generated stub for new_coin_external_deposit_tags */
struct chain_coin_mvt *new_coin_external_deposit_tags(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag *tags)

{ fprintf(stderr, "new_coin_external_deposit_tags called!\n"); abort(); }
/* Generated stub for new_coin_external_spend */
struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
Expand All @@ -163,6 +171,15 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED,
enum mvt_tag tag)

{ fprintf(stderr, "new_coin_external_spend called!\n"); abort(); }
/* Generated stub for new_coin_external_spend_tags */
struct chain_coin_mvt *new_coin_external_spend_tags(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag *tags)

{ fprintf(stderr, "new_coin_external_spend_tags called!\n"); abort(); }
/* Generated stub for new_coin_wallet_deposit_tagged */
struct chain_coin_mvt *new_coin_wallet_deposit_tagged(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
Expand Down
7 changes: 6 additions & 1 deletion plugins/bkpr/bookkeeper.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ static bool new_missed_channel_account(struct command *cmd,
chain_ev->spending_txid = NULL;
chain_ev->payment_id = NULL;
chain_ev->ignored = false;
chain_ev->stealable = false;

/* Update the account info too */
tags = tal_arr(chain_ev, enum mvt_tag, 1);
Expand Down Expand Up @@ -1234,8 +1235,12 @@ parse_and_log_chain_move(struct command *cmd,
e->tag = mvt_tag_str(tags[0]);

e->ignored = false;
for (size_t i = 0; i < tal_count(tags); i++)
e->stealable = false;
for (size_t i = 0; i < tal_count(tags); i++) {
e->ignored |= tags[i] == IGNORED;
e->stealable |= tags[i] == STEALABLE;
}


db_begin_transaction(db);
acct = find_account(cmd, db, acct_name);
Expand Down
4 changes: 4 additions & 0 deletions plugins/bkpr/chain_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ struct chain_event {
/* Is the node's wallet ignoring this? */
bool ignored;

/* Is this chain output stealable? If so
* we'll need to watch it for longer */
bool stealable;

/* Amount we received in this event */
struct amount_msat credit;

Expand Down
1 change: 1 addition & 0 deletions plugins/bkpr/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ static struct migration db_migrations[] = {
{SQL("ALTER TABLE chain_events ADD origin TEXT;"), NULL},
{SQL("ALTER TABLE accounts ADD closed_count INTEGER DEFAULT 0;"), NULL},
{SQL("ALTER TABLE chain_events ADD ignored INTEGER;"), NULL},
{SQL("ALTER TABLE chain_events ADD stealable INTEGER;"), NULL},
};

static bool db_migrate(struct plugin *p, struct db *db)
Expand Down
Loading

0 comments on commit 0617690

Please sign in to comment.