tlv: Add typesafe fromwire codegen for TLV namespaces

We were weaving in and out of generic code through `fromwire_tlvs` with custom
parameters defining the types in that namespace. This hard-wires the parser
with the namespace's types. Slowly trying to deprecate `fromwire_tlvs` in
favor of this typesafe variant.
This commit is contained in:
Christian Decker 2019-11-18 19:52:20 +01:00 committed by Rusty Russell
parent 2255024ead
commit 5794c83b4d
2 changed files with 94 additions and 0 deletions

View File

@ -66,6 +66,7 @@ struct ${tlv.struct_name()} {
% for tlv in tlvs.values():
struct ${tlv.struct_name()} *${tlv.struct_name()}_new(const tal_t *ctx);
bool fromwire_${tlv.name}(const u8 **cursor, size_t *max, struct ${tlv.struct_name()} *record);
% if tlv.name in options.expose_tlv_type:
#define TLVS_${tlv.name.upper()}_ARRAY_SIZE ${len(tlv.messages)}

View File

@ -3,10 +3,16 @@
/* Original template can be found at tools/gen/impl_template */
#include <${header_filename}>
#include <assert.h>
#include <ccan/array_size/array_size.h>
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <stdio.h>
#ifndef SUPERVERBOSE
#define SUPERVERBOSE(...)
#endif
% for comment in top_comments:
/*${comment} */
% endfor
@ -211,6 +217,93 @@ ${static}const struct tlv_record_type tlvs_${tlv.name}[] = {
{ ${msg.number}, towire_${msg.struct_name()}, fromwire_${msg.struct_name()} },
% endfor
};
bool fromwire_${tlv.name}(const u8 **cursor, size_t *max, struct ${tlv.struct_name()} *record)
{
size_t num_types = ${len(tlv.messages)};
const struct tlv_record_type *types = tlvs_${tlv.name};
while (*max > 0) {
struct tlv_field field;
/* BOLT #1:
*
* A `varint` is a variable-length, unsigned integer encoding
* using the [BigSize](#appendix-a-bigsize-test-vectors)
* format
*/
field.numtype = fromwire_bigsize(cursor, max);
/* BOLT #1:
* - if a `type` or `length` is not minimally encoded:
* - MUST fail to parse the `tlv_stream`.
*/
if (!*cursor) {
SUPERVERBOSE("type");
goto fail;
}
field.length = fromwire_bigsize(cursor, max);
/* BOLT #1:
* - if a `type` or `length` is not minimally encoded:
* - MUST fail to parse the `tlv_stream`.
*/
if (!*cursor) {
SUPERVERBOSE("length");
goto fail;
}
/* BOLT #1:
* - if `length` exceeds the number of bytes remaining in the
* message:
* - MUST fail to parse the `tlv_stream`.
*/
if (field.length > *max) {
SUPERVERBOSE("value");
goto fail;
}
field.value = tal_dup_arr(record, u8, *cursor, field.length, 0);
/* BOLT #1:
* - if `type` is known:
* - MUST decode the next `length` bytes using the known
* encoding for `type`.
*/
field.meta = NULL;
for (size_t i = 0; i < num_types; i++)
if (types[i].type == field.numtype)
field.meta = &types[i];
if (field.meta) {
/* Length of message can't exceed 16 bits anyway. */
size_t tlvlen = field.length;
field.meta->fromwire(cursor, &tlvlen, record);
if (!*cursor)
goto fail;
/* BOLT #1:
* - if `length` is not exactly equal to that required
* for the known encoding for `type`:
* - MUST fail to parse the `tlv_stream`.
*/
if (tlvlen != 0) {
SUPERVERBOSE("greater than encoding length");
goto fail;
}
} else {
/* We didn't read from *cursor through a fromwire, so
* update manually. */
*cursor += field.length;
}
/* We've read bytes in ->fromwire, so update max */
*max -= field.length;
tal_arr_expand(&record->fields, field);
}
return true;
fail:
fromwire_fail(cursor, max);
return false;
}
% endfor ## END TLV's
% for msg in messages: ## START Wire Messages