From e8d10edbe5c26a817831e150d1dd8b6c46f31919 Mon Sep 17 00:00:00 2001 From: lisa neigut Date: Wed, 1 Apr 2020 21:16:32 -0500 Subject: [PATCH] coin moves: record onchain movements after they've been resolved We pass them back to lightningd who sends out a notification for them --- lightningd/onchain_control.c | 22 +++++ onchaind/Makefile | 1 + onchaind/onchain_wire.csv | 3 + onchaind/onchaind.c | 133 ++++++++++++++++++++++++++ onchaind/test/run-grind_feerate-bug.c | 15 +++ onchaind/test/run-grind_feerate.c | 15 +++ 6 files changed, 189 insertions(+) diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 92f25451a..1172d5493 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -178,6 +178,24 @@ static void watch_tx_and_outputs(struct channel *channel, onchain_txo_watched); } +static void handle_onchain_log_coin_move(struct channel *channel, const u8 *msg) +{ + struct channel_id channel_id; + struct chain_coin_mvt *mvt = tal(NULL, struct chain_coin_mvt); + + if (!fromwire_onchain_notify_coin_mvt(msg, mvt)) { + channel_internal_error(channel, "Invalid onchain notify_coin_mvt"); + return; + } + + derive_channel_id(&channel_id, &channel->funding_txid, + channel->funding_outnum); + mvt->account_name = + type_to_string(mvt, struct channel_id, &channel_id); + notify_chain_mvt(channel->peer->ld, mvt); + tal_free(mvt); +} + static void handle_onchain_broadcast_tx(struct channel *channel, const u8 *msg) { struct bitcoin_tx *tx; @@ -384,6 +402,10 @@ static unsigned int onchain_msg(struct subd *sd, const u8 *msg, const int *fds U onchain_annotate_txout(sd->channel, msg); break; + case WIRE_ONCHAIN_NOTIFY_COIN_MVT: + handle_onchain_log_coin_move(sd->channel, msg); + break; + /* We send these, not receive them */ case WIRE_ONCHAIN_INIT: case WIRE_ONCHAIN_SPENT: diff --git a/onchaind/Makefile b/onchaind/Makefile index 09f5feb62..3985cca6e 100644 --- a/onchaind/Makefile +++ b/onchaind/Makefile @@ -50,6 +50,7 @@ ONCHAIND_COMMON_OBJS := \ common/amount.o \ common/bigsize.o \ common/bip32.o \ + common/coin_mvt.o \ common/daemon.o \ common/daemon_conn.o \ common/derive_basepoints.o \ diff --git a/onchaind/onchain_wire.csv b/onchaind/onchain_wire.csv index 298646d5f..51e564587 100644 --- a/onchaind/onchain_wire.csv +++ b/onchaind/onchain_wire.csv @@ -1,3 +1,4 @@ +#include #include #include #include @@ -120,3 +121,5 @@ msgdata,onchain_annotate_txin,txid,bitcoin_txid, msgdata,onchain_annotate_txin,innum,u32, msgdata,onchain_annotate_txin,type,enum wallet_tx_type, +msgtype,onchain_notify_coin_mvt,5037 +msgdata,onchain_notify_coin_mvt,mvt,chain_coin_mvt, diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 007caa3a1..94c8a9d6b 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,136 @@ struct tracked_output { struct sha256 *payment_hash; }; +static void send_coin_mvt(struct chain_coin_mvt *mvt TAKES) +{ + wire_sync_write(REQ_FD, + take(towire_onchain_notify_coin_mvt(NULL, mvt))); + + if (taken(mvt)) + tal_free(mvt); +} + +static void record_htlc_fulfilled(const struct bitcoin_txid *txid, + struct tracked_output *out, + bool we_fulfilled) +{ + struct chain_coin_mvt *mvt; + + /* we're recording the *deposit* of a utxo which contained channel + * funds (htlc). + * + * since we really don't know if this was a 'routed' or 'destination' + * htlc here, we record it as a 'deposit/withdrawal' type */ + mvt = new_chain_coin_mvt_sat(NULL, NULL, + txid, &out->txid, + out->outnum, + out->payment_hash, + ONCHAIN_HTLC, + out->sat, we_fulfilled, + BTC); + + send_coin_mvt(take(mvt)); +} + +static void update_ledger_chain_fees(const struct bitcoin_txid *txid, + struct amount_sat fees) +{ + struct chain_coin_mvt *mvt; + mvt = new_chain_coin_mvt_sat(NULL, NULL, + txid, NULL, 0, NULL, + CHAIN_FEES, fees, + false, BTC); + + if (!mvt) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "unable to convert %s to msat", + type_to_string(tmpctx, struct amount_sat, + &fees)); + send_coin_mvt(take(mvt)); +} + +/* Log the fees paid on this transaction as 'chain fees'. note that + * you *cannot* pass a chaintopology-originated tx to this method, + * as they don't have the input_amounts populated */ +static struct amount_sat record_chain_fees_tx(const struct bitcoin_txid *txid, + const struct bitcoin_tx *tx) +{ + struct amount_sat fees; + fees = bitcoin_tx_compute_fee(tx); + status_debug("recording chain fees for tx %s", + type_to_string(tmpctx, struct bitcoin_txid, txid)); + update_ledger_chain_fees(txid, fees); + return fees; +} + +static void record_channel_withdrawal_minus_fees(const struct bitcoin_txid *tx_txid, + struct tracked_output *out, + struct amount_sat fees) +{ + struct chain_coin_mvt *mvt; + struct amount_sat emitted_amt; + + if (!amount_sat_sub(&emitted_amt, out->sat, fees)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "unable to subtract %s from %s", + type_to_string(tmpctx, struct amount_sat, + &fees), + type_to_string(tmpctx, struct amount_sat, + &out->sat)); + + mvt = new_chain_coin_mvt_sat(NULL, NULL, + tx_txid, &out->txid, + out->outnum, NULL, WITHDRAWAL, + emitted_amt, false, + BTC); + + if (!mvt) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "unable to convert %s to msat", + type_to_string(tmpctx, struct amount_sat, + &out->sat)); + + send_coin_mvt(take(mvt)); +} + + +static bool is_our_htlc_tx(struct tracked_output *out) +{ + return out->resolved && + (out->resolved->tx_type == OUR_HTLC_TIMEOUT_TX + || out->resolved->tx_type == OUR_HTLC_SUCCESS_TX); +} + +static bool is_channel_deposit(struct tracked_output *out) +{ + return out->resolved && + (out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US + || out->resolved->tx_type == OUR_HTLC_SUCCESS_TX); +} + +static void record_coin_movements(struct tracked_output *out, + const struct bitcoin_tx *tx, + const struct bitcoin_txid *txid) +{ + struct amount_sat fees; + /* there is a case where we've fulfilled an htlc onchain, + * in which case we log a deposit to the channel */ + if (is_channel_deposit(out)) + record_htlc_fulfilled(txid, out, true); + + + /* record fees paid for the tx here */ + /* FIXME: for now, every resolution generates its own tx, + * this will need to be updated if we switch to batching */ + fees = record_chain_fees_tx(txid, tx); + + /* we don't record a channel withdrawal until we get to + * the 'exit' utxo, which for local commitment htlc txs + * is the child htlc_tx's output */ + if (!is_our_htlc_tx(out)) + record_channel_withdrawal_minus_fees(txid, out, fees); +} + /* We vary feerate until signature they offered matches. */ static bool grind_htlc_tx_fee(struct amount_sat *fee, struct bitcoin_tx *tx, @@ -1064,6 +1195,8 @@ static void output_spent(const struct chainparams *chainparams, || out->resolved->tx_type == OUR_HTLC_TIMEOUT_TX) resolve_htlc_tx(chainparams, outs, i, tx, &txid, tx_blockheight); + + record_coin_movements(out, out->proposal->tx, &txid); return; } diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index bc26d2b09..bae0c2031 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -97,6 +97,18 @@ void memleak_remove_referenced(struct htable *memtable UNNEEDED, const void *roo void memleak_scan_region(struct htable *memtable UNNEEDED, const void *p UNNEEDED, size_t bytelen UNNEEDED) { fprintf(stderr, "memleak_scan_region called!\n"); abort(); } +/* Generated stub for new_chain_coin_mvt_sat */ +struct chain_coin_mvt *new_chain_coin_mvt_sat(const tal_t *ctx UNNEEDED, + const char *account_name UNNEEDED, + const struct bitcoin_txid *tx_txid UNNEEDED, + const struct bitcoin_txid *output_txid UNNEEDED, + u32 vout UNNEEDED, + struct sha256 *payment_hash UNNEEDED, + enum mvt_tag tag UNNEEDED, + struct amount_sat amt_sat UNNEEDED, + bool is_credit UNNEEDED, + enum mvt_unit_type unit UNNEEDED) +{ fprintf(stderr, "new_chain_coin_mvt_sat called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } @@ -168,6 +180,9 @@ u8 *towire_onchain_init_reply(const tal_t *ctx UNNEEDED) /* Generated stub for towire_onchain_missing_htlc_output */ u8 *towire_onchain_missing_htlc_output(const tal_t *ctx UNNEEDED, const struct htlc_stub *htlc UNNEEDED) { fprintf(stderr, "towire_onchain_missing_htlc_output called!\n"); abort(); } +/* Generated stub for towire_onchain_notify_coin_mvt */ +u8 *towire_onchain_notify_coin_mvt(const tal_t *ctx UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) +{ fprintf(stderr, "towire_onchain_notify_coin_mvt called!\n"); abort(); } /* Generated stub for towire_onchain_unwatch_tx */ u8 *towire_onchain_unwatch_tx(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "towire_onchain_unwatch_tx called!\n"); abort(); } diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 8d11c1549..0f2a322de 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -109,6 +109,18 @@ void memleak_remove_referenced(struct htable *memtable UNNEEDED, const void *roo void memleak_scan_region(struct htable *memtable UNNEEDED, const void *p UNNEEDED, size_t bytelen UNNEEDED) { fprintf(stderr, "memleak_scan_region called!\n"); abort(); } +/* Generated stub for new_chain_coin_mvt_sat */ +struct chain_coin_mvt *new_chain_coin_mvt_sat(const tal_t *ctx UNNEEDED, + const char *account_name UNNEEDED, + const struct bitcoin_txid *tx_txid UNNEEDED, + const struct bitcoin_txid *output_txid UNNEEDED, + u32 vout UNNEEDED, + struct sha256 *payment_hash UNNEEDED, + enum mvt_tag tag UNNEEDED, + struct amount_sat amt_sat UNNEEDED, + bool is_credit UNNEEDED, + enum mvt_unit_type unit UNNEEDED) +{ fprintf(stderr, "new_chain_coin_mvt_sat called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } @@ -186,6 +198,9 @@ u8 *towire_onchain_init_reply(const tal_t *ctx UNNEEDED) /* Generated stub for towire_onchain_missing_htlc_output */ u8 *towire_onchain_missing_htlc_output(const tal_t *ctx UNNEEDED, const struct htlc_stub *htlc UNNEEDED) { fprintf(stderr, "towire_onchain_missing_htlc_output called!\n"); abort(); } +/* Generated stub for towire_onchain_notify_coin_mvt */ +u8 *towire_onchain_notify_coin_mvt(const tal_t *ctx UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) +{ fprintf(stderr, "towire_onchain_notify_coin_mvt called!\n"); abort(); } /* Generated stub for towire_onchain_unwatch_tx */ u8 *towire_onchain_unwatch_tx(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "towire_onchain_unwatch_tx called!\n"); abort(); }