From 347703465727c554c05d9de21f490b10692d47f3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 18 Jul 2019 14:50:32 +0930 Subject: [PATCH] tests: add test for tlvstream (from BOLT 1 test vectors). https://github.com/lightningnetwork/lightning-rfc/pull/631 Signed-off-by: Rusty Russell --- wire/fromwire.c | 24 +- wire/test/run-tlvstream.c | 854 ++++++++++++++++++++++++++++++++++++++ wire/tlvstream.c | 36 +- 3 files changed, 905 insertions(+), 9 deletions(-) create mode 100644 wire/test/run-tlvstream.c diff --git a/wire/fromwire.c b/wire/fromwire.c index b99f74762..d00fb20de 100644 --- a/wire/fromwire.c +++ b/wire/fromwire.c @@ -15,6 +15,10 @@ #include #include +#ifndef SUPERVERBOSE +#define SUPERVERBOSE(...) +#endif + /* Sets *cursor to NULL and returns NULL when extraction fails. */ const void *fromwire_fail(const u8 **cursor, size_t *max) { @@ -31,6 +35,8 @@ const u8 *fromwire(const u8 **cursor, size_t *max, void *copy, size_t n) /* Just make sure we don't leak uninitialized mem! */ if (copy) memset(copy, 0, n); + if (*cursor) + SUPERVERBOSE("less than encoding length"); return fromwire_fail(cursor, max); } *cursor += n; @@ -103,6 +109,7 @@ static u64 fromwire_tlv_uint(const u8 **cursor, size_t *max, size_t maxlen) */ length = *max; if (length > maxlen) { + SUPERVERBOSE("greater than encoding length"); fromwire_fail(cursor, max); return 0; } @@ -116,6 +123,7 @@ static u64 fromwire_tlv_uint(const u8 **cursor, size_t *max, size_t maxlen) * - MUST fail to parse the `tlv_stream`. */ if (length > 0 && bytes[sizeof(bytes) - length] == 0) { + SUPERVERBOSE("not minimal"); fromwire_fail(cursor, max); return 0; } @@ -163,18 +171,24 @@ u64 fromwire_bigsize(const u8 **cursor, size_t *max) switch(flag) { case 0xff: ret = fromwire_u64(cursor, max); - if ((ret >> 32) == 0) + if ((ret >> 32) == 0) { + SUPERVERBOSE("not minimal encoded"); fromwire_fail(cursor, max); + } break; case 0xfe: ret = fromwire_u32(cursor, max); - if ((ret >> 16) == 0) + if ((ret >> 16) == 0) { + SUPERVERBOSE("not minimal encoded"); fromwire_fail(cursor, max); + } break; case 0xfd: ret = fromwire_u16(cursor, max); - if (ret < 0xfd) + if (ret < 0xfd) { + SUPERVERBOSE("not minimal encoded"); fromwire_fail(cursor, max); + } break; default: ret = flag; @@ -189,8 +203,10 @@ void fromwire_pubkey(const u8 **cursor, size_t *max, struct pubkey *pubkey) if (!fromwire(cursor, max, der, sizeof(der))) return; - if (!pubkey_from_der(der, sizeof(der), pubkey)) + if (!pubkey_from_der(der, sizeof(der), pubkey)) { + SUPERVERBOSE("not a valid point"); fromwire_fail(cursor, max); + } } void fromwire_node_id(const u8 **cursor, size_t *max, struct node_id *id) diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c new file mode 100644 index 000000000..3b39f7196 --- /dev/null +++ b/wire/test/run-tlvstream.c @@ -0,0 +1,854 @@ +#include +#include +#include +#include + +static const char *reason; +#define SUPERVERBOSE(r) do { reason = (r); } while(0) + +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* AUTOGENERATED MOCKS END */ + +/* FIXME: Autogenerate these! */ +/* BOLT-EXPERIMENTAL #1: + * 1. tlvs: `n1` + * 2. types: + * 1. type: 1 (`tlv1`) + * 2. data: + * * [`tu64`:`amount_msat`] + * 1. type: 2 (`tlv2`) + * 2. data: + * * [`short_channel_id`:`scid`] + * 1. type: 3 (`tlv3`) + * 2. data: + * * [`point`:`node_id`] + * * [`u64`:`amount_msat_1`] + * * [`u64`:`amount_msat_2`] + * 1. type: 254 (`tlv4`) + * 2. data: + * * [`u16`:`cltv_delta`] + */ +struct tlv_n1_tlv1 { + u64 amount_msat; +}; + +struct tlv_n1_tlv2 { + struct short_channel_id scid; +}; + +struct tlv_n1_tlv3 { + struct pubkey node_id; + u64 amount_msat_1; + u64 amount_msat_2; +}; + +struct tlv_n1_tlv4 { + u16 cltv_delta; +}; + +struct tlv_n1 { + struct tlv_n1_tlv1 *tlv1; + struct tlv_n1_tlv2 *tlv2; + struct tlv_n1_tlv3 *tlv3; + struct tlv_n1_tlv4 *tlv4; +}; + +static struct tlv_n1 *tlv_n1_new(const tal_t *ctx) +{ + /* Initialize everything to NULL. (Quiet, C pedants!) */ + return talz(ctx, struct tlv_n1); +} + +static u8 *towire_tlv_n1_tlv1(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_n1 *r = vrecord; + u8 *ptr; + + if (!r->tlv1) + return NULL; + + ptr = tal_arr(ctx, u8, 0); + towire_tu64(&ptr, r->tlv1->amount_msat); + return ptr; +} + +static u8 *towire_tlv_n1_tlv2(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_n1 *r = vrecord; + u8 *ptr; + + if (!r->tlv2) + return NULL; + + ptr = tal_arr(ctx, u8, 0); + towire_short_channel_id(&ptr, &r->tlv2->scid); + return ptr; +} + +static u8 *towire_tlv_n1_tlv3(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_n1 *r = vrecord; + u8 *ptr; + + if (!r->tlv3) + return NULL; + + ptr = tal_arr(ctx, u8, 0); + towire_pubkey(&ptr, &r->tlv3->node_id); + towire_u64(&ptr, r->tlv3->amount_msat_1); + towire_u64(&ptr, r->tlv3->amount_msat_2); + return ptr; +} + +static u8 *towire_tlv_n1_tlv4(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_n1 *r = vrecord; + u8 *ptr; + + if (!r->tlv4) + return NULL; + + ptr = tal_arr(ctx, u8, 0); + towire_u16(&ptr, r->tlv4->cltv_delta); + return ptr; +} + +static void fromwire_tlv_n1_tlv1(const u8 **cursor, size_t *max, void *vrecord) +{ + struct tlv_n1 *r = vrecord; + + r->tlv1 = tal(r, struct tlv_n1_tlv1); + r->tlv1->amount_msat = fromwire_tu64(cursor, max); +} + +static void fromwire_tlv_n1_tlv2(const u8 **cursor, size_t *max, void *vrecord) +{ + struct tlv_n1 *r = vrecord; + + r->tlv2 = tal(r, struct tlv_n1_tlv2); + fromwire_short_channel_id(cursor, max, &r->tlv2->scid); +} + +static void fromwire_tlv_n1_tlv3(const u8 **cursor, size_t *max, void *vrecord) +{ + struct tlv_n1 *r = vrecord; + + r->tlv3 = tal(r, struct tlv_n1_tlv3); + + fromwire_pubkey(cursor, max, &r->tlv3->node_id); + r->tlv3->amount_msat_1 = fromwire_u64(cursor, max); + r->tlv3->amount_msat_2 = fromwire_u64(cursor, max); +} + +static void fromwire_tlv_n1_tlv4(const u8 **cursor, size_t *max, void *vrecord) +{ + struct tlv_n1 *r = vrecord; + + r->tlv4 = tal(r, struct tlv_n1_tlv4); + r->tlv4->cltv_delta = fromwire_u16(cursor, max); +} + +static const struct tlv_record_type tlvs_n1[] = { + { 1, towire_tlv_n1_tlv1, fromwire_tlv_n1_tlv1 }, + { 2, towire_tlv_n1_tlv2, fromwire_tlv_n1_tlv2 }, + { 3, towire_tlv_n1_tlv3, fromwire_tlv_n1_tlv3 }, + { 254, towire_tlv_n1_tlv4, fromwire_tlv_n1_tlv4 }, +}; + +/* BOLT-EXPERIMENTAL #1: + * 1. tlvs: `n2` + * 2. types: + * 1. type: 0 (`tlv1`) + * 2. data: + * * [`tu64`:`amount_msat`] + * 1. type: 11 (`tlv2`) + * 2. data: + * * [`tu32`:`cltv_expiry`] + */ +struct tlv_n2_tlv1 { + u64 amount_msat; +}; + +struct tlv_n2_tlv2 { + u16 cltv_expiry; +}; + +struct tlv_n2 { + struct tlv_n2_tlv1 *tlv1; + struct tlv_n2_tlv2 *tlv2; +}; + +static struct tlv_n2 *tlv_n2_new(const tal_t *ctx) +{ + /* Initialize everything to NULL. (Quiet, C pedants!) */ + return talz(ctx, struct tlv_n2); +} + +static u8 *towire_tlv_n2_tlv1(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_n2 *r = vrecord; + u8 *ptr; + + if (!r->tlv1) + return NULL; + + ptr = tal_arr(ctx, u8, 0); + towire_tu64(&ptr, r->tlv1->amount_msat); + return ptr; +} + +static u8 *towire_tlv_n2_tlv2(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_n2 *r = vrecord; + u8 *ptr; + + if (!r->tlv2) + return NULL; + + ptr = tal_arr(ctx, u8, 0); + towire_u16(&ptr, r->tlv2->cltv_expiry); + return ptr; +} + +static void fromwire_tlv_n2_tlv1(const u8 **cursor, size_t *max, void *vrecord) +{ + struct tlv_n2 *r = vrecord; + + r->tlv1 = tal(r, struct tlv_n2_tlv1); + r->tlv1->amount_msat = fromwire_tu64(cursor, max); +} + +static void fromwire_tlv_n2_tlv2(const u8 **cursor, size_t *max, void *vrecord) +{ + struct tlv_n2 *r = vrecord; + + r->tlv2 = tal(r, struct tlv_n2_tlv2); + r->tlv2->cltv_expiry = fromwire_u16(cursor, max); +} + +static const struct tlv_record_type tlvs_n2[] = { + { 0, towire_tlv_n2_tlv1, fromwire_tlv_n2_tlv1 }, + { 11, towire_tlv_n2_tlv2, fromwire_tlv_n2_tlv2 }, +}; + +/* BOLT #1 +### TLV Decoding Failures + +The following TLV streams in any namespace should trigger a decoding failure: + +1. Invalid stream: 0xfd +2. Reason: type truncated + +1. Invalid stream: 0xfd01 +2. Reason: type truncated + +1. Invalid stream: 0xfd0001 00 +2. Reason: not minimally encoded type + +1. Invalid stream: 0xfd0101 +2. Reason: missing length + +1. Invalid stream: 0x0f fd +2. Reason: (length truncated) + +1. Invalid stream: 0x0f fd26 +2. Reason: (length truncated) + +1. Invalid stream: 0x0f fd2602 +2. Reason: missing value + +1. Invalid stream: 0x0f fd0001 00 +2. Reason: not minimally encoded length + +1. Invalid stream: 0x0f fd0201 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +2. Reason: value truncated + +The following TLV streams in either namespace should trigger a +decoding failure: + +1. Invalid stream: 0x12 00 +2. Reason: unknown even type. + +1. Invalid stream: 0xfd0102 00 +2. Reason: unknown even type. + +1. Invalid stream: 0xfe01000002 00 +2. Reason: unknown even type. + +1. Invalid stream: 0xff0100000000000002 00 +2. Reason: unknown even type. +*/ + +struct invalid_stream { + const char *hex; + const char *reason; +}; + +static struct invalid_stream invalid_streams_either[] = { + { "fd", "type truncated" }, + { "fd01", "type truncated" }, + { "fd0001 00", "not minimally encoded type" }, + { "fd0101", "missing length" }, + { "0f fd", "(length truncated)" }, + { "0f fd26", "(length truncated)" }, + { "0f fd2602", "missing value" }, + { "0f fd0001 00", "not minimally encoded length" }, + { "0f fd0201 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "value truncated" }, + + { "12 00", "unknown even type." }, + { "fd0102 00", "unknown even type." }, + { "fe01000002 00", "unknown even type." }, + { "ff0100000000000002 00", "unknown even type." }, +}; + +/* BOLT-EXPERIMENTAL #1: + * + * The following TLV streams in namespace `n1` should trigger a decoding +failure: + +1. Invalid stream: 0x01 09 ffffffffffffffffff +2. Reason: greater than encoding length for `n1`s `tlv1`. + +1. Invalid stream: 0x01 01 00 +2. Reason: encoding for `n1`s `tlv1`s `amount_msat` is not minimal + +1. Invalid stream: 0x01 02 0001 +2. Reason: encoding for `n1`s `tlv1`s `amount_msat` is not minimal + +1. Invalid stream: 0x01 03 000100 +2. Reason: encoding for `n1`s `tlv1`s `amount_msat` is not minimal + +1. Invalid stream: 0x01 04 00010000 +2. Reason: encoding for `n1`s `tlv1`s `amount_msat` is not minimal + +1. Invalid stream: 0x01 05 0001000000 +2. Reason: encoding for `n1`s `tlv1`s `amount_msat` is not minimal + +1. Invalid stream: 0x01 06 000100000000 +2. Reason: encoding for `n1`s `tlv1`s `amount_msat` is not minimal + +1. Invalid stream: 0x01 07 00010000000000 +2. Reason: encoding for `n1`s `tlv1`s `amount_msat` is not minimal + +1. Invalid stream: 0x01 08 0001000000000000 +2. Reason: encoding for `n1`s `tlv1`s `amount_msat` is not minimal + +1. Invalid stream: 0x02 07 01010101010101 +2. Reason: less than encoding length for `n1`s `tlv2`. + +1. Invalid stream: 0x02 09 010101010101010101 +2. Reason: greater than encoding length for `n1`s `tlv2`. + +1. Invalid stream: 0x03 21 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb +2. Reason: less than encoding length for `n1`s `tlv3`. + +1. Invalid stream: 0x03 29 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb0000000000000001 +2. Reason: less than encoding length for `n1`s `tlv3`. + +1. Invalid stream: 0x03 30 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb000000000000000100000000000001 +2. Reason: less than encoding length for `n1`s `tlv3`. + +1. Invalid stream: 0x03 31 043da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb00000000000000010000000000000002 +2. Reason: `n1`s `node_id` is not a valid point. + +1. Invalid stream: 0x03 32 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb0000000000000001000000000000000001 +2. Reason: greater than encoding length for `n1`s `tlv3`. + +1. Invalid stream: 0xfd00fe 00 +2. Reason: less than encoding length for `n1`s `tlv4`. + +1. Invalid stream: 0xfd00fe 01 01 +2. Reason: less than encoding length for `n1`s `tlv4`. + +1. Invalid stream: 0xfd00fe 03 010101 +2. Reason: greater than encoding length for `n1`s `tlv4`. + +1. Invalid stream: 0x00 00 +2. Reason: unknown even field for `n1`s namespace. +*/ + +static struct invalid_stream invalid_streams_n1[] = { + { "01 09 ffffffffffffffffff", "greater than encoding length for `n1`s `tlv1`." }, + { "01 01 00", "encoding for `n1`s `tlv1`s `amount_msat` is not minimal" }, + { "01 02 0001", "encoding for `n1`s `tlv1`s `amount_msat` is not minimal" }, + { "01 03 000100", "encoding for `n1`s `tlv1`s `amount_msat` is not minimal" }, + { "01 04 00010000", "encoding for `n1`s `tlv1`s `amount_msat` is not minimal" }, + { "01 05 0001000000", "encoding for `n1`s `tlv1`s `amount_msat` is not minimal" }, + { "01 06 000100000000", "encoding for `n1`s `tlv1`s `amount_msat` is not minimal" }, + { "01 07 00010000000000", "encoding for `n1`s `tlv1`s `amount_msat` is not minimal" }, + { "01 08 0001000000000000", "encoding for `n1`s `tlv1`s `amount_msat` is not minimal" }, + { "02 07 01010101010101", "less than encoding length for `n1`s `tlv2`." }, + { "02 09 010101010101010101", "greater than encoding length for `n1`s `tlv2`." }, + { "03 21 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb", "less than encoding length for `n1`s `tlv3`." }, + { "03 29 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb0000000000000001", "less than encoding length for `n1`s `tlv3`." }, + { "03 30 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb000000000000000100000000000001", "less than encoding length for `n1`s `tlv3`." }, + { "03 31 043da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb00000000000000010000000000000002", "`n1`s `node_id` is not a valid point." }, + { "03 32 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb0000000000000001000000000000000001", "greater than encoding length for `n1`s `tlv3`." }, + { "fd00fe 00", "less than encoding length for `n1`s `tlv4`." }, + { "fd00fe 01 01", "less than encoding length for `n1`s `tlv4`." }, + { "fd00fe 03 010101", "greater than encoding length for `n1`s `tlv4`." }, + { "00 00", "unknown even field for `n1`s namespace." }, +}; + +/* BOLT-EXPERIMENTAL #1: +### TLV Stream Decoding Failure + +Any appending of an invalid stream to a valid stream should trigger +a decoding failure. + +Any appending of a higher-numbered valid stream to a lower-numbered +valid stream should not trigger a decoding failure. + +In addition, the following TLV streams in namespace `n1` should +trigger a decoding failure: + +1. Invalid stream: 0x02 08 0000000000000226 01 01 2a +2. Reason: valid tlv records but invalid ordering + +1. Invalid stream: 0x02 08 0000000000000231 02 08 0000000000000451 +2. Reason: duplicate tlv type + +1. Invalid stream: 0x1f 00 0f 01 2a +2. Reason: valid (ignored) tlv records but invalid ordering + +1. Invalid stream: 0x1f 00 1f 01 2a +2. Reason: duplicate tlv type (ignored) + */ +static struct invalid_stream invalid_streams_n1_combo[] = { + { "02 08 0000000000000226 01 01 2a", "valid tlv records but invalid ordering" }, + { "02 08 0000000000000231 02 08 0000000000000451", "duplicate tlv type" }, + { "1f 00 0f 01 2a", "valid (ignored) tlv records but invalid ordering" }, + { "1f 00 1f 01 2a", "duplicate tlv type (ignored)" } +}; + +/* BOLT-EXPERIMENTAL #1: +The following TLV stream in namespace `n2` should trigger a decoding +failure: + +1. Invalid stream: 0xffffffffffffffffff 00 00 00 +2. Reason: valid tlv records but invalid ordering +*/ +static struct invalid_stream invalid_streams_n2_combo[] = { + { "ffffffffffffffffff 00 00 00", "valid tlv records but invalid ordering" }, +}; + +/* BOLT-EXPERIMENTAL #1: + * +### TLV Decoding Successes + +The following TLV streams in either namespace should correctly decode, +and be ignored: + +1. Valid stream: 0x +2. Explanation: empty message + +1. Valid stream: 0x21 00 +2. Explanation: Unknown odd type. + +1. Valid stream: 0xfd0201 00 +2. Explanation: Unknown odd type. + +1. Valid stream: 0xfd00fd 00 +2. Explanation: Unknown odd type. + +1. Valid stream: 0xfd00ff 00 +2. Explanation: Unknown odd type. + +1. Valid stream: 0xfe02000001 00 +2. Explanation: Unknown odd type. + +1. Valid stream: 0xff0200000000000001 00 +2. Explanation: Unknown odd type. + +The following TLV streams in `n1` namespace should correctly decode, +with the values given here: + +1. Valid stream: 0x01 00 +2. Values: `tlv1` `amount_msat`=0 + +1. Valid stream: 0x01 01 01 +2. Values: `tlv1` `amount_msat`=1 + +1. Valid stream: 0x01 02 0100 +2. Values: `tlv1` `amount_msat`=256 + +1. Valid stream: 0x01 03 010000 +2. Values: `tlv1` `amount_msat`=65536 + +1. Valid stream: 0x01 04 01000000 +2. Values: `tlv1` `amount_msat`=16777216 + +1. Valid stream: 0x01 05 0100000000 +2. Values: `tlv1` `amount_msat`=4294967296 + +1. Valid stream: 0x01 06 010000000000 +2. Values: `tlv1` `amount_msat`=1099511627776 + +1. Valid stream: 0x01 07 01000000000000 +2. Values: `tlv1` `amount_msat`=281474976710656 + +1. Valid stream: 0x01 08 0100000000000000 +2. Values: `tlv1` `amount_msat`=72057594037927936 + +1. Valid stream: 0x02 08 0000000000000226 +2. Values: `tlv2` `scid`=0x0x550 + +1. Valid stream: 0x03 31 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb00000000000000010000000000000002 +2. Values: `tlv3` `node_id`=023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb `amount_msat_1`=1 `amount_msat_2`=2 + +1. Valid stream: 0xfd00fe 02 0226 +2. Values: `tlv4` `cltv_delta`=550 +*/ + +struct valid_stream { + const char *hex; + const struct tlv_n1 expect; +}; + +static struct tlv_n1_tlv1 tlv1_0 = { .amount_msat = 0 }; +static struct tlv_n1_tlv1 tlv1_1 = { .amount_msat = 1 }; +static struct tlv_n1_tlv1 tlv1_256 = { .amount_msat = 256 }; +static struct tlv_n1_tlv1 tlv1_65536 = { .amount_msat = 65536 }; +static struct tlv_n1_tlv1 tlv1_16777216 = { .amount_msat = 16777216 }; +static struct tlv_n1_tlv1 tlv1_4294967296 = { .amount_msat = 4294967296ULL }; +static struct tlv_n1_tlv1 tlv1_1099511627776 = { .amount_msat = 1099511627776ULL}; +static struct tlv_n1_tlv1 tlv1_281474976710656 = { .amount_msat = 281474976710656ULL }; +static struct tlv_n1_tlv1 tlv1_72057594037927936 = { .amount_msat = 72057594037927936ULL }; +static struct tlv_n1_tlv2 tlv2_0x0x550 = { .scid.u64 = 0x000000000226 }; +/* node_id filled in at runtime. */ +static struct tlv_n1_tlv3 tlv3_node_id = { { { { 0 } } }, .amount_msat_1 = 1, .amount_msat_2 = 2 }; +static struct tlv_n1_tlv4 tlv4_550 = { .cltv_delta = 550 }; + +static struct valid_stream valid_streams[] = { + /* Valid but no (known) content. */ + { "", {NULL, NULL, NULL, NULL} }, + { "21 00", {NULL, NULL, NULL, NULL} }, + { "fd0201 00", {NULL, NULL, NULL, NULL} }, + { "fd00fd 00", {NULL, NULL, NULL, NULL} }, + { "fd00ff 00", {NULL, NULL, NULL, NULL} }, + { "fe02000001 00", {NULL, NULL, NULL, NULL} }, + { "ff0200000000000001 00", {NULL, NULL, NULL, NULL} }, + + /* TLV1 */ + { "01 00", { .tlv1 = &tlv1_0 } }, + { "01 01 01", { .tlv1 = &tlv1_1 } }, + { "01 02 0100", { .tlv1 = &tlv1_256 } }, + { "01 03 010000", { .tlv1 = &tlv1_65536 } }, + { "01 04 01000000", { .tlv1 = &tlv1_16777216 } }, + { "01 05 0100000000", { .tlv1 = &tlv1_4294967296 } }, + { "01 06 010000000000", { .tlv1 = &tlv1_1099511627776 } }, + { "01 07 01000000000000", { .tlv1 = &tlv1_281474976710656 } }, + { "01 08 0100000000000000", { .tlv1 = &tlv1_72057594037927936 } }, + { "02 08 0000000000000226", { .tlv2 = &tlv2_0x0x550 } }, + { "03 31 023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb00000000000000010000000000000002", { .tlv3 = &tlv3_node_id } }, + { "fd00fe 02 0226", { .tlv4 = &tlv4_550 } }, +}; + +static bool tlv_n1_eq(const struct tlv_n1 *a, const struct tlv_n1 *b) +{ + if (a->tlv1) { + if (!b->tlv1) + return false; + if (a->tlv1->amount_msat != b->tlv1->amount_msat) + return false; + } else if (b->tlv1) + return false; + + if (a->tlv2) { + if (!b->tlv2) + return false; + if (!short_channel_id_eq(&a->tlv2->scid, &b->tlv2->scid)) + return false; + } else if (b->tlv2) + return false; + + if (a->tlv3) { + if (!b->tlv3) + return false; + if (!pubkey_eq(&a->tlv3->node_id, &b->tlv3->node_id)) + return false; + if (a->tlv3->amount_msat_1 != b->tlv3->amount_msat_1) + return false; + if (a->tlv3->amount_msat_2 != b->tlv3->amount_msat_2) + return false; + } else if (b->tlv3) + return false; + + if (a->tlv4) { + if (!b->tlv4) + return false; + if (a->tlv4->cltv_delta != b->tlv4->cltv_delta) + return false; + } else if (b->tlv4) + return false; + + return true; +} + +/* hexstr is valid, so pull out first field */ +static u64 pull_type(const char *hexstr) +{ + u8 v; + + hex_decode(hexstr, 2, &v, sizeof(v)); + switch (v) { + case 0xfd: { + u16 d; + hex_decode(hexstr + 2, 2*sizeof(d), &d, sizeof(d)); + return be16_to_cpu(d); + } + case 0xfe: { + u32 d; + hex_decode(hexstr + 2, 2*sizeof(d), &d, sizeof(d)); + return be32_to_cpu(d); + } + case 0xff: { + u64 d; + hex_decode(hexstr + 2, 2*sizeof(d), &d, sizeof(d)); + return be64_to_cpu(d); + } + default: + return v; + } +} + +static u8 *stream(const tal_t *ctx, const char *hex) +{ + size_t i, j; + char *str = tal_arr(tmpctx, char, strlen(hex) + 1); + + /* Remove spaces and use normal helper */ + for (i = j = 0; i <= strlen(hex); i++) { + if (hex[i] != ' ') + str[j++] = hex[i]; + } + return tal_hexdata(ctx, str, strlen(str)); +} + +static u8 *stream2(const tal_t *ctx, const char *hex1, const char *hex2) +{ + u8 *a = stream(tmpctx, hex1), *b = stream(tmpctx, hex2), *ret; + + ret = tal_dup_arr(ctx, u8, a, tal_count(a), tal_count(b)); + memcpy(ret + tal_count(a), b, tal_count(b)); + return ret; +} + +static bool ignored_fields(const struct tlv_n1 *tlv_n1) +{ + return tlv_n1->tlv1 == NULL + && tlv_n1->tlv2 == NULL + && tlv_n1->tlv3 == NULL + && tlv_n1->tlv4 == NULL; +} + +int main(void) +{ + setup_locale(); + wally_init(0); + secp256k1_ctx = wally_get_secp_context(); + + setup_tmpctx(); + + if (!pubkey_from_hexstr("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb", 66, &tlv3_node_id.node_id)) + abort(); + + for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_either); i++) { + struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + struct tlv_n2 *tlv_n2 = tlv_n2_new(tmpctx); + const u8 *p, *orig_p; + size_t max; + + orig_p = stream(tmpctx, invalid_streams_either[i].hex); + max = tal_count(orig_p); + p = orig_p; + assert(!fromwire_tlvs(&p, &max, tlvs_n1, ARRAY_SIZE(tlvs_n1), + tlv_n1)); + assert(!p); + assert(strstr(invalid_streams_either[i].reason, reason)); + max = tal_count(orig_p); + p = orig_p; + assert(!fromwire_tlvs(&p, &max, tlvs_n2, ARRAY_SIZE(tlvs_n2), + tlv_n2)); + assert(!p); + assert(strstr(invalid_streams_either[i].reason, reason)); + } + + for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n1); i++) { + struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + const u8 *p; + size_t max; + + p = stream(tmpctx, invalid_streams_n1[i].hex); + max = tal_count(p); + assert(!fromwire_tlvs(&p, &max, tlvs_n1, ARRAY_SIZE(tlvs_n1), + tlv_n1)); + assert(!p); + assert(strstr(invalid_streams_n1[i].reason, reason)); + } + + for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n1_combo); i++) { + struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + const u8 *p; + size_t max; + + p = stream(tmpctx, invalid_streams_n1_combo[i].hex); + max = tal_count(p); + assert(!fromwire_tlvs(&p, &max, tlvs_n1, ARRAY_SIZE(tlvs_n1), + tlv_n1)); + assert(!p); + assert(strstr(invalid_streams_n1_combo[i].reason, reason)); + } + + for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n2_combo); i++) { + struct tlv_n2 *tlv_n2 = tlv_n2_new(tmpctx); + const u8 *p; + size_t max; + + p = stream(tmpctx, invalid_streams_n2_combo[i].hex); + max = tal_count(p); + assert(!fromwire_tlvs(&p, &max, tlvs_n2, ARRAY_SIZE(tlvs_n2), + tlv_n2)); + assert(!p); + assert(strstr(invalid_streams_n2_combo[i].reason, reason)); + } + + for (size_t i = 0; i < ARRAY_SIZE(valid_streams); i++) { + struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + const u8 *orig_p, *p; + u8 *p2; + size_t max; + + orig_p = stream(tmpctx, valid_streams[i].hex); + + max = tal_count(orig_p); + p = orig_p; + assert(fromwire_tlvs(&p, &max, tlvs_n1, ARRAY_SIZE(tlvs_n1), + tlv_n1)); + assert(p); + assert(max == 0); + assert(tlv_n1_eq(tlv_n1, &valid_streams[i].expect)); + + /* Re-encoding should give the same results (except + * ignored fields tests!) */ + if (ignored_fields(tlv_n1)) + continue; + + p2 = tal_arr(tmpctx, u8, 0); + towire_tlvs(&p2, tlvs_n1, ARRAY_SIZE(tlvs_n1), tlv_n1); + assert(memeq(p2, tal_count(p2), orig_p, tal_count(orig_p))); + } + + /* BOLT-EXPERIMENTAL #1: + * + * Any appending of an invalid stream to a valid stream should trigger + * a decoding failure. + */ + for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_either); i++) { + for (size_t j = 0; j < ARRAY_SIZE(valid_streams); j++) { + struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + struct tlv_n2 *tlv_n2 = tlv_n2_new(tmpctx); + const u8 *orig_p, *p; + size_t max; + + /* Append */ + orig_p = stream2(tmpctx, valid_streams[j].hex, + invalid_streams_either[i].hex); + max = tal_count(orig_p); + p = orig_p; + assert(!fromwire_tlvs(&p, &max, + tlvs_n1, ARRAY_SIZE(tlvs_n1), + tlv_n1)); + assert(!p); + max = tal_count(orig_p); + p = orig_p; + assert(!fromwire_tlvs(&p, &max, + tlvs_n2, ARRAY_SIZE(tlvs_n2), + tlv_n2)); + assert(!p); + } + } + + for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n1); i++) { + for (size_t j = 0; j < ARRAY_SIZE(valid_streams); j++) { + struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + const u8 *p; + size_t max; + + p = stream2(tmpctx, valid_streams[j].hex, + invalid_streams_n1[i].hex); + max = tal_count(p); + assert(!fromwire_tlvs(&p, &max, + tlvs_n1, ARRAY_SIZE(tlvs_n1), + tlv_n1)); + assert(!p); + } + } + + for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n1_combo); i++) { + for (size_t j = 0; j < ARRAY_SIZE(valid_streams); j++) { + struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + const u8 *p; + size_t max; + + p = stream2(tmpctx, valid_streams[j].hex, + invalid_streams_n1_combo[i].hex); + max = tal_count(p); + assert(!fromwire_tlvs(&p, &max, + tlvs_n1, ARRAY_SIZE(tlvs_n1), + tlv_n1)); + assert(!p); + } + } + + /* BOLT-EXPERIMENTAL #1: + * + * Any appending of a higher-numbered valid stream to a lower-numbered + * valid stream should not trigger a decoding failure. + */ + for (size_t i = 0; i < ARRAY_SIZE(valid_streams); i++) { + for (size_t j = i+1; j < ARRAY_SIZE(valid_streams); j++) { + struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + const u8 *orig_p, *p; + size_t max; + bool expect_success; + + if (strlen(valid_streams[i].hex) == 0 + || strlen(valid_streams[j].hex) == 0) + continue; + + orig_p = stream2(tmpctx, valid_streams[i].hex, + valid_streams[j].hex); + max = tal_count(orig_p); + p = orig_p; + + /* This comparison works for our simple cases. */ + expect_success = pull_type(valid_streams[i].hex) + < pull_type(valid_streams[j].hex); + + assert(fromwire_tlvs(&p, &max, + tlvs_n1, ARRAY_SIZE(tlvs_n1), + tlv_n1) == expect_success); + + if (!expect_success) + continue; + + /* Re-encoding should give the same results (except + * ignored fields tests!) */ + if (ignored_fields(&valid_streams[i].expect) + || ignored_fields(&valid_streams[j].expect)) + continue; + + u8 *p2 = tal_arr(tmpctx, u8, 0); + towire_tlvs(&p2, tlvs_n1, ARRAY_SIZE(tlvs_n1), tlv_n1); + assert(memeq(orig_p, tal_count(orig_p), + p2, tal_count(p2))); + } + } + tal_free(tmpctx); + wally_cleanup(0); +} diff --git a/wire/tlvstream.c b/wire/tlvstream.c index 2ee863c48..1c4262284 100644 --- a/wire/tlvstream.c +++ b/wire/tlvstream.c @@ -2,6 +2,10 @@ #include #include +#ifndef SUPERVERBOSE +#define SUPERVERBOSE(...) +#endif + static const struct tlv_record_type * find_record_type(u64 type, const struct tlv_record_type types[], @@ -40,29 +44,47 @@ bool fromwire_tlvs(const u8 **cursor, size_t *max, * format */ type = fromwire_bigsize(cursor, max); + + /* BOLT-EXPERIMENTAL #1: + * - if a `type` or `length` is not minimally encoded: + * - MUST fail to parse the `tlv_stream`. + */ + if (!*cursor) { + SUPERVERBOSE("type"); + goto fail; + } length = fromwire_bigsize(cursor, max); /* BOLT-EXPERIMENTAL #1: * - if a `type` or `length` is not minimally encoded: * - MUST fail to parse the `tlv_stream`. */ - if (!*cursor) + if (!*cursor) { + SUPERVERBOSE("length"); goto fail; + } /* BOLT-EXPERIMENTAL #1: * - if `length` exceeds the number of bytes remaining in the * message: * - MUST fail to parse the `tlv_stream`. */ - if (length > *max) + if (length > *max) { + SUPERVERBOSE("value"); goto fail; + } /* BOLT-EXPERIMENTAL #1: * - if decoded `type`s are not monotonically-increasing: * - MUST fail to parse the `tlv_stream`. */ - if (!first && type <= prev_type) + if (!first && type <= prev_type) { + if (type == prev_type) + SUPERVERBOSE("duplicate tlv type"); + else + SUPERVERBOSE("invalid ordering"); goto fail; + } /* BOLT-EXPERIMENTAL #1: * - if `type` is known: @@ -83,8 +105,10 @@ bool fromwire_tlvs(const u8 **cursor, size_t *max, * for the known encoding for `type`: * - MUST fail to parse the `tlv_stream`. */ - if (tlvlen != 0) + if (tlvlen != 0) { + SUPERVERBOSE("greater than encoding length"); goto fail; + } /* We've read bytes in ->fromwire, so update max */ *max -= length; @@ -98,8 +122,10 @@ bool fromwire_tlvs(const u8 **cursor, size_t *max, */ if (type & 1) fromwire(cursor, max, NULL, length); - else + else { + SUPERVERBOSE("unknown even"); goto fail; + } } first = false; prev_type = type;