Compare commits
21 Commits
v3.0.0
...
zomp-3.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
| df98da8caa | |||
| 3097e90156 | |||
| 02a6731f58 | |||
| 6ca65805bd | |||
| 2a0a397afa | |||
| 093bcd6204 | |||
| 6601ad2d38 | |||
| 578ebe2a8a | |||
| 8269dbd71e | |||
| 08cc0a9fcd | |||
| 84f20ab683 | |||
| 7497345928 | |||
| 822a269f75 | |||
| 0699f35b03 | |||
| 3829e29a63 | |||
| 52e9d30f76 | |||
| da7f00ae5d | |||
| 326fca709f | |||
| e860e217a0 | |||
| bc48b5d62f | |||
| 7b9c1b856b |
@@ -3,7 +3,7 @@ version: 2.1
|
||||
executors:
|
||||
aebuilder:
|
||||
docker:
|
||||
- image: aeternity/builder
|
||||
- image: aeternity/builder:bionic-otp23
|
||||
user: builder
|
||||
working_directory: ~/aebytecode
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ test: local
|
||||
@$(REBAR) as local eunit
|
||||
|
||||
ebin/%.beam: src/%.erl
|
||||
erlc -o $(dir $@) $<
|
||||
erlc +debug_info -o $(dir $@) $<
|
||||
|
||||
$(GENERATED_SRC): $(GENERATOR_DEPS)
|
||||
erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
%%% @author Thomas Arts
|
||||
%%% @doc Allow to run QuickCheck tests as eunit tests
|
||||
%%% `rebar3 as eqc eunit --cover`
|
||||
%%% or `rebar3 as eqc eunit --module=aeb_fate_code`
|
||||
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
|
||||
%%%
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||
|
||||
-module(aeb_fate_code_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-define(EQC_EUNIT(Module, PropName, Ms),
|
||||
{ atom_to_list(PropName),
|
||||
{timeout, (Ms * 10) div 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
|
||||
|
||||
quickcheck_test_() ->
|
||||
{setup, fun() -> eqc:start() end,
|
||||
[ ?EQC_EUNIT(aefate_code_eqc, prop_opcodes, 200),
|
||||
?EQC_EUNIT(aefate_code_eqc, prop_serializes, 3000),
|
||||
?EQC_EUNIT(aefate_code_eqc, prop_fail_serializes, 3000),
|
||||
?EQC_EUNIT(aefate_code_eqc, prop_fuzz, 3000)
|
||||
]}.
|
||||
@@ -1,27 +0,0 @@
|
||||
%%% @author Thomas Arts
|
||||
%%% @doc Allow to run QuickCheck tests as eunit tests
|
||||
%%% `rebar3 as eqc eunit --cover`
|
||||
%%% or `rebar3 as eqc eunit --module=aeb_fate_data`
|
||||
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
|
||||
%%%
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||
|
||||
-module(aeb_fate_data_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-define(EQC_EUNIT(Module, PropName, Ms),
|
||||
{ atom_to_list(PropName),
|
||||
{timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
|
||||
|
||||
quickcheck_test_() ->
|
||||
{setup, fun() -> eqc:start() end,
|
||||
[ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500),
|
||||
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_order, 2000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_fuzz, 2000)
|
||||
]}.
|
||||
@@ -1,27 +0,0 @@
|
||||
%%% @author Thomas Arts
|
||||
%%% @doc Allow to run QuickCheck tests as eunit tests
|
||||
%%% `rebar3 as eqc eunit --cover`
|
||||
%%% or `rebar3 as eqc eunit --module=aeb_fate_encoding`
|
||||
%%% Note that for obtaining cover file, one needs `rebar3 as eqc cover
|
||||
%%%
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13 Dec 2018 by Thomas Arts
|
||||
|
||||
-module(aeb_fate_encoding_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
-define(EQC_EUNIT(Module, PropName, Ms),
|
||||
{ atom_to_list(PropName),
|
||||
{timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
|
||||
|
||||
quickcheck_test_() ->
|
||||
{setup, fun() -> eqc:start() end,
|
||||
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_no_maps_in_keys, 1000),
|
||||
?EQC_EUNIT(aefate_eqc, prop_idempotent, 1000)
|
||||
]}.
|
||||
@@ -1,167 +0,0 @@
|
||||
%%% @author Thomas Arts
|
||||
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
|
||||
%%%
|
||||
%%% We want to be sure that we can deserialize all FATE assembler that is accepted on chain.
|
||||
%%%
|
||||
%%% We test something slightly weaker here,
|
||||
%%% viz. All FATE assembler we serialize, we can deserialize
|
||||
%%%
|
||||
%%% Negative testing modelled:
|
||||
%%% Failure 1: function names differ from 4 bytes
|
||||
%%% Failure 2: pointer to empty code block
|
||||
%%% Failure 3: end_BB operation as not ending block or not at end of block
|
||||
%%% - empty code blocks
|
||||
%%% - blocks that are not of the form (not end_bb)* end_bb.
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||
|
||||
-module(aefate_code_eqc).
|
||||
|
||||
-include_lib("eqc/include/eqc.hrl").
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
%%-define(Failure(Failures, Number), case lists:member(Number, Failures) of true -> 1; false -> 0 end)
|
||||
|
||||
|
||||
prop_serializes() ->
|
||||
in_parallel(
|
||||
?FORALL(FateCode, fate_code(0),
|
||||
begin
|
||||
{T0, Binary} = timer:tc(fun() -> aeb_fate_code:serialize(FateCode) end),
|
||||
?WHENFAIL(eqc:format("serialized:\n ~120p~n", [Binary]),
|
||||
begin
|
||||
{T1, Decoded} = timer:tc(fun() -> aeb_fate_code:deserialize(Binary) end),
|
||||
measure(binary_size, size(Binary),
|
||||
measure(serialize, T0 / 1000,
|
||||
measure(deserialize, T1 / 1000,
|
||||
conjunction([{equal, equals(Decoded, FateCode)},
|
||||
{serialize_time, T0 / 1000 < 500},
|
||||
{deserialize_time, T1 / 1000 < 500}]))))
|
||||
end)
|
||||
end)).
|
||||
|
||||
prop_fail_serializes() ->
|
||||
conjunction([{Failure, eqc:counterexample(
|
||||
?FORALL(FateCode, fate_code(Failure),
|
||||
?FORALL(Binary, catch aeb_fate_code:serialize(FateCode),
|
||||
is_binary(Binary))))
|
||||
=/= true} || Failure <- [1, 2, 3, 4, 5] ]).
|
||||
|
||||
prop_fuzz() ->
|
||||
in_parallel(
|
||||
?FORALL(Binary, ?LET(FateCode, fate_code(0), aeb_fate_code:serialize(FateCode)),
|
||||
?FORALL(FuzzedBin, fuzz(Binary),
|
||||
try aeb_fate_code:deserialize(FuzzedBin) of
|
||||
Code ->
|
||||
?WHENFAIL(eqc:format("Code:\n ~p\n", [Code]),
|
||||
begin
|
||||
Bin1 = aeb_fate_code:serialize(Code),
|
||||
Code1 = aeb_fate_code:deserialize(Bin1),
|
||||
?WHENFAIL(eqc:format("Reserialized\n ~120p\n", [Bin1]),
|
||||
equals(Code, Code1))
|
||||
end)
|
||||
catch _:_ -> true
|
||||
end))).
|
||||
|
||||
prop_opcodes() ->
|
||||
?FORALL(Opcode, choose(0, 16#ff),
|
||||
try M = aeb_fate_opcodes:mnemonic(Opcode),
|
||||
?WHENFAIL(eqc:format("opcode ~p -> ~p", [Opcode, M]),
|
||||
conjunction([{valid, lists:member(Opcode, valid_opcodes())},
|
||||
{eq, equals(aeb_fate_opcodes:m_to_op(M), Opcode)}]))
|
||||
catch
|
||||
_:_ ->
|
||||
not lists:member(Opcode, valid_opcodes())
|
||||
end).
|
||||
|
||||
|
||||
valid_opcodes() ->
|
||||
[ Op || #{opcode := Op} <- aeb_fate_generate_ops:get_ops() ].
|
||||
|
||||
|
||||
fate_code(Failure) ->
|
||||
?SIZED(Size,
|
||||
?LET({FMap, SMap, AMap},
|
||||
{non_empty(map(if Failure == 1 -> binary(1);
|
||||
true -> binary(4) end,
|
||||
{sublist(lists:sort([private, payable])), %% deserialize sorts them
|
||||
{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})),
|
||||
small_map(small_fate_data_key(5), small_fate_data(4)),
|
||||
small_map(small_fate_data_key(5), small_fate_data(4))},
|
||||
aeb_fate_code:update_annotations(
|
||||
aeb_fate_code:update_symbols(
|
||||
aeb_fate_code:update_functions(
|
||||
aeb_fate_code:new(), FMap), SMap), AMap))).
|
||||
|
||||
short_list(Max, Gen) ->
|
||||
?LET(N, choose(0, Max), eqc_gen:list(N, Gen)).
|
||||
|
||||
small_map(KeyGen, ValGen) ->
|
||||
?LET(KeyVals, short_list(6, {KeyGen, ValGen}),
|
||||
return(maps:from_list(KeyVals))).
|
||||
|
||||
bbs_code(Failure) ->
|
||||
frequency([{if Failure == 2 -> 5; true -> 0 end, #{0 => []}},
|
||||
{10, ?LET(BBs, short_list(6, bb_code(Failure)),
|
||||
maps:from_list(
|
||||
lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]).
|
||||
|
||||
bb_code(Failure) ->
|
||||
EndBB = [ Op || Op <- valid_opcodes(), aeb_fate_opcodes:end_bb(Op) ],
|
||||
NonEndBB = valid_opcodes() -- EndBB,
|
||||
frequency(
|
||||
[{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(short_list(6, elements(NonEndBB))), bblock(Failure, Ops))},
|
||||
{if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {short_list(6, elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))},
|
||||
{10, ?LET({Ops, Op}, {short_list(6, elements(NonEndBB)), elements(EndBB)},
|
||||
bblock(Failure, Ops ++ [Op]))}]).
|
||||
|
||||
bblock(Failure, Ops) ->
|
||||
[ begin
|
||||
Mnemonic = aeb_fate_opcodes:mnemonic(Op),
|
||||
Arity = aeb_fate_opcodes:args(Op),
|
||||
case Arity of
|
||||
0 -> Mnemonic;
|
||||
_ -> list_to_tuple([Mnemonic |
|
||||
[ frequency([{if Failure == 5 -> 5; true -> 0 end, {stack, nat()}},
|
||||
{5, {stack, 0}},
|
||||
{5, {arg, nat()}},
|
||||
{5, {var, nat()}},
|
||||
{5, {immediate, small_fate_data(4)}}]) ||
|
||||
_ <- lists:seq(1, Arity) ]])
|
||||
end
|
||||
end || Op <- Ops ].
|
||||
|
||||
fuzz(Binary) ->
|
||||
?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0, 255)},
|
||||
begin
|
||||
M = N * 8,
|
||||
<<X:M, _:8, Z/binary>> = Binary,
|
||||
<<X:M, Inj:8, Z/binary>>
|
||||
end).
|
||||
|
||||
prop_small() ->
|
||||
?FORALL(Value, small_fate_data(4),
|
||||
begin
|
||||
Bin = aeb_fate_encoding:serialize(Value),
|
||||
Size = byte_size(Bin),
|
||||
measure(size, Size,
|
||||
?WHENFAIL(eqc:format("Size: ~p\n", [Size]),
|
||||
Size < 1000))
|
||||
end).
|
||||
|
||||
prop_small_type() ->
|
||||
?FORALL(Type, ?SIZED(Size, aefate_type_eqc:fate_type(Size div 3)),
|
||||
begin
|
||||
Bin = iolist_to_binary(aeb_fate_encoding:serialize_type(Type)),
|
||||
Size = byte_size(Bin),
|
||||
measure(size, Size,
|
||||
?WHENFAIL(eqc:format("Size: ~p\n", [Size]),
|
||||
Size < 1000))
|
||||
end).
|
||||
|
||||
small_fate_data(N) ->
|
||||
?SIZED(Size, resize(Size div N, aefate_eqc:fate_data())).
|
||||
|
||||
small_fate_data_key(N) ->
|
||||
?SIZED(Size, ?LET(Data, aefate_eqc:fate_data(Size div N, []), eqc_symbolic:eval(Data))).
|
||||
@@ -1,211 +0,0 @@
|
||||
%%% @author Thomas Arts
|
||||
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
|
||||
%%%
|
||||
%%% We need to be able to generate data that serializes with ?LONG_LIST, ?LONG_TUPLE etc.
|
||||
%%% In other words make some rather broad terms as well as some deep terms
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||
|
||||
-module(aefate_eqc).
|
||||
|
||||
-include_lib("eqc/include/eqc.hrl").
|
||||
-include("../include/aeb_fate_data.hrl").
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
prop_roundtrip() ->
|
||||
?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)).
|
||||
|
||||
prop_format_scan() ->
|
||||
?FORALL(FateData, fate_data([variant, map]),
|
||||
?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)).
|
||||
|
||||
prop_serializes() ->
|
||||
?FORALL({Data, Garbage}, {fate_data(), binary()},
|
||||
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]),
|
||||
begin
|
||||
Binary = <<(aeb_fate_encoding:serialize(Data))/binary, Garbage/binary>>,
|
||||
{FateData, Rest} = aeb_fate_encoding:deserialize_one(Binary),
|
||||
measure(binary_size, size(Binary),
|
||||
conjunction([{equal, equals(Data, FateData)},
|
||||
{rest, equals(Garbage, Rest)},
|
||||
{size, size(Binary) < 500000}]))
|
||||
end)).
|
||||
|
||||
prop_no_maps_in_keys() ->
|
||||
?FORALL(FateData, fate_bad_map(), %% may contain a map in its keys
|
||||
begin
|
||||
HasMapInKeys = lists:any(fun(K) -> has_map(K) end, maps:keys(FateData)),
|
||||
try aeb_fate_encoding:serialize(FateData),
|
||||
?WHENFAIL(eqc:format("Should not serialize, contains a map in key\n", []),
|
||||
not HasMapInKeys)
|
||||
catch error:Reason ->
|
||||
?WHENFAIL(eqc:format("(~p) Should serialize\n", [Reason]), HasMapInKeys)
|
||||
end
|
||||
end).
|
||||
|
||||
prop_fuzz() ->
|
||||
in_parallel(
|
||||
?FORALL(Binary, ?LET(FateData, ?SIZED(Size, resize(Size div 4, fate_data())), aeb_fate_encoding:serialize(FateData)),
|
||||
?FORALL(InjectedBin, injection(Binary),
|
||||
try Org = aeb_fate_encoding:deserialize(InjectedBin),
|
||||
NewBin = aeb_fate_encoding:serialize(Org),
|
||||
NewOrg = aeb_fate_encoding:deserialize(NewBin),
|
||||
measure(success, 1,
|
||||
?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]),
|
||||
equals(NewBin, InjectedBin)))
|
||||
catch _:_ ->
|
||||
true
|
||||
end))).
|
||||
|
||||
|
||||
prop_order() ->
|
||||
?FORALL(Items, vector(3, fate_data([variant, map])),
|
||||
begin
|
||||
%% Use lt to take minimum
|
||||
Min = lt_min(Items),
|
||||
Max = lt_max(Items),
|
||||
conjunction([ {minimum, is_empty([ {Min, '>', I} || I<-Items, aeb_fate_data:lt(I, Min)])},
|
||||
{maximum, is_empty([ {Max, '<', I} || I<-Items, aeb_fate_data:lt(Max, I)])},
|
||||
{asym, aeb_fate_data:lt(Min, Max) orelse Min == Max}])
|
||||
end).
|
||||
|
||||
lt_min([X, Y | Rest]) ->
|
||||
case aeb_fate_data:lt(X, Y) of
|
||||
true -> lt_min([X | Rest]);
|
||||
false -> lt_min([Y| Rest])
|
||||
end;
|
||||
lt_min([X]) -> X.
|
||||
|
||||
lt_max([X, Y | Rest]) ->
|
||||
case aeb_fate_data:lt(X, Y) of
|
||||
true -> lt_max([Y | Rest]);
|
||||
false -> lt_max([X| Rest])
|
||||
end;
|
||||
lt_max([X]) -> X.
|
||||
|
||||
prop_idempotent() ->
|
||||
?FORALL(Items, list({fate_data_key(), fate_data()}),
|
||||
equals(aeb_fate_encoding:sort(Items),
|
||||
aeb_fate_encoding:sort(aeb_fate_encoding:sort(Items)))).
|
||||
|
||||
|
||||
|
||||
fate_data(Kind) ->
|
||||
?SIZED(Size, ?LET(Data, fate_data(Size, Kind), eqc_symbolic:eval(Data))).
|
||||
|
||||
fate_data() ->
|
||||
fate_data([map, variant, store_map]).
|
||||
|
||||
%% keys may contain variants but no maps
|
||||
fate_data_key() ->
|
||||
fate_data([variant]).
|
||||
|
||||
fate_data(0, Options) ->
|
||||
?LAZY(
|
||||
frequency(
|
||||
[{50, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])},
|
||||
{10, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(),
|
||||
fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}] ++
|
||||
[{1, fate_store_map()} || lists:member(store_map, Options)]));
|
||||
fate_data(Size, Options) ->
|
||||
?LAZY(
|
||||
oneof([fate_data(0, Options),
|
||||
fate_list(Size, Options),
|
||||
fate_tuple(Size, Options)] ++
|
||||
[fate_variant(Size, Options)
|
||||
|| lists:member(variant, Options)] ++
|
||||
[fate_map(Size, Options)
|
||||
|| lists:member(map, Options)])).
|
||||
|
||||
|
||||
fate_integer() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_integer(X))).
|
||||
fate_bits() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_bits(X))).
|
||||
fate_boolean() -> ?LET(X, elements([true, false]), return(aeb_fate_data:make_boolean(X))).
|
||||
fate_nil() -> aeb_fate_data:make_list([]).
|
||||
fate_unit() -> aeb_fate_data:make_unit().
|
||||
fate_string() -> ?LET(X, frequency([{10, non_quote_string()}, {2, list(non_quote_string())},
|
||||
{1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}]),
|
||||
return(aeb_fate_data:make_string(X))).
|
||||
fate_address() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_address(X))).
|
||||
fate_bytes() -> ?LET(X, non_empty(binary()), return(aeb_fate_data:make_bytes(X))).
|
||||
fate_contract() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_contract(X))).
|
||||
fate_oracle() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle(X))).
|
||||
fate_oracle_q() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle_query(X))).
|
||||
fate_channel() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_channel(X))).
|
||||
|
||||
fate_values(Size, N, Options) ->
|
||||
eqc_gen:list(N, fate_data(Size div max(1, N), Options)).
|
||||
|
||||
%% May shrink to fate_unit
|
||||
fate_tuple(Size, Options) ->
|
||||
?LET(N, choose(0, 6),
|
||||
?LETSHRINK(Elements, fate_values(Size, N, Options),
|
||||
return(aeb_fate_data:make_tuple(list_to_tuple(Elements))))).
|
||||
|
||||
fate_variant(Size, Options) ->
|
||||
?LET({L1, L2, {tuple, Args}}, {list(choose(0, 255)), list(choose(0,255)), fate_tuple(Size, Options)},
|
||||
return(aeb_fate_data:make_variant(L1 ++ [tuple_size(Args)] ++ L2,
|
||||
length(L1), Args))).
|
||||
|
||||
fate_list(Size, Options) ->
|
||||
?LET(N, frequency([{20, choose(0, 6)}, {1, choose(64 - 3, 64 + 3)}]),
|
||||
?LETSHRINK(Vs, fate_values(Size, N, Options),
|
||||
return(aeb_fate_data:make_list(Vs)))).
|
||||
|
||||
fate_map(Size, Options) ->
|
||||
?LET(N, choose(0, 6),
|
||||
?LETSHRINK(Values, fate_values(Size, N, Options),
|
||||
?LET(Keys, vector(length(Values), fate_data(Size div max(1, N * 2), Options -- [map, store_map])),
|
||||
return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
|
||||
|
||||
fate_store_map() ->
|
||||
%% only #{} is allowed as cache in serialization
|
||||
?LET(X, oneof([int(), largeint()]),
|
||||
return(aeb_fate_data:make_store_map(abs(X)))).
|
||||
|
||||
fate_bad_map() ->
|
||||
?LET(N, choose(0, 6),
|
||||
?LET(Values, vector(N, ?SIZED(Size, resize(Size div 8, fate_data()))),
|
||||
?LET(Keys, vector(N, ?SIZED(Size, resize(Size div 4, fate_data()))),
|
||||
return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
|
||||
|
||||
non_quote_string() ->
|
||||
?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []).
|
||||
|
||||
char() ->
|
||||
choose(1, 255).
|
||||
|
||||
injection(Binary) ->
|
||||
?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0,255)},
|
||||
begin
|
||||
M = N * 8,
|
||||
<<X:M, _:8, Z/binary>> = Binary,
|
||||
<<X:M, Inj:8, Z/binary>>
|
||||
end).
|
||||
|
||||
is_empty(L) ->
|
||||
?WHENFAIL(eqc:format("~p\n", [L]), L == []).
|
||||
|
||||
has_map(L) when is_list(L) ->
|
||||
lists:any(fun(V) -> has_map(V) end, L);
|
||||
has_map(T) when is_tuple(T) ->
|
||||
has_map(tuple_to_list(T));
|
||||
has_map(M) when is_map(M) ->
|
||||
true;
|
||||
has_map(?FATE_STORE_MAP(_, _)) ->
|
||||
true;
|
||||
has_map(_) ->
|
||||
false.
|
||||
@@ -1,56 +0,0 @@
|
||||
%%% @author Thomas Arts
|
||||
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
|
||||
%%% Properties for testing Fate type representations
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||
|
||||
-module(aefate_type_eqc).
|
||||
|
||||
-include_lib("eqc/include/eqc.hrl").
|
||||
|
||||
-compile([export_all, nowarn_export_all]).
|
||||
|
||||
kind(X) when is_atom(X) -> X;
|
||||
kind(T) when is_tuple(T) -> element(1, T).
|
||||
|
||||
prop_roundtrip() ->
|
||||
?FORALL(FateType, fate_type(),
|
||||
collect(kind(FateType),
|
||||
begin
|
||||
Serialized = aeb_fate_encoding:serialize_type(FateType),
|
||||
BinSerialized = list_to_binary(Serialized),
|
||||
?WHENFAIL(eqc:format("Serialized ~p to ~p (~p)~n", [FateType, Serialized, BinSerialized]),
|
||||
begin
|
||||
{Type, <<>>} = aeb_fate_encoding:deserialize_type(BinSerialized),
|
||||
equals(Type, FateType)
|
||||
end)
|
||||
end)).
|
||||
|
||||
|
||||
fate_type() ->
|
||||
?SIZED(Size, fate_type(Size)).
|
||||
|
||||
fate_type(0) ->
|
||||
oneof([integer,
|
||||
boolean,
|
||||
address,
|
||||
{bytes, nat()},
|
||||
contract,
|
||||
oracle,
|
||||
channel,
|
||||
bits,
|
||||
string]);
|
||||
fate_type(Size) ->
|
||||
?LAZY(
|
||||
oneof([fate_type(0),
|
||||
{list, fate_type(Size div 2)},
|
||||
?LETSHRINK(Ts, fate_types(Size), {tuple, Ts}),
|
||||
?LETSHRINK(Ts, fate_types(Size), {variant, Ts}),
|
||||
?LETSHRINK([T1, T2], vector(2, fate_type(Size div 2)),
|
||||
{map, T1, T2})])).
|
||||
|
||||
fate_types(Size) ->
|
||||
?LET(N, choose(0, 6),
|
||||
eqc_gen:list(N, fate_type(Size div max(2, N)))).
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
%% -*- mode: erlang; indent-tabs-mode: nil -*-
|
||||
|
||||
{minimum_otp_vsn, "20.1"}.
|
||||
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {eblake2, "1.0.0"}
|
||||
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
|
||||
{ref, "47aaa8f"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
]}.
|
||||
|
||||
{escript_incl_apps, [aebytecode, eblake2, aeserialization, getopt]}.
|
||||
{escript_main_app, aebytecode}.
|
||||
{escript_name, aefateasm}.
|
||||
{escript_emu_args, "%%!"}.
|
||||
|
||||
{pre_hooks,
|
||||
[{"(linux|darwin|solaris|win32)", compile, "make sources"},
|
||||
{"(freebsd)", compile, "gmake sources"}]}.
|
||||
|
||||
{provider_hooks, [{post, [{compile, escriptize}]}]}.
|
||||
|
||||
|
||||
{dialyzer, [
|
||||
{warnings, [unknown]},
|
||||
{plt_apps, all_deps},
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, getopt]}
|
||||
]}.
|
||||
|
||||
|
||||
{relx, [{release, {aebytecode, "2.0.1"},
|
||||
[aebytecode, eblake2, getopt]},
|
||||
|
||||
{dev_mode, true},
|
||||
{include_erts, false},
|
||||
|
||||
{extended_start_script, true}]}.
|
||||
|
||||
{profiles, [{binary, [
|
||||
{deps, [ {eblake2, "1.0.0"}
|
||||
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
|
||||
{ref, "47aaa8f"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
]},
|
||||
|
||||
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
|
||||
escriptize,
|
||||
"cp \"$REBAR_BUILD_DIR/bin/aefateasm\" ./aefateasm"},
|
||||
{"win32",
|
||||
escriptize,
|
||||
"robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* "
|
||||
"/njs /njh /nfl /ndl & exit /b 0"} % silence things
|
||||
]}
|
||||
]},
|
||||
{eqc, [{erl_opts, [{parse_transform, eqc_cover}, {d, 'EQC'}]},
|
||||
{extra_src_dirs, ["quickcheck"]} %% May not be called eqc!
|
||||
]}
|
||||
]}.
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
{"1.1.0",
|
||||
[{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
{ref,"47aaa8f5434b365c50a35bfd1490340b19241991"}},
|
||||
0},
|
||||
{<<"base58">>,
|
||||
{git,"https://github.com/aeternity/erl-base58.git",
|
||||
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
|
||||
1},
|
||||
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
|
||||
{<<"enacl">>,
|
||||
{git,"https://github.com/aeternity/enacl.git",
|
||||
{ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}},
|
||||
1},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
|
||||
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}
|
||||
].
|
||||
@@ -8,6 +8,7 @@
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeb_aevm_abi).
|
||||
-vsn("3.2.1").
|
||||
-define(HASH_SIZE, 32).
|
||||
|
||||
-export([ create_calldata/4
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
-module(aeb_aevm_data).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export_type([data/0,
|
||||
type/0,
|
||||
|
||||
+2
-1
@@ -29,13 +29,14 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeb_asm).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ file/2
|
||||
, pp/1
|
||||
, to_hexstring/1
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeb_opcodes.hrl").
|
||||
|
||||
|
||||
pp(Asm) ->
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeb_disassemble).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ pp/1,
|
||||
format/2,
|
||||
format_address/1
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeb_opcodes.hrl").
|
||||
|
||||
|
||||
pp(Binary) ->
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeb_fate_abi).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ create_calldata/2
|
||||
, decode_calldata/2
|
||||
@@ -16,7 +17,7 @@
|
||||
, get_function_type_from_function_hash/2
|
||||
, abi_version/0 ]).
|
||||
|
||||
-include("../include/aeb_fate_data.hrl").
|
||||
-include("aeb_fate_data.hrl").
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeb_fate_asm).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ assemble_file/3
|
||||
, asm_to_bytecode/2
|
||||
@@ -94,8 +95,8 @@
|
||||
, to_asm/1
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
|
||||
-include_lib("aebytecode/include/aeb_fate_data.hrl").
|
||||
-include("aeb_fate_opcodes.hrl").
|
||||
-include("aeb_fate_data.hrl").
|
||||
-define(HASH_BYTES, 32).
|
||||
|
||||
assemble_file(InFile, OutFile, Options) ->
|
||||
@@ -356,7 +357,7 @@ parse_tuple(Tokens) ->
|
||||
|
||||
|
||||
parse_variant([{start_variant,_line}
|
||||
, {'[', _line}
|
||||
, {'[', _}
|
||||
| Rest]) ->
|
||||
{Arities, Rest2} = parse_list(Rest),
|
||||
%% Make sure Arities is a list of bytes.
|
||||
@@ -364,7 +365,7 @@ parse_variant([{start_variant,_line}
|
||||
is_integer(A), A < 256],
|
||||
|
||||
[{'|',_}
|
||||
, {int,_line, Tag}
|
||||
, {int,_, Tag}
|
||||
, {'|',_}
|
||||
, {'(',_} | Rest3] = Rest2,
|
||||
{Elements , [{end_variant, _} | Rest4]} = parse_tuple(Rest3),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeb_fate_code).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ annotations/1
|
||||
, deserialize/1
|
||||
@@ -24,14 +25,12 @@
|
||||
, symbols/1
|
||||
]).
|
||||
|
||||
-include("../include/aeb_fate_opcodes.hrl").
|
||||
-include("../include/aeb_fate_data.hrl").
|
||||
-include("aeb_fate_opcodes.hrl").
|
||||
-include("aeb_fate_data.hrl").
|
||||
|
||||
-ifdef(EQC).
|
||||
-export([ update_annotations/2
|
||||
, update_functions/2
|
||||
, update_symbols/2]).
|
||||
-endif.
|
||||
|
||||
-record(fcode, { functions = #{} :: map()
|
||||
, symbols = #{} :: map()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
-include("aeb_fate_data.hrl").
|
||||
|
||||
-module(aeb_fate_data).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-type fate_integer() :: ?FATE_INTEGER_T.
|
||||
-type fate_boolean() :: ?FATE_BOOLEAN_T.
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
%%
|
||||
%% ------------------------------------------------------------------------
|
||||
-module(aeb_fate_encoding).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ deserialize/1
|
||||
, deserialize_one/1
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
-module(aeb_fate_generate_docs).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([generate_documentation/2, generate_documentation/3]).
|
||||
|
||||
-export(
|
||||
[ gen_protocol_opcodes_flags_and_gas/1
|
||||
, gen_protocol_description_of_operations/1
|
||||
, gen_protocol_opcodes/1
|
||||
]).
|
||||
|
||||
-define(LIMA_PROTOCOL_VSN, 4).
|
||||
-define(IRIS_PROTOCOL_VSN, 5).
|
||||
|
||||
generate_documentation(Filename, Fields) ->
|
||||
generate_documentation(Filename, Fields, fun(_) -> true end).
|
||||
generate_documentation(Filename, Fields, Filter) when is_function(Filter, 1) ->
|
||||
{ok, File} = file:open(Filename, [write, {encoding, utf8}]),
|
||||
Header =
|
||||
lists:flatten(
|
||||
"|" ++ [" " ++ header_name(F) ++ " |" || F <- Fields] ++ "\n"
|
||||
),
|
||||
Separator =
|
||||
lists:flatten(
|
||||
"|" ++ [" " ++ ["-" || _ <- header_name(F)] ++ " |" || F <- Fields] ++ "\n"
|
||||
),
|
||||
Instructions =
|
||||
lists:flatten(
|
||||
[gen_doc_for_op(Op, Fields)
|
||||
++ "\n" || Op <- aeb_fate_generate_ops:get_ops(), Filter(Op)]),
|
||||
io:format(File, "~ts~ts~ts\n", [Header, Separator, Instructions]),
|
||||
file:close(File).
|
||||
|
||||
header_name(opname) ->
|
||||
"Name";
|
||||
header_name(opcode) ->
|
||||
"Opcode";
|
||||
header_name(arity) ->
|
||||
"Arity";
|
||||
header_name(end_bb) ->
|
||||
"Ends basic block";
|
||||
header_name(in_auth) ->
|
||||
"Allowed in auth";
|
||||
header_name(offchain) ->
|
||||
"Allowed offchain";
|
||||
header_name(format) ->
|
||||
"Args";
|
||||
header_name(doc) ->
|
||||
"Description";
|
||||
header_name(gas) ->
|
||||
"Gas cost";
|
||||
header_name(arg_types) ->
|
||||
"Arg types";
|
||||
header_name(res_type) ->
|
||||
"Res type".
|
||||
|
||||
gen_doc_for_op(#{ opname := OpName
|
||||
, opcode := OpCode
|
||||
, arity := Arity
|
||||
, end_bb := EndBB
|
||||
, in_auth := InAuth
|
||||
, offchain := AllowedOffchain
|
||||
, format := FateFormat
|
||||
, doc := Doc
|
||||
, gas := Gas
|
||||
, arg_types := ArgTypes
|
||||
, res_type := ResType
|
||||
}, Fields) ->
|
||||
"| " ++
|
||||
string:join(
|
||||
[ case Field of
|
||||
opname -> io_lib:format("`~s`", [OpName]);
|
||||
opcode -> io_lib:format("0x~.16b", [OpCode]);
|
||||
arity -> io_lib:format("~p", [Arity]);
|
||||
end_bb -> io_lib:format("~p", [EndBB]);
|
||||
in_auth -> io_lib:format("~p", [InAuth]);
|
||||
offchain -> io_lib:format("~p", [AllowedOffchain]);
|
||||
format ->
|
||||
case FateFormat of
|
||||
[] -> "";
|
||||
_ -> lists:join(
|
||||
" ",
|
||||
[format_arg_doc(A) ||
|
||||
A <-
|
||||
lists:zip(FateFormat,
|
||||
lists:seq(0,length(FateFormat)-1))])
|
||||
end;
|
||||
doc -> Doc;
|
||||
gas when is_integer(Gas) -> io_lib:format("~p", [Gas]);
|
||||
gas when is_list(Gas) ->
|
||||
lists:flatten(
|
||||
string:join(
|
||||
[ io_lib:format(
|
||||
"~p (~s)",
|
||||
[GasVal, protocol_name(Prot)]
|
||||
)
|
||||
|| {Prot, GasVal} <- Gas
|
||||
], ", "));
|
||||
arg_types -> io_lib:format("~p", [ArgTypes]);
|
||||
res_type -> io_lib:format("~p", [ResType])
|
||||
end
|
||||
|| Field <- Fields
|
||||
],
|
||||
" | ") ++ " |".
|
||||
|
||||
protocol_name(?LIMA_PROTOCOL_VSN) ->
|
||||
"lima";
|
||||
protocol_name(?IRIS_PROTOCOL_VSN) ->
|
||||
"iris".
|
||||
|
||||
format_arg_doc({a, N}) -> io_lib:format("Arg~w", [N]);
|
||||
format_arg_doc({is,_N}) -> "Identifier";
|
||||
format_arg_doc({ii,_N}) -> "Integer";
|
||||
format_arg_doc({li,_N}) -> "[Integers]";
|
||||
format_arg_doc({t,_N}) -> "Type".
|
||||
|
||||
|
||||
%% --- protocol documentation ---
|
||||
|
||||
gen_protocol_description_of_operations(Filename) ->
|
||||
generate_documentation(
|
||||
Filename, [opname, format, doc, arg_types, res_type]
|
||||
).
|
||||
|
||||
gen_protocol_opcodes_flags_and_gas(Filename) ->
|
||||
generate_documentation(
|
||||
Filename, [opcode, opname, end_bb, in_auth, offchain, gas]
|
||||
).
|
||||
|
||||
gen_protocol_opcodes(Filename) ->
|
||||
generate_documentation(
|
||||
Filename, [opcode, opname]
|
||||
).
|
||||
@@ -1,8 +1,8 @@
|
||||
-module(aeb_fate_generate_ops).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ gen_and_halt/1
|
||||
, generate/0
|
||||
, generate_documentation/1
|
||||
, get_ops/0
|
||||
, test_asm_generator/1 ]).
|
||||
|
||||
@@ -46,7 +46,7 @@ check_numbering(_, []) -> true.
|
||||
-define(GAS_IRIS(A, B), [{?IRIS_PROTOCOL_VSN, B}, {?LIMA_PROTOCOL_VSN, A}]).
|
||||
|
||||
ops_defs() ->
|
||||
%% Opname, Opcode, end_bb, in_auth offchain, gas, format, Constructor, ArgType, ResType, Documentation
|
||||
%% Opname, Opcode, end_bb, in_auth,offchain, gas, format, Constructor, ArgType, ResType, Documentation
|
||||
[ { 'RETURN', 16#00, true, true, true, ?GAS(10), [], return, {}, any, "Return from function call, top of stack is return value . The type of the retun value has to match the return type of the function."}
|
||||
, { 'RETURNR', 16#01, true, true, true, ?GAS(10), [a], returnr, {any}, any, "Push Arg0 and return from function. The type of the retun value has to match the return type of the function."}
|
||||
, { 'CALL', 16#02, true, true, true, ?GAS(10), [a], call, {string}, any, "Call the function Arg0 with args on stack. The types of the arguments has to match the argument typs of the function."}
|
||||
@@ -226,13 +226,23 @@ ops_defs() ->
|
||||
|
||||
, { 'CALL_PGR', 16#a2, true, false, true, ?GAS(100), [a,is,a,a,a,a,a], call_pgr, {contract, string, typerep, typerep, integer, integer, bool}, variant, "Potentially protected remote call. Arg5 is protected flag, otherwise as CALL_GR."}
|
||||
|
||||
, { 'CREATE', 16#a3, true, false, true, ?GAS(10000), [a,a,a], create, {contract_bytearray, typerep, integer}, contract, "Deploys a contract with a bytecode Arg1 and value Arg3. The `init` arguments should be placed on the stack and match the type in Arg2. Writes contract address to stack top."}
|
||||
, { 'CLONE', 16#a4, true, false, true, ?GAS(5000), [a,a,a,a], clone, {contract, typerep, integer, bool}, any, "Clones the contract under Arg1 and deploys it with value of Arg3. The `init` arguments should be placed on the stack and match the type in Arg2. Writes contract (or `None` on fail when protected) to stack top."}
|
||||
, { 'CLONE_G', 16#a5, true, false, true, ?GAS(5000), [a,a,a,a,a], clone_g, {contract, typerep, integer, integer, bool}, any, "Like `CLONE` but additionally limits gas of `init` call to Arg3"}
|
||||
, { 'BYTECODE_HASH', 16#a6, false, true, true, ?GAS(100), [a,a], bytecode_hash, {contract}, variant, "Arg0 := hash of the deserialized contract's bytecode under address given in Arg1 (or `None` on fail)."}
|
||||
, { 'CREATE', 16#a3, true, false, true, ?GAS(10000), [a,a,a], create, {contract_bytearray, typerep, integer}, contract, "Deploys a contract with a bytecode Arg1 and value Arg3. The `init` arguments should be placed on the stack and match the type in Arg2. Writes contract address to the top of the accumulator stack. If an account on the resulting address did exist before the call, the `payable` flag will be updated."}
|
||||
, { 'CLONE', 16#a4, true, false, true, ?GAS(5000), [a,a,a,a], clone, {contract, typerep, integer, bool}, any, "Clones the contract under Arg1 and deploys it with value of Arg3. The `init` arguments should be placed on the stack and match the type in Arg2. Writes contract (or `None` on fail when protected) to the top of the accumulator stack. Does not copy the existing contract's store – it will be initialized by a fresh call to the `init` function. If an account on the resulting address did exist before the call, the `payable` flag will be updated."}
|
||||
, { 'CLONE_G', 16#a5, true, false, true, ?GAS(5000), [a,a,a,a,a], clone_g, {contract, typerep, integer, integer, bool}, any, "Like `CLONE` but additionally limits the gas of the `init` call by Arg3"}
|
||||
, { 'BYTECODE_HASH', 16#a6, false, true, true, ?GAS(100), [a,a], bytecode_hash, {contract}, variant, "Arg0 := hash of the deserialized contract's bytecode under address given in Arg1 (or `None` on fail). Fails on AEVM contracts and contracts deployed before Iris."}
|
||||
|
||||
, { 'FEE', 16#a7, false, true, true, ?GAS(10), [a], fee, {}, integer, "Arg0 := The fee for the current call tx."}
|
||||
|
||||
, { 'ADDRESS_TO_BYTES', 16#a8, false, true, true, ?GAS(10), [a, a], addr_to_bytes, {address}, bytes, "Arg0 := the byte representation of the address"}
|
||||
, { 'POSEIDON', 16#a9, false, true, true, ?GAS(6000), [a, a, a], poseidon, {integer, integer}, integer, "Arg0 := the Poseidon hash of Arg1 and Arg2 - all integers in the BLS12-381 scalar field"}
|
||||
, { 'MULMOD', 16#aa, false, true, true, ?GAS(10), [a, a, a, a], mulmod, {integer, integer, integer}, integer, "Arg0 := (Arg1 * Arg2) mod Arg3"}
|
||||
, { 'BAND', 16#ab, false, true, true, ?GAS(10), [a, a, a], bin_and, {integer, integer}, integer, "Arg0 := Arg1 & Arg2"}
|
||||
, { 'BOR', 16#ac, false, true, true, ?GAS(10), [a, a, a], bin_or, {integer, integer}, integer, "Arg0 := Arg1 | Arg2"}
|
||||
, { 'BXOR', 16#ad, false, true, true, ?GAS(10), [a, a, a], bin_xor, {integer, integer}, integer, "Arg0 := Arg1 ^ Arg2"}
|
||||
, { 'BNOT', 16#ae, false, true, true, ?GAS(10), [a, a], bin_not, {integer}, integer, "Arg0 := ~Arg1"}
|
||||
, { 'BSL', 16#af, false, true, true, ?GAS(10), [a, a, a], bin_sl, {integer, integer}, integer, "Arg0 := Arg1 << Arg2"}
|
||||
, { 'BSR', 16#b0, false, true, true, ?GAS(10), [a, a, a], bin_sr, {integer, integer}, integer, "Arg0 := Arg1 >> Arg2"}
|
||||
|
||||
, { 'DEACTIVATE', 16#fa, false, true, true, ?GAS(10), [], deactivate, {}, none, "Mark the current contract for deactivation."}
|
||||
, { 'ABORT', 16#fb, true, true, true, ?GAS(10), [a], abort, {string}, none, "Abort execution (dont use all gas) with error message in Arg0."}
|
||||
, { 'EXIT', 16#fc, true, true, true, ?GAS(10), [a], exit, {string}, none, "Abort execution (use upp all gas) with error message in Arg0."}
|
||||
@@ -778,50 +788,3 @@ gen_variant() ->
|
||||
3 -> "(| 2 | 0 | ( " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ) |)"
|
||||
end.
|
||||
|
||||
|
||||
%% TODO: add gas cost and end_bb/in_auth?
|
||||
generate_documentation(Filename) ->
|
||||
{ok, File} = file:open(Filename, [write]),
|
||||
Instructions = lists:flatten([gen_doc(Op)++"\n" || Op <- get_ops()]),
|
||||
io:format(File,
|
||||
"### Operations\n\n"
|
||||
"| OpCode | Name | Args | Description |\n"
|
||||
"| --- | --- | --- | --- |\n"
|
||||
"~s"
|
||||
, [Instructions]),
|
||||
io:format(File, "\n", []),
|
||||
file:close(File).
|
||||
|
||||
gen_doc(#{ opname := Name
|
||||
, opcode := OpCode
|
||||
, arity := _Arity
|
||||
, end_bb := _EndBB
|
||||
, format := FateFormat
|
||||
, macro := _Macro
|
||||
, type_name := _TypeName
|
||||
, doc := Doc
|
||||
, gas := _Gas
|
||||
, type := _Type
|
||||
, constructor := _Constructor
|
||||
, constructor_type := _ConstructorType
|
||||
}) ->
|
||||
Arguments =
|
||||
case FateFormat of
|
||||
[] -> "";
|
||||
_ -> lists:join(" ",
|
||||
[format_arg_doc(A) ||
|
||||
A <-
|
||||
lists:zip(FateFormat,
|
||||
lists:seq(0,length(FateFormat)-1))])
|
||||
end,
|
||||
io_lib:format("| 0x~.16b | ~w | ~s | ~s |\n",
|
||||
[ OpCode
|
||||
, Name
|
||||
, Arguments
|
||||
, Doc]).
|
||||
|
||||
format_arg_doc({a, N}) -> io_lib:format("Arg~w", [N]);
|
||||
format_arg_doc({is,_N}) -> "Identifier";
|
||||
format_arg_doc({ii,_N}) -> "Integer";
|
||||
format_arg_doc({li,_N}) -> "[Integers]";
|
||||
format_arg_doc({t,_N}) -> "Type".
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
%%% @end
|
||||
%%% -------------------------------------------------------------------
|
||||
-module(aeb_fate_maps).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-include("aeb_fate_data.hrl").
|
||||
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
-module(aeb_heap).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ to_binary/1
|
||||
, to_binary/2
|
||||
@@ -22,8 +23,8 @@
|
||||
|
||||
-export_type([binary_value/0, heap_value/0, offset/0, heap_fragment/0]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_typerep_def.hrl").
|
||||
-include_lib("aebytecode/include/aeb_heap.hrl").
|
||||
-include("aeb_typerep_def.hrl").
|
||||
-include("aeb_heap.hrl").
|
||||
|
||||
-type word() :: non_neg_integer().
|
||||
-type pointer() :: word().
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeb_memory).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([binary_to_words/1]).
|
||||
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeb_opcodes).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([ dup/1
|
||||
, mnemonic/1
|
||||
@@ -17,7 +18,7 @@
|
||||
, swap/1
|
||||
]).
|
||||
|
||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||
-include("aeb_opcodes.hrl").
|
||||
|
||||
|
||||
%%====================================================================
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(aeb_primops).
|
||||
-vsn("3.2.1").
|
||||
-export([ is_local_primop_op/1
|
||||
, op_needs_type_check/1
|
||||
]).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{application, aebytecode,
|
||||
[{description, "Bytecode definitions, serialization and deserialization for aeternity."},
|
||||
{vsn, "2.1.0"},
|
||||
{vsn, "3.2.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
-module(aefateasm).
|
||||
-vsn("3.2.1").
|
||||
|
||||
-export([main/1]).
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{a_email,[]}.
|
||||
{author,"Aeternity Authors"}.
|
||||
{c_email,[]}.
|
||||
{copyright,"Aeternity Authors"}.
|
||||
{deps,[{"otpr","aeserialization",{0,1,0}},
|
||||
{"otpr","eblake2",{1,0,0}},
|
||||
{"otpr","getopt",{1,0,2}}]}.
|
||||
{desc,"A library and stand alone assembler for aeternity bytecode. This version supports AEVM bytecode and FATE bytecode."}.
|
||||
{file_exts,[]}.
|
||||
{key_name,none}.
|
||||
{license,skip}.
|
||||
{modules,[]}.
|
||||
{name,"AE Bytecode"}.
|
||||
{package_id,{"otpr","aebytecode",{3,2,1}}}.
|
||||
{prefix,none}.
|
||||
{repo_url,"https://github.com/aeternity/aebytecode"}.
|
||||
{tags,["aeternity","blockchain","fate","bytecode","crypto","ae"]}.
|
||||
{type,lib}.
|
||||
{ws_url,[]}.
|
||||
@@ -0,0 +1,20 @@
|
||||
#! /bin/bash
|
||||
|
||||
# This is a small pre-packaging source generation and include correction script that should be
|
||||
# run before packaging this project for use with ZX/Zomp.
|
||||
|
||||
rm -rf _build
|
||||
rm -f src/aeb_fate_opcodes.erl src/aeb_fate_ops.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl
|
||||
make sources
|
||||
cd src
|
||||
for f in $(ls --ignore=aeb_fate_generate_ops.erl | grep erl)
|
||||
do
|
||||
echo "Updating includes in: $f"
|
||||
sed -i 's/aebytecode\/include\///g' "$f"
|
||||
sed -i 's/\.\.\/include\///g' "$f"
|
||||
sed -i 's/include_lib/include/g' "$f"
|
||||
done
|
||||
cd ..
|
||||
rm -f ebin/*.beam
|
||||
rm -f rebar*
|
||||
rm -rf quickcheck
|
||||
Reference in New Issue
Block a user