lightningd/closing: subdaemon for closing negotiation.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2017-07-04 10:19:43 +09:30
parent d40334ce18
commit e5a8a7502c
5 changed files with 481 additions and 0 deletions

View File

@ -2,6 +2,7 @@
#define LIGHTNING_CLOSE_TX_H
#include "config.h"
#include "lightning.pb-c.h"
#include <ccan/short_types/short_types.h>
#include <ccan/tal/tal.h>
struct sha256_double;

View File

@ -124,6 +124,7 @@ include lightningd/handshake/Makefile
include lightningd/gossip/Makefile
include lightningd/opening/Makefile
include lightningd/channel/Makefile
include lightningd/closing/Makefile
$(LIGHTNINGD_OBJS) $(LIGHTNINGD_LIB_OBJS): $(LIGHTNINGD_HEADERS)

View File

@ -0,0 +1,59 @@
#! /usr/bin/make
# Designed to be run one level up
lightningd/closing-wrongdir:
$(MAKE) -C ../.. lightningd/closing-all
default: lightningd/closing-all
lightningd/closing-all: lightningd/lightningd_closing
# lightningd/closing needs these:
LIGHTNINGD_CLOSING_HEADERS_GEN := \
lightningd/closing/gen_closing_wire.h
LIGHTNINGD_CLOSING_HEADERS_NOGEN :=
LIGHTNINGD_CLOSING_HEADERS := $(LIGHTNINGD_CLOSING_HEADERS_GEN) $(LIGHTNINGD_CLOSING_HEADERS_NOGEN)
LIGHTNINGD_CLOSING_SRC := lightningd/closing/closing.c \
$(LIGHTNINGD_CLOSING_HEADERS:.h=.c)
LIGHTNINGD_CLOSING_OBJS := $(LIGHTNINGD_CLOSING_SRC:.c=.o)
# Control daemon uses this:
LIGHTNINGD_CLOSING_CONTROL_HEADERS := $(LIGHTNINGD_CLOSING_HEADERS)
LIGHTNINGD_CLOSING_CONTROL_SRC := $(LIGHTNINGD_CLOSING_HEADERS:.h=.c)
LIGHTNINGD_CLOSING_CONTROL_OBJS := $(LIGHTNINGD_CLOSING_CONTROL_SRC:.c=.o)
LIGHTNINGD_CLOSING_GEN_SRC := $(filter lightningd/closing/gen_%, $(LIGHTNINGD_CLOSING_SRC) $(LIGHTNINGD_CLOSING_CONTROL_SRC))
LIGHTNINGD_CLOSING_SRC_NOGEN := $(filter-out lightningd/closing/gen_%, $(LIGHTNINGD_CLOSING_SRC))
# Add to headers which any object might need.
LIGHTNINGD_HEADERS_GEN += $(LIGHTNINGD_CLOSING_HEADERS_GEN)
LIGHTNINGD_HEADERS_NOGEN += $(LIGHTNINGD_CLOSING_HEADERS_NOGEN)
$(LIGHTNINGD_CLOSING_OBJS): $(LIGHTNINGD_HEADERS)
lightningd/closing/gen_closing_wire.h: $(WIRE_GEN) lightningd/closing/closing_wire.csv
$(WIRE_GEN) --header $@ closing_wire_type < lightningd/closing/closing_wire.csv > $@
lightningd/closing/gen_closing_wire.c: $(WIRE_GEN) lightningd/closing/closing_wire.csv
$(WIRE_GEN) ${@:.c=.h} closing_wire_type < lightningd/closing/closing_wire.csv > $@
LIGHTNINGD_CLOSING_OBJS := $(LIGHTNINGD_CLOSING_SRC:.c=.o) $(LIGHTNINGD_CLOSING_GEN_SRC:.c=.o)
lightningd/lightningd_closing: $(LIGHTNINGD_OLD_LIB_OBJS) $(LIGHTNINGD_LIB_OBJS) $(LIGHTNINGD_CLOSING_OBJS) $(WIRE_ONION_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(WIRE_OBJS) $(BITCOIN_OBJS) $(CCAN_OBJS) $(CCAN_SHACHAIN48_OBJ) $(LIGHTNINGD_HSM_CLIENT_OBJS) $(LIBBASE58_OBJS) libsecp256k1.a libsodium.a libwallycore.a
$(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)
check-source: $(LIGHTNINGD_CLOSING_SRC_NOGEN:%=check-src-include-order/%)
check-source-bolt: $(LIGHTNINGD_CLOSING_SRC:%=bolt-check/%) $(LIGHTNINGD_CLOSING_HEADERS:%=bolt-check/%)
check-whitespace: $(LIGHTNINGD_CLOSING_SRC_NOGEN:%=check-whitespace/%) $(LIGHTNINGD_CLOSING_HEADERS_NOGEN:%=check-whitespace/%)
clean: lightningd/closing-clean
lightningd/closing-clean:
$(RM) $(LIGHTNINGD_CLOSING_OBJS) gen_*
-include lightningd/closing/test/Makefile

View File

@ -0,0 +1,366 @@
#include <bitcoin/script.h>
#include <close_tx.h>
#include <daemon/htlc.h>
#include <errno.h>
#include <inttypes.h>
#include <lightningd/closing/gen_closing_wire.h>
#include <lightningd/crypto_sync.h>
#include <lightningd/debug.h>
#include <lightningd/derive_basepoints.h>
#include <lightningd/status.h>
#include <lightningd/subd.h>
#include <signal.h>
#include <stdio.h>
#include <type_to_string.h>
#include <utils.h>
#include <version.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,
u8 *scriptpubkey[NUM_SIDES],
const struct sha256_double *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)
status_failed(WIRE_CLOSING_NEGOTIATION_ERROR,
"Funder cannot afford fee %"PRIu64
" (%"PRIu64" and %"PRIu64")",
fee, satoshi_out[LOCAL],
satoshi_out[REMOTE]);
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)
status_failed(WIRE_CLOSING_NEGOTIATION_ERROR,
"Both outputs below dust limit");
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;
}
int main(int argc, char *argv[])
{
struct crypto_state cs;
const tal_t *ctx = tal_tmpctx(NULL);
u8 *msg;
struct privkey seed;
struct pubkey funding_pubkey[NUM_SIDES];
struct sha256_double funding_txid;
u16 funding_txout;
u64 funding_satoshi, satoshi_out[NUM_SIDES];
u64 our_dust_limit;
u64 minfee, maxfee, sent_fee;
s64 last_received_fee = -1;
enum side funder;
u8 *scriptpubkey[NUM_SIDES], *funding_wscript;
struct channel_id channel_id;
struct secrets secrets;
secp256k1_ecdsa_signature sig;
if (argc == 2 && streq(argv[1], "--version")) {
printf("%s\n", version());
exit(0);
}
subdaemon_debug(argc, argv);
/* We handle write returning errors! */
signal(SIGCHLD, SIG_IGN);
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY
| SECP256K1_CONTEXT_SIGN);
status_setup_sync(REQ_FD);
msg = wire_sync_read(ctx, REQ_FD);
if (!fromwire_closing_init(ctx, msg, NULL,
&cs, &seed,
&funding_txid, &funding_txout,
&funding_satoshi,
&funding_pubkey[REMOTE],
&funder,
&satoshi_out[LOCAL],
&satoshi_out[REMOTE],
&our_dust_limit,
&minfee, &maxfee, &sent_fee,
&scriptpubkey[LOCAL],
&scriptpubkey[REMOTE])) {
status_failed(WIRE_CLOSING_PEER_BAD_MESSAGE,
"Bad init message %s", tal_hex(ctx, msg));
}
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]);
/* 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 then retransmit the last `closing_signed`.
*/
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 with the node responsible for paying
* the bitcoin fee paying `fee_satoshis`, then removing any
* output which is below its own `dust_limit_satoshis`. The
* sender MAY then also eliminate its own output from the
* mutual close transaction.
*/
tx = close_tx(tmpctx, scriptpubkey,
&funding_txid,
funding_txout,
funding_satoshi,
satoshi_out, funder, sent_fee, our_dust_limit);
if (!tx)
status_failed(WIRE_CLOSING_NEGOTIATION_ERROR,
"Both outputs below dust limit");
/* BOLT #2:
*
* The sender MAY then also eliminate its own output from the
* mutual close transaction.
*/
/* (We don't do this). */
sign_tx_input(tx, 0, NULL, funding_wscript,
&secrets.funding_privkey,
&funding_pubkey[LOCAL],
&sig);
/* Tell master we're making an offer, wait for db commit. */
msg = towire_closing_offered_signature(tmpctx, sent_fee, &sig);
if (!wire_sync_write(REQ_FD, msg))
status_failed(WIRE_CLOSING_INTERNAL_ERROR,
"Writing offer to master failed: %s",
strerror(errno));
msg = wire_sync_read(tmpctx, REQ_FD);
if (!fromwire_closing_offered_signature_reply(msg, NULL))
status_failed(WIRE_CLOSING_INTERNAL_ERROR,
"Reading offer reply from master failed");
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(WIRE_CLOSING_PEER_WRITE_FAILED,
"Writing closing_signed");
/* Did we just agree with them? If so, we're done. */
if (sent_fee == last_received_fee)
break;
again:
msg = sync_crypto_read(tmpctx, &cs, PEER_FD);
if (!msg)
status_failed(WIRE_CLOSING_PEER_READ_FAILED,
"Reading input");
/* We don't send gossip at this stage, but we can recv it */
if (is_gossip_msg(msg)) {
if (!wire_sync_write(GOSSIP_FD, take(msg)))
status_failed(WIRE_CLOSING_GOSSIP_FAILED,
"Writing gossip");
goto again;
}
/* 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;
}
if (!fromwire_closing_signed(msg, NULL, &channel_id,
&received_fee, &sig))
status_failed(WIRE_CLOSING_PEER_BAD_MESSAGE,
"Expected closing_signed: %s",
tal_hex(trc, msg));
/* BOLT #2:
*
* The receiver MUST check `signature` is valid for either the
* close transaction with the given `fee_satoshis` as detailed
* above and its own `dust_limit_satoshis` OR that same
* transaction with the sender's output eliminated, and MUST
* fail the connection if it is not.
*/
tx = close_tx(tmpctx, 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];
trimmed = close_tx(tmpctx, 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)) {
status_failed(WIRE_CLOSING_PEER_BAD_MESSAGE,
"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");
}
}
status_trace("Received fee offer %"PRIu64, received_fee);
/* Is fee reasonable? Tell master. */
if (received_fee < minfee) {
status_trace("Fee too low, below %"PRIu64, minfee);
limit_fee = minfee;
} else if (received_fee > maxfee) {
status_trace("Fee too high, above %"PRIu64, maxfee);
limit_fee = maxfee;
} else {
status_trace("Fee accepted.");
msg = towire_closing_received_signature(tmpctx,
received_fee,
&sig);
if (!wire_sync_write(REQ_FD, take(msg)))
status_failed(WIRE_CLOSING_INTERNAL_ERROR,
"Writing received to master: %s",
strerror(errno));
msg = wire_sync_read(tmpctx, REQ_FD);
if (!fromwire_closing_received_signature_reply(msg,NULL))
status_failed(WIRE_CLOSING_INTERNAL_ERROR,
"Bad received reply from master");
limit_fee = received_fee;
}
/* BOLT #2:
*
* Once a node has sent or received a `closing_signed` with
* matching `fee_satoshis` it SHOULD close the connection and
* SHOULD sign and broadcast the final closing transaction.
*/
if (received_fee == sent_fee)
break;
/* Check that they moved in right direction. Not really
* a requirement that we check, but good to catch their bugs. */
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)
status_failed(WIRE_CLOSING_NEGOTIATION_ERROR,
"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)
status_failed(WIRE_CLOSING_NEGOTIATION_ERROR,
"Their fee jumped %"
PRIu64" to %"PRIu64
" when ours was %"PRIu64,
last_received_fee,
received_fee,
sent_fee);
}
/* BOLT #2:
*
* ...otherwise it SHOULD 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)
status_failed(WIRE_CLOSING_NEGOTIATION_ERROR,
"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! */
wire_sync_write(REQ_FD, take(towire_closing_complete(ctx)));
tal_free(ctx);
return 0;
}

View File

@ -0,0 +1,54 @@
# Shouldn't happen
closing_bad_command,0x8000
# Also shouldn't happen
closing_gossip_failed,0x8001
closing_internal_error,0x8003
# These are due to peer.
closing_peer_write_failed,0x8010
closing_peer_read_failed,0x8011
closing_peer_bad_message,0x8012
closing_peer_bad_message,,len,u16
closing_peer_bad_message,,msg,len*u8
closing_negotiation_error,0x8013
closing_negotiation_error,,len,u16
closing_negotiation_error,,msg,len*u8
#include <lightningd/cryptomsg.h>
#include <lightningd/htlc_wire.h>
# Begin! (passes peer fd, gossipd-client fd)
closing_init,1
closing_init,,crypto_state,struct crypto_state
closing_init,,seed,struct privkey
closing_init,,funding_txid,struct sha256_double
closing_init,,funding_txout,u16
closing_init,,funding_satoshi,u64
closing_init,,remote_fundingkey,struct pubkey
closing_init,,funder,enum side
closing_init,,local_msatoshi,u64
closing_init,,remote_msatoshi,u64
closing_init,,our_dust_limit,u64
closing_init,,min_fee_satoshi,u64
closing_init,,max_fee_satoshi,u64
closing_init,,initial_fee_satoshi,u64
closing_init,,local_scriptpubkey_len,u16
closing_init,,local_scriptpubkey,local_scriptpubkey_len*u8
closing_init,,remote_scriptpubkey_len,u16
closing_init,,remote_scriptpubkey,remote_scriptpubkey_len*u8
# We received an offer, save signature.
closing_received_signature,2
closing_received_signature,,fee_satoshi,u64
closing_received_signature,,signature,secp256k1_ecdsa_signature
closing_received_signature_reply,102
# We sent an offer, save it in case we crash.
closing_offered_signature,3
closing_offered_signature,,fee_satoshi,u64
closing_offered_signature,,signature,secp256k1_ecdsa_signature
closing_offered_signature_reply,103
# Negotiations complete, we're exiting.
closing_complete,4
1 # Shouldn't happen
2 closing_bad_command,0x8000
3 # Also shouldn't happen
4 closing_gossip_failed,0x8001
5 closing_internal_error,0x8003
6 # These are due to peer.
7 closing_peer_write_failed,0x8010
8 closing_peer_read_failed,0x8011
9 closing_peer_bad_message,0x8012
10 closing_peer_bad_message,,len,u16
11 closing_peer_bad_message,,msg,len*u8
12 closing_negotiation_error,0x8013
13 closing_negotiation_error,,len,u16
14 closing_negotiation_error,,msg,len*u8
15 #include <lightningd/cryptomsg.h>
16 #include <lightningd/htlc_wire.h>
17 # Begin! (passes peer fd, gossipd-client fd)
18 closing_init,1
19 closing_init,,crypto_state,struct crypto_state
20 closing_init,,seed,struct privkey
21 closing_init,,funding_txid,struct sha256_double
22 closing_init,,funding_txout,u16
23 closing_init,,funding_satoshi,u64
24 closing_init,,remote_fundingkey,struct pubkey
25 closing_init,,funder,enum side
26 closing_init,,local_msatoshi,u64
27 closing_init,,remote_msatoshi,u64
28 closing_init,,our_dust_limit,u64
29 closing_init,,min_fee_satoshi,u64
30 closing_init,,max_fee_satoshi,u64
31 closing_init,,initial_fee_satoshi,u64
32 closing_init,,local_scriptpubkey_len,u16
33 closing_init,,local_scriptpubkey,local_scriptpubkey_len*u8
34 closing_init,,remote_scriptpubkey_len,u16
35 closing_init,,remote_scriptpubkey,remote_scriptpubkey_len*u8
36 # We received an offer, save signature.
37 closing_received_signature,2
38 closing_received_signature,,fee_satoshi,u64
39 closing_received_signature,,signature,secp256k1_ecdsa_signature
40 closing_received_signature_reply,102
41 # We sent an offer, save it in case we crash.
42 closing_offered_signature,3
43 closing_offered_signature,,fee_satoshi,u64
44 closing_offered_signature,,signature,secp256k1_ecdsa_signature
45 closing_offered_signature_reply,103
46 # Negotiations complete, we're exiting.
47 closing_complete,4