Compare commits
4 Commits
f54a33a293
...
87477e8c9c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
87477e8c9c | ||
![]() |
72fe195bff | ||
![]() |
8eb907091a | ||
![]() |
560cdb8cea |
208
src/hz.erl
208
src/hz.erl
@ -76,6 +76,7 @@
|
|||||||
|
|
||||||
-export_type([chain_node/0, network_id/0, chain_error/0]).
|
-export_type([chain_node/0, network_id/0, chain_error/0]).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
-type chain_node() :: {inet:ip_address(), inet:port_number()}.
|
-type chain_node() :: {inet:ip_address(), inet:port_number()}.
|
||||||
-type network_id() :: string().
|
-type network_id() :: string().
|
||||||
@ -1385,10 +1386,7 @@ prepare_contract(File) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
prepare_aaci(ACI) ->
|
prepare_aaci(ACI) ->
|
||||||
% NOTE this will also pick up the main contract; as a result the main
|
Types = lists:foldl(fun prepare_namespace_types/2, #{}, ACI),
|
||||||
% contract extraction later on shouldn't bother with typedefs.
|
|
||||||
Contracts = [ContractDef || #{contract := ContractDef} <- ACI],
|
|
||||||
Types = simplify_contract_types(Contracts, #{}),
|
|
||||||
|
|
||||||
[{NameBin, SpecDefs}] =
|
[{NameBin, SpecDefs}] =
|
||||||
[{N, F}
|
[{N, F}
|
||||||
@ -1399,22 +1397,29 @@ prepare_aaci(ACI) ->
|
|||||||
Specs = simplify_specs(SpecDefs, #{}, Types),
|
Specs = simplify_specs(SpecDefs, #{}, Types),
|
||||||
{aaci, Name, Specs, Types}.
|
{aaci, Name, Specs, Types}.
|
||||||
|
|
||||||
simplify_contract_types([], Types) ->
|
prepare_namespace_types(#{namespace := NS}, Types) ->
|
||||||
Types;
|
prepare_namespace_types2(NS, false, Types);
|
||||||
simplify_contract_types([Next | Rest], Types) ->
|
prepare_namespace_types(#{contract := NS}, Types) ->
|
||||||
TypeDefs = maps:get(typedefs, Next),
|
prepare_namespace_types2(NS, true, Types).
|
||||||
NameBin = maps:get(name, Next),
|
|
||||||
|
prepare_namespace_types2(NS, IsContract, Types) ->
|
||||||
|
TypeDefs = maps:get(typedefs, NS),
|
||||||
|
NameBin = maps:get(name, NS),
|
||||||
Name = binary_to_list(NameBin),
|
Name = binary_to_list(NameBin),
|
||||||
Types2 = maps:put(Name, {[], contract}, Types),
|
Types2 = case IsContract of
|
||||||
Types3 = case maps:find(state, Next) of
|
true ->
|
||||||
|
maps:put(Name, {[], contract}, Types);
|
||||||
|
false ->
|
||||||
|
Types
|
||||||
|
end,
|
||||||
|
Types3 = case maps:find(state, NS) of
|
||||||
{ok, StateDefACI} ->
|
{ok, StateDefACI} ->
|
||||||
StateDefOpaque = opaque_type([], StateDefACI),
|
StateDefOpaque = opaque_type([], StateDefACI),
|
||||||
maps:put(Name ++ ".state", {[], StateDefOpaque}, Types2);
|
maps:put(Name ++ ".state", {[], StateDefOpaque}, Types2);
|
||||||
error ->
|
error ->
|
||||||
Types2
|
Types2
|
||||||
end,
|
end,
|
||||||
Types4 = simplify_typedefs(TypeDefs, Types3, Name ++ "."),
|
simplify_typedefs(TypeDefs, Types3, Name ++ ".").
|
||||||
simplify_contract_types(Rest, Types4).
|
|
||||||
|
|
||||||
simplify_typedefs([], Types, _NamePrefix) ->
|
simplify_typedefs([], Types, _NamePrefix) ->
|
||||||
Types;
|
Types;
|
||||||
@ -1638,12 +1643,39 @@ substitute_opaque_type(Bindings, {var, VarName}) ->
|
|||||||
false -> {error, invalid_aci};
|
false -> {error, invalid_aci};
|
||||||
{_, TypeArg} -> {ok, TypeArg}
|
{_, TypeArg} -> {ok, TypeArg}
|
||||||
end;
|
end;
|
||||||
|
substitute_opaque_type(Bindings, {variant, Args}) ->
|
||||||
|
case substitute_variant_types(Bindings, Args, []) of
|
||||||
|
{ok, Result} -> {ok, {variant, Result}};
|
||||||
|
Error -> Error
|
||||||
|
end;
|
||||||
|
substitute_opaque_type(Bindings, {record, Args}) ->
|
||||||
|
case substitute_record_types(Bindings, Args, []) of
|
||||||
|
{ok, Result} -> {ok, {record, Result}};
|
||||||
|
Error -> Error
|
||||||
|
end;
|
||||||
substitute_opaque_type(Bindings, {Connective, Args}) ->
|
substitute_opaque_type(Bindings, {Connective, Args}) ->
|
||||||
case substitute_opaque_types(Bindings, Args, []) of
|
case substitute_opaque_types(Bindings, Args, []) of
|
||||||
{ok, Result} -> {ok, {Connective, Result}};
|
{ok, Result} -> {ok, {Connective, Result}};
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
substitute_opaque_type(_Bindings, Type) -> {ok, Type}.
|
substitute_opaque_type(_Bindings, Type) ->
|
||||||
|
{ok, Type}.
|
||||||
|
|
||||||
|
substitute_variant_types(Bindings, [{VariantName, Elements} | Rest], Acc) ->
|
||||||
|
case substitute_opaque_types(Bindings, Elements, []) of
|
||||||
|
{ok, Result} -> substitute_variant_types(Bindings, Rest, [{VariantName, Result} | Acc]);
|
||||||
|
Error -> Error
|
||||||
|
end;
|
||||||
|
substitute_variant_types(_Bindings, [], Acc) ->
|
||||||
|
{ok, lists:reverse(Acc)}.
|
||||||
|
|
||||||
|
substitute_record_types(Bindings, [{ElementName, Type} | Rest], Acc) ->
|
||||||
|
case substitute_opaque_type(Bindings, Type) of
|
||||||
|
{ok, Result} -> substitute_record_types(Bindings, Rest, [{ElementName, Result} | Acc]);
|
||||||
|
Error -> Error
|
||||||
|
end;
|
||||||
|
substitute_record_types(_Bindings, [], Acc) ->
|
||||||
|
{ok, lists:reverse(Acc)}.
|
||||||
|
|
||||||
substitute_opaque_types(Bindings, [Next | Rest], Acc) ->
|
substitute_opaque_types(Bindings, [Next | Rest], Acc) ->
|
||||||
case substitute_opaque_type(Bindings, Next) of
|
case substitute_opaque_type(Bindings, Next) of
|
||||||
@ -2134,3 +2166,151 @@ eu(N, Size) ->
|
|||||||
% /v3/debug/check-tx/pool/{hash}
|
% /v3/debug/check-tx/pool/{hash}
|
||||||
% /v3/debug/token-supply/height/{height}
|
% /v3/debug/token-supply/height/{height}
|
||||||
% /v3/debug/crash
|
% /v3/debug/crash
|
||||||
|
|
||||||
|
|
||||||
|
%%% Simple coerce/3 tests
|
||||||
|
|
||||||
|
try_coerce(Type, Sophia, Fate) ->
|
||||||
|
FateActual = coerce(Type, Sophia, to_fate),
|
||||||
|
SophiaActual = coerce(Type, Fate, from_fate),
|
||||||
|
case {ok, Fate} == FateActual of
|
||||||
|
true ->
|
||||||
|
ok;
|
||||||
|
false ->
|
||||||
|
erlang:error({to_fate_failed, Fate, FateActual})
|
||||||
|
end,
|
||||||
|
case {ok, Sophia} == SophiaActual of
|
||||||
|
true ->
|
||||||
|
ok;
|
||||||
|
false ->
|
||||||
|
erlang:error({from_fate_failed, Sophia, SophiaActual})
|
||||||
|
end,
|
||||||
|
ok.
|
||||||
|
|
||||||
|
coerce_int_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type(integer, #{}),
|
||||||
|
try_coerce(Type, 123, 123).
|
||||||
|
|
||||||
|
coerce_address_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type(address, #{}),
|
||||||
|
try_coerce(Type,
|
||||||
|
"ak_2FTnrGfV8qsfHpaSEHpBrziioCpwwzLqSevHqfxQY3PaAAdARx",
|
||||||
|
{address, <<164,136,155,90,124,22,40,206,255,76,213,56,238,123,
|
||||||
|
167,208,53,78,40,235,2,163,132,36,47,183,228,151,9,
|
||||||
|
210,39,214>>}).
|
||||||
|
|
||||||
|
coerce_contract_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type(contract, #{}),
|
||||||
|
try_coerce(Type,
|
||||||
|
"ct_2FTnrGfV8qsfHpaSEHpBrziioCpwwzLqSevHqfxQY3PaAAdARx",
|
||||||
|
{contract, <<164,136,155,90,124,22,40,206,255,76,213,56,238,123,
|
||||||
|
167,208,53,78,40,235,2,163,132,36,47,183,228,151,9,
|
||||||
|
210,39,214>>}).
|
||||||
|
|
||||||
|
coerce_bool_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type(boolean, #{}),
|
||||||
|
try_coerce(Type, true, true),
|
||||||
|
try_coerce(Type, false, false).
|
||||||
|
|
||||||
|
coerce_string_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type(string, #{}),
|
||||||
|
try_coerce(Type, "hello world", <<"hello world">>).
|
||||||
|
|
||||||
|
coerce_list_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type({list, [string]}, #{}),
|
||||||
|
try_coerce(Type, ["hello world", [65, 32, 65]], [<<"hello world">>, <<65, 32, 65>>]).
|
||||||
|
|
||||||
|
coerce_map_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type({map, [string, {list, [integer]}]}, #{}),
|
||||||
|
try_coerce(Type, #{"a" => "a", "b" => "b"}, #{<<"a">> => "a", <<"b">> => "b"}).
|
||||||
|
|
||||||
|
coerce_tuple_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type({tuple, [integer, string]}, #{}),
|
||||||
|
try_coerce(Type, {123, "456"}, {tuple, {123, <<"456">>}}).
|
||||||
|
|
||||||
|
coerce_variant_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type({variant, [{"A", [integer]},
|
||||||
|
{"B", [integer, integer]}]},
|
||||||
|
#{}),
|
||||||
|
try_coerce(Type, {"A", 123}, {variant, [1, 2], 0, {123}}),
|
||||||
|
try_coerce(Type, {"B", 456, 789}, {variant, [1, 2], 1, {456, 789}}).
|
||||||
|
|
||||||
|
coerce_record_test() ->
|
||||||
|
{ok, Type} = flatten_opaque_type({record, [{"a", integer}, {"b", integer}]}, #{}),
|
||||||
|
try_coerce(Type, #{"a" => 123, "b" => 456}, {tuple, {123, 456}}).
|
||||||
|
|
||||||
|
|
||||||
|
%%% Complex AACI paramter and namespace tests
|
||||||
|
|
||||||
|
aaci_from_string(String) ->
|
||||||
|
case aeso_compiler:from_string(String, [{aci, json}]) of
|
||||||
|
{ok, #{aci := ACI}} -> {ok, prepare_aaci(ACI)};
|
||||||
|
Error -> Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
namespace_coerce_test() ->
|
||||||
|
Contract = "
|
||||||
|
namespace N =
|
||||||
|
record pair = { a : int, b : int }
|
||||||
|
|
||||||
|
contract C =
|
||||||
|
entrypoint f(): N.pair = { a = 1, b = 2 }
|
||||||
|
",
|
||||||
|
{ok, AACI} = aaci_from_string(Contract),
|
||||||
|
{ok, {[], Output}} = aaci_lookup_spec(AACI, "f"),
|
||||||
|
try_coerce(Output, #{"a" => 123, "b" => 456}, {tuple, {123, 456}}).
|
||||||
|
|
||||||
|
record_substitution_test() ->
|
||||||
|
Contract = "
|
||||||
|
contract C =
|
||||||
|
record pair('t) = { a : 't, b : 't }
|
||||||
|
entrypoint f(): pair(int) = { a = 1, b = 2 }
|
||||||
|
",
|
||||||
|
{ok, AACI} = aaci_from_string(Contract),
|
||||||
|
{ok, {[], Output}} = aaci_lookup_spec(AACI, "f"),
|
||||||
|
try_coerce(Output, #{"a" => 123, "b" => 456}, {tuple, {123, 456}}).
|
||||||
|
|
||||||
|
tuple_substitution_test() ->
|
||||||
|
Contract = "
|
||||||
|
contract C =
|
||||||
|
type triple('t1, 't2) = int * 't1 * 't2
|
||||||
|
entrypoint f(): triple(int, string) = (1, 2, \"hello\")
|
||||||
|
",
|
||||||
|
{ok, AACI} = aaci_from_string(Contract),
|
||||||
|
{ok, {[], Output}} = aaci_lookup_spec(AACI, "f"),
|
||||||
|
try_coerce(Output, {1, 2, "hello"}, {tuple, {1, 2, <<"hello">>}}).
|
||||||
|
|
||||||
|
variant_substitution_test() ->
|
||||||
|
Contract = "
|
||||||
|
contract C =
|
||||||
|
datatype adt('a, 'b) = Left('a, 'b) | Right('b, int)
|
||||||
|
entrypoint f(): adt(string, int) = Left(\"hi\", 1)
|
||||||
|
",
|
||||||
|
{ok, AACI} = aaci_from_string(Contract),
|
||||||
|
{ok, {[], Output}} = aaci_lookup_spec(AACI, "f"),
|
||||||
|
try_coerce(Output, {"Left", "hi", 1}, {variant, [2, 2], 0, {<<"hi">>, 1}}),
|
||||||
|
try_coerce(Output, {"Right", 2, 3}, {variant, [2, 2], 1, {2, 3}}).
|
||||||
|
|
||||||
|
nested_coerce_test() ->
|
||||||
|
Contract = "
|
||||||
|
contract C =
|
||||||
|
type pair('t) = 't * 't
|
||||||
|
record r = { f1 : pair(int), f2: pair(string) }
|
||||||
|
entrypoint f(): r = { f1 = (1, 2), f2 = (\"a\", \"b\") }
|
||||||
|
",
|
||||||
|
{ok, AACI} = aaci_from_string(Contract),
|
||||||
|
{ok, {[], Output}} = aaci_lookup_spec(AACI, "f"),
|
||||||
|
try_coerce(Output,
|
||||||
|
#{ "f1" => {1, 2}, "f2" => {"a", "b"}},
|
||||||
|
{tuple, {{tuple, {1, 2}}, {tuple, {<<"a">>, <<"b">>}}}}).
|
||||||
|
|
||||||
|
state_coerce_test() ->
|
||||||
|
Contract = "
|
||||||
|
contract C =
|
||||||
|
type state = int
|
||||||
|
entrypoint init(): state = 0
|
||||||
|
",
|
||||||
|
{ok, AACI} = aaci_from_string(Contract),
|
||||||
|
{ok, {[], Output}} = aaci_lookup_spec(AACI, "init"),
|
||||||
|
try_coerce(Output, 0, 0).
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user