diff --git a/devtools/Makefile b/devtools/Makefile index 577b0dd2b..8cfa41663 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -1,6 +1,6 @@ DEVTOOLS_SRC := devtools/gen_print_wire.c devtools/gen_print_onion_wire.c devtools/print_wire.c DEVTOOLS_OBJS := $(DEVTOOLS_SRC:.c=.o) -DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding +DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose DEVTOOLS_TOOL_SRC := $(DEVTOOLS:=.c) DEVTOOLS_TOOL_OBJS := $(DEVTOOLS_TOOL_SRC:.c=.o) @@ -66,6 +66,8 @@ devtools/mkcommit: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) common/d devtools/mkfunding: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o common/funding_tx.o common/utxo.o common/permute_tx.o common/key_derive.o devtools/mkfunding.o +devtools/mkclose: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o devtools/mkclose.o + # Make sure these depend on everything. ALL_PROGRAMS += $(DEVTOOLS) ALL_OBJS += $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS) diff --git a/devtools/mkclose.c b/devtools/mkclose.c new file mode 100644 index 000000000..a9f9abf37 --- /dev/null +++ b/devtools/mkclose.c @@ -0,0 +1,188 @@ +/* Code to make a mutual close tx, useful for generating test cases. + * + * For example, in the spec tests we use the following: + * + * lightning/devtools/mkclose 189c40b0728f382fe91c87270926584e48e0af3a6789f37454afee6c7560311d 0 0.00999877btc 253 0.00999877btc 0000000000000000000000000000000000000000000000000000000000000010 0000000000000000000000000000000000000000000000000000000000000020 026957e53b46df017bd6460681d068e1d23a7b027de398272d0b15f59b78d060a9 03a9f795ff2e4c27091f40e8f8277301824d1c3dfa6b0204aa92347314e41b1033 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool verbose = false; + +void status_fmt(enum log_level level, const char *fmt, ...) +{ + if (verbose) { + va_list ap; + + va_start(ap, fmt); + printf("#TRACE: "); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); + } +} + +void status_failed(enum status_failreason reason, const char *fmt, ...) +{ + abort(); +} + +static char *sig_as_hex(const struct bitcoin_signature *sig) +{ + u8 compact_sig[64]; + + secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, + compact_sig, + &sig->s); + return tal_hexstr(NULL, compact_sig, sizeof(compact_sig)); +} + +int main(int argc, char *argv[]) +{ + struct pubkey funding_pubkey[NUM_SIDES], outkey[NUM_SIDES]; + struct privkey funding_privkey[NUM_SIDES]; + struct amount_sat funding_amount; + struct bitcoin_txid funding_txid; + unsigned int funding_outnum; + u32 feerate_per_kw; + struct amount_sat fee; + struct bitcoin_signature local_sig, remote_sig; + struct amount_msat local_msat, remote_msat; + int argnum, num_outputs; + struct bitcoin_tx *tx; + u8 **witness; + const u8 *funding_wscript; + const struct chainparams *chainparams = chainparams_for_network("bitcoin"); + const struct amount_sat dust_limit = AMOUNT_SAT(546); + + setup_locale(); + + secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | + SECP256K1_CONTEXT_SIGN); + + if (argv[1] && streq(argv[1], "-v")) { + verbose = true; + argv++; + argc--; + } + + if (argc != 10) + errx(1, "Usage: mkclose [-v] \n" + "(Fee is deducted from local-msat)"); + + argnum = 1; + if (!bitcoin_txid_from_hex(argv[argnum], + strlen(argv[argnum]), &funding_txid)) + errx(1, "Bad funding-txid"); + argnum++; + funding_outnum = atoi(argv[argnum++]); + if (!parse_amount_sat(&funding_amount, argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad funding-amount"); + argnum++; + feerate_per_kw = atoi(argv[argnum++]); + if (!parse_amount_msat(&local_msat, + argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad local-msat"); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &funding_privkey[LOCAL], sizeof(funding_privkey[LOCAL]))) + errx(1, "Parsing local-funding_privkey"); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &funding_privkey[REMOTE], sizeof(funding_privkey[REMOTE]))) + errx(1, "Parsing remote-funding_privkey"); + argnum++; + if (!pubkey_from_hexstr(argv[argnum], strlen(argv[argnum]), + &outkey[LOCAL])) + errx(1, "Parsing local-close-pubkey"); + argnum++; + if (!pubkey_from_hexstr(argv[argnum], strlen(argv[argnum]), + &outkey[REMOTE])) + errx(1, "Parsing remote-close-pubkey"); + argnum++; + + fee = commit_tx_base_fee(feerate_per_kw, 0); + if (!amount_msat_sub_sat(&local_msat, local_msat, fee)) + errx(1, "Can't afford fee %s", + type_to_string(NULL, struct amount_sat, &fee)); + if (!amount_sat_sub_msat(&remote_msat, funding_amount, local_msat)) + errx(1, "Can't afford local_msat"); + + if (!pubkey_from_privkey(&funding_privkey[LOCAL], &funding_pubkey[LOCAL]) + || !pubkey_from_privkey(&funding_privkey[REMOTE], &funding_pubkey[REMOTE])) + errx(1, "Bad deriving funding pubkeys"); + + tx = bitcoin_tx(NULL, chainparams, 1, 2); + + /* Our input spends the anchor tx output. */ + bitcoin_tx_add_input(tx, &funding_txid, funding_outnum, + BITCOIN_TX_DEFAULT_SEQUENCE, funding_amount, NULL); + + num_outputs = 0; + if (amount_msat_greater_eq_sat(local_msat, dust_limit)) { + u8 *script = scriptpubkey_p2wpkh(NULL, &outkey[LOCAL]); + printf("# local witness script: %s\n", tal_hex(NULL, script)); + /* One output is to us. */ + bitcoin_tx_add_output(tx, script, + amount_msat_to_sat_round_down(local_msat)); + num_outputs++; + } else + printf("# local output trimmed\n"); + + if (amount_msat_greater_eq_sat(remote_msat, dust_limit)) { + u8 *script = scriptpubkey_p2wpkh(NULL, &outkey[REMOTE]); + printf("# remote witness script: %s\n", tal_hex(NULL, script)); + /* Other output is to them. */ + bitcoin_tx_add_output(tx, script, + amount_msat_to_sat_round_down(remote_msat)); + num_outputs++; + } else + printf("# remote output trimmed\n"); + + /* Can't have no outputs at all! */ + if (num_outputs == 0) + errx(1, "Can't afford any output!"); + + funding_wscript = bitcoin_redeem_2of2(NULL, + &funding_pubkey[LOCAL], + &funding_pubkey[REMOTE]); + printf("# funding witness script = %s\n", + tal_hex(NULL, funding_wscript)); + + /* Need input amount for signing */ + tx->input_amounts[0] = tal_dup(tx, struct amount_sat, &funding_amount); + sign_tx_input(tx, 0, NULL, funding_wscript, + &funding_privkey[LOCAL], + &funding_pubkey[LOCAL], + SIGHASH_ALL, &local_sig); + printf("localsig: %s\n", sig_as_hex(&local_sig)); + sign_tx_input(tx, 0, NULL, funding_wscript, + &funding_privkey[REMOTE], + &funding_pubkey[REMOTE], + SIGHASH_ALL, &remote_sig); + printf("remotesig: %s\n", sig_as_hex(&remote_sig)); + + witness = + bitcoin_witness_2of2(NULL, &local_sig, &remote_sig, + &funding_pubkey[LOCAL], + &funding_pubkey[REMOTE]); + bitcoin_tx_input_set_witness(tx, 0, witness); + printf("# signed close transaction: %s\n", + tal_hex(NULL, linearize_tx(NULL, tx))); + + return 0; +}