From 4ed2bd0cd1d7bf23e2abf850184898e4ca980185 Mon Sep 17 00:00:00 2001 From: Jarvis Carroll Date: Tue, 23 Sep 2025 14:35:42 +1000 Subject: [PATCH] coerce bytes --- src/hz.erl | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/hz.erl b/src/hz.erl index cf7b978..9c3b202 100644 --- a/src/hz.erl +++ b/src/hz.erl @@ -1520,6 +1520,8 @@ opaque_type(Params, #{variant := VariantDefs}) -> {variant, Variants}; opaque_type(Params, #{tuple := TypeDefs}) -> {tuple, [opaque_type(Params, Type) || Type <- TypeDefs]}; +opaque_type(_, #{bytes := Count}) -> + {bytes, [Count]}; opaque_type(Params, Pair) when is_map(Pair) -> [{Name, TypeArgs}] = maps:to_list(Pair), {opaque_type_name(Name), [opaque_type(Params, Arg) || Arg <- TypeArgs]}. @@ -1544,7 +1546,6 @@ opaque_type_name(<<"string">>) -> string; opaque_type_name(<<"address">>) -> address; opaque_type_name(<<"hash">>) -> hash; opaque_type_name(<<"signature">>) -> signature; -opaque_type_name(<<"bytes">>) -> bytes; opaque_type_name(<<"contract">>) -> contract; opaque_type_name(<<"list">>) -> list; opaque_type_name(<<"map">>) -> map; @@ -1615,6 +1616,10 @@ annotate_types([], _Types, Acc) -> annotate_type_subexpressions(PrimitiveType, _Types) when is_atom(PrimitiveType) -> {ok, PrimitiveType}; +annotate_type_subexpressions({bytes, [Count]}, _Types) -> + % bytes is weird, because it has an argument, but that argument isn't an + % opaque type. + {ok, {bytes, [Count]}}; annotate_type_subexpressions({variant, VariantsOpaque}, Types) -> case annotate_variants(VariantsOpaque, Types, []) of {ok, Variants} -> {ok, {variant, Variants}}; @@ -1843,6 +1848,8 @@ coerce({O, N, string}, Str, Direction) -> StrBin -> {ok, StrBin} end; +coerce({O, N, {bytes, [Count]}}, Bytes, _Direction) when is_binary(Bytes) -> + coerce_bytes(O, N, Count, Bytes); coerce({_, _, {list, [Type]}}, Data, Direction) when is_list(Data) -> coerce_list(Type, Data, Direction); coerce({_, _, {map, [KeyType, ValType]}}, Data, Direction) when is_map(Data) -> @@ -1892,6 +1899,15 @@ coerce({O, N, _}, Data, from_fate) -> {ok, Data}; coerce({O, N, _}, Data, _) -> single_error({invalid, O, N, Data}). +coerce_bytes(O, N, _, Bytes) when bit_size(Bytes) rem 8 /= 0 -> + single_error({partial_bytes, O, N, bit_size(Bytes)}); +coerce_bytes(_, _, any, Bytes) -> + {ok, Bytes}; +coerce_bytes(O, N, Count, Bytes) when byte_size(Bytes) /= Count -> + single_error({incorrect_size, O, N, Bytes}); +coerce_bytes(_, _, _, Bytes) -> + {ok, Bytes}. + coerce_chain_object(O, N, T, Tag, S) -> case decode_chain_object(Tag, S) of {ok, Data} -> {ok, coerce_chain_object2(T, Data)}; @@ -2594,3 +2610,12 @@ param_test() -> try_coerce(Input, 0, 0), try_coerce(Output, 0, 0). +bytes_test() -> + Contract = " + contract C = + entrypoint f(): bytes(4) * bytes() = (#DEADBEEF, Bytes.to_any_size(#112233)) + ", + {ok, AACI} = aaci_from_string(Contract), + {ok, {[], Output}} = aaci_lookup_spec(AACI, "f"), + try_coerce(Output, {<<"abcd">>, <<"efghi">>}, {tuple, {<<"abcd">>, <<"efghi">>}}). +