Add split_any, to_fixed_size, size, to_any_size, Int.to_bytes and String.to_bytes

This commit is contained in:
Hans Svensson 2023-05-05 13:05:47 +02:00
parent fda58ec430
commit ae83c434fd
6 changed files with 118 additions and 45 deletions

View File

@ -1,5 +1,8 @@
include "List.aes"
namespace String =
// Gives a bytes() representation of the string
function to_bytes(s : string) : bytes() = StringInternal.to_bytes(s)
// Computes the SHA3/Keccak hash of the string
function sha3(s : string) : hash = StringInternal.sha3(s)
// Computes the SHA256 hash of the string.

View File

@ -282,6 +282,8 @@ decode_type(#{list := [Et]}) ->
decode_type(#{map := Ets}) ->
Ts = decode_types(Ets),
["map",$(,lists:join(",", Ts),$)];
decode_type(#{bytes := any}) ->
["bytes()"];
decode_type(#{bytes := Len}) ->
["bytes(", integer_to_list(Len), ")"];
decode_type(#{variant := Ets}) ->

View File

@ -89,8 +89,9 @@
-type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}.
-type byte_constraint() :: {is_bytes, utype()}
| {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}.
-type byte_constraint() :: {is_bytes, term(), utype()}
| {is_fixed_bytes, term(), utype()}
| {add_bytes, aeso_syntax:ann(), concat | split | split_any, utype(), utype(), utype()}.
-type aens_resolve_constraint() :: {aens_resolve_type, utype()}.
-type oracle_type_constraint() :: {oracle_type, aeso_syntax:ann(), utype()}.
@ -829,6 +830,7 @@ global_env() ->
[{"length", Fun1(String, Int)},
{"concat", Fun([String, String], String)},
{"to_list", Fun1(String, List(Char))},
{"to_bytes", Fun1(String, Bytes(any))},
{"from_list", Fun1(List(Char), String)},
{"to_upper", Fun1(String, String)},
{"to_lower", Fun1(String, String)},
@ -859,14 +861,19 @@ global_env() ->
%% Bytes
BytesScope = #scope
{ funs = MkDefs(
[{"to_int", Fun1(Bytes(any), Int)},
{"to_str", Fun1(Bytes(any), String)},
{"concat", FunC(bytes_concat, [Bytes(any), Bytes(any)], Bytes(any))},
{"split", FunC(bytes_split, [Bytes(any)], Pair(Bytes(any), Bytes(any)))}
[{"to_int", Fun1(Bytes('_'), Int)},
{"to_str", Fun1(Bytes('_'), String)},
{"to_fixed_size", Fun1(Bytes(any), Option(Bytes(fixed)))},
{"to_any_size", Fun1(Bytes(fixed), Bytes(any))},
{"size", Fun1(Bytes('_'), Int)},
{"concat", FunC(bytes_concat, [Bytes('_'), Bytes('_')], Bytes('_'))},
{"split", FunC1(bytes_split, Bytes(fixed), Pair(Bytes(fixed), Bytes(fixed)))},
{"split_any", Fun([Bytes(any), Int], Option(Pair(Bytes(any), Bytes(any))))}
]) },
%% Conversion
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)},
{"to_bytes", Fun([Int, Int], Bytes(any))},
{"mulmod", Fun([Int, Int, Int], Int)}]) },
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
@ -1790,8 +1797,8 @@ lookup_name(Env = #env{ namespace = NS, current_function = CurFn }, As, Id, Opti
Freshen = proplists:get_value(freshen, Options, false),
check_stateful(Env, Id, Ty),
Ty1 = case Ty of
{type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty);
_ when Freshen -> freshen_type(As, Ty);
{type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty, [{fun_name, Id}]);
_ when Freshen -> freshen_type(As, Ty, [{fun_name, Id}]);
_ -> Ty
end,
{set_qname(QId, Id), Ty1}
@ -2672,7 +2679,8 @@ destroy_and_report_unsolved_constraints(Env) ->
(_) -> false
end, OtherCs2),
{BytesCs, OtherCs4} =
lists:partition(fun({is_bytes, _}) -> true;
lists:partition(fun({is_bytes, _, _}) -> true;
({is_fixed_bytes, _, _}) -> true;
({add_bytes, _, _, _, _, _}) -> true;
(_) -> false
end, OtherCs3),
@ -2801,15 +2809,21 @@ solve_constraint(Env, C = #dependent_type_constraint{}) ->
check_named_argument_constraint(Env, C);
solve_constraint(Env, C = #named_argument_constraint{}) ->
check_named_argument_constraint(Env, C);
solve_constraint(_Env, {is_bytes, _}) -> ok;
solve_constraint(Env, {add_bytes, Ann, _, A0, B0, C0}) ->
solve_constraint(_Env, {is_bytes, _, _}) -> ok;
solve_constraint(_Env, {is_fixed_bytes, _, _}) -> ok;
solve_constraint(Env, {add_bytes, Ann, Action, A0, B0, C0}) ->
A = unfold_types_in_type(Env, dereference(A0)),
B = unfold_types_in_type(Env, dereference(B0)),
C = unfold_types_in_type(Env, dereference(C0)),
case {A, B, C} of
{{bytes_t, _, M}, {bytes_t, _, N}, _} -> unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
{{bytes_t, _, M}, _, {bytes_t, _, R}} when R >= M -> unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
{_, {bytes_t, _, N}, {bytes_t, _, R}} when R >= N -> unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
{{bytes_t, _, M}, {bytes_t, _, N}, _} when is_integer(M), is_integer(N) ->
unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
{{bytes_t, _, M}, _, {bytes_t, _, R}} when is_integer(M), is_integer(R), R >= M ->
unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
{_, {bytes_t, _, N}, {bytes_t, _, R}} when is_integer(N), is_integer(R), R >= N ->
unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
{{bytes_t, _, _}, {bytes_t, _, _}, _} when Action == concat ->
unify(Env, {bytes_t, Ann, any}, C, {at, Ann});
_ -> ok
end;
solve_constraint(_, _) -> ok.
@ -2818,18 +2832,29 @@ check_bytes_constraints(Env, Constraints) ->
InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints,
T <- [A, B, C],
element(1, T) /= bytes_t ],
InSplitConstraint = [ T || {add_bytes, _, split, A, B, C} <- Constraints,
T <- [A, B, C],
element(1, T) /= bytes_t ],
%% Skip is_bytes constraints for types that occur in add_bytes constraints
%% (no need to generate error messages for both is_bytes and add_bytes).
Skip = fun({is_bytes, T}) -> lists:member(T, InAddConstraint);
Skip = fun({is_bytes, _, T}) -> lists:member(T, InAddConstraint);
({is_fixed_bytes, _, T}) -> lists:member(T, InSplitConstraint);
(_) -> false end,
[ check_bytes_constraint(Env, C) || C <- Constraints, not Skip(C) ].
check_bytes_constraint(Env, {is_bytes, Type}) ->
check_bytes_constraint(Env, {is_bytes, Ann, Type}) ->
Type1 = unfold_types_in_type(Env, instantiate(Type)),
case Type1 of
{bytes_t, _, _} -> ok;
{bytes_t, _, N} when is_integer(N); N == any -> ok;
_ ->
type_error({unknown_byte_length, Type})
type_error({unknown_byte_type, Ann, Type})
end;
check_bytes_constraint(Env, {is_fixed_bytes, Ann, Type}) ->
Type1 = unfold_types_in_type(Env, instantiate(Type)),
case Type1 of
{bytes_t, _, N} when is_integer(N) -> ok;
_ ->
type_error({unknown_byte_length, Ann, Type})
end;
check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) ->
A = unfold_types_in_type(Env, instantiate(A0)),
@ -3292,35 +3317,45 @@ create_freshen_tvars() ->
destroy_freshen_tvars() ->
ets_delete(freshen_tvars).
freshen_type(Ann, Type) ->
freshen_type(Ann, Type, Ctx) ->
create_freshen_tvars(),
Type1 = freshen(Ann, Type),
Type1 = freshen(Ann, Type, Ctx),
destroy_freshen_tvars(),
Type1.
freshen(Type) ->
freshen(aeso_syntax:get_ann(Type), Type).
freshen(aeso_syntax:get_ann(Type), Type, none).
freshen(Ann, {tvar, _, Name}) ->
freshen(Ann, {tvar, _, Name}, _Ctx) ->
NewT = case ets_lookup(freshen_tvars, Name) of
[] -> fresh_uvar(Ann);
[{Name, T}] -> T
end,
ets_insert(freshen_tvars, {Name, NewT}),
NewT;
freshen(Ann, {bytes_t, _, any}) ->
freshen(Ann, {bytes_t, _, '_'}, Ctx) ->
X = fresh_uvar(Ann),
add_constraint({is_bytes, X}),
add_constraint({is_bytes, Ctx, X}),
X;
freshen(Ann, T) when is_tuple(T) ->
list_to_tuple(freshen(Ann, tuple_to_list(T)));
freshen(Ann, [A | B]) ->
[freshen(Ann, A) | freshen(Ann, B)];
freshen(_, X) ->
freshen(Ann, {bytes_t, _, fixed}, Ctx) ->
X = fresh_uvar(Ann),
add_constraint({is_fixed_bytes, Ctx, X}),
X;
freshen(Ann, {fun_t, FAnn, NamedArgs, Args, Result}, Ctx) when is_list(Args) ->
{fun_t, FAnn, freshen(Ann, NamedArgs, Ctx),
[ freshen(Ann, Arg, [{arg, Ix} | Ctx]) || {Arg, Ix} <- lists:zip(Args, lists:seq(1, length(Args))) ],
freshen(Ann, Result, [result | Ctx])};
freshen(Ann, {fun_t, FAnn, NamedArgs, Arg, Result}, Ctx) ->
{fun_t, FAnn, freshen(Ann, NamedArgs, Ctx), freshen(Ann, Arg, Ctx), freshen(Ann, Result, [result | Ctx])};
freshen(Ann, T, Ctx) when is_tuple(T) ->
list_to_tuple(freshen(Ann, tuple_to_list(T), Ctx));
freshen(Ann, [A | B], Ctx) ->
[freshen(Ann, A, Ctx) | freshen(Ann, B, Ctx)];
freshen(_, X, _Ctx) ->
X.
freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}) ->
FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig)),
freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}, Ctx) ->
FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig), Ctx),
apply_typesig_constraint(Ann, Constr, FunT),
FunT.
@ -4236,6 +4271,18 @@ pp_type(Type) ->
pp_type(Label, Type) ->
prettypr:format(prettypr:beside(prettypr:text(Label), aeso_pretty:type(Type, [show_generated])), 80, 80).
pp_context([{fun_name, Id}]) -> ["a call to ", pp(Id)];
pp_context([result | Ctx]) -> ["the result of ", pp_context(Ctx)];
pp_context([{arg, N} | Ctx]) ->
Cnt = fun(1) -> "first";
(2) -> "second";
(3) -> "third";
(I) -> io_lib:format("~pth", [I])
end,
["the ", Cnt(N), " argument of ", pp_context(Ctx)];
pp_context(none) -> "unknown context".
src_file(T) -> aeso_syntax:get_ann(file, T, no_file).
include_type(T) -> aeso_syntax:get_ann(include_type, T, none).
line_number(T) -> aeso_syntax:get_ann(line, T, 0).

View File

@ -291,14 +291,15 @@ builtins() ->
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
{"pairing", 2}, {"miller_loop", 2}, {"final_exp", 1},
{"int_to_fr", 1}, {"int_to_fp", 1}, {"fr_to_int", 1}, {"fp_to_int", 1}]},
{["StringInternal"], [{"length", 1}, {"concat", 2}, {"to_list", 1}, {"from_list", 1},
{["StringInternal"], [{"length", 1}, {"concat", 2}, {"to_list", 1}, {"from_list", 1}, {"to_bytes", 1},
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}, {"to_lower", 1}, {"to_upper", 1}]},
{["Char"], [{"to_int", 1}, {"from_int", 1}]},
{["Auth"], [{"tx_hash", none}, {"tx", none}]},
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
{["Int"], [{"to_str", 1}, {"mulmod", 2}]},
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}, {"to_fixed_size", 1},
{"to_any_size", 1}, {"size", 1}, {"split_any", 2}]},
{["Int"], [{"to_str", 1}, {"to_bytes", 2}, {"mulmod", 2}]},
{["Address"], [{"to_str", 1}, {"to_bytes", 1}, {"to_contract", 1},
{"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
],
@ -611,6 +612,9 @@ expr_to_fcode(Env, Type, {qid, Ann, X}) ->
{builtin_u, FAnn, B = bytes_split, Ar} ->
{fun_t, _, _, _, {tuple_t, _, [{bytes_t, _, N}, _]}} = Type,
{builtin_u, FAnn, B, Ar, [{lit, FAnn, {int, N}}]};
{builtin_u, FAnn, B = bytes_to_fixed_size, Ar} ->
{fun_t, _, _, _, {app_t, _, {id, _, "option"}, [{bytes_t, _, N}]}} = Type,
{builtin_u, FAnn, B, Ar, [{lit, FAnn, {int, N}}]};
Other -> Other
end;
@ -1166,13 +1170,13 @@ stmts_to_fcode(Env, [Expr | Stmts]) ->
op_builtins() ->
[map_from_list, map_to_list, map_delete, map_member, map_size,
stringinternal_length, stringinternal_concat, stringinternal_to_list, stringinternal_from_list,
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
stringinternal_to_bytes, stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
bits_difference, int_to_str, int_mulmod, address_to_str, address_to_bytes, crypto_verify_sig,
address_to_contract,
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, crypto_poseidon,
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union, bits_difference,
int_to_str, int_to_bytes, int_mulmod,
address_to_str, address_to_bytes, address_to_contract,
crypto_verify_sig, crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
crypto_poseidon, crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
mcl_bls12_381_g2_neg, mcl_bls12_381_g2_norm, mcl_bls12_381_g2_valid,

View File

@ -539,6 +539,14 @@ builtin_to_scode(Env, bytes_concat, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split_any, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split_any(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_to_fixed_size, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_fixed_size(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_to_any_size, [A]) ->
[to_scode(Env, A)]; %% no_op!
builtin_to_scode(Env, bytes_size, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_size(?a, ?a), Args);
builtin_to_scode(Env, abort, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
builtin_to_scode(Env, exit, [_] = Args) ->
@ -683,6 +691,7 @@ op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
op_to_scode(stringinternal_to_bytes) -> aeb_fate_ops:str_to_bytes(?a, ?a);
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
@ -699,6 +708,7 @@ op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
op_to_scode(address_to_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a);
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
op_to_scode(int_to_bytes) -> aeb_fate_ops:int_to_bytes(?a, ?a, ?a);
op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a);
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
@ -1019,9 +1029,11 @@ attributes(I) ->
{'APPEND', A, B, C} -> Pure(A, [B, C]);
{'STR_JOIN', A, B, C} -> Pure(A, [B, C]);
{'INT_TO_STR', A, B} -> Pure(A, B);
{'INT_TO_BYTES', A, B, C} -> Pure(A, [B, C]);
{'ADDR_TO_STR', A, B} -> Pure(A, B);
{'STR_REVERSE', A, B} -> Pure(A, B);
{'STR_LENGTH', A, B} -> Pure(A, B);
{'STR_TO_BYTES', A, B} -> Pure(A, B);
{'INT_TO_ADDR', A, B} -> Pure(A, B);
{'VARIANT', A, B, C, D} -> Pure(A, [?a, B, C, D]);
{'VARIANT_TEST', A, B, C} -> Pure(A, [B, C]);
@ -1055,6 +1067,9 @@ attributes(I) ->
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SPLIT_ANY', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SIZE', A, B} -> Pure(A, B);
{'BYTES_TO_FIXED_SIZE', A, B, C} -> Pure(A, [B, C]);
{'ORACLE_CHECK', A, B, C, D} -> Pure(A, [B, C, D]);
{'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
{'IS_ORACLE', A, B} -> Pure(A, [B]);

View File

@ -275,7 +275,9 @@ type({tuple_t, _, Args}) ->
tuple_type(Args);
type({args_t, _, Args}) ->
args_type(Args);
type({bytes_t, _, any}) -> text("bytes(_)");
type({bytes_t, _, any}) -> text("bytes()");
type({bytes_t, _, '_'}) -> text("bytes(_)");
type({bytes_t, _, fixed}) -> text("bytes(_)");
type({bytes_t, _, Len}) ->
text(lists:concat(["bytes(", Len, ")"]));
type({if_t, _, Id, Then, Else}) ->