From 04d64dfe9bfcdf64d076a1425d6281a45255adaf Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 10 Apr 2019 15:41:59 +0200 Subject: [PATCH 1/5] Extend QuickCheck model to use the make_BLA functions to create the fate data --- quickcheck/aefate_eqc.erl | 49 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/quickcheck/aefate_eqc.erl b/quickcheck/aefate_eqc.erl index 3049c41..7be968e 100644 --- a/quickcheck/aefate_eqc.erl +++ b/quickcheck/aefate_eqc.erl @@ -13,17 +13,16 @@ -compile([export_all, nowarn_export_all]). prop_roundtrip() -> - ?FORALL(FateData, fate_type(), + ?FORALL(FateData, fate_data(), measure(bytes, size(term_to_binary(FateData)), begin Serialized = aeb_fate_encoding:serialize(FateData), ?WHENFAIL(eqc:format("Serialized ~p to ~p~n", [FateData, Serialized]), equals(aeb_fate_encoding:deserialize(Serialized), FateData)) - end - )). + end)). prop_format() -> - ?FORALL(FateData, fate_type(), + ?FORALL(FateData, fate_data(), ?WHENFAIL(eqc:format("Trying to format ~p failed~n",[FateData]), begin String = aeb_fate_data:format(FateData), @@ -31,16 +30,16 @@ prop_format() -> end)). prop_format_scan() -> - ?FORALL(FateData, fate_type(), - ?WHENFAIL(eqc:format("Trying to format ~p failed~n~p~n", [FateData, catch unicode:characters_to_list(aeb_fate_data:format(FateData), utf8) ]), + ?FORALL(FateData, fate_data(), + ?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]), begin String = aeb_fate_data:format(FateData), {ok, _Scanned, _} = aeb_fate_asm_scan:scan(unicode:characters_to_list(String)), true end)). -fate_type() -> - ?SIZED(Size, fate_type(Size, [map])). +fate_data() -> + ?SIZED(Size, ?LET(Data, fate_type(Size, [map]), eqc_symbolic:eval(Data))). fate_type(0, _Options) -> ?LAZY( @@ -70,33 +69,33 @@ fate_type(Size, Options) -> ])). -fate_integer() -> oneof([int(), largeint()]). -fate_bits() -> {bits, oneof([int(), largeint()])}. -fate_boolean() -> elements([true, false]). -fate_nil() -> []. -fate_unit() -> {tuple, {}}. -fate_string() -> ?SUCHTHAT(S, utf8(), string:find(S, "\"") == nomatch). -fate_address() -> {address, non_zero_binary(256 div 8)}. -fate_hash() -> {hash, non_zero_binary(32)}. -fate_signature() -> {signature, non_zero_binary(64)}. -fate_contract() -> {contract, non_zero_binary(256 div 8)}. -fate_oracle() -> {oracle, non_zero_binary(256 div 8)}. -fate_name() -> {name, non_zero_binary(256 div 8)}. -fate_channel() -> {channel, non_zero_binary(256 div 8)}. +fate_integer() -> {call, aeb_fate_data, make_integer, [oneof([int(), largeint()])]}. +fate_bits() -> {call, aeb_fate_data, make_bits, [oneof([int(), largeint()])]}. +fate_boolean() -> {call, aeb_fate_data, make_boolean, [elements([true, false])]}. +fate_nil() -> {call, aeb_fate_data, make_list, [[]]}. +fate_unit() -> {call, aeb_fate_data, make_unit, []}. +fate_string() -> {call, aeb_fate_data, make_string, [?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == [])]}. +fate_address() -> {call, aeb_fate_data, make_address, [non_zero_binary(256 div 8)]}. +fate_hash() -> {call, aeb_fate_data, make_hash, [non_zero_binary(32)]}. +fate_signature() -> {call, aeb_fate_data, make_signature, [non_zero_binary(64)]}. +fate_contract() -> {call, aeb_fate_data, make_contract, [non_zero_binary(256 div 8)]}. +fate_oracle() -> {call, aeb_fate_data, make_oracle, [non_zero_binary(256 div 8)]}. +fate_name() -> {call, aeb_fate_data, make_name, [non_zero_binary(256 div 8)]}. +fate_channel() -> {call, aeb_fate_data, make_channel, [non_zero_binary(256 div 8)]}. %% May shrink to fate_unit fate_tuple(ListGen) -> - {tuple, ?LET(Elements, ListGen, list_to_tuple(Elements))}. + {call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}. fate_variant(TupleGen) -> ?LET({L1, L2, Tuple}, {list(choose(0, 255)), list(choose(0,255)), TupleGen}, - {variant, L1 ++ [size(Tuple)] ++ L2, length(L1), Tuple}). + {call, aeb_fate_data, make_variant, [L1 ++ [size(Tuple)] ++ L2, length(L1), Tuple]}). fate_list(Gen) -> - oneof([fate_nil(), ?SHRINK(list(Gen), [fate_nil()])]). + {call, aeb_fate_data, make_list, [oneof([fate_nil(), ?SHRINK(list(Gen), [fate_nil()])])]}. fate_map(KeyGen, ValGen) -> - map(KeyGen, ValGen). + {call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}. non_zero_binary(N) -> From e1e2236a26b30a80133a2b2e5649e77e709eff00 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 10 Apr 2019 15:42:33 +0200 Subject: [PATCH 2/5] If we have destructors for maps, lists, etc, we need one for tuples as well --- include/aeb_fate_data.hrl | 1 + 1 file changed, 1 insertion(+) diff --git a/include/aeb_fate_data.hrl b/include/aeb_fate_data.hrl index c439abd..17b07df 100644 --- a/include/aeb_fate_data.hrl +++ b/include/aeb_fate_data.hrl @@ -54,6 +54,7 @@ -define(FATE_INTEGER_VALUE(X), (X)). -define(FATE_LIST_VALUE(X), (X)). +-define(FATE_TUPLE_ELEMENTS(X), (tuple_to_list(element(2, X)))). -define(FATE_STRING_VALUE(X), (X)). -define(FATE_ADDRESS_VALUE(X), (element(2, X))). -define(FATE_HASH_VALUE(X), (element(2, X))). From 0261b76314faf0634c924fb127f645c5efaed8c7 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 10 Apr 2019 15:43:25 +0200 Subject: [PATCH 3/5] tuple_to_list is now replaced by a constructor and the "convenience" functions will be moved to apps/aefate tests were they are needed --- src/aeb_fate_data.erl | 75 ------------------------------------------- 1 file changed, 75 deletions(-) diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 37e9cd0..e5e07f0 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -94,14 +94,10 @@ , make_channel/1 , make_bits/1 , make_unit/0 - , tuple_to_list/1 - , decode/1 - , encode/1 ]). -export([format/1]). - make_boolean(true) -> ?FATE_TRUE; make_boolean(false) -> ?FATE_FALSE. make_list([]) -> ?FATE_NIL; @@ -138,68 +134,7 @@ make_variant(Arities, Tag, Values) -> end end. -tuple_to_list(?FATE_TUPLE(T)) -> erlang:tuple_to_list(T). -%% Encode is a convinience function for testing, encoding an Erlang term -%% to a Fate term, but it can not distinguish between e.g. 32-byte strings -%% and addresses. Therfore an extra tuple layer on the erlang side for -%% addresses and bits. -encode({bits, Term}) when is_integer(Term) -> make_bits(Term); -%% TODO: check that each byte is in base58 -encode({address, B}) when is_binary(B) -> make_address(B); -encode({address, I}) when is_integer(I) -> B = <>, make_address(B); -encode({address, S}) when is_list(S) -> - make_address(encode_address(account_pubkey, S)); -encode({hash, H}) when is_binary(H) -> make_hash(H); -encode({hash, H}) when is_list(H) -> make_hash(base64:decode(H)); -encode({signature, S}) when is_binary(S) -> make_signature(S); -encode({signature, S}) when is_list(S) -> - make_signature(encode_address(signature, S)); -encode({contract, B}) when is_binary(B) -> make_contract(B); -encode({contract, I}) when is_integer(I) -> B = <>, make_contract(B); -encode({contract, S}) when is_list(S) -> - make_contract(encode_address(contract_pubkey, S)); -encode({oracle, B}) when is_binary(B) -> make_oracle(B); -encode({oracle, I}) when is_integer(I) -> B = <>, make_oracle(B); -encode({oracle, S}) when is_list(S) -> - make_oracle(encode_address(oracle_pubkey, S)); -encode({name, B}) when is_binary(B) -> make_name(B); -encode({name, I}) when is_integer(I) -> B = <>, make_name(B); -encode({name, S}) when is_list(S) -> - make_name(encode_address(name, S)); -encode({channel, B}) when is_binary(B) -> make_channel(B); -encode({channel, I}) when is_integer(I) -> B = <>, make_channel(B); -encode({channel, S}) when is_list(S) -> - make_channel(encode_address(channel, S)); -encode({variant, Arities, Tag, Values}) -> make_variant(Arities, Tag, Values); -encode(Term) when is_integer(Term) -> make_integer(Term); -encode(Term) when is_boolean(Term) -> make_boolean(Term); -encode(Term) when is_list(Term) -> make_list([encode(E) || E <- Term]); -encode(Term) when is_tuple(Term) -> - make_tuple(list_to_tuple([encode(E) || E <- erlang:tuple_to_list(Term)])); -encode(Term) when is_map(Term) -> - make_map(maps:from_list([{encode(K), encode(V)} || {K,V} <- maps:to_list(Term)])); -encode(Term) when is_binary(Term) -> make_string(Term). - - - -decode(I) when ?IS_FATE_INTEGER(I) -> I; -decode(?FATE_TRUE) -> true; -decode(?FATE_FALSE) -> false; -decode(L) when ?IS_FATE_LIST(L) -> [decode(E) || E <- L]; -decode(?FATE_ADDRESS(<>)) -> {address, Address}; -decode(?FATE_HASH(H)) -> {hash, H}; -decode(?FATE_SIGNATURE(S)) -> {signature, S}; -decode(?FATE_CONTRACT(X)) -> {contract, X}; -decode(?FATE_ORACLE(X)) -> {oracle, X}; -decode(?FATE_NAME(X)) -> {name, X}; -decode(?FATE_CHANNEL(X)) -> {channel, X}; -decode(?FATE_BITS(Bits)) -> {bits, Bits}; -decode(?FATE_TUPLE(T)) -> erlang:list_to_tuple([decode(E) || E <- T]); -decode(?FATE_VARIANT(Arities, Tag, Values)) -> {variant, Arities, Tag, Values}; -decode(S) when ?IS_FATE_STRING(S) -> binary_to_list(S); -decode(M) when ?IS_FATE_MAP(M) -> - maps:from_list([{decode(K), decode(V)} || {K, V} <- maps:to_list(M)]). -spec format(fate_type()) -> iolist(). format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I)); @@ -257,13 +192,3 @@ format_list(List) -> format_kvs(List) -> lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]). - -encode_address(Type, S) when is_list(S) -> - B = list_to_binary(S), - try aeser_api_encoder:decode(B) of - {Type, Encoding} -> - Encoding; - _ -> erlang:error({bad_address_encoding, Type, S}) - catch _:_ -> - erlang:error({bad_address_encoding, Type, S}) - end. From 436ba457e932fa5d2c5da263aec929cffaca1e15 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 10 Apr 2019 15:43:50 +0200 Subject: [PATCH 4/5] Robuster way to create binaries from strings --- src/aeb_fate_data.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index e5e07f0..04f78e2 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -115,7 +115,7 @@ make_channel(X) -> ?FATE_CHANNEL(X). make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I). make_bits(I) when is_integer(I) -> ?FATE_BITS(I). make_string(S) when is_list(S) -> - ?FATE_STRING(list_to_binary(lists:flatten(S))); + ?FATE_STRING(iolist_to_binary(S)); make_string(S) when is_binary(S) -> ?FATE_STRING(S). %% Tag points to the selected variant (zero based) From 734c8e8e401bbbdb4d0d2ba8e569e5db1daf4ec9 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 10 Apr 2019 15:44:53 +0200 Subject: [PATCH 5/5] ignore generated aefate file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b43c201..b42f787 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ src/aeb_fate_pp.erl *.aes~ doc cover +aefate +current_counterexample.eqc