diff --git a/common/initial_commit_tx.h b/common/initial_commit_tx.h index 9bbb01200..12a5e1b44 100644 --- a/common/initial_commit_tx.h +++ b/common/initial_commit_tx.h @@ -6,6 +6,7 @@ #include #include +struct bitcoin_txid; struct keyset; /* BOLT #3: diff --git a/devtools/Makefile b/devtools/Makefile index 647f2414c..bf96a74f2 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 := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding DEVTOOLS_TOOL_SRC := $(DEVTOOLS:=.c) DEVTOOLS_TOOL_OBJS := $(DEVTOOLS_TOOL_SRC:.c=.o) @@ -61,6 +61,10 @@ 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/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 + # Make sure these depend on everything. ALL_PROGRAMS += $(DEVTOOLS) ALL_OBJS += $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS) diff --git a/devtools/mkcommit.c b/devtools/mkcommit.c new file mode 100644 index 000000000..aba668f9c --- /dev/null +++ b/devtools/mkcommit.c @@ -0,0 +1,367 @@ +/* For example, in the spec tests we use the following: + * + * lightning/devtools/mkcommit 0 41085b995c1f591cfc3ae79ccde012bf0b37c7bde23d80a61c9732bdd6210b2f 0 999878sat 253 999878sat local \ + 6 546 9900sat \ + 6 546 9900sat \ + 0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000021 0000000000000000000000000000000000000000000000000000000000000022 0000000000000000000000000000000000000000000000000000000000000023 0000000000000000000000000000000000000000000000000000000000000024 \ + 0000000000000000000000000000000000000000000000000000000000000010 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 0000000000000000000000000000000000000000000000000000000000000011 0000000000000000000000000000000000000000000000000000000000000012 0000000000000000000000000000000000000000000000000000000000000013 0000000000000000000000000000000000000000000000000000000000000014 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void status_fmt(enum log_level level, const char *fmt, ...) +{ +} + +/* Code to make a commitment tx, useful for generating test cases. */ +static int parse_secrets(char *argv[], + struct secrets *secrets, + struct sha256 *seed, + const char *desc) +{ + int argnum = 0; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &secrets->funding_privkey, + sizeof(secrets->funding_privkey))) + errx(1, "Parsing %s.funding_privkey", desc); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + seed, sizeof(*seed))) + errx(1, "Parsing %s seed", desc); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &secrets->revocation_basepoint_secret, + sizeof(secrets->revocation_basepoint_secret))) + errx(1, "Parsing %s.revocation_basepoint_secret", desc); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &secrets->payment_basepoint_secret, + sizeof(secrets->payment_basepoint_secret))) + errx(1, "Parsing %s.payment_basepoint_secret", desc); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &secrets->delayed_payment_basepoint_secret, + sizeof(secrets->delayed_payment_basepoint_secret))) + errx(1, "Parsing %s.delayed_payment_basepoint_secret", desc); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &secrets->htlc_basepoint_secret, + sizeof(secrets->htlc_basepoint_secret))) + errx(1, "Parsing %s.htlc_basepoint_secret", desc); + argnum++; + return argnum; +} + +static void print_basepoints(const char *desc, + const struct secrets *secrets, + const struct sha256 *shaseed, + const struct basepoints *basepoints, + const struct pubkey *fundingkey, + u64 commitnum) +{ + struct secret per_commitment_secret; + struct pubkey per_commitment_point; + + printf("## %s\n", desc); + printf("# funding_privkey=%s\n", + type_to_string(NULL, struct secret, &secrets->funding_privkey.secret)); + printf("funding_pubkey=%s\n", + type_to_string(NULL, struct pubkey, fundingkey)); + printf("# revocation_basepoint_secret=%s\n", + type_to_string(NULL, struct secret, + &secrets->revocation_basepoint_secret)); + printf("revocation_basepoint=%s\n", + type_to_string(NULL, struct pubkey, &basepoints->revocation)); + printf("# payment_basepoint_secret=%s\n", + type_to_string(NULL, struct secret, + &secrets->payment_basepoint_secret)); + printf("payment_basepoint=%s\n", + type_to_string(NULL, struct pubkey, &basepoints->payment)); + printf("# delayed_payment_basepoint_secret=%s\n", + type_to_string(NULL, struct secret, + &secrets->delayed_payment_basepoint_secret)); + printf("delayed_payment_basepoint=%s\n", + type_to_string(NULL, struct pubkey, &basepoints->delayed_payment)); + printf("# htlc_basepoint_secret=%s\n", + type_to_string(NULL, struct secret, + &secrets->htlc_basepoint_secret)); + printf("htlc_basepoint=%s\n", + type_to_string(NULL, struct pubkey, &basepoints->htlc)); + if (!per_commit_secret(shaseed, &per_commitment_secret, commitnum)) + errx(1, "Bad deriving %s per_commitment_secret", desc); + if (!per_commit_point(shaseed, &per_commitment_point, commitnum)) + errx(1, "Bad deriving %s per_commitment_point", desc); + printf("# shachain seed=%s\n", + type_to_string(NULL, struct sha256, shaseed)); + printf("# per_commitment_secret %"PRIu64"=%s\n", + commitnum, + type_to_string(NULL, struct secret, &per_commitment_secret)); + printf("per_commitment_point %"PRIu64"=%s\n\n", + commitnum, + 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) +{ + int argnum = 0; + settings->to_self_delay = atoi(argv[argnum]); + argnum++; + if (!parse_amount_sat(&settings->dustlimit, + argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad %s dustlimit", desc); + argnum++; + if (!parse_amount_sat(&settings->reserve, + argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad %s reserve", desc); + argnum++; + return argnum; +} + +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 secrets local, remote; + struct sha256 localseed, remoteseed; + struct basepoints localbase, remotebase; + struct pubkey funding_localkey, funding_remotekey; + u64 commitnum; + struct amount_sat funding_amount; + struct bitcoin_txid funding_txid; + unsigned int funding_outnum; + unsigned int feerate_per_kw; + 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 amount_msat local_msat, remote_msat; + int argnum; + struct bitcoin_tx *local_tx, *remote_tx; + enum side fee_payer; + char *err; + u8 **witness; + + 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" + " \n" + " \n" + " \n" + "Where are:\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "); + + argnum = 1; + commitnum = atol(argv[argnum++]); + 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 (streq(argv[argnum], "local")) + fee_payer = LOCAL; + else if (streq(argv[argnum], "remote")) + fee_payer = REMOTE; + else + 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_secrets(argv + argnum, &local, &localseed, "local"); + argnum += parse_secrets(argv + argnum, &remote, &remoteseed, "remote"); + + if (!amount_sat_sub_msat(&remote_msat, funding_amount, local_msat)) + errx(1, "Can't afford local_msat"); + + if (!pubkey_from_privkey(&local.funding_privkey, &funding_localkey) + || !pubkey_from_secret(&local.revocation_basepoint_secret, + &localbase.revocation) + || !pubkey_from_secret(&local.payment_basepoint_secret, + &localbase.payment) + || !pubkey_from_secret(&local.delayed_payment_basepoint_secret, + &localbase.delayed_payment) + || !pubkey_from_secret(&local.htlc_basepoint_secret, + &localbase.htlc)) + errx(1, "Bad deriving local basepoints"); + + if (!pubkey_from_privkey(&remote.funding_privkey, &funding_remotekey) + || !pubkey_from_secret(&remote.revocation_basepoint_secret, + &remotebase.revocation) + || !pubkey_from_secret(&remote.payment_basepoint_secret, + &remotebase.payment) + || !pubkey_from_secret(&remote.delayed_payment_basepoint_secret, + &remotebase.delayed_payment) + || !pubkey_from_secret(&remote.htlc_basepoint_secret, + &remotebase.htlc)) + errx(1, "Bad deriving remote basepoints"); + + print_basepoints("local", + &local, &localseed, + &localbase, &funding_localkey, commitnum); + print_basepoints("remote", + &remote, &remoteseed, + &remotebase, &funding_remotekey, commitnum); + + u8 *funding_wscript = bitcoin_redeem_2of2(NULL, + &funding_localkey, + &funding_remotekey); + + /* Create the local commitment_tx */ + 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); + + printf("## local_commitment\n" + "# input amount %s, funding_wscript %s, key %s\n", + type_to_string(NULL, struct amount_sat, &funding_amount), + 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))); + + sign_tx_input(local_tx, 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, + &remote.funding_privkey, + &funding_remotekey, + SIGHASH_ALL, + &remote_sig); + printf("remotesig_on_local: %s\n", sig_as_hex(&remote_sig)); + + 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))); + + /* 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); + + printf("## remote_commitment\n" + "# input amount %s, funding_wscript %s, key %s\n", + type_to_string(NULL, struct amount_sat, &funding_amount), + 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))); + + sign_tx_input(remote_tx, 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, + &remote.funding_privkey, + &funding_remotekey, + SIGHASH_ALL, + &remote_sig); + printf("remotesig_on_remote: %s\n", sig_as_hex(&remote_sig)); + + witness = + bitcoin_witness_2of2(NULL, &local_sig, &remote_sig, + &funding_localkey, &funding_remotekey); + bitcoin_tx_input_set_witness(remote_tx, 0, witness); + + printf("# signed remote commitment: %s\n", + tal_hex(NULL, linearize_tx(NULL, remote_tx))); + + return 0; +} diff --git a/devtools/mkfunding.c b/devtools/mkfunding.c new file mode 100644 index 000000000..7180acc45 --- /dev/null +++ b/devtools/mkfunding.c @@ -0,0 +1,133 @@ +/* For example, in the spec tests we use the following keys: + * + * lightning/devtools/mkfunding 16835ac8c154b616baac524163f41fb0c4f82c7b972ad35d4d6f18d854f6856b 1 0.01btc 253 76edf0c303b9e692da9cb491abedef46ca5b81d32f102eb4648461b239cb0f99 0000000000000000000000000000000000000000000000000000000000000010 0000000000000000000000000000000000000000000000000000000000000020 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void status_fmt(enum log_level level, const char *fmt, ...) +{ +} + +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 privkey input_privkey; + struct privkey local_funding_privkey, remote_funding_privkey; + struct pubkey funding_localkey, funding_remotekey, inputkey; + struct amount_sat fee, funding_amount; + unsigned int feerate_per_kw; + int argnum; + struct bitcoin_tx *tx; + u16 outnum; + size_t weight; + struct utxo input; + const struct utxo **utxomap; + struct bitcoin_signature sig; + struct bitcoin_txid txid; + + setup_locale(); + + secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | + SECP256K1_CONTEXT_SIGN); + + if (argc != 1 + 7) + errx(1, "Usage: mkfunding "); + + input.is_p2sh = false; + input.close_info = NULL; + + argnum = 1; + if (!bitcoin_txid_from_hex(argv[argnum], + strlen(argv[argnum]), &input.txid)) + errx(1, "Bad input-txid"); + argnum++; + input.outnum = atoi(argv[argnum++]); + if (!parse_amount_sat(&input.amount, argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad input-amount"); + argnum++; + feerate_per_kw = atoi(argv[argnum++]); + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &input_privkey, sizeof(input_privkey))) + errx(1, "Parsing input-privkey"); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &local_funding_privkey, sizeof(local_funding_privkey))) + errx(1, "Parsing local-funding-privkey"); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &remote_funding_privkey, sizeof(remote_funding_privkey))) + errx(1, "Parsing remote-funding-privkey"); + argnum++; + + if (!pubkey_from_privkey(&input_privkey, &inputkey) + || !pubkey_from_privkey(&local_funding_privkey, &funding_localkey) + || !pubkey_from_privkey(&remote_funding_privkey, &funding_remotekey)) + errx(1, "Bad privkeys"); + + /* nVersion, input count, output count, nLocktime */ + weight = 4 * (4 + 1 + 1 + 4); + /* Single output: Satoshis, script length, p2wsh. */ + weight += 4 * (8 + 1 + BITCOIN_SCRIPTPUBKEY_P2WSH_LEN); + /* Single input: txid, index, scriptlen, nSequence */ + weight += 4 * (32 + 4 + 1 + 4); + /* Single witness: witness element count, len[0], sig, len[2], key */ + weight += 1 + (1 + 73 + 1 + 33); + + fee = amount_tx_fee(feerate_per_kw, weight); + if (!amount_sat_sub(&funding_amount, input.amount, fee)) + errx(1, "Input %s can't afford fee %s", + type_to_string(NULL, struct amount_sat, &input.amount), + type_to_string(NULL, struct amount_sat, &fee)); + + /* funding_tx wants a utxo array. */ + utxomap = tal_arr(tmpctx, const struct utxo *, 1); + utxomap[0] = &input; + + /* No change output, so we don't need a bip32 base. */ + tx = funding_tx(NULL, &outnum, utxomap, funding_amount, + &funding_localkey, &funding_remotekey, + AMOUNT_SAT(0), NULL, NULL); + + /* P2WSH of inputkey */ + bitcoin_tx_input_set_script(tx, 0, NULL); + sign_tx_input(tx, 0, NULL, p2wpkh_scriptcode(NULL, &inputkey), + &input_privkey, &inputkey, + SIGHASH_ALL, &sig); + + printf("# funding sig: %s\n", sig_as_hex(&sig)); + printf("# funding amount: %s\n", + type_to_string(NULL, struct amount_sat, &funding_amount)); + + bitcoin_txid(tx, &txid); + printf("# funding txid: %s\n", + type_to_string(NULL, struct bitcoin_txid, &txid)); + + bitcoin_tx_input_set_witness( + tx, 0, bitcoin_witness_p2wpkh(NULL, &sig, &inputkey)); + + printf("tx: %s\n", tal_hex(NULL, linearize_tx(NULL, tx))); + + return 0; +}