mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +01:00
lightningd/closing: subdaemon for closing negotiation.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
d40334ce18
commit
e5a8a7502c
@ -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;
|
||||
|
@ -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)
|
||||
|
||||
|
59
lightningd/closing/Makefile
Normal file
59
lightningd/closing/Makefile
Normal 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
|
366
lightningd/closing/closing.c
Normal file
366
lightningd/closing/closing.c
Normal 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;
|
||||
}
|
54
lightningd/closing/closing_wire.csv
Normal file
54
lightningd/closing/closing_wire.csv
Normal 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
|
|
Loading…
Reference in New Issue
Block a user