You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
583 lines
17 KiB
583 lines
17 KiB
/* FIXME: We don't relay from gossipd at all here. */
|
|
#include <bitcoin/script.h>
|
|
#include <ccan/structeq/structeq.h>
|
|
#include <closingd/gen_closing_wire.h>
|
|
#include <common/close_tx.h>
|
|
#include <common/crypto_sync.h>
|
|
#include <common/derive_basepoints.h>
|
|
#include <common/htlc.h>
|
|
#include <common/peer_billboard.h>
|
|
#include <common/peer_failed.h>
|
|
#include <common/read_peer_msg.h>
|
|
#include <common/status.h>
|
|
#include <common/subdaemon.h>
|
|
#include <common/type_to_string.h>
|
|
#include <common/utils.h>
|
|
#include <common/version.h>
|
|
#include <common/wire_error.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <wire/peer_wire.h>
|
|
#include <wire/wire_sync.h>
|
|
|
|
/* stdin == requests, 3 == peer, 4 = gossip */
|
|
#define REQ_FD STDIN_FILENO
|
|
#define PEER_FD 3
|
|
#define GOSSIP_FD 4
|
|
|
|
static struct bitcoin_tx *close_tx(const tal_t *ctx,
|
|
struct crypto_state *cs,
|
|
u64 gossip_index,
|
|
const struct channel_id *channel_id,
|
|
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 fee,
|
|
uint64_t dust_limit)
|
|
{
|
|
struct bitcoin_tx *tx;
|
|
|
|
if (satoshi_out[funder] < fee)
|
|
peer_failed(cs, gossip_index, channel_id,
|
|
"Funder cannot afford fee %"PRIu64
|
|
" (%"PRIu64" and %"PRIu64")",
|
|
fee, satoshi_out[LOCAL],
|
|
satoshi_out[REMOTE]);
|
|
|
|
status_trace("Making close tx at = %"PRIu64"/%"PRIu64" fee %"PRIu64,
|
|
satoshi_out[LOCAL], satoshi_out[REMOTE], fee);
|
|
|
|
/* FIXME: We need to allow this! */
|
|
tx = create_close_tx(ctx,
|
|
scriptpubkey[LOCAL], scriptpubkey[REMOTE],
|
|
funding_txid,
|
|
funding_txout,
|
|
funding_satoshi,
|
|
satoshi_out[LOCAL] - (funder == LOCAL ? fee : 0),
|
|
satoshi_out[REMOTE] - (funder == REMOTE ? fee : 0),
|
|
dust_limit);
|
|
if (!tx)
|
|
peer_failed(cs, gossip_index, channel_id,
|
|
"Both outputs below dust limit:"
|
|
" funding = %"PRIu64
|
|
" fee = %"PRIu64
|
|
" dust_limit = %"PRIu64
|
|
" LOCAL = %"PRIu64
|
|
" REMOTE = %"PRIu64,
|
|
funding_satoshi,
|
|
fee,
|
|
dust_limit,
|
|
satoshi_out[LOCAL],
|
|
satoshi_out[REMOTE]);
|
|
return tx;
|
|
}
|
|
|
|
static void do_reconnect(struct crypto_state *cs,
|
|
u64 gossip_index,
|
|
const struct channel_id *channel_id,
|
|
const u64 next_index[NUM_SIDES],
|
|
u64 revocations_received)
|
|
{
|
|
u8 *msg;
|
|
struct channel_id their_channel_id;
|
|
u64 next_local_commitment_number, next_remote_revocation_number;
|
|
|
|
/* BOLT #2:
|
|
*
|
|
* On reconnection, a node MUST transmit `channel_reestablish` for
|
|
* each channel, and MUST wait for to receive the other node's
|
|
* `channel_reestablish` message before sending any other messages for
|
|
* that channel. The sending node MUST set
|
|
* `next_local_commitment_number` to the commitment number of the next
|
|
* `commitment_signed` it expects to receive, and MUST set
|
|
* `next_remote_revocation_number` to the commitment number of the
|
|
* next `revoke_and_ack` message it expects to receive.
|
|
*/
|
|
msg = towire_channel_reestablish(NULL, channel_id,
|
|
next_index[LOCAL],
|
|
revocations_received);
|
|
if (!sync_crypto_write(cs, PEER_FD, take(msg)))
|
|
peer_failed_connection_lost();
|
|
|
|
/* Wait for them to say something interesting */
|
|
while ((msg = read_peer_msg(tmpctx, cs, gossip_index, channel_id,
|
|
sync_crypto_write_arg,
|
|
status_fail_io,
|
|
NULL)) == NULL)
|
|
clean_tmpctx();
|
|
|
|
if (!fromwire_channel_reestablish(msg, &their_channel_id,
|
|
&next_local_commitment_number,
|
|
&next_remote_revocation_number)) {
|
|
peer_failed(cs, gossip_index, channel_id,
|
|
"bad reestablish msg: %s %s",
|
|
wire_type_name(fromwire_peektype(msg)),
|
|
tal_hex(tmpctx, msg));
|
|
}
|
|
status_trace("Got reestablish commit=%"PRIu64" revoke=%"PRIu64,
|
|
next_local_commitment_number,
|
|
next_remote_revocation_number);
|
|
|
|
/* FIXME: Spec says to re-xmit funding_locked here if we haven't
|
|
* done any updates. */
|
|
|
|
/* BOLT #2:
|
|
*
|
|
* On reconnection if the node has sent a previous `closing_signed` it
|
|
* MUST send another `closing_signed`
|
|
*/
|
|
|
|
/* Since we always transmit closing_signed immediately, if
|
|
* we're reconnecting we consider ourselves to have transmitted once,
|
|
* and we'll immediately do the retransmit now anyway. */
|
|
}
|
|
|
|
static void send_offer(struct crypto_state *cs,
|
|
u64 gossip_index,
|
|
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)
|
|
{
|
|
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, gossip_index, 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(NULL, channel_id, fee_to_offer, &our_sig);
|
|
if (!sync_crypto_write(cs, PEER_FD, take(msg)))
|
|
peer_failed_connection_lost();
|
|
}
|
|
|
|
static void tell_master_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))
|
|
master_badmsg(WIRE_CLOSING_RECEIVED_SIGNATURE_REPLY, msg);
|
|
tal_free(msg);
|
|
}
|
|
|
|
/* Returns fee they offered. */
|
|
static uint64_t receive_offer(struct crypto_state *cs,
|
|
u64 gossip_index,
|
|
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)
|
|
{
|
|
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 {
|
|
clean_tmpctx();
|
|
|
|
msg = read_peer_msg(tmpctx, cs, gossip_index, channel_id,
|
|
sync_crypto_write_arg,
|
|
status_fail_io,
|
|
NULL);
|
|
|
|
/* 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 (msg && 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 (msg && fromwire_peektype(msg) == WIRE_SHUTDOWN)
|
|
msg = tal_free(msg);
|
|
} while (!msg);
|
|
|
|
if (!fromwire_closing_signed(msg, &their_channel_id,
|
|
&received_fee, &their_sig))
|
|
peer_failed(cs, gossip_index, channel_id,
|
|
"Expected closing_signed: %s",
|
|
tal_hex(tmpctx, 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, gossip_index, 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, gossip_index, 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(cs, gossip_index, 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(&their_sig, tx);
|
|
}
|
|
|
|
return received_fee;
|
|
}
|
|
|
|
struct feerange {
|
|
enum side higher_side;
|
|
u64 min, max;
|
|
|
|
bool allow_mistakes;
|
|
};
|
|
|
|
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,
|
|
u64 gossip_index,
|
|
const struct channel_id *channel_id,
|
|
struct feerange *feerange,
|
|
u64 offer, enum side side)
|
|
{
|
|
if (offer < feerange->min || offer > feerange->max) {
|
|
if (!feerange->allow_mistakes || side != REMOTE)
|
|
peer_failed(cs, gossip_index, channel_id,
|
|
"%s offer %"PRIu64
|
|
" not between %"PRIu64" and %"PRIu64,
|
|
side == LOCAL ? "local" : "remote",
|
|
offer, feerange->min, feerange->max);
|
|
|
|
status_trace("Allowing deprecated out-of-range fee");
|
|
return;
|
|
}
|
|
|
|
/* 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,
|
|
u64 gossip_index,
|
|
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(cs, gossip_index, 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;
|
|
const tal_t *ctx = tal(NULL, char);
|
|
u8 *msg;
|
|
struct privkey seed;
|
|
struct pubkey funding_pubkey[NUM_SIDES];
|
|
struct bitcoin_txid funding_txid;
|
|
u16 funding_txout;
|
|
u64 funding_satoshi, satoshi_out[NUM_SIDES];
|
|
u64 our_dust_limit;
|
|
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;
|
|
bool reconnected;
|
|
u64 next_index[NUM_SIDES], revocations_received;
|
|
u64 gossip_index;
|
|
enum side whose_turn;
|
|
bool deprecated_api;
|
|
|
|
subdaemon_setup(argc, argv);
|
|
|
|
status_setup_sync(REQ_FD);
|
|
|
|
msg = wire_sync_read(tmpctx, REQ_FD);
|
|
if (!fromwire_closing_init(ctx, msg,
|
|
&cs, &gossip_index, &seed,
|
|
&funding_txid, &funding_txout,
|
|
&funding_satoshi,
|
|
&funding_pubkey[REMOTE],
|
|
&funder,
|
|
&satoshi_out[LOCAL],
|
|
&satoshi_out[REMOTE],
|
|
&our_dust_limit,
|
|
&min_fee_to_accept, &commitment_fee,
|
|
&offer[LOCAL],
|
|
&scriptpubkey[LOCAL],
|
|
&scriptpubkey[REMOTE],
|
|
&reconnected,
|
|
&next_index[LOCAL],
|
|
&next_index[REMOTE],
|
|
&revocations_received,
|
|
&deprecated_api))
|
|
master_badmsg(WIRE_CLOSING_INIT, msg);
|
|
|
|
status_trace("satoshi_out = %"PRIu64"/%"PRIu64,
|
|
satoshi_out[LOCAL], satoshi_out[REMOTE]);
|
|
status_trace("dustlimit = %"PRIu64, our_dust_limit);
|
|
status_trace("fee = %"PRIu64, offer[LOCAL]);
|
|
derive_channel_id(&channel_id, &funding_txid, funding_txout);
|
|
derive_basepoints(&seed, &funding_pubkey[LOCAL], NULL,
|
|
&secrets, NULL);
|
|
|
|
funding_wscript = bitcoin_redeem_2of2(ctx,
|
|
&funding_pubkey[LOCAL],
|
|
&funding_pubkey[REMOTE]);
|
|
|
|
if (reconnected)
|
|
do_reconnect(&cs, gossip_index, &channel_id,
|
|
next_index, revocations_received);
|
|
|
|
peer_billboard(true, "Negotiating closing fee between %"PRIu64
|
|
" and %"PRIu64" satoshi (ideal %"PRIu64")",
|
|
min_fee_to_accept, commitment_fee, offer[LOCAL]);
|
|
|
|
/* BOLT #2:
|
|
*
|
|
* The funding node:
|
|
* - after `shutdown` has been received, AND no HTLCs remain in either
|
|
* commitment transaction:
|
|
* - SHOULD send a `closing_signed` message.
|
|
*/
|
|
whose_turn = funder;
|
|
for (size_t i = 0; i < 2; i++, whose_turn = !whose_turn) {
|
|
if (whose_turn == LOCAL) {
|
|
send_offer(&cs, gossip_index,
|
|
&channel_id, funding_pubkey,
|
|
funding_wscript,
|
|
scriptpubkey, &funding_txid, funding_txout,
|
|
funding_satoshi, satoshi_out, funder,
|
|
our_dust_limit, &secrets, offer[LOCAL]);
|
|
} else {
|
|
if (i == 0)
|
|
peer_billboard(false, "Waiting for their initial"
|
|
" closing fee offer");
|
|
else
|
|
peer_billboard(false, "Waiting for their initial"
|
|
" closing fee offer:"
|
|
" ours was %"PRIu64" satoshi",
|
|
offer[LOCAL]);
|
|
offer[REMOTE]
|
|
= receive_offer(&cs, gossip_index,
|
|
&channel_id, funding_pubkey,
|
|
funding_wscript,
|
|
scriptpubkey, &funding_txid,
|
|
funding_txout, funding_satoshi,
|
|
satoshi_out, funder,
|
|
our_dust_limit,
|
|
min_fee_to_accept);
|
|
}
|
|
}
|
|
|
|
/* 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, gossip_index, &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, gossip_index, &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, gossip_index,
|
|
&channel_id,
|
|
&feerange, offer[REMOTE],
|
|
min_fee_to_accept);
|
|
send_offer(&cs, gossip_index, &channel_id,
|
|
funding_pubkey,
|
|
funding_wscript,
|
|
scriptpubkey, &funding_txid, funding_txout,
|
|
funding_satoshi, satoshi_out, funder,
|
|
our_dust_limit, &secrets, offer[LOCAL]);
|
|
} else {
|
|
peer_billboard(false, "Waiting for another"
|
|
" closing fee offer:"
|
|
" ours was %"PRIu64" satoshi,"
|
|
" theirs was %"PRIu64" satoshi,",
|
|
offer[LOCAL], offer[REMOTE]);
|
|
offer[REMOTE]
|
|
= receive_offer(&cs, gossip_index, &channel_id,
|
|
funding_pubkey,
|
|
funding_wscript,
|
|
scriptpubkey, &funding_txid,
|
|
funding_txout, funding_satoshi,
|
|
satoshi_out, funder,
|
|
our_dust_limit,
|
|
min_fee_to_accept);
|
|
}
|
|
}
|
|
|
|
peer_billboard(true, "We agreed on a closing fee of %"PRIu64" satoshi",
|
|
offer[LOCAL]);
|
|
|
|
/* We're done! */
|
|
wire_sync_write(REQ_FD,
|
|
take(towire_closing_complete(NULL, gossip_index)));
|
|
tal_free(ctx);
|
|
tal_free(tmpctx);
|
|
|
|
return 0;
|
|
}
|
|
|