diff --git a/lightningd/commit_tx.c b/lightningd/commit_tx.c index f522413bd..8bea76842 100644 --- a/lightningd/commit_tx.c +++ b/lightningd/commit_tx.c @@ -73,13 +73,11 @@ u64 htlc_success_fee(u64 feerate_per_kw) return feerate_per_kw * 673 / 1000; } -static const struct htlc **untrimmed(const tal_t *ctx, - const struct htlc **htlcs, - enum side side, - u64 htlc_fee, u64 dust_limit_satoshis) +static bool trim(const struct htlc *htlc, + u64 feerate_per_kw, u64 dust_limit_satoshis, + enum side side) { - const struct htlc **arr; - size_t i, n; + u64 htlc_fee; /* BOLT #3: * @@ -88,6 +86,10 @@ static const struct htlc **untrimmed(const tal_t *ctx, * owner, the commitment transaction MUST NOT contain that output, * otherwise it MUST be generated as specified in [Offered HTLC * Outputs](#offered-htlc-outputs). + */ + if (htlc_owner(htlc) == side) + htlc_fee = htlc_timeout_fee(feerate_per_kw); + /* BOLT #3: * * For every received HTLC, if the HTLC amount minus the HTLC-success * fee would be less than `dust-limit-satoshis` set by the transaction @@ -95,22 +97,25 @@ static const struct htlc **untrimmed(const tal_t *ctx, * otherwise it MUST be generated as specified in [Received HTLC * Outputs](#received-htlc-outputs). */ - arr = tal_arr(ctx, const struct htlc *, tal_count(htlcs)); - for (i = n = 0; i < tal_count(htlcs); i++) { - if (htlc_owner(htlcs[i]) != side) - continue; - if (htlcs[i]->msatoshi / 1000 < dust_limit_satoshis + htlc_fee) - continue; - arr[n++] = htlcs[i]; - } + else + htlc_fee = htlc_success_fee(feerate_per_kw); - assert(n <= tal_count(arr)); - tal_resize(&arr, n); + return htlc->msatoshi / 1000 < dust_limit_satoshis + htlc_fee; +} - return arr; +size_t commit_tx_num_untrimmed(const struct htlc **htlcs, + u64 feerate_per_kw, u64 dust_limit_satoshis, + enum side side) +{ + size_t i, n; + + for (i = n = 0; i < tal_count(htlcs); i++) + n += !trim(htlcs[i], feerate_per_kw, dust_limit_satoshis, side); + + return n; } -static u64 commit_tx_base_fee(u64 feerate_per_kw, size_t num_untrimmed_htlcs) +u64 commit_tx_base_fee(u64 feerate_per_kw, size_t num_untrimmed_htlcs) { u64 weight; @@ -139,6 +144,38 @@ static u64 commit_tx_base_fee(u64 feerate_per_kw, size_t num_untrimmed_htlcs) return feerate_per_kw * weight / 1000; } +static void add_offered_htlc_out(struct bitcoin_tx *tx, size_t n, + const struct htlc *htlc, + const struct pubkey *selfkey, + const struct pubkey *otherkey) +{ + u8 *wscript = bitcoin_wscript_htlc_offer(tx, + selfkey, otherkey, + &htlc->rhash); + tx->output[n].amount = htlc->msatoshi / 1000; + tx->output[n].script = scriptpubkey_p2wsh(tx, wscript); + SUPERVERBOSE("# HTLC offered amount %"PRIu64" wscript %s\n", + tx->output[n].amount, tal_hex(wscript, wscript)); + tal_free(wscript); +} + +static void add_received_htlc_out(struct bitcoin_tx *tx, size_t n, + const struct htlc *htlc, + const struct pubkey *selfkey, + const struct pubkey *otherkey) +{ + u8 *wscript = bitcoin_wscript_htlc_receive(tx, + &htlc->expiry, + selfkey, otherkey, + &htlc->rhash); + tx->output[n].amount = htlc->msatoshi / 1000; + tx->output[n].script = scriptpubkey_p2wsh(tx->output, wscript); + SUPERVERBOSE("# HTLC received amount %"PRIu64" wscript %s\n", + tx->output[n].amount, + tal_hex(wscript, wscript)); + tal_free(wscript); +} + struct bitcoin_tx *commit_tx(const tal_t *ctx, const struct sha256_double *funding_txid, unsigned int funding_txout, @@ -159,10 +196,9 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, enum side side) { const tal_t *tmpctx = tal_tmpctx(ctx); - const struct htlc **offered, **received; u64 base_fee_msat; struct bitcoin_tx *tx; - size_t i, n; + size_t i, n, untrimmed; assert(self_pay_msat + other_pay_msat <= funding_satoshis * 1000); @@ -171,22 +207,16 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * 1. Calculate which committed HTLCs need to be trimmed (see * [Trimmed Outputs](#trimmed-outputs)). */ - offered = untrimmed(tmpctx, htlcs, side, - htlc_timeout_fee(feerate_per_kw), - dust_limit_satoshis); - received = untrimmed(tmpctx, htlcs, !side, - htlc_success_fee(feerate_per_kw), - dust_limit_satoshis); + untrimmed = commit_tx_num_untrimmed(htlcs, + feerate_per_kw, + dust_limit_satoshis, side); /* BOLT #3: * * 2. Calculate the base [commitment transaction * fee](#fee-calculation). */ - base_fee_msat = commit_tx_base_fee(feerate_per_kw, - tal_count(offered) - + tal_count(received)) - * 1000; + base_fee_msat = commit_tx_base_fee(feerate_per_kw, untrimmed) * 1000; SUPERVERBOSE("# base commitment transaction fee = %"PRIu64"\n", base_fee_msat / 1000); @@ -200,45 +230,41 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, &self_pay_msat, &other_pay_msat); /* Worst-case sizing: both to-local and to-remote outputs. */ - tx = bitcoin_tx(ctx, 1, tal_count(offered) + tal_count(received) + 2); + tx = bitcoin_tx(ctx, 1, untrimmed + 2); /* We keep track of which outputs have which HTLCs */ *htlcmap = tal_arr(tx, const struct htlc *, tal_count(tx->output)); + /* This could be done in a single loop, but we follow the BOLT + * literally to make comments in test vectors clearer. */ + + n = 0; /* BOLT #3: * - * 3. For every offered HTLC, if it is not trimmed, add an [offered - * HTLC output](#offered-htlc-outputs). + * 3. For every offered HTLC, if it is not trimmed, add an + * [offered HTLC output](#offered-htlc-outputs). */ - n = 0; - for (i = 0; i < tal_count(offered); i++, n++) { - u8 *wscript = bitcoin_wscript_htlc_offer(tmpctx, - selfkey, otherkey, - &offered[i]->rhash); - tx->output[n].amount = offered[i]->msatoshi / 1000; - tx->output[n].script = scriptpubkey_p2wsh(tx, wscript); - (*htlcmap)[n] = offered[i]; - SUPERVERBOSE("# HTLC offered amount %"PRIu64" wscript %s\n", - tx->output[n].amount, - tal_hex(tmpctx, wscript)); + for (i = 0; i < tal_count(htlcs); i++) { + if (htlc_owner(htlcs[i]) != side) + continue; + if (trim(htlcs[i], feerate_per_kw, dust_limit_satoshis, side)) + continue; + add_offered_htlc_out(tx, n, htlcs[i], selfkey, otherkey); + (*htlcmap)[n++] = htlcs[i]; } /* BOLT #3: * - * 4. For every received HTLC, if it is not trimmed, add an [received - * HTLC output](#received-htlc-outputs). + * 4. For every received HTLC, if it is not trimmed, add an + * [received HTLC output](#received-htlc-outputs). */ - for (i = 0; i < tal_count(received); i++, n++) { - u8 *wscript = bitcoin_wscript_htlc_receive(tmpctx, - &received[i]->expiry, - selfkey, otherkey, - &received[i]->rhash); - tx->output[n].amount = received[i]->msatoshi / 1000; - tx->output[n].script = scriptpubkey_p2wsh(tx, wscript); - (*htlcmap)[n] = received[i]; - SUPERVERBOSE("# HTLC received amount %"PRIu64" wscript %s\n", - tx->output[n].amount, - tal_hex(tmpctx, wscript)); + for (i = 0; i < tal_count(htlcs); i++) { + if (htlc_owner(htlcs[i]) == side) + continue; + if (trim(htlcs[i], feerate_per_kw, dust_limit_satoshis, side)) + continue; + add_received_htlc_out(tx, n, htlcs[i],selfkey, otherkey); + (*htlcmap)[n++] = htlcs[i]; } /* BOLT #3: diff --git a/lightningd/commit_tx.h b/lightningd/commit_tx.h index 650350b17..2473c1db8 100644 --- a/lightningd/commit_tx.h +++ b/lightningd/commit_tx.h @@ -20,6 +20,23 @@ u64 commit_number_obscurer(const struct pubkey *opener_payment_basepoint, u64 htlc_success_fee(u64 feerate_per_kw); u64 htlc_timeout_fee(u64 feerate_per_kw); +/** + * commit_tx_num_untrimmed: how many of these htlc outputs will commit tx have? + * @htlcs: tal_arr of HTLCs + * @feerate_per_kw: feerate to use + * @dust_limit_satoshis: dust limit below which to trim outputs. + * @side: from which side's point of view + * + * We need @side because HTLC fees are different for offered and + * received HTLCs. + */ +size_t commit_tx_num_untrimmed(const struct htlc **htlcs, + u64 feerate_per_kw, u64 dust_limit_satoshis, + enum side side); + +/* Helper to calculate the base fee if we add this extra htlc */ +u64 commit_tx_base_fee(u64 feerate_per_kw, size_t num_untrimmed_htlcs); + /** * commit_tx: create (unsigned) commitment tx to spend the funding tx output * @ctx: context to allocate transaction and @htlc_map from. diff --git a/lightningd/test/Makefile b/lightningd/test/Makefile index 30a7c0623..707fc390e 100644 --- a/lightningd/test/Makefile +++ b/lightningd/test/Makefile @@ -10,7 +10,7 @@ update-mocks: $(LIGHTNINGD_TEST_SRC:%=update-mocks/%) $(LIGHTNINGD_TEST_PROGRAMS): $(CCAN_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(LIBBASE58_OBJS) $(LIGHTNINGD_OLD_LIB_OBJS) utils.o libsecp256k1.a libsodium.a -$(LIGHTNINGD_TEST_OBJS): $(LIGHTNINGD_HEADERS) +$(LIGHTNINGD_TEST_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_SRC) $(LIGHTNINGD_LIB_SRC) lightningd/tests: $(LIGHTNINGD_TEST_PROGRAMS:%=unittest/%)