From 12d8be4fdcfe3a1a60306c212b9f6870924fe36b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 17 Jul 2019 07:17:14 +0930 Subject: [PATCH] devtools: have mkcommit support HTLCs. This allows for complete channel simulation, including HTLC transactions, but means we use higher-level primitives to make the easy. Signed-off-by: Rusty Russell --- devtools/Makefile | 2 +- devtools/mkcommit.c | 362 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 283 insertions(+), 81 deletions(-) diff --git a/devtools/Makefile b/devtools/Makefile index bf96a74f2..a62888440 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -61,7 +61,7 @@ devtools/gen_print_onion_wire.o: devtools/gen_print_onion_wire.h devtools/print_ devtools/bolt11-cli: $(DEVTOOLS_OBJS) $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/bolt11-cli.o -devtools/mkcommit: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) common/derive_basepoints.o common/keyset.o common/key_derive.o common/initial_commit_tx.o common/permute_tx.o wire/fromwire.o wire/towire.o devtools/mkcommit.o +devtools/mkcommit: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) common/derive_basepoints.o common/keyset.o common/key_derive.o common/initial_commit_tx.o common/permute_tx.o wire/fromwire.o wire/towire.o devtools/mkcommit.o channeld/full_channel.o common/initial_channel.o common/htlc_state.o common/pseudorand.o common/htlc_tx.o channeld/commit_tx.o common/htlc_trim.o devtools/mkfunding: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o common/funding_tx.o common/utxo.o common/permute_tx.o common/key_derive.o devtools/mkfunding.o diff --git a/devtools/mkcommit.c b/devtools/mkcommit.c index aba668f9c..0a55cd0bc 100644 --- a/devtools/mkcommit.c +++ b/devtools/mkcommit.c @@ -10,17 +10,36 @@ #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(); } /* Code to make a commitment tx, useful for generating test cases. */ @@ -111,24 +130,25 @@ static void print_basepoints(const char *desc, type_to_string(NULL, struct pubkey, &per_commitment_point)); } -struct settings { - u32 to_self_delay; - struct amount_sat dustlimit; - struct amount_sat reserve; -}; - -static int parse_settings(char *argv[], - struct settings *settings, - const char *desc) +static int parse_config(char *argv[], + struct channel_config *config, + const char *desc) { int argnum = 0; - settings->to_self_delay = atoi(argv[argnum]); + + config->id = 0; + /* FIXME: Allow overriding these on cmdline! */ + config->max_htlc_value_in_flight = AMOUNT_MSAT(-1ULL); + config->htlc_minimum = AMOUNT_MSAT(0); + config->max_accepted_htlcs = 483; + + config->to_self_delay = atoi(argv[argnum]); argnum++; - if (!parse_amount_sat(&settings->dustlimit, + if (!parse_amount_sat(&config->dust_limit, argv[argnum], strlen(argv[argnum]))) errx(1, "Bad %s dustlimit", desc); argnum++; - if (!parse_amount_sat(&settings->reserve, + if (!parse_amount_sat(&config->channel_reserve, argv[argnum], strlen(argv[argnum]))) errx(1, "Bad %s reserve", desc); argnum++; @@ -145,6 +165,58 @@ static char *sig_as_hex(const struct bitcoin_signature *sig) return tal_hexstr(NULL, compact_sig, sizeof(compact_sig)); } +static int parse_htlc(char *argv[], + struct added_htlc **htlcs, + enum htlc_state **htlc_states, + struct preimage **preimages) +{ + struct added_htlc add; + int argnum = 0; + struct preimage preimage; + + add.id = tal_count(*htlcs); + if (streq(argv[argnum], "local")) + tal_arr_expand(htlc_states, SENT_ADD_ACK_REVOCATION); + else if (streq(argv[argnum], "remote")) + tal_arr_expand(htlc_states, RCVD_ADD_ACK_REVOCATION); + else + errx(1, "Bad htlc offer: %s should be 'local' or 'remote'", + argv[argnum]); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &preimage, sizeof(preimage))) + errx(1, "Bad payment-preimage %s", argv[argnum]); + tal_arr_expand(preimages, preimage); + sha256(&add.payment_hash, &preimage, sizeof(preimage)); + argnum++; + if (!parse_amount_msat(&add.amount, + argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad htlc amount %s", argv[argnum]); + argnum++; + add.cltv_expiry = atoi(argv[argnum]); + argnum++; + + printf("# HTLC %"PRIu64": %s amount=%s preimage=%s payment_hash=%s cltv=%u\n", + add.id, argv[0], + type_to_string(tmpctx, struct amount_msat, &add.amount), + type_to_string(tmpctx, struct preimage, &preimage), + type_to_string(tmpctx, struct sha256, &add.payment_hash), + add.cltv_expiry); + + tal_arr_expand(htlcs, add); + return argnum; +} + +static const struct preimage *preimage_of(const struct sha256 *hash, + const struct added_htlc *htlcs, + const struct preimage *preimages) +{ + for (size_t i = 0; i < tal_count(preimages); i++) + if (sha256_eq(hash, &htlcs[i].payment_hash)) + return preimages + i; + abort(); +} + int main(int argc, char *argv[]) { struct secrets local, remote; @@ -155,26 +227,38 @@ int main(int argc, char *argv[]) struct amount_sat funding_amount; struct bitcoin_txid funding_txid; unsigned int funding_outnum; - unsigned int feerate_per_kw; + u32 feerate_per_kw[NUM_SIDES]; struct pubkey local_per_commit_point, remote_per_commit_point; - struct keyset localkeys, remotekeys; struct bitcoin_signature local_sig, remote_sig; - struct settings localsettings, remotesettings; + struct channel_config localconfig, remoteconfig; struct amount_msat local_msat, remote_msat; int argnum; - struct bitcoin_tx *local_tx, *remote_tx; + struct bitcoin_tx **local_txs, **remote_txs; enum side fee_payer; - char *err; u8 **witness; + const u8 **wscripts; + struct channel *channel; + struct added_htlc *htlcs = tal_arr(NULL, struct added_htlc, 0); + enum htlc_state *hstates = tal_arr(NULL, enum htlc_state, 0); + struct preimage *preimages = tal_arr(NULL, struct preimage, 0); + const struct htlc **htlcmap; + struct privkey local_htlc_privkey, remote_htlc_privkey; + struct pubkey local_htlc_pubkey, remote_htlc_pubkey; setup_locale(); secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); - if (argc != 1 + 7 + 3*2 + 6*2) - errx(1, "Usage: mkcommit \n" - "Where are:\n" + if (argv[1] && streq(argv[1], "-v")) { + verbose = true; + argv++; + argc--; + } + + if (argc < 1 + 7 + 3*2 + 6*2) + errx(1, "Usage: mkcommit [-v] [...]\n" + "Where are:\n" " \n" " \n" " \n" @@ -184,7 +268,12 @@ int main(int argc, char *argv[]) " \n" " \n" " \n" - " "); + " \n" + "Where s are:\n" + " \n" + " \n" + " \n" + " \n"); argnum = 1; commitnum = atol(argv[argnum++]); @@ -196,7 +285,7 @@ int main(int argc, char *argv[]) if (!parse_amount_sat(&funding_amount, argv[argnum], strlen(argv[argnum]))) errx(1, "Bad funding-amount"); argnum++; - feerate_per_kw = atoi(argv[argnum++]); + feerate_per_kw[LOCAL] = feerate_per_kw[REMOTE] = atoi(argv[argnum++]); if (!parse_amount_msat(&local_msat, argv[argnum], strlen(argv[argnum]))) errx(1, "Bad local-msat"); @@ -209,8 +298,8 @@ int main(int argc, char *argv[]) errx(1, "fee-payer must be 'local' or 'remote'"); argnum++; - argnum += parse_settings(argv + argnum, &localsettings, "local"); - argnum += parse_settings(argv + argnum, &remotesettings, "remote"); + argnum += parse_config(argv + argnum, &localconfig, "local"); + argnum += parse_config(argv + argnum, &remoteconfig, "remote"); argnum += parse_secrets(argv + argnum, &local, &localseed, "local"); argnum += parse_secrets(argv + argnum, &remote, &remoteseed, "remote"); @@ -218,6 +307,11 @@ int main(int argc, char *argv[]) if (!amount_sat_sub_msat(&remote_msat, funding_amount, local_msat)) errx(1, "Can't afford local_msat"); + printf("## HTLCs\n"); + while (argnum < argc) + argnum += parse_htlc(argv + argnum, &htlcs, &hstates, &preimages); + printf("\n"); + if (!pubkey_from_privkey(&local.funding_privkey, &funding_localkey) || !pubkey_from_secret(&local.revocation_basepoint_secret, &localbase.revocation) @@ -247,6 +341,21 @@ int main(int argc, char *argv[]) &remote, &remoteseed, &remotebase, &funding_remotekey, commitnum); + channel = new_full_channel(NULL, + &chainparams_for_network("regtest") + ->genesis_blockhash, + &funding_txid, funding_outnum, 1, + funding_amount, + local_msat, + feerate_per_kw, + &localconfig, &remoteconfig, + &localbase, &remotebase, + &funding_localkey, &funding_remotekey, + fee_payer); + + if (!channel_force_htlcs(channel, htlcs, hstates, NULL, NULL, NULL, NULL)) + errx(1, "Cannot add HTLCs"); + u8 *funding_wscript = bitcoin_redeem_2of2(NULL, &funding_localkey, &funding_remotekey); @@ -255,27 +364,8 @@ int main(int argc, char *argv[]) if (!per_commit_point(&localseed, &local_per_commit_point, commitnum)) errx(1, "Bad deriving local per-commitment-point"); - if (!derive_keyset(&local_per_commit_point, &localbase, &remotebase, - &localkeys)) - errx(1, "Bad deriving local keyset"); - - local_tx = initial_commit_tx(NULL, &funding_txid, funding_outnum, - funding_amount, fee_payer, - localsettings.to_self_delay, - &localkeys, - feerate_per_kw, - localsettings.dustlimit, - local_msat, - remote_msat, - localsettings.reserve, - commitnum - ^ commit_number_obscurer(&localbase.payment, - &remotebase.payment), - LOCAL, &err); - if (!local_tx) - errx(1, "Can't make local commit tx: %s", err); - local_tx->input_amounts[0] - = tal_dup(local_tx, struct amount_sat, &funding_amount); + local_txs = channel_txs(NULL, &htlcmap, &wscripts, channel, + &local_per_commit_point, commitnum, LOCAL); printf("## local_commitment\n" "# input amount %s, funding_wscript %s, key %s\n", @@ -283,16 +373,16 @@ int main(int argc, char *argv[]) tal_hex(NULL, funding_wscript), type_to_string(NULL, struct pubkey, &funding_localkey)); printf("# unsigned local commitment tx: %s\n", - tal_hex(NULL, linearize_tx(NULL, local_tx))); + tal_hex(NULL, linearize_tx(NULL, local_txs[0]))); - sign_tx_input(local_tx, 0, NULL, funding_wscript, + sign_tx_input(local_txs[0], 0, NULL, funding_wscript, &local.funding_privkey, &funding_localkey, SIGHASH_ALL, &local_sig); printf("localsig_on_local: %s\n", sig_as_hex(&local_sig)); - sign_tx_input(local_tx, 0, NULL, funding_wscript, + sign_tx_input(local_txs[0], 0, NULL, funding_wscript, &remote.funding_privkey, &funding_remotekey, SIGHASH_ALL, @@ -302,36 +392,83 @@ int main(int argc, char *argv[]) witness = bitcoin_witness_2of2(NULL, &local_sig, &remote_sig, &funding_localkey, &funding_remotekey); - bitcoin_tx_input_set_witness(local_tx, 0, witness); - - printf("# signed local commitment: %s\n\n", - tal_hex(NULL, linearize_tx(NULL, local_tx))); + bitcoin_tx_input_set_witness(local_txs[0], 0, witness); + + printf("# signed local commitment: %s\n", + tal_hex(NULL, linearize_tx(NULL, local_txs[0]))); + + if (!derive_simple_privkey(&local.htlc_basepoint_secret, + &localbase.htlc, + &local_per_commit_point, + &local_htlc_privkey)) + errx(1, "Failure deriving local htlc privkey"); + + if (!derive_simple_key(&localbase.htlc, + &local_per_commit_point, + &local_htlc_pubkey)) + errx(1, "Failure deriving local htlc pubkey"); + + if (!derive_simple_privkey(&remote.htlc_basepoint_secret, + &remotebase.htlc, + &local_per_commit_point, + &remote_htlc_privkey)) + errx(1, "Failure deriving remote htlc privkey"); + + if (!derive_simple_key(&remotebase.htlc, + &local_per_commit_point, + &remote_htlc_pubkey)) + errx(1, "Failure deriving remote htlc pubkey"); + + for (size_t i = 0; i < tal_count(htlcmap); i++) { + struct bitcoin_signature local_htlc_sig, remote_htlc_sig; + struct amount_sat amt; + + if (!htlcmap[i]) + continue; + printf("# Output %zu: %s HTLC %"PRIu64"\n", + i, side_to_str(htlc_owner(htlcmap[i])), htlcmap[i]->id); + printf("# unsigned htlc tx for output %zu: %s\n", + i, tal_hex(NULL, linearize_tx(NULL, local_txs[1+i]))); + amt = amount_msat_to_sat_round_down(htlcmap[i]->amount); + local_txs[1+i]->input_amounts[0] + = tal_dup(local_txs[1+i], struct amount_sat, &amt); + + printf("# wscript: %s\n", tal_hex(NULL, wscripts[1+i])); + sign_tx_input(local_txs[1+i], 0, NULL, wscripts[1+i], + &local_htlc_privkey, &local_htlc_pubkey, + SIGHASH_ALL, &local_htlc_sig); + sign_tx_input(local_txs[1+i], 0, NULL, wscripts[1+i], + &remote_htlc_privkey, &remote_htlc_pubkey, + SIGHASH_ALL, &remote_htlc_sig); + printf("localsig_on_local output %zu: %s\n", + i, sig_as_hex(&local_htlc_sig)); + printf("remotesig_on_local output %zu: %s\n", + i, sig_as_hex(&remote_htlc_sig)); + + if (htlc_owner(htlcmap[i]) == LOCAL) + witness = bitcoin_witness_htlc_timeout_tx(NULL, + &local_htlc_sig, + &remote_htlc_sig, + wscripts[1+i]); + else + witness = bitcoin_witness_htlc_success_tx(NULL, + &local_htlc_sig, + &remote_htlc_sig, + preimage_of(&htlcmap[i]->rhash, htlcs, preimages), + wscripts[1+i]); + bitcoin_tx_input_set_witness(local_txs[1+i], 0, witness); + printf("htlc tx for output %zu: %s\n", + i, tal_hex(NULL, linearize_tx(NULL, local_txs[1+i]))); + } + printf("\n"); /* Create the remote commitment tx */ if (!per_commit_point(&remoteseed, &remote_per_commit_point, commitnum)) errx(1, "Bad deriving remote per-commitment-point"); - if (!derive_keyset(&remote_per_commit_point, &remotebase, &localbase, - &remotekeys)) - errx(1, "Bad deriving remote keyset"); - - remote_tx = initial_commit_tx(NULL, &funding_txid, funding_outnum, - funding_amount, - fee_payer, - remotesettings.to_self_delay, - &remotekeys, - feerate_per_kw, - remotesettings.dustlimit, - remote_msat, - local_msat, - remotesettings.reserve, - commitnum - ^ commit_number_obscurer(&localbase.payment, - &remotebase.payment), - REMOTE, &err); - if (!remote_tx) - errx(1, "Can't make remote commit tx: %s", err); - remote_tx->input_amounts[0] - = tal_dup(remote_tx, struct amount_sat, &funding_amount); + remote_txs = channel_txs(NULL, &htlcmap, &wscripts, channel, + &remote_per_commit_point, commitnum, REMOTE); + remote_txs[0]->input_amounts[0] + = tal_dup(remote_txs[0], struct amount_sat, &funding_amount); printf("## remote_commitment\n" "# input amount %s, funding_wscript %s, key %s\n", @@ -339,16 +476,16 @@ int main(int argc, char *argv[]) tal_hex(NULL, funding_wscript), type_to_string(NULL, struct pubkey, &funding_remotekey)); printf("# unsigned remote commitment tx: %s\n", - tal_hex(NULL, linearize_tx(NULL, remote_tx))); + tal_hex(NULL, linearize_tx(NULL, remote_txs[0]))); - sign_tx_input(remote_tx, 0, NULL, funding_wscript, + sign_tx_input(remote_txs[0], 0, NULL, funding_wscript, &local.funding_privkey, &funding_localkey, SIGHASH_ALL, &local_sig); printf("localsig_on_remote: %s\n", sig_as_hex(&local_sig)); - sign_tx_input(remote_tx, 0, NULL, funding_wscript, + sign_tx_input(remote_txs[0], 0, NULL, funding_wscript, &remote.funding_privkey, &funding_remotekey, SIGHASH_ALL, @@ -358,10 +495,75 @@ int main(int argc, char *argv[]) witness = bitcoin_witness_2of2(NULL, &local_sig, &remote_sig, &funding_localkey, &funding_remotekey); - bitcoin_tx_input_set_witness(remote_tx, 0, witness); + bitcoin_tx_input_set_witness(remote_txs[0], 0, witness); printf("# signed remote commitment: %s\n", - tal_hex(NULL, linearize_tx(NULL, remote_tx))); + tal_hex(NULL, linearize_tx(NULL, remote_txs[0]))); + + if (!derive_simple_privkey(&local.htlc_basepoint_secret, + &localbase.htlc, + &remote_per_commit_point, + &local_htlc_privkey)) + errx(1, "Failure deriving local htlc privkey"); + + if (!derive_simple_key(&localbase.htlc, + &remote_per_commit_point, + &local_htlc_pubkey)) + errx(1, "Failure deriving local htlc pubkey"); + + if (!derive_simple_privkey(&remote.htlc_basepoint_secret, + &remotebase.htlc, + &remote_per_commit_point, + &remote_htlc_privkey)) + errx(1, "Failure deriving remote htlc privkey"); + + if (!derive_simple_key(&remotebase.htlc, + &remote_per_commit_point, + &remote_htlc_pubkey)) + errx(1, "Failure deriving remote htlc pubkey"); + + for (size_t i = 0; i < tal_count(htlcmap); i++) { + struct bitcoin_signature local_htlc_sig, remote_htlc_sig; + struct amount_sat amt; + + if (!htlcmap[i]) + continue; + printf("# Output %zu: %s HTLC %"PRIu64"\n", + i, side_to_str(htlc_owner(htlcmap[i])), htlcmap[i]->id); + printf("# unsigned htlc tx for output %zu: %s\n", + i, tal_hex(NULL, linearize_tx(NULL, remote_txs[1+i]))); + amt = amount_msat_to_sat_round_down(htlcmap[i]->amount); + remote_txs[1+i]->input_amounts[0] + = tal_dup(remote_txs[1+i], struct amount_sat, &amt); + + printf("# wscript: %s\n", tal_hex(NULL, wscripts[1+i])); + sign_tx_input(remote_txs[1+i], 0, NULL, wscripts[1+i], + &local_htlc_privkey, &local_htlc_pubkey, + SIGHASH_ALL, &local_htlc_sig); + sign_tx_input(remote_txs[1+i], 0, NULL, wscripts[1+i], + &remote_htlc_privkey, &remote_htlc_pubkey, + SIGHASH_ALL, &remote_htlc_sig); + printf("localsig_on_remote output %zu: %s\n", + i, sig_as_hex(&local_htlc_sig)); + printf("remotesig_on_remote output %zu: %s\n", + i, sig_as_hex(&remote_htlc_sig)); + + if (htlc_owner(htlcmap[i]) == REMOTE) + witness = bitcoin_witness_htlc_timeout_tx(NULL, + &remote_htlc_sig, + &local_htlc_sig, + wscripts[1+i]); + else + witness = bitcoin_witness_htlc_success_tx(NULL, + &remote_htlc_sig, + &local_htlc_sig, + preimage_of(&htlcmap[i]->rhash, htlcs, preimages), + wscripts[1+i]); + bitcoin_tx_input_set_witness(remote_txs[1+i], 0, witness); + printf("htlc tx for output %zu: %s\n", + i, tal_hex(NULL, linearize_tx(NULL, remote_txs[1+i]))); + } + printf("\n"); return 0; }