From 87232ec25ce8bf2a3595aad68184f8117cc6a6f5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 5 Jul 2017 15:03:40 +0930 Subject: [PATCH] lightningd: new peer state CLOSINGD_COMPLETE. This is a transitional state, while we're waiting to see the closing tx onchain (which is To Be Implemented). The simplest way to do re-transmission is to re-use closingd, and just disallow any updates. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 39 ++++++++++++++++++++++++++++++--------- lightningd/peer_state.h | 3 +++ tests/test_lightningd.py | 18 ++++++++++-------- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 8f044bcdd..e66a2828e 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -299,7 +299,8 @@ static bool get_peer_gossipfd_closingd_reply(struct subd *subd, const u8 *msg, goto close_gossipfd; } - if (peer->state != CLOSINGD_SIGEXCHANGE) { + if (peer->state != CLOSINGD_SIGEXCHANGE + && peer->state != CLOSINGD_COMPLETE) { log_unusual(subd->log, "Gossipd gave fd, but peer %s %s", type_to_string(ggf, struct pubkey, &ggf->id), peer_state_name(peer->state)); @@ -401,6 +402,7 @@ static bool peer_reconnected(struct lightningd *ld, return true; case CLOSINGD_SIGEXCHANGE: + case CLOSINGD_COMPLETE: /* We need the gossipfd now */ get_gossip_fd_for_closingd_reconnect(ld, id, peer->unique_id, fd, cs); return true; @@ -1181,6 +1183,18 @@ static int peer_received_closing_signature(struct peer *peer, const u8 *msg) return -1; } + /* If we were only doing this to retransmit, we should only send one. */ + if (peer->state == CLOSINGD_COMPLETE) { + if (fee_satoshi != peer->closing_fee_sent) { + peer_internal_error(peer, + "CLOSINGD_COMPLETE:" + " Bad offer %"PRIu64 + " not %"PRIu64, + fee_satoshi, peer->closing_fee_sent); + return -1; + } + } + /* FIXME: Make sure offer is in useful range! */ /* FIXME: Make sure signature is correct! */ /* FIXME: save to db. */ @@ -1207,14 +1221,21 @@ static int peer_offered_closing_signature(struct peer *peer, const u8 *msg) return -1; } - /* FIXME: Make sure offer is in useful range! */ - /* FIXME: Make sure signature is correct! */ - /* FIXME: save to db. */ + /* If we were only doing this to retransmit, we ignore its offer. */ + if (peer->state == CLOSINGD_COMPLETE) { + log_debug(peer->log, + "CLOSINGD_COMPLETE: Ignoring their offer %"PRIu64, + fee_satoshi); + } else { + /* FIXME: Make sure offer is in useful range! */ + /* FIXME: Make sure signature is correct! */ + /* FIXME: save to db. */ - peer->closing_fee_sent = fee_satoshi; - tal_free(peer->closing_sig_sent); - peer->closing_sig_sent - = tal_dup(peer, secp256k1_ecdsa_signature, &sig); + peer->closing_fee_sent = fee_satoshi; + tal_free(peer->closing_sig_sent); + peer->closing_sig_sent + = tal_dup(peer, secp256k1_ecdsa_signature, &sig); + } /* OK, you can continue now. */ subd_send_msg(peer->owner, @@ -1285,7 +1306,7 @@ static int peer_closing_complete(struct peer *peer, const u8 *msg) * if they beat us to the broadcast). */ broadcast_tx(peer->ld->topology, peer, tx, NULL); - /* FIXME: Set state. */ + peer_set_condition(peer, CLOSINGD_SIGEXCHANGE, CLOSINGD_COMPLETE); return -1; } diff --git a/lightningd/peer_state.h b/lightningd/peer_state.h index 3a6e8d4c3..4c557752a 100644 --- a/lightningd/peer_state.h +++ b/lightningd/peer_state.h @@ -23,6 +23,9 @@ enum peer_state { /* Exchanging signatures on closing tx. */ CLOSINGD_SIGEXCHANGE, + /* Waiting for onchain event. */ + CLOSINGD_COMPLETE, + /* Various onchain states. */ ONCHAIND_CHEATED, ONCHAIND_THEIR_UNILATERAL, diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index ba89bb6fd..925799198 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -718,11 +718,12 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('-> CLOSINGD_SIGEXCHANGE') l2.daemon.wait_for_log('-> CLOSINGD_SIGEXCHANGE') - # And should put closing into mempool. - l1.daemon.wait_for_log('sendrawtx exit 0') - l2.daemon.wait_for_log('sendrawtx exit 0') + # And should put closing into mempool (happens async, so + # CLOSINGD_COMPLETE may come first). + l1.daemon.wait_for_logs(['sendrawtx exit 0', '-> CLOSINGD_COMPLETE']) + l2.daemon.wait_for_logs(['sendrawtx exit 0', '-> CLOSINGD_COMPLETE']) assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 1 - + def test_closing_negotiation_reconnect(self): disconnects = ['-WIRE_CLOSING_SIGNED', '@WIRE_CLOSING_SIGNED', @@ -744,11 +745,12 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('-> CLOSINGD_SIGEXCHANGE') l2.daemon.wait_for_log('-> CLOSINGD_SIGEXCHANGE') - # And should put closing into mempool. - l1.daemon.wait_for_log('sendrawtx exit 0') - l2.daemon.wait_for_log('sendrawtx exit 0') + # And should put closing into mempool (happens async, so + # CLOSINGD_COMPLETE may come first). + l1.daemon.wait_for_logs(['sendrawtx exit 0', '-> CLOSINGD_COMPLETE']) + l2.daemon.wait_for_logs(['sendrawtx exit 0', '-> CLOSINGD_COMPLETE']) assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 1 - + def test_json_addfunds(self): sat = 10**6 l1 = self.node_factory.get_node(legacy=False)