diff --git a/devtools/.gitignore b/devtools/.gitignore index 4ce9c28f7..570238049 100644 --- a/devtools/.gitignore +++ b/devtools/.gitignore @@ -5,3 +5,5 @@ dump-gossipstore gossipwith mkcommit mkfunding +mkclose +mkgossip diff --git a/devtools/Makefile b/devtools/Makefile index 4f56bcb20..8f5d127c5 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/mkclose +DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip DEVTOOLS_TOOL_SRC := $(DEVTOOLS:=.c) DEVTOOLS_TOOL_OBJS := $(DEVTOOLS_TOOL_SRC:.c=.o) @@ -72,6 +72,8 @@ devtools/mkfunding: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fr devtools/mkclose: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o devtools/mkclose.o +devtools/mkgossip: $(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/mkgossip.o + # Make sure these depend on everything. ALL_PROGRAMS += $(DEVTOOLS) ALL_OBJS += $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS) diff --git a/devtools/mkfunding.c b/devtools/mkfunding.c index 9e14faf8e..10be8d91d 100644 --- a/devtools/mkfunding.c +++ b/devtools/mkfunding.c @@ -1,6 +1,10 @@ /* For example, in the spec tests we use the following keys: * * lightning/devtools/mkfunding 16835ac8c154b616baac524163f41fb0c4f82c7b972ad35d4d6f18d854f6856b 1 0.01btc 253 76edf0c303b9e692da9cb491abedef46ca5b81d32f102eb4648461b239cb0f99 0000000000000000000000000000000000000000000000000000000000000010 0000000000000000000000000000000000000000000000000000000000000020 + * + * lightning/devtools/mkfunding 16835ac8c154b616baac524163f41fb0c4f82c7b972ad35d4d6f18d854f6856b 0 0.02btc 253 bc2f48a76a6b8815940accaf01981d3b6347a68fbe844f81c50ecbadf27cd179 0000000000000000000000000000000000000000000000000000000000000030 0000000000000000000000000000000000000000000000000000000000000040 + * + * lightning/devtools/mkfunding 16835ac8c154b616baac524163f41fb0c4f82c7b972ad35d4d6f18d854f6856b 3 0.03btc 253 16c5027616e940d1e72b4c172557b3b799a93c0582f924441174ea556aadd01c 0000000000000000000000000000000000000000000000000000000000000050 0000000000000000000000000000000000000000000000000000000000000060 */ #include #include diff --git a/devtools/mkgossip.c b/devtools/mkgossip.c new file mode 100644 index 000000000..08b2d0882 --- /dev/null +++ b/devtools/mkgossip.c @@ -0,0 +1,328 @@ +/* For example, in the spec tests we use the following channels: + * + * lightning/devtools/mkgossip 103x1x0 06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f 0000000000000000000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000000000000000003 0000000000000000000000000000000000000000000000000000000000000010 0000000000000000000000000000000000000000000000000000000000000020 "" 1565587763 144 0 1000 10 "" "01080808082607" 1565587763 48 0 100 11 100000 "0151b6887026070220014c4e1cc141001e6f65fffec8a825260703c43068ceb641d7b25c3a26070441cf248da2034dfa9351a9e946d71ce86f561f50b67753fd8e385d44647bf62cdb91032607" + * + * lightning/devtools/mkgossip 109x1x0 06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f 0000000000000000000000000000000000000000000000000000000000000004 0000000000000000000000000000000000000000000000000000000000000005 0000000000000000000000000000000000000000000000000000000000000030 0000000000000000000000000000000000000000000000000000000000000040 "" 1565587764 144 0 1000 10 "" "" 1565587765 48 0 100 11 100000 022a03b0c0000300d000000000240020012607 + * + * lightning/devtools/mkgossip 115x1x0 06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f 0000000000000000000000000000000000000000000000000000000000000003 0000000000000000000000000000000000000000000000000000000000000004 0000000000000000000000000000000000000000000000000000000000000050 0000000000000000000000000000000000000000000000000000000000000060 "" 1565597764 144 0 1000 10 "" "0441cf248da2034dfa9351a9e946d71ce86f561f50b67753fd8e385d44647bf62cdb91032607" 1565597765 48 0 100 11 100000 "" + */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct update_opts { + u32 timestamp; + u32 cltv_expiry_delta; + struct amount_msat min; + struct amount_msat feebase; + u32 fee_proportional_millionths; + struct amount_msat *max; + u8 *addresses; +}; + +static int parse_options(char *argv[], struct update_opts *opts, + const char *desc) +{ + int argnum = 0; + + opts->timestamp = atol(argv[argnum++]); + if (!opts->timestamp) + errx(1, "Bad %s.timestamp", desc); + opts->cltv_expiry_delta = atol(argv[argnum++]); + if (!opts->cltv_expiry_delta) + errx(1, "Bad %s.cltv_expiry_delta", desc); + if (!parse_amount_msat(&opts->min, argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad %s.min", desc); + argnum++; + if (!parse_amount_msat(&opts->feebase, + argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad %s.feebase", desc); + argnum++; + opts->fee_proportional_millionths = atol(argv[argnum++]); + if (!opts->fee_proportional_millionths) + errx(1, "Bad %s.fee_proportional_millionths", desc); + + if (streq(argv[argnum], "")) + opts->max = NULL; + else { + opts->max = tal(NULL, struct amount_msat); + if (!parse_amount_msat(opts->max, + argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad %s.max", desc); + } + argnum++; + opts->addresses = tal_hexdata(NULL, argv[argnum], strlen(argv[argnum])); + if (!opts->addresses) + errx(1, "Bad %s.addresses", desc); + argnum++; + return argnum; +} + +static char *sig_as_hex(const secp256k1_ecdsa_signature *sig) +{ + u8 compact_sig[64]; + + secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, + compact_sig, + sig); + return tal_hexstr(NULL, compact_sig, sizeof(compact_sig)); +} + +/* BOLT-61a1365a45cc8b463ddbbe3429d350f8eac787dd #7: + * + * The checksum of a `channel_update` is the CRC32C checksum as specified in + * [RFC3720](https://tools.ietf.org/html/rfc3720#appendix-B.4) of this + * `channel_update` without its `signature` and `timestamp` fields. + */ +static u32 crc32_of_update(const u8 *channel_update) +{ + u32 sum; + + /* BOLT #7: + * + * 1. type: 258 (`channel_update`) + * 2. data: + * * [`signature`:`signature`] + * * [`chain_hash`:`chain_hash`] + * * [`short_channel_id`:`short_channel_id`] + * * [`u32`:`timestamp`] + *... + */ + /* We already checked it's valid before accepting */ + assert(tal_count(channel_update) > 2 + 64 + 32 + 8 + 4); + sum = crc32c(0, channel_update + 2 + 64, 32 + 8); + sum = crc32c(sum, channel_update + 2 + 64 + 32 + 8 + 4, + tal_count(channel_update) - (64 + 2 + 32 + 8 + 4)); + return sum; +} + +static void print_update(const struct bitcoin_blkid *chainhash, + const struct short_channel_id *scid, + const struct update_opts *opts, + bool is_lesser_key, + const struct privkey *privkey) +{ + /* 2 bytes msg type + 64 bytes of signature */ + const size_t channel_update_offset = 2 + 64; + struct sha256_double hash; + secp256k1_ecdsa_signature sig; + u8 *cupdate; + + memset(&sig, 0, sizeof(sig)); + if (opts->max) { + cupdate = towire_channel_update_option_channel_htlc_max + (NULL, &sig, chainhash, scid, opts->timestamp, + ROUTING_OPT_HTLC_MAX_MSAT, + is_lesser_key ? 0 : ROUTING_FLAGS_DIRECTION, + opts->cltv_expiry_delta, + opts->min, + opts->feebase.millisatoshis, /* Raw: devtools code */ + opts->fee_proportional_millionths, + *opts->max); + } else { + cupdate = towire_channel_update + (NULL, &sig, chainhash, scid, opts->timestamp, + 0, + is_lesser_key ? 0 : ROUTING_FLAGS_DIRECTION, + opts->cltv_expiry_delta, + opts->min, + opts->feebase.millisatoshis, /* Raw: devtools code */ + opts->fee_proportional_millionths); + } + sha256_double(&hash, cupdate + channel_update_offset, + tal_count(cupdate) - channel_update_offset); + sign_hash(privkey, &hash, &sig); + + printf("type=channel_update\n"); + printf(" signature=%s\n", sig_as_hex(&sig)); + printf(" chain_hash=%s\n", tal_hexstr(NULL, chainhash, sizeof(*chainhash))); + printf(" short_channel_id=%s\n", short_channel_id_to_str(NULL, scid)); + printf(" timestamp=%u\n", opts->timestamp); + printf(" message_flags=%u\n", + opts->max ? ROUTING_OPT_HTLC_MAX_MSAT : 0); + printf(" channel_flags=%u\n", + is_lesser_key ? 0 : ROUTING_FLAGS_DIRECTION); + printf(" cltv_expiry_delta=%u\n", + opts->cltv_expiry_delta); + printf(" htlc_minimum_msat=%"PRIu64"\n", + opts->min.millisatoshis); /* Raw: devtools code */ + printf(" fee_base_msat=%"PRIu64"\n", + opts->feebase.millisatoshis); /* Raw: devtools code */ + printf(" fee_proportional_millionths=%u\n", + opts->fee_proportional_millionths); + if (opts->max) + printf(" htlc_maximum_msat=%"PRIu64"\n", + opts->max->millisatoshis); /* Raw: devtools code */ + printf("# crc32c checksum: %08x\n", crc32_of_update(cupdate)); +} + +static void print_nannounce(const struct node_id *nodeid, + const struct update_opts *opts, + const struct privkey *privkey) +{ + /* 2 bytes msg type + 64 bytes of signature */ + const size_t node_announcement_offset = 2 + 64; + struct sha256_double hash; + secp256k1_ecdsa_signature sig; + char alias[33]; + u8 *nannounce; + + memset(&sig, 0, sizeof(sig)); + assert(hex_str_size(sizeof(*nodeid)) >= sizeof(alias)); + hex_encode(nodeid, hex_data_size(sizeof(alias)), alias, sizeof(alias)); + nannounce = towire_node_announcement(NULL, &sig, NULL, opts->timestamp, + nodeid, nodeid->k, (u8 *)alias, + opts->addresses); + sha256_double(&hash, nannounce + node_announcement_offset, + tal_count(nannounce) - node_announcement_offset); + sign_hash(privkey, &hash, &sig); + + printf("type=node_announcement\n"); + printf(" signature=%s\n", sig_as_hex(&sig)); + printf(" features=%s\n", tal_hex(NULL, NULL)); + printf(" timestamp=%u\n", opts->timestamp); + printf(" node_id=%s\n", node_id_to_hexstr(NULL, nodeid)); + printf(" rgb_color=%s\n", tal_hexstr(NULL, nodeid->k, 3)); + printf(" alias=%s\n", tal_hexstr(NULL, alias, 32)); + printf(" addresses=%s\n", tal_hex(NULL, opts->addresses)); +} + +int main(int argc, char *argv[]) +{ + struct privkey node_privkey[2], funding_privkey[2]; + struct pubkey node[2], bitcoin[2]; + struct node_id nodeid[2]; + int lesser_key; + struct short_channel_id scid; + struct bitcoin_blkid chainhash; + secp256k1_ecdsa_signature nodesig[2], bitcoinsig[2]; + const u8 *features; + u8 *cannounce; + /* 2 bytes msg type + 256 bytes of signatures */ + const size_t channel_announcement_offset = 2 + 256; + int argnum; + struct sha256_double hash; + struct update_opts opts[2]; + + setup_locale(); + + secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | + SECP256K1_CONTEXT_SIGN); + + if (argc != 8 + 7 * 2) + errx(1, "Usage: mkgossip update-opts-1 update-opts-2\n" + "Where is:\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "); + + argnum = 1; + if (!short_channel_id_from_str(argv[argnum], strlen(argv[argnum]), &scid)) + errx(1, "Bad scid"); + argnum++; + /* Don't do endian-reversing insanity here! */ + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &chainhash, sizeof(chainhash))) + errx(1, "Parsing chainhash"); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &node_privkey[0], sizeof(node_privkey[0]))) + errx(1, "Parsing node-privkey1"); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &node_privkey[1], sizeof(node_privkey[2]))) + errx(1, "Parsing node-privkey2"); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &funding_privkey[0], sizeof(funding_privkey[0]))) + errx(1, "Parsing funding-privkey1"); + argnum++; + if (!hex_decode(argv[argnum], strlen(argv[argnum]), + &funding_privkey[1], sizeof(funding_privkey[2]))) + errx(1, "Parsing funding-privkey2"); + argnum++; + features = tal_hexdata(NULL, argv[argnum], strlen(argv[argnum])); + if (!features) + errx(1, "Parsing hexfeatures"); + argnum++; + + argnum += parse_options(argv + argnum, &opts[0], "update-opts1"); + argnum += parse_options(argv + argnum, &opts[1], "update-opts2"); + + if (!pubkey_from_privkey(&node_privkey[0], &node[0]) + || !pubkey_from_privkey(&node_privkey[1], &node[1]) + || !pubkey_from_privkey(&funding_privkey[0], &bitcoin[0]) + || !pubkey_from_privkey(&funding_privkey[1], &bitcoin[1])) + errx(1, "Bad privkeys"); + + lesser_key = pubkey_idx(&node[0], &node[1]); + node_id_from_pubkey(&nodeid[0], &node[0]); + node_id_from_pubkey(&nodeid[1], &node[1]); + + /* First make msg with dummy sigs. */ + memset(nodesig, 0, sizeof(nodesig)); + memset(bitcoinsig, 0, sizeof(bitcoinsig)); + + cannounce = towire_channel_announcement(NULL, + &nodesig[lesser_key], + &nodesig[!lesser_key], + &bitcoinsig[lesser_key], + &bitcoinsig[!lesser_key], + features, &chainhash, + &scid, + &nodeid[lesser_key], + &nodeid[!lesser_key], + &bitcoin[lesser_key], + &bitcoin[!lesser_key]); + sha256_double(&hash, cannounce + channel_announcement_offset, + tal_count(cannounce) - channel_announcement_offset); + sign_hash(&node_privkey[0], &hash, &nodesig[0]); + sign_hash(&funding_privkey[0], &hash, &bitcoinsig[0]); + sign_hash(&node_privkey[1], &hash, &nodesig[1]); + sign_hash(&funding_privkey[1], &hash, &bitcoinsig[1]); + + printf("type=channel_announcement\n"); + printf(" node_signature_1=%s\n", sig_as_hex(&nodesig[lesser_key])); + printf(" node_signature_2=%s\n", sig_as_hex(&nodesig[!lesser_key])); + printf(" bitcoin_signature_1=%s\n", sig_as_hex(&bitcoinsig[lesser_key])); + printf(" bitcoin_signature_2=%s\n", sig_as_hex(&bitcoinsig[!lesser_key])); + printf(" features=%s\n", tal_hex(NULL, features)); + printf(" chain_hash=%s\n", tal_hexstr(NULL, &chainhash, sizeof(chainhash))); + printf(" short_channel_id=%s\n", short_channel_id_to_str(NULL, &scid)); + printf(" node_id_1=%s\n", + node_id_to_hexstr(NULL, &nodeid[lesser_key])); + printf(" node_id_2=%s\n", + node_id_to_hexstr(NULL, &nodeid[!lesser_key])); + printf(" bitcoin_key_1=%s\n", + pubkey_to_hexstr(NULL, &bitcoin[lesser_key])); + printf(" bitcoin_key_2=%s\n", + pubkey_to_hexstr(NULL, &bitcoin[!lesser_key])); + + printf("\n#Node 1:\n"); + print_update(&chainhash, &scid, &opts[0], lesser_key == 0, + &node_privkey[0]); + print_nannounce(&nodeid[0], &opts[0], &node_privkey[0]); + + printf("\n#Node 2:\n"); + print_update(&chainhash, &scid, &opts[1], lesser_key == 1, + &node_privkey[1]); + + print_nannounce(&nodeid[1], &opts[1], &node_privkey[1]); + + return 0; +}