diff --git a/closingd/closing.c b/closingd/closing.c index e0f84acd7..323c69d98 100644 --- a/closingd/closing.c +++ b/closingd/closing.c @@ -74,15 +74,6 @@ static struct bitcoin_tx *close_tx(const tal_t *ctx, return tx; } -static u64 one_towards(u64 target, u64 value) -{ - if (value > target) - return value-1; - else if (value < target) - return value+1; - return value; -} - /* Handle random messages we might get, returning the first non-handled one. */ static u8 *closing_read_peer_msg(const tal_t *ctx, struct crypto_state *cs, @@ -157,6 +148,281 @@ static void do_reconnect(struct crypto_state *cs, tal_free(tmpctx); } +static void send_offer(struct crypto_state *cs, + const struct channel_id *channel_id, + const struct pubkey funding_pubkey[NUM_SIDES], + const u8 *funding_wscript, + u8 *scriptpubkey[NUM_SIDES], + const struct bitcoin_txid *funding_txid, + unsigned int funding_txout, + u64 funding_satoshi, + const u64 satoshi_out[NUM_SIDES], + enum side funder, + uint64_t our_dust_limit, + const struct secrets *secrets, + uint64_t fee_to_offer) +{ + const tal_t *tmpctx = tal_tmpctx(NULL); + struct bitcoin_tx *tx; + secp256k1_ecdsa_signature our_sig; + u8 *msg; + + /* BOLT #2: + * + * The sender MUST set `signature` to the Bitcoin signature of + * the close transaction as specified in [BOLT + * #3](03-transactions.md#closing-transaction). + */ + tx = close_tx(tmpctx, cs, channel_id, + scriptpubkey, + funding_txid, + funding_txout, + funding_satoshi, + satoshi_out, + funder, fee_to_offer, our_dust_limit); + + /* BOLT #3: + * + * ## Closing Transaction + *... + * Each node offering a signature... MAY also eliminate its + * own output. + */ + /* (We don't do this). */ + sign_tx_input(tx, 0, NULL, funding_wscript, + &secrets->funding_privkey, + &funding_pubkey[LOCAL], + &our_sig); + + status_trace("sending fee offer %"PRIu64, fee_to_offer); + + msg = towire_closing_signed(tmpctx, channel_id, fee_to_offer, &our_sig); + if (!sync_crypto_write(cs, PEER_FD, take(msg))) + status_failed(STATUS_FAIL_PEER_IO, + "Writing closing_signed"); + + tal_free(tmpctx); +} + +static void tell_master_their_offer(u64 their_offer, + const secp256k1_ecdsa_signature *their_sig, + const struct bitcoin_tx *tx) +{ + u8 *msg = towire_closing_received_signature(NULL, their_sig, tx); + if (!wire_sync_write(REQ_FD, take(msg))) + status_failed(STATUS_FAIL_MASTER_IO, + "Writing received to master: %s", + strerror(errno)); + + /* Wait for master to ack, to make sure it's in db. */ + msg = wire_sync_read(NULL, REQ_FD); + if (!fromwire_closing_received_signature_reply(msg,NULL)) + master_badmsg(WIRE_CLOSING_RECEIVED_SIGNATURE_REPLY, msg); + tal_free(msg); +} + +/* Returns fee they offered. */ +static uint64_t receive_offer(struct crypto_state *cs, + const struct channel_id *channel_id, + const struct pubkey funding_pubkey[NUM_SIDES], + const u8 *funding_wscript, + u8 *scriptpubkey[NUM_SIDES], + const struct bitcoin_txid *funding_txid, + unsigned int funding_txout, + u64 funding_satoshi, + const u64 satoshi_out[NUM_SIDES], + enum side funder, + uint64_t our_dust_limit, + u64 min_fee_to_accept) +{ + const tal_t *tmpctx = tal_tmpctx(NULL); + u8 *msg; + struct channel_id their_channel_id; + u64 received_fee; + secp256k1_ecdsa_signature their_sig; + struct bitcoin_tx *tx; + + /* Wait for them to say something interesting */ + do { + msg = closing_read_peer_msg(tmpctx, cs, channel_id); + + /* BOLT #2: + * + * On reconnection, a node MUST ignore a redundant + * `funding_locked` if it receives one. + */ + /* This should only happen if we've made no commitments, but + * we don't have to check that: it's their problem. */ + if (fromwire_peektype(msg) == WIRE_FUNDING_LOCKED) + msg = tal_free(msg); + /* BOLT #2: + * + * ...if the node has sent a previous `shutdown` it MUST + * retransmit it. + */ + else if (fromwire_peektype(msg) == WIRE_SHUTDOWN) + msg = tal_free(msg); + } while (!msg); + + if (!fromwire_closing_signed(msg, NULL, &their_channel_id, + &received_fee, &their_sig)) + peer_failed(PEER_FD, cs, channel_id, + "Expected closing_signed: %s", + tal_hex(trc, msg)); + + /* BOLT #2: + * + * The receiver MUST check `signature` is valid for either + * variant of close transaction specified in [BOLT + * #3](03-transactions.md#closing-transaction), and MUST fail + * the connection if it is not. + */ + tx = close_tx(tmpctx, cs, channel_id, + scriptpubkey, + funding_txid, + funding_txout, + funding_satoshi, + satoshi_out, funder, received_fee, our_dust_limit); + + if (!check_tx_sig(tx, 0, NULL, funding_wscript, + &funding_pubkey[REMOTE], &their_sig)) { + /* Trim it by reducing their output to minimum */ + struct bitcoin_tx *trimmed; + u64 trimming_satoshi_out[NUM_SIDES]; + + if (funder == REMOTE) + trimming_satoshi_out[REMOTE] = received_fee; + else + trimming_satoshi_out[REMOTE] = 0; + trimming_satoshi_out[LOCAL] = satoshi_out[LOCAL]; + + /* BOLT #3: + * + * Each node offering a signature MUST subtract the fee given + * by `fee_satoshis` from the output to the funder; it MUST + * then remove any output below its own `dust_limit_satoshis`, + * and MAY also eliminate its own output. + */ + trimmed = close_tx(tmpctx, cs, channel_id, + scriptpubkey, + funding_txid, + funding_txout, + funding_satoshi, + trimming_satoshi_out, + funder, received_fee, our_dust_limit); + if (!trimmed + || !check_tx_sig(trimmed, 0, NULL, funding_wscript, + &funding_pubkey[REMOTE], &their_sig)) { + peer_failed(PEER_FD, cs, channel_id, + "Bad closing_signed signature for" + " %s (and trimmed version %s)", + type_to_string(tmpctx, + struct bitcoin_tx, + tx), + trimmed ? + type_to_string(tmpctx, + struct bitcoin_tx, + trimmed) + : "NONE"); + } + tx = trimmed; + } + + status_trace("Received fee offer %"PRIu64, received_fee); + + /* Master sorts out what is best offer, we just tell it any above min */ + if (received_fee >= min_fee_to_accept) { + status_trace("...offer is reasonable"); + tell_master_their_offer(received_fee, &their_sig, tx); + } + + tal_free(tmpctx); + return received_fee; +} + +struct feerange { + enum side higher_side; + u64 min, max; +}; + +static void init_feerange(struct feerange *feerange, + u64 commitment_fee, + const u64 offer[NUM_SIDES]) +{ + feerange->min = 0; + + /* BOLT #2: + * + * - MUST set `fee_satoshis` less than or equal to the base + * fee of the final commitment transaction, as calculated + * in [BOLT #3](03-transactions.md#fee-calculation). + */ + feerange->max = commitment_fee; + + if (offer[LOCAL] > offer[REMOTE]) + feerange->higher_side = LOCAL; + else + feerange->higher_side = REMOTE; + + status_trace("Feerange init %"PRIu64"-%"PRIu64", %s higher", + feerange->min, feerange->max, + feerange->higher_side == LOCAL ? "local" : "remote"); +} + +static void adjust_feerange(struct crypto_state *cs, + const struct channel_id *channel_id, + struct feerange *feerange, + u64 offer, enum side side) +{ + if (offer < feerange->min || offer > feerange->max) + peer_failed(PEER_FD, cs, channel_id, + "%s offer %"PRIu64 + " not between %"PRIu64" and %"PRIu64, + side == LOCAL ? "local" : "remote", + offer, feerange->min, feerange->max); + + + /* BOLT #2: + * + * ...otherwise it MUST propose a value strictly between the received + * `fee_satoshis` and its previously-sent `fee_satoshis`. + */ + if (side == feerange->higher_side) + feerange->max = offer - 1; + else + feerange->min = offer + 1; + + status_trace("Feerange %s update %"PRIu64": now %"PRIu64"-%"PRIu64, + side == LOCAL ? "local" : "remote", + offer, feerange->min, feerange->max); +} + +/* Figure out what we should offer now. */ +static u64 adjust_offer(struct crypto_state *cs, + const struct channel_id *channel_id, + const struct feerange *feerange, + u64 remote_offer, + u64 min_fee_to_accept) +{ + /* Within 1 satoshi? Agree. */ + if (feerange->min + 1 >= feerange->max) + return remote_offer; + + /* Max is below our minimum acceptable? */ + if (feerange->max < min_fee_to_accept) + peer_failed(PEER_FD, cs, channel_id, + "Feerange %"PRIu64"-%"PRIu64 + " below minimum acceptable %"PRIu64, + feerange->min, feerange->max, + min_fee_to_accept); + + /* Bisect between our minimum and max. */ + if (feerange->min > min_fee_to_accept) + min_fee_to_accept = feerange->min; + + return (feerange->max + min_fee_to_accept)/2; +} + int main(int argc, char *argv[]) { struct crypto_state cs; @@ -168,16 +434,16 @@ int main(int argc, char *argv[]) u16 funding_txout; u64 funding_satoshi, satoshi_out[NUM_SIDES]; u64 our_dust_limit; - u64 minfee, feelimit, maxfee, sent_fee; - s64 last_received_fee = -1; + u64 min_fee_to_accept, commitment_fee, offer[NUM_SIDES]; + struct feerange feerange; enum side funder; u8 *scriptpubkey[NUM_SIDES], *funding_wscript; struct channel_id channel_id; struct secrets secrets; - secp256k1_ecdsa_signature sig; bool reconnected; u64 next_index[NUM_SIDES], revocations_received; u64 gossip_index; + enum side whose_turn; subdaemon_setup(argc, argv); @@ -193,7 +459,8 @@ int main(int argc, char *argv[]) &satoshi_out[LOCAL], &satoshi_out[REMOTE], &our_dust_limit, - &minfee, &maxfee, &sent_fee, + &min_fee_to_accept, &commitment_fee, + &offer[LOCAL], &scriptpubkey[LOCAL], &scriptpubkey[REMOTE], &reconnected, @@ -205,7 +472,7 @@ int main(int argc, char *argv[]) status_trace("satoshi_out = %"PRIu64"/%"PRIu64, satoshi_out[LOCAL], satoshi_out[REMOTE]); status_trace("dustlimit = %"PRIu64, our_dust_limit); - status_trace("fee = %"PRIu64, sent_fee); + status_trace("fee = %"PRIu64, offer[LOCAL]); derive_channel_id(&channel_id, &funding_txid, funding_txout); derive_basepoints(&seed, &funding_pubkey[LOCAL], NULL, &secrets, NULL); @@ -219,249 +486,66 @@ int main(int argc, char *argv[]) /* BOLT #2: * - * Nodes SHOULD send a `closing_signed` message after `shutdown` has - * been received and no HTLCs remain in either commitment transaction. - */ - - /* BOLT #2: - * - * On reconnection, ... if the node has sent a previous - * `closing_signed` it MUST send another `closing_signed`, otherwise - * if the node has sent a previous `shutdown` it MUST retransmit it. + * The funding node: + * - after `shutdown` has been received, AND no HTLCs remain in either + * commitment transaction: + * - SHOULD send a `closing_signed` message. */ - for (;;) { - const tal_t *tmpctx = tal_tmpctx(ctx); - struct bitcoin_tx *tx; - u64 received_fee, limit_fee, new_fee; - - /* BOLT #2: - * - * The sender MUST set `signature` to the Bitcoin signature of - * the close transaction as specified in [BOLT - * #3](03-transactions.md#closing-transaction). - */ - tx = close_tx(tmpctx, &cs, &channel_id, - scriptpubkey, - &funding_txid, - funding_txout, - funding_satoshi, - satoshi_out, funder, sent_fee, our_dust_limit); - - /* BOLT #3: - * - * ## Closing Transaction - *... - * Each node offering a signature... MAY also eliminate its - * own output. - */ - /* (We don't do this). */ - sign_tx_input(tx, 0, NULL, funding_wscript, - &secrets.funding_privkey, - &funding_pubkey[LOCAL], - &sig); - - status_trace("sending fee offer %"PRIu64, sent_fee); - - /* Now send closing offer */ - msg = towire_closing_signed(tmpctx, &channel_id, sent_fee, &sig); - if (!sync_crypto_write(&cs, PEER_FD, take(msg))) - status_failed(STATUS_FAIL_PEER_IO, - "Writing closing_signed"); - - /* Did we just agree with them? If so, we're done. */ - if (sent_fee == last_received_fee) - break; - - again: - /* Wait for them to say something interesting */ - msg = closing_read_peer_msg(tmpctx, &cs, &channel_id); - - /* BOLT #2: - * - * On reconnection, a node MUST ignore a redundant - * `funding_locked` if it receives one. - */ - /* This should only happen if we've made no commitments, but - * we don't have to check that: it's their problem. */ - if (fromwire_peektype(msg) == WIRE_FUNDING_LOCKED) { - tal_free(msg); - goto again; - } - - /* BOLT #2: - * - * ...if the node has sent a previous `shutdown` it MUST - * retransmit it. - */ - if (fromwire_peektype(msg) == WIRE_SHUTDOWN) { - tal_free(msg); - goto again; - } - - if (!fromwire_closing_signed(msg, NULL, &channel_id, - &received_fee, &sig)) - peer_failed(PEER_FD, &cs, &channel_id, - "Expected closing_signed: %s", - tal_hex(trc, msg)); - - /* BOLT #2: - * - * The receiver MUST check `signature` is valid for either - * variant of close transaction specified in [BOLT - * #3](03-transactions.md#closing-transaction), and MUST fail - * the connection if it is not. - */ - tx = close_tx(tmpctx, &cs, &channel_id, - scriptpubkey, - &funding_txid, - funding_txout, - funding_satoshi, - satoshi_out, funder, received_fee, our_dust_limit); - - if (!check_tx_sig(tx, 0, NULL, funding_wscript, - &funding_pubkey[REMOTE], &sig)) { - /* Trim it by reducing their output to minimum */ - struct bitcoin_tx *trimmed; - u64 trimming_satoshi_out[NUM_SIDES]; - - if (funder == REMOTE) - trimming_satoshi_out[REMOTE] = received_fee; - else - trimming_satoshi_out[REMOTE] = 0; - trimming_satoshi_out[LOCAL] = satoshi_out[LOCAL]; - - /* BOLT #3: - * - * Each node offering a signature MUST subtract the - * fee given by `fee_satoshis` from the output to the - * funder; it MUST then remove any output below its - * own `dust_limit_satoshis`, and MAY also eliminate - * its own output. - */ - trimmed = close_tx(tmpctx, &cs, &channel_id, - scriptpubkey, - &funding_txid, - funding_txout, - funding_satoshi, - trimming_satoshi_out, - funder, received_fee, our_dust_limit); - if (!trimmed - || !check_tx_sig(trimmed, 0, NULL, funding_wscript, - &funding_pubkey[REMOTE], &sig)) { - peer_failed(PEER_FD, &cs, &channel_id, - "Bad closing_signed signature for" - " %s (and trimmed version %s)", - type_to_string(tmpctx, - struct bitcoin_tx, - tx), - trimmed ? - type_to_string(tmpctx, - struct bitcoin_tx, - trimmed) - : "NONE"); - } - tx = trimmed; - } - - status_trace("Received fee offer %"PRIu64, received_fee); - - /* BOLT #2: - * - * Otherwise, the recipient MUST fail the connection if - * `fee_satoshis` is greater than the base fee of the final - * commitment transaction as calculated in [BOLT #3] */ - if (received_fee > maxfee) - peer_failed(PEER_FD, &cs, &channel_id, - "Bad closing_signed fee %"PRIu64" > %"PRIu64, - received_fee, maxfee); - - /* Is fee reasonable? Tell master. */ - if (received_fee < minfee) { - status_trace("Fee too low, below %"PRIu64, minfee); - limit_fee = minfee; + whose_turn = funder; + for (size_t i = 0; i < 2; i++, whose_turn = !whose_turn) { + if (whose_turn == LOCAL) { + send_offer(&cs, &channel_id, funding_pubkey, + funding_wscript, + scriptpubkey, &funding_txid, funding_txout, + funding_satoshi, satoshi_out, funder, + our_dust_limit, &secrets, offer[LOCAL]); } else { - status_trace("Fee accepted."); - msg = towire_closing_received_signature(tmpctx, - &sig, tx); - if (!wire_sync_write(REQ_FD, take(msg))) - status_failed(STATUS_FAIL_MASTER_IO, - "Writing received to master: %s", - strerror(errno)); - msg = wire_sync_read(tmpctx, REQ_FD); - if (!fromwire_closing_received_signature_reply(msg,NULL)) - master_badmsg(WIRE_CLOSING_RECEIVED_SIGNATURE_REPLY, - msg); - limit_fee = received_fee; + offer[REMOTE] + = receive_offer(&cs, &channel_id, funding_pubkey, + funding_wscript, + scriptpubkey, &funding_txid, + funding_txout, funding_satoshi, + satoshi_out, funder, + our_dust_limit, + min_fee_to_accept); } + } - /* BOLT #2: - * - * If `fee_satoshis` is equal to its previously sent - * `fee_satoshis`, the receiver SHOULD sign and broadcast the - * final closing transaction and MAY close the connection. - */ - if (received_fee == sent_fee) - break; - - /* BOLT #2: - * - * the recipient SHOULD fail the connection if `fee_satoshis` - * is not strictly between its last-sent `fee_satoshis` and - * its previously-received `fee_satoshis`, unless it has - * reconnected since then. */ - if (last_received_fee != -1) { - bool previous_dir = sent_fee < last_received_fee; - bool dir = received_fee < last_received_fee; - bool next_dir = sent_fee < received_fee; - - /* They went away from our offer? */ - if (dir != previous_dir) - peer_failed(PEER_FD, &cs, &channel_id, - "Their fee went %" - PRIu64" to %"PRIu64 - " when ours was %"PRIu64, - last_received_fee, - received_fee, - sent_fee); - - /* They jumped over our offer? */ - if (next_dir != previous_dir) - peer_failed(PEER_FD, &cs, &channel_id, - "Their fee jumped %" - PRIu64" to %"PRIu64 - " when ours was %"PRIu64, - last_received_fee, - received_fee, - sent_fee); + /* Now we have first two points, we can init fee range. */ + init_feerange(&feerange, commitment_fee, offer); + + /* Now apply the one constraint from above (other is inside loop). */ + adjust_feerange(&cs, &channel_id, &feerange, + offer[!whose_turn], !whose_turn); + + /* Now any extra rounds required. */ + while (offer[LOCAL] != offer[REMOTE]) { + /* If they differ, adjust feerate. */ + adjust_feerange(&cs, &channel_id, &feerange, + offer[whose_turn], whose_turn); + + /* Now its the other side's turn. */ + whose_turn = !whose_turn; + + if (whose_turn == LOCAL) { + offer[LOCAL] = adjust_offer(&cs, &channel_id, + &feerange, offer[REMOTE], + min_fee_to_accept); + send_offer(&cs, &channel_id, funding_pubkey, + funding_wscript, + scriptpubkey, &funding_txid, funding_txout, + funding_satoshi, satoshi_out, funder, + our_dust_limit, &secrets, offer[LOCAL]); + } else { + offer[REMOTE] + = receive_offer(&cs, &channel_id, funding_pubkey, + funding_wscript, + scriptpubkey, &funding_txid, + funding_txout, funding_satoshi, + satoshi_out, funder, + our_dust_limit, + min_fee_to_accept); } - - /* BOLT #2: - * - * ...otherwise it MUST propose a value strictly between the - * received `fee_satoshis` and its previously-sent - * `fee_satoshis`. - */ - - /* We do it by bisection, with twists: - * 1. Don't go outside limits, or reach them immediately: - * treat out-of-limit offers as on-limit offers. - * 2. Round towards the target, otherwise we can't close - * a final 1-satoshi gap. - * - * Note: Overflow impossible here, since fee <= funder amount */ - new_fee = one_towards(limit_fee, limit_fee + sent_fee) / 2; - - /* If we didn't move, give up (we're ~ at min/max). */ - if (new_fee == sent_fee) - peer_failed(PEER_FD, &cs, &channel_id, - "Final fee %"PRIu64" vs %"PRIu64 - " at limits %"PRIu64"-%"PRIu64, - sent_fee, received_fee, - minfee, maxfee); - - last_received_fee = received_fee; - sent_fee = new_fee; - tal_free(tmpctx); } /* We're done! */