Skip to content

Commit 05c9a03

Browse files
TheBlueMattjkczyz
authored andcommitted
Add a custom TLV read/write variant
At various points we've been stuck in our TLV read/write variants but just want to break out and write some damn code to initialize a field and some more code to decide what to write for a TLV. We added the write-side part of this with the `legacy` TLV read/write variant, but its useful to also be able to specify a function which is called on the read side. Here we add a `custom` TLV read/write variant which calls a method both on read and write to either decide what to write or to map a read value (if any) to the final field.
1 parent 817ab5e commit 05c9a03

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

lightning-macros/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ fn process_fields(group: Group) -> proc_macro::TokenStream {
138138
if let TokenTree::Group(group) = ty_info {
139139
let first_group_tok = group.stream().into_iter().next().unwrap();
140140
if let TokenTree::Ident(ident) = first_group_tok {
141-
if ident.to_string() == "legacy" {
141+
if ident.to_string() == "legacy" || ident.to_string() == "custom" {
142142
continue;
143143
}
144144
}
@@ -155,13 +155,13 @@ fn process_fields(group: Group) -> proc_macro::TokenStream {
155155
computed_fields
156156
}
157157

158-
/// Scans a match statement for legacy fields which should be skipped.
158+
/// Scans a match statement for legacy or custom fields which should be skipped.
159159
///
160160
/// This is used internally in LDK's TLV serialization logic and is not expected to be used by
161161
/// other crates.
162162
///
163163
/// Wraps a `match self {..}` statement and scans the fields in the match patterns (in the form
164-
/// `ref $field_name: $field_ty`) for types marked `legacy`, skipping those fields.
164+
/// `ref $field_name: $field_ty`) for types marked `legacy` or `custom`, skipping those fields.
165165
///
166166
/// Specifically, it expects input like the following, simply dropping `field3` and the
167167
/// `: $field_ty` after each field name.

lightning/src/util/ser_macros.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ macro_rules! _encode_tlv {
6363
}
6464
$crate::_encode_tlv!($stream, $optional_type, value, option);
6565
} };
66+
($stream: expr, $optional_type: expr, $optional_field: expr, (custom, $fieldty: ty, $read: expr, $write: expr) $(, $self: ident)?) => { {
67+
$crate::_encode_tlv!($stream, $optional_type, $optional_field, (legacy, $fieldty, $write) $(, $self)?);
68+
} };
6669
($stream: expr, $type: expr, $field: expr, optional_vec $(, $self: ident)?) => {
6770
if !$field.is_empty() {
6871
$crate::_encode_tlv!($stream, $type, $field, required_vec);
@@ -232,6 +235,9 @@ macro_rules! _get_varint_length_prefixed_tlv_length {
232235
($len: expr, $optional_type: expr, $optional_field: expr, (legacy, $fieldty: ty, $write: expr) $(, $self: ident)?) => {
233236
$crate::_get_varint_length_prefixed_tlv_length!($len, $optional_type, $write($($self)?), option);
234237
};
238+
($len: expr, $optional_type: expr, $optional_field: expr, (custom, $fieldty: ty, $read: expr, $write: expr) $(, $self: ident)?) => {
239+
$crate::_get_varint_length_prefixed_tlv_length!($len, $optional_type, $optional_field, (legacy, $fieldty, $write) $(, $self)?);
240+
};
235241
($len: expr, $type: expr, $field: expr, optional_vec $(, $self: ident)?) => {
236242
if !$field.is_empty() {
237243
$crate::_get_varint_length_prefixed_tlv_length!($len, $type, $field, required_vec);
@@ -317,6 +323,16 @@ macro_rules! _check_decoded_tlv_order {
317323
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (legacy, $fieldty: ty, $write: expr)) => {{
318324
// no-op
319325
}};
326+
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (custom, $fieldty: ty, $read: expr, $write: expr) $(, $self: ident)?) => {{
327+
// Note that $type may be 0 making the second comparison always false
328+
#[allow(unused_comparisons)]
329+
let invalid_order =
330+
($last_seen_type.is_none() || $last_seen_type.unwrap() < $type) && $typ.0 > $type;
331+
if invalid_order {
332+
let read_result: Result<_, DecodeError> = $read(None);
333+
$field = read_result?.into();
334+
}
335+
}};
320336
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (required, explicit_type: $fieldty: ty)) => {{
321337
_check_decoded_tlv_order!($last_seen_type, $typ, $type, $field, required);
322338
}};
@@ -385,6 +401,15 @@ macro_rules! _check_missing_tlv {
385401
($last_seen_type: expr, $type: expr, $field: ident, (legacy, $fieldty: ty, $write: expr)) => {{
386402
// no-op
387403
}};
404+
($last_seen_type: expr, $type: expr, $field: ident, (custom, $fieldty: ty, $read: expr, $write: expr)) => {{
405+
// Note that $type may be 0 making the second comparison always false
406+
#[allow(unused_comparisons)]
407+
let missing_req_type = $last_seen_type.is_none() || $last_seen_type.unwrap() < $type;
408+
if missing_req_type {
409+
let read_result: Result<_, DecodeError> = $read(None);
410+
$field = read_result?.into();
411+
}
412+
}};
388413
($last_seen_type: expr, $type: expr, $field: ident, (required, explicit_type: $fieldty: ty)) => {{
389414
_check_missing_tlv!($last_seen_type, $type, $field, required);
390415
}};
@@ -441,6 +466,12 @@ macro_rules! _decode_tlv {
441466
($outer_reader: expr, $reader: expr, $field: ident, (legacy, $fieldty: ty, $write: expr)) => {{
442467
$crate::_decode_tlv!($outer_reader, $reader, $field, (option, explicit_type: $fieldty));
443468
}};
469+
($outer_reader: expr, $reader: expr, $field: ident, (custom, $fieldty: ty, $read: expr, $write: expr)) => {{
470+
let read_field: $fieldty;
471+
$crate::_decode_tlv!($outer_reader, $reader, read_field, required);
472+
let read_result: Result<_, DecodeError> = $read(Some(read_field));
473+
$field = read_result?.into();
474+
}};
444475
($outer_reader: expr, $reader: expr, $field: ident, (required, explicit_type: $fieldty: ty)) => {{
445476
let _field: &$fieldty = &$field;
446477
_decode_tlv!($outer_reader, $reader, $field, required);
@@ -830,6 +861,9 @@ macro_rules! _init_tlv_based_struct_field {
830861
($field: ident, (legacy, $fieldty: ty, $write: expr)) => {
831862
$crate::_init_tlv_based_struct_field!($field, option)
832863
};
864+
($field: ident, (custom, $fieldty: ty, $read: expr, $write: expr)) => {
865+
$crate::_init_tlv_based_struct_field!($field, required)
866+
};
833867
($field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {
834868
$crate::_init_tlv_based_struct_field!($field, option)
835869
};
@@ -896,6 +930,9 @@ macro_rules! _init_tlv_field_var {
896930
($field: ident, (legacy, $fieldty: ty, $write: expr)) => {
897931
$crate::_init_tlv_field_var!($field, (option, explicit_type: $fieldty));
898932
};
933+
($field: ident, (custom, $fieldty: ty, $read: expr, $write: expr)) => {
934+
$crate::_init_tlv_field_var!($field, required);
935+
};
899936
($field: ident, (required, explicit_type: $fieldty: ty)) => {
900937
let mut $field = $crate::util::ser::RequiredWrapper::<$fieldty>(None);
901938
};
@@ -979,6 +1016,12 @@ macro_rules! _decode_and_build {
9791016
/// called with the object being serialized and a returned `Option` and is written as a TLV if
9801017
/// `Some`. When reading, an optional field of type `$ty` is read (which can be used in later
9811018
/// `default_value` or `static_value` fields by referring to the value by name).
1019+
/// If `$fieldty` is `(custom, $ty, $read, $write)` then, when writing, the same behavior as
1020+
/// `legacy`, above is used. When reading, if a TLV is present, it is read as `$ty` and the
1021+
/// `$read` method is called with `Some(decoded_$ty_object)`. If no TLV is present, the field
1022+
/// will be initialized by calling `$read(None)`. `$read` should return a
1023+
/// `Result<field type, DecodeError>` (note that the processed field type may differ from `$ty`;
1024+
/// `$ty` is the type as de/serialized, not necessarily the actual field type).
9821025
///
9831026
/// For example,
9841027
/// ```

0 commit comments

Comments
 (0)