Compare commits

...

29 Commits

Author SHA1 Message Date
skkw 3f85375cb2 adding subname TX 2019-08-30 13:46:29 +02:00
Ulf Norell e7f2be7ce8 Merge pull request #74 from aeternity/fate-map-fixes
Fate map fixes
2019-08-27 10:55:37 +02:00
Ulf Norell c6475fe1c2 Fix typo 2019-08-26 11:57:21 +02:00
Ulf Norell 4e4c20c387 Remember to unfold store map caches too! 2019-08-26 09:06:43 +02:00
Thomas Arts 1d5e5be252 Merge pull request #72 from aeternity/PT-166330348-check-map-keys-fate
Pt 166330348 check map keys fate
2019-08-26 08:37:52 +02:00
Ulf Norell 6efa4a0cb8 Merge pull request #71 from aeternity/missing-case-in-allocate-maps
Add missing case for map tombstones
2019-08-23 17:13:24 +02:00
Thomas Arts 59b7b786ac Update quickcheck/aefate_code_eqc.erl
Co-Authored-By: Hans Svensson <hanssv@gmail.com>
2019-08-23 15:50:39 +02:00
Thomas Arts f31887c2ed Update properties 2019-08-23 15:00:12 +02:00
Thomas Arts d794566363 Fix check for no maps in keys 2019-08-23 14:45:15 +02:00
Ulf Norell 850a5e2c35 Add missing case for map tombstones 2019-08-23 14:30:07 +02:00
Thomas Arts c270c794c3 Fix tests to new datastructure containing attributes 2019-08-23 11:31:54 +02:00
Hans Svensson 10cc127883 Merge pull request #70 from aeternity/PT-167996886-a_proper_ecverify
PT-167996886 a proper ecverify
2019-08-21 11:07:59 +02:00
Hans Svensson 50df849709 VERIFY_SIG_SECP256K1 was too long for code generation 2019-08-21 09:26:01 +02:00
Hans Svensson dfa9b80a3c Change ECVERIFY to VERIFY_SIG and add proper ECVERIFY 2019-08-21 09:25:28 +02:00
Hans Svensson befa1e3ff9 Merge pull request #69 from aeternity/PT-162578406-payable_modifier
PT-162578406 Add payable modifier
2019-08-19 08:54:31 +02:00
Hans Svensson efb4afeafa Add IS_PAYABLE opcode 2019-08-16 09:31:27 +02:00
Hans Svensson e75336486e Track payable (and private) in FATE/AEVM type info
Privateness is only tracked for FATE.
2019-08-16 09:31:27 +02:00
Ulf Norell fdd660a219 Merge pull request #67 from aeternity/PT-167221635-remote-type-check
PT-167221635 remote type check
2019-08-16 09:10:47 +02:00
Tino Breddin 3954bd22da Merge pull request #64 from aeternity/newby/ecrecover
[PT-167805291] Add opcode for ecrecover
2019-08-14 16:08:45 +02:00
Ulf Norell 13211887a3 Update src/aeb_fate_generate_ops.erl
Co-Authored-By: Hans Svensson <hanssv@gmail.com>
2019-08-14 09:32:23 +02:00
Ulf Norell 834ab298d1 typereps are values 2019-08-14 09:27:51 +02:00
Ulf Norell 52781060b2 fix type spec 2019-08-14 09:01:53 +02:00
Ulf Norell 3721fde7e8 Add typereps to remote call instructions 2019-08-14 09:01:53 +02:00
Tino Breddin 23ee7e0ca4 Add missing crypto entries to all_instructions 2019-08-13 16:24:17 +02:00
Tino Breddin 197dfd5da1 Let ecrecover only require two parameters 2019-08-13 15:15:03 +02:00
John Newby 44ec31d958 fixed ordering 2019-08-12 13:52:27 +02:00
John Newby 8fde1e5e24 Added FATE opcode for ecrecover 2019-08-12 13:36:49 +02:00
johnsnewby 7c6a80fef7 Update include/aeb_opcodes.hrl
Co-Authored-By: Hans Svensson <hanssv@gmail.com>
2019-08-12 10:52:47 +02:00
John Newby c0bc71b0b7 Added opcode for ecrecover 2019-08-09 16:32:00 +02:00
15 changed files with 257 additions and 118 deletions
+1
View File
@@ -17,6 +17,7 @@
-define(FATE_VOID_T, void).
-define(FATE_TUPLE_T, {tuple, tuple()}).
-define(FATE_BITS_T, {bits, integer()}).
-define(FATE_TYPEREP_T, {typerep, fate_type_type()}).
-define(IS_FATE_INTEGER(X), (is_integer(X))).
-define(IS_FATE_LIST(X), (is_list(X))).
+11 -7
View File
@@ -176,6 +176,7 @@
-define(PRIM_CALL_AENS_UPDATE, 203).
-define(PRIM_CALL_AENS_TRANSFER, 204).
-define(PRIM_CALL_AENS_REVOKE, 205).
-define(PRIM_CALL_AENS_SUBNAME, 206).
-define(PRIM_CALL_IN_MAP_RANGE(__TTYPE__), (((__TTYPE__) > 299) andalso ((__TTYPE__) < 400))).
-define(PRIM_CALL_MAP_EMPTY, 300).
@@ -186,13 +187,15 @@
-define(PRIM_CALL_MAP_TOLIST, 305).
-define(PRIM_CALL_IN_CRYPTO_RANGE(__TTYPE__), (((__TTYPE__) > 399) andalso ((__TTYPE__) < 500))).
-define(PRIM_CALL_CRYPTO_ECVERIFY, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-define(PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, 410).
-define(PRIM_CALL_CRYPTO_VERIFY_SIG, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-define(PRIM_CALL_CRYPTO_VERIFY_SIG_SECP256K1, 410).
-define(PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, 420).
-define(PRIM_CALL_CRYPTO_ECRECOVER_SECP256K1, 421).
-define(PRIM_CALL_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))).
-define(PRIM_CALL_AUTH_TX_HASH, 500).
@@ -200,3 +203,4 @@
-define(PRIM_CALL_IN_ADDRESS_RANGE(__TTYPE__), (((__TTYPE__) > 599) andalso ((__TTYPE__) < 700))).
-define(PRIM_CALL_ADDR_IS_ORACLE, 600).
-define(PRIM_CALL_ADDR_IS_CONTRACT, 601).
-define(PRIM_CALL_ADDR_IS_PAYABLE, 610).
+1
View File
@@ -22,5 +22,6 @@ 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)
]}.
+3 -2
View File
@@ -77,7 +77,7 @@ prop_opcodes() ->
valid_opcodes() ->
lists:seq(0, 16#7c) ++ lists:seq(16#fa, 16#fd).
lists:seq(0, 16#7f) ++ lists:seq(16#fa, 16#fd).
fate_code(Failure) ->
@@ -85,7 +85,8 @@ fate_code(Failure) ->
?LET({FMap, SMap, AMap},
{non_empty(map(if Failure == 1 -> binary(1);
true -> binary(4) end,
{{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})),
{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(
+52 -10
View File
@@ -10,6 +10,7 @@
-module(aefate_eqc).
-include_lib("eqc/include/eqc.hrl").
-include("../include/aeb_fate_data.hrl").
-compile([export_all, nowarn_export_all]).
@@ -23,7 +24,7 @@ prop_roundtrip() ->
end)).
prop_format_scan() ->
?FORALL(FateData, fate_data(),
?FORALL(FateData, fate_data([variant, map]),
?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]),
begin
String = aeb_fate_data:format(FateData),
@@ -43,6 +44,18 @@ prop_serializes() ->
{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)),
@@ -59,13 +72,14 @@ prop_fuzz() ->
prop_order() ->
?FORALL(Items, vector(3, fate_data()),
?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)])}])
{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]) ->
@@ -88,18 +102,24 @@ prop_idempotent() ->
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() ->
?SIZED(Size, ?LET(Data, fate_data(Size, [map, variant]), eqc_symbolic:eval(Data))).
fate_data([map, variant, store_map]).
%% keys may contain variants but no maps
fate_data_key() ->
?SIZED(Size, ?LET(Data, fate_data(Size div 4, [variant]), eqc_symbolic:eval(Data))).
fate_data([variant]).
fate_data(0, _Options) ->
fate_data(0, Options) ->
?LAZY(
frequency(
[{5, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])},
{1, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(),
fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}]));
[{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),
@@ -148,9 +168,20 @@ fate_list(Size, Options) ->
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])),
?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 ] == []).
@@ -167,3 +198,14 @@ injection(Binary) ->
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.
+71 -33
View File
@@ -11,14 +11,15 @@
-define(HASH_SIZE, 32).
-export([ create_calldata/4
, check_calldata/2
, function_type_info/3
, check_calldata/3
, function_type_info/4
, function_type_hash/3
, arg_typerep_from_function/2
, type_hash_from_function_name/2
, typereps_from_type_hash/2
, function_name_from_type_hash/2
, get_function_hash_from_calldata/1
, is_payable/2
, abi_version/0
]).
@@ -27,6 +28,7 @@
-type typerep() :: aeb_aevm_data:type().
-type function_type_info() :: { FunctionHash :: hash()
, FunctionName :: function_name()
, Payable :: boolean()
, ArgType :: binary() %% binary typerep
, OutType :: binary() %% binary typerep
}.
@@ -51,30 +53,40 @@ create_calldata(FunName, Args, ArgTypes0, RetType) ->
Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}),
{ok, Data}.
-spec check_calldata(binary(), type_info()) ->
-spec check_calldata(binary(), type_info(), boolean()) ->
{'ok', typerep(), typerep()} | {'error', atom()}.
check_calldata(CallData, TypeInfo) ->
check_calldata(CallData, TypeInfo, CheckPayable) ->
%% The first element of the CallData should be the function name
case get_function_hash_from_calldata(CallData) of
{ok, Hash} ->
case typereps_from_type_hash(Hash, TypeInfo) of
{ok, ArgType, OutType} ->
try aeb_heap:from_binary({tuple, [word, ArgType]}, CallData) of
{ok, _Something} ->
{ok, {tuple, [word, ArgType]}, OutType};
{error, _} ->
{error, bad_call_data}
catch
_T:_E ->
{error, bad_call_data}
end;
{error, _} ->
{error, unknown_function}
end;
check_calldata(Hash, CallData, TypeInfo, CheckPayable);
{error, _What} ->
{error, bad_call_data}
end.
check_calldata(Hash, CallData, TypeInfo, true) ->
case is_payable(Hash, TypeInfo) of
{ok, true} -> check_calldata(Hash, CallData, TypeInfo, false);
{ok, false} -> {error, function_is_not_payable};
Err = {error, _} -> Err
end;
check_calldata(Hash, CallData, TypeInfo, false) ->
case typereps_from_type_hash(Hash, TypeInfo) of
{ok, ArgType, OutType} ->
try aeb_heap:from_binary({tuple, [word, ArgType]}, CallData) of
{ok, _Something} ->
{ok, {tuple, [word, ArgType]}, OutType};
{error, _} ->
{error, bad_call_data}
catch
_T:_E ->
{error, bad_call_data}
end;
{error, _} ->
{error, unknown_function}
end.
-spec get_function_hash_from_calldata(CallData::binary()) ->
{ok, binary()} | {error, term()}.
get_function_hash_from_calldata(CallData) ->
@@ -86,12 +98,13 @@ get_function_hash_from_calldata(CallData) ->
%%%===================================================================
%%% Handle type info from contract meta data
-spec function_type_info(function_name(), [typerep()], typerep()) ->
-spec function_type_info(function_name(), boolean(), [typerep()], typerep()) ->
function_type_info().
function_type_info(Name, ArgTypes, OutType) ->
function_type_info(Name, Payable, ArgTypes, OutType) ->
ArgType = {tuple, ArgTypes},
{ function_type_hash(Name, ArgType, OutType)
, Name
, Payable
, aeb_heap:to_binary(ArgType)
, aeb_heap:to_binary(OutType)
}.
@@ -110,35 +123,46 @@ function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
{'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
arg_typerep_from_function(Function, TypeInfo) ->
case lists:keyfind(Function, 2, TypeInfo) of
{_TypeHash, Function, ArgTypeBin,_OutTypeBin} ->
case aeb_heap:from_binary(typerep, ArgTypeBin) of
{ok, ArgType} -> {ok, ArgType};
{error,_} -> {error, bad_type_data}
end;
{_TypeHash, Function, ArgTypeBin, _OutTypeBin} ->
arg_typerep_from_type_binary(ArgTypeBin);
{_TypeHash, Function, _Payable, ArgTypeBin, _OutTypeBin} ->
arg_typerep_from_type_binary(ArgTypeBin);
false ->
{error, unknown_function}
end.
arg_typerep_from_type_binary(ArgTBin) ->
case aeb_heap:from_binary(typerep, ArgTBin) of
{ok, ArgT} -> {ok, ArgT};
{error,_} -> {error, bad_type_data}
end.
-spec typereps_from_type_hash(hash(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
typereps_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash,_Function, ArgTypeBin, OutTypeBin} ->
case {aeb_heap:from_binary(typerep, ArgTypeBin),
aeb_heap:from_binary(typerep, OutTypeBin)} of
{{ok, ArgType}, {ok, OutType}} -> {ok, ArgType, OutType};
{_, _} -> {error, bad_type_data}
end;
{TypeHash, _Function, ArgTypeBin, OutTypeBin} ->
typereps_from_type_binaries(ArgTypeBin, OutTypeBin);
{TypeHash, _Function, _Payable, ArgTypeBin, OutTypeBin} ->
typereps_from_type_binaries(ArgTypeBin, OutTypeBin);
false ->
{error, unknown_function}
end.
typereps_from_type_binaries(ArgTBin, OutTBin) ->
case {aeb_heap:from_binary(typerep, ArgTBin), aeb_heap:from_binary(typerep, OutTBin)} of
{{ok, ArgT}, {ok, OutT}} -> {ok, ArgT, OutT};
{_, _} -> {error, bad_type_data}
end.
-spec function_name_from_type_hash(hash(), type_info()) ->
{'ok', function_name()}
| {'error', 'unknown_function'}.
function_name_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash, Function,_ArgTypeBin,_OutTypeBin} ->
{TypeHash, Function, _ArgTypeBin, _OutTypeBin} ->
{ok, Function};
{TypeHash, Function, _Payable, _ArgTypeBin, _OutTypeBin} ->
{ok, Function};
false ->
{error, unknown_function}
@@ -149,8 +173,22 @@ function_name_from_type_hash(TypeHash, TypeInfo) ->
| {'error', 'unknown_function'}.
type_hash_from_function_name(Name, TypeInfo) ->
case lists:keyfind(Name, 2, TypeInfo) of
{TypeHash, Name,_ArgTypeBin,_OutTypeBin} ->
{TypeHash, Name, _ArgTypeBin, _OutTypeBin} ->
{ok, TypeHash};
{TypeHash, Name, _Payable, _ArgTypeBin, _OutTypeBin} ->
{ok, TypeHash};
false ->
{error, unknown_function}
end.
-spec is_payable(hash(), type_info()) -> {ok, boolean()} | {error, 'unknown_function'}.
is_payable(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash, _Function, _ArgTypeBin, _OutTypeBin} ->
{ok, true};
{TypeHash, _Function, Payable, _ArgTypeBin, _OutTypeBin} ->
{ok, Payable};
false ->
{error, unknown_function}
end.
+1 -1
View File
@@ -72,6 +72,6 @@ get_function_type_from_function_hash(SymbolHash, FateCode) ->
case maps:get(SymbolHash, Functions, undefined) of
undefined ->
{error, no_function_matching_function_hash};
{{ArgTypes, RetType}, _Code} ->
{_Attrs, {ArgTypes, RetType}, _Code} ->
{ok, ArgTypes, RetType}
end.
+2 -2
View File
@@ -153,7 +153,7 @@ format_functions(Functions, Symbols) ->
lists:sort(maps:to_list(CodeMap)),
Symbols)
||
{Name, {Sig, CodeMap}} <- maps:to_list(Functions)].
{Name, {_Attrs, Sig, CodeMap}} <- maps:to_list(Functions)].
format(Name, Sig, BBs, Symbols) ->
@@ -484,7 +484,7 @@ insert_fun({NameString, ArgType, RetType}, Code, #{ fate_code := FateCode
{FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode),
BodyByteCode = aeb_fate_code:serialize_code(lists:reverse(Code)),
SigByteCode = aeb_fate_code:serialize_signature({ArgType, RetType}),
FunByteCode = [?FUNCTION, Id, SigByteCode, BodyByteCode],
FunByteCode = [?FUNCTION, Id, aeb_fate_encoding:serialize(0), SigByteCode, BodyByteCode],
Env#{ functions => Funs#{ Id => FunByteCode }
, fate_code => FateCode1}.
+42 -21
View File
@@ -11,7 +11,7 @@
, deserialize/1
, functions/1
, insert_annotation/4
, insert_fun/4
, insert_fun/5
, insert_symbol/2
, new/0
, serialize/1
@@ -72,9 +72,9 @@ symbol_identifier(Bin) ->
{ok, <<X:4/binary,_/binary>> } = eblake2:blake2b(?HASH_BYTES, Bin),
X.
insert_fun(Name, {ArgType, RetType}, #{} = BBs, FCode) ->
insert_fun(Name, Attrs, {ArgType, RetType}, #{} = BBs, FCode) ->
{F1, ID} = insert_symbol(Name, FCode),
update_functions(F1, #{ID => {{ArgType, RetType}, BBs}}).
update_functions(F1, #{ID => {Attrs, {ArgType, RetType}, BBs}}).
insert_symbol(Name, #fcode{ symbols = Syms } = F) ->
ID = symbol_identifier(Name),
@@ -128,10 +128,17 @@ to_hexstring(ByteList) ->
serialize_functions(#fcode{ functions = Functions }) ->
%% Sort the functions on name to get a canonical serialisation.
iolist_to_binary(
lists:foldr(fun({Id, {Sig, C}}, Acc) ->
[[?FUNCTION, Id, serialize_signature(Sig), serialize_bbs(C)] | Acc]
lists:foldr(fun({Id, {Attrs, Sig, C}}, Acc) ->
[[?FUNCTION, Id, serialize_attributes(Attrs), serialize_signature(Sig), serialize_bbs(C)] | Acc]
end, [], lists:sort(maps:to_list(Functions)))).
serialize_attributes(Attrs) ->
AttrVal = lists:sum([ attr_value(Attr) || Attr <- Attrs ]),
aeb_fate_encoding:serialize(?MAKE_FATE_INTEGER(AttrVal)).
attr_value(private) -> 1;
attr_value(payable) -> 2.
serialize_signature({Args, RetType}) ->
[aeb_fate_encoding:serialize_type({tuple, Args}) |
aeb_fate_encoding:serialize_type(RetType)].
@@ -139,7 +146,7 @@ serialize_signature({Args, RetType}) ->
serialize_symbol_table(#fcode{ symbols = Symbols }) ->
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)).
serialize_annotations(#fcode{ annotations = Annotations}) ->
serialize_annotations(#fcode{ annotations = Annotations }) ->
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)).
serialize_bbs(#{} = BBs) ->
@@ -166,8 +173,8 @@ serialize_op(Op) ->
sanity_check(#fcode{ functions = Funs }) ->
_ = [ case Def of
{_, BBs} when byte_size(Id) == 4 -> sanity_check_bbs(BBs);
_ -> error({illegal_function_id, Id})
{_, _, BBs} when byte_size(Id) == 4 -> sanity_check_bbs(BBs);
_ -> error({illegal_function_id, Id})
end || {Id, Def} <- maps:to_list(Funs) ],
ok.
@@ -303,33 +310,35 @@ deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
, bb := 0
, current_bb_code := []
} = Env) ->
{Sig, Rest2} = deserialize_signature(Rest),
Env2 = Env#{function => {<<A,B,C,D>>, Sig}},
deserialize_functions(Rest2, Env2);
{Attrs, Rest2} = deserialize_attributes(Rest),
{Sig, Rest3} = deserialize_signature(Rest2),
Env2 = Env#{function => {<<A,B,C,D>>, Attrs, Sig}},
deserialize_functions(Rest3, Env2);
deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
#{ function := {F, Sig}
#{ function := {F, Attrs, Sig}
, bb := BB
, current_bb_code := Code
, code := Program
, functions := Funs} = Env) ->
{NewSig, Rest2} = deserialize_signature(Rest),
{NewAttrs, Rest2} = deserialize_attributes(Rest),
{NewSig, Rest3} = deserialize_signature(Rest2),
case Code of
[] ->
Env2 = Env#{ bb => 0
, current_bb_code => []
, function => {<<A,B,C,D>>, NewSig}
, function => {<<A,B,C,D>>, NewAttrs, NewSig}
, code => #{}
, functions => Funs#{F => {Sig, Program}}},
deserialize_functions(Rest2, Env2);
, functions => Funs#{F => {Attrs, Sig, Program}}},
deserialize_functions(Rest3, Env2);
_ ->
Env2 = Env#{ bb => 0
, current_bb_code => []
, function => {<<A,B,C,D>>, NewSig}
, function => {<<A,B,C,D>>, NewAttrs, NewSig}
, code => #{}
, functions =>
Funs#{F => {Sig,
Funs#{F => {Attrs, Sig,
Program#{ BB => lists:reverse(Code)}}}},
deserialize_functions(Rest2, Env2)
deserialize_functions(Rest3, Env2)
end;
deserialize_functions(<<_Op:8, _Rest/binary>>,
#{ function := none }) ->
@@ -351,7 +360,7 @@ deserialize_functions(<<Op:8, Rest/binary>>,
deserialize_functions(<<>>, #{ function := none
, functions := Funs}) ->
Funs;
deserialize_functions(<<>>, #{ function := {F, Sig}
deserialize_functions(<<>>, #{ function := {F, Attrs, Sig}
, bb := BB
, current_bb_code := Code
, code := Program
@@ -361,7 +370,7 @@ deserialize_functions(<<>>, #{ function := {F, Sig}
[] -> Program;
_ -> Program#{ BB => lists:reverse(Code)}
end,
Funs#{F => {Sig, FunctionCode}}.
Funs#{F => {Attrs, Sig, FunctionCode}}.
deserialize_op(Op, Rest, Code) ->
OpName = aeb_fate_opcodes:mnemonic(Op),
@@ -399,6 +408,18 @@ deserialize_n_args(N, <<M7:2, M6:2, M5:2, M4:2, M3:2, M2:2, M1:2, M0:2,
end
end, Rest, ArgMods).
deserialize_attributes(Binary) ->
{AttrVal, Rest} = aeb_fate_encoding:deserialize_one(Binary),
Attrs = [ attr(AVal) || AVal <- attr_vals(1, AttrVal) ],
{lists:sort(Attrs), Rest}.
attr_vals(_, 0) -> [];
attr_vals(X, N) when N rem 2 == 0 -> attr_vals(X + 1, N div 2);
attr_vals(X, N) -> [X | attr_vals(X + 1, N div 2)].
attr(1) -> private;
attr(2) -> payable.
deserialize_signature(Binary) ->
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary),
{RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest),
+3 -1
View File
@@ -21,6 +21,7 @@
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_bits() :: ?FATE_BITS_T.
-type fate_typerep() :: ?FATE_TYPEREP_T.
-type fate_type_type() :: integer
| boolean
@@ -54,7 +55,8 @@
| fate_channel()
| fate_variant()
| fate_map()
| fate_bits().
| fate_bits()
| fate_typerep().
-export_type([fate_type/0
, fate_boolean/0
+6
View File
@@ -487,5 +487,11 @@ sort(KVList) ->
valid_key_type(K) when ?IS_FATE_MAP(K) ->
error({map_as_key_in_map, K});
valid_key_type(?FATE_STORE_MAP(_, _) = K) ->
error({map_as_key_in_map, K});
valid_key_type(K) when is_list(K) ->
lists:all(fun(E) -> valid_key_type(E) end, K);
valid_key_type(K) when is_tuple(K) ->
lists:all(fun(E) -> valid_key_type(E) end, tuple_to_list(K));
valid_key_type(_K) ->
true.
+34 -27
View File
@@ -4,7 +4,7 @@
, generate/0
, generate_documentation/1
, get_ops/0
, test_asm_generator/1]).
, test_asm_generator/1 ]).
gen_and_halt([SrcDirArg, IncludeDirArg]) ->
generate(atom_to_list(SrcDirArg),
@@ -46,10 +46,10 @@ ops_defs() ->
[ { 'RETURN', 16#00, true, true, true, 2, [], 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, 2, [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, 4, [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."}
, { 'CALL_R', 16#03, true, false, true, 8, [a,is,ii,a], call_r, {contract, string, integer, integer}, any, "Remote call to contract Arg0 and Arg2-ary function Arg1 with value Arg3. The types of the arguments has to match the argument typs of the function."}
, { 'CALL_R', 16#03, true, false, true, 8, [a,is,a,a,a], call_r, {contract, string, typerep, typerep, integer}, any, "Remote call to contract Arg0 and function Arg1 of type Arg2 => Arg3 with value Arg4. The types of the arguments has to match the argument types of the function."}
, { 'CALL_T', 16#04, true, true, true, 4, [a], call_t, {string}, any, "Tail call to function Arg0. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
, { 'UNUSED_1', 16#05, false, false, true, 8, [], unused_1, {}, none, "Was CALL_TR."}
, { 'CALL_GR', 16#06, true, false, true, 8, [a,is,ii,a,a], call_gr, {contract, string, integer, integer, integer}, any, "Remote call with gas cap in Arg3. Otherwise as CALL_R."}
, { 'CALL_GR', 16#06, true, false, true, 8, [a,is,a,a,a,a], call_gr, {contract, string, typerep, typerep, integer, integer}, any, "Remote call with gas cap in Arg4. Otherwise as CALL_R."}
, { 'UNUSED_2', 16#07, false, false, true, 8, [], unused_2, {}, none, "Was CALL_GTR."}
, { 'JUMP', 16#08, true, true, true, 3, [ii], jump, {integer}, none, "Jump to a basic block. The basic block has to exist in the current function."}
, { 'JUMPIF', 16#09, true, true, true, 4, [a,ii], jumpif, {boolean, integer}, none, "Conditional jump to a basic block. If Arg0 then jump to Arg1."}
@@ -161,11 +161,11 @@ ops_defs() ->
, { 'MAP_TO_LIST', 16#70, false, true, true, 3, [a,a], map_to_list, {map}, list, "Arg0 := The tuple list representation of the map Arg1."}
, { 'STR_LENGTH', 16#71, false, true, true, 3, [a,a], str_length, {string}, integer, "Arg0 := The length of the string Arg1."}
, { 'ECVERIFY', 16#72, false, true, true, 1300, [a,a,a,a], ecverify, {bytes, address, bytes}, boolean, "Arg0 := ecverify(Hash, PubKey, Signature)"}
, { 'ECVERIFY_SECP256K1', 16#73, false, true, true, 1300, [a,a,a,a], ecverify_secp256k1, {bytes, bytes, bytes}, boolean, "Arg0 := ecverify_secp256k1(Hash, PubKey, Signature)"}
, { 'VERIFY_SIG', 16#72, false, true, true, 1300, [a,a,a,a], verify_sig, {bytes, address, bytes}, boolean, "Arg0 := verify_sig(Hash, PubKey, Signature)"}
, { 'VERIFY_SIG_SECP256K1', 16#73, false, true, true, 1300, [a,a,a,a], verify_sig_secp256k1, {bytes, bytes, bytes}, boolean, "Arg0 := verify_sig_secp256k1(Hash, PubKey, Signature)"}
, { 'CONTRACT_TO_ADDRESS', 16#74, false, true, true, 3, [a,a], contract_to_address, {contract}, address, "Arg0 := Arg1 - A no-op type conversion"}
, { 'AUTH_TX_HASH', 16#75, false, true, true, 3, [a], auth_tx_hash, {}, variant, "If in GA authentication context return Some(TxHash) otherwise None."}
, { 'CONTRACT_TO_ADDRESS', 16#74, false, true, true, 3, [a,a], contract_to_address, {contract}, address, "Arg0 := Arg1 - A no-op type conversion"}
, { 'AUTH_TX_HASH', 16#75, false, true, true, 3, [a], auth_tx_hash, {}, variant, "If in GA authentication context return Some(TxHash) otherwise None."}
, { 'BYTES_TO_INT', 16#76, false, true, true, 3, [a,a], bytes_to_int, {bytes}, integer, "Arg0 := bytes_to_int(Arg1)"}
, { 'BYTES_TO_STR', 16#77, false, true, true, 3, [a,a], bytes_to_str, {bytes}, string, "Arg0 := bytes_to_str(Arg1)"}
@@ -173,10 +173,14 @@ ops_defs() ->
, { 'ORACLE_CHECK', 16#78, false, false, true, 3, [a,a,a,a], oracle_check, {oracle, typerep, typerep}, bool, "Arg0 := is Arg1 an oracle with the given query (Arg2) and response (Arg3) types"}
, { 'ORACLE_CHECK_QUERY', 16#79, false, false, true, 3, [a,a,a,a,a], oracle_check_query, {oracle, oracle_query, typerep, typerep}, bool, "Arg0 := is Arg2 a query for the oracle Arg1 with the given types (Arg3, Arg4)"}
, { 'IS_ORACLE', 16#7a, false, false, true, 3, [a,a], is_oracle, {address}, bool, "Arg0 := is Arg1 an oracle"}
, { 'IS_CONTRACT', 16#7b, false, false, true, 3, [a,a], is_contract, {address}, bool, "Arg0 := is Arg1 a contract"}
, { 'CREATOR', 16#7c, false, true, true, 3, [a], contract_creator, {}, address, "Arg0 := contract creator"}
, { 'IS_ORACLE', 16#7a, false, false, true, 3, [a,a], is_oracle, {address}, bool, "Arg0 := is Arg1 an oracle"}
, { 'IS_CONTRACT', 16#7b, false, false, true, 3, [a,a], is_contract, {address}, bool, "Arg0 := is Arg1 a contract"}
, { 'IS_PAYABLE', 16#7c, false, false, true, 3, [a,a], is_payable, {address}, bool, "Arg0 := is Arg1 a payable address"}
, { 'CREATOR', 16#7d, false, true, true, 3, [a], contract_creator, {}, address, "Arg0 := contract creator"}
, { 'ECVERIFY_SECP256K1', 16#7e, false, true, true, 1300, [a,a,a,a], ecverify_secp256k1, {bytes, bytes, bytes}, bytes, "Arg0 := ecverify_secp256k1(Hash, Addr, Signature)"}
, { 'ECRECOVER_SECP256K1', 16#7f, false, true, true, 1300, [a,a,a], ecrecover_secp256k1, {bytes, bytes}, bytes, "Arg0 := ecrecover_secp256k1(Hash, Signature)"}
, { 'AENS_SUBNAME', 16#80, false, false, false, 3, [a,a,a,a], aens_subname, {signature, address, string, map}, none, "Claim subbnames of name in Arg2 from owner Arg1. Arg0 contains delegation signature. Arg3 is map describing subname prefixes to claim and their pointers."}
, { 'DEACTIVATE', 16#fa, false, true, true, 3, [], deactivate, {}, none, "Mark the current contract for deactivation."}
, { 'ABORT', 16#fb, true, true, true, 3, [a], abort, {string}, none, "Abort execution (dont use all gas) with error message in Arg0."}
, { 'EXIT', 16#fc, true, true, true, 3, [a], exit, {string}, none, "Abort execution (use upp all gas) with error message in Arg0."}
@@ -277,7 +281,7 @@ generate_code_ops(Modulename, SrcDir, Ops) ->
file:close(File).
gen_type(#{type_name := TypeName, type := Type}) ->
lists:flatten(io_lib:format("-type ~-26s :: ~s.\n",
lists:flatten(io_lib:format("-type ~-29s :: ~s.\n",
[TypeName, Type])).
gen_fate_code_type(#{type_name := TypeName}) ->
@@ -355,27 +359,27 @@ ops_exports(Module, HrlFile, Exports) ->
[Module, Exports])).
gen_mnemonic(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("mnemonic(~21s) -> ~21w ;\n",
lists:flatten(io_lib:format("mnemonic(~24s) -> ~24w ;\n",
[Macro, Name])).
gen_m_to_op(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n",
lists:flatten(io_lib:format("m_to_op(~24w) -> ~24s ;\n",
[Name, Macro])).
gen_args(#{macro := Macro, arity := Arity}) ->
lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n",
lists:flatten(io_lib:format("args(~24s) -> ~2w ;\n",
[Macro, Arity])).
gen_bb(#{macro := Macro, end_bb := EndBB}) ->
lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n",
lists:flatten(io_lib:format("end_bb(~24s) -> ~w ;\n",
[Macro, EndBB])).
gen_in_auth(#{macro := Macro, in_auth := InAuth}) ->
lists:flatten(io_lib:format("in_auth(~21s) -> ~w ;\n",
lists:flatten(io_lib:format("in_auth(~24s) -> ~w ;\n",
[Macro, InAuth])).
gen_allowed_offchain(#{macro := Macro, offchain := Offchain}) ->
lists:flatten(io_lib:format("allowed_offchain(~21s) -> ~w ;\n",
lists:flatten(io_lib:format("allowed_offchain(~24s) -> ~w ;\n",
[Macro, Offchain])).
prelude(Doc) ->
@@ -392,7 +396,7 @@ prelude(Doc) ->
gen_defines(#{opname := Name, opcode := OpCode}) ->
lists:flatten(io_lib:format("-define(~-26w, 16#~2.16.0b).\n", [Name, OpCode])).
lists:flatten(io_lib:format("-define(~-29w, 16#~2.16.0b).\n", [Name, OpCode])).
gen([]) ->
[];
@@ -489,28 +493,32 @@ gen_asm_pp(Module, Path, Ops) ->
file:close(File).
gen_format(#{opname := Name}) when (Name =:= 'CALL_R') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Arity, Value}, Symbols) ->\n"
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, ArgType, RetType, Value}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Arity), \" \", "
"format_arg(a, ArgType), \" \", "
"format_arg(a, RetType), \" \", "
"format_arg(a, Value)];\n"
"format_op({~w, Contract, {immediate, Function}, Arity, Value}, Symbols) ->\n"
"format_op({~w, Contract, {immediate, Function}, ArgType, RetType, Value}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Arity), \" \", "
"format_arg(a, ArgType), \" \", "
"format_arg(a, RetType), \" \", "
"format_arg(a, Value)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name}) when (Name =:= 'CALL_GR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Arity, Value, Gas}, Symbols) ->\n"
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, ArgType, RetType, Value, Gas}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Arity), \" \", "
"format_arg(a, ArgType), \" \", "
"format_arg(a, RetType), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n"
"format_op({~w, Contract, {immediate, Function}, Arity, Value, Gas}, Symbols) ->\n"
"format_op({~w, Contract, {immediate, Function}, ArgType, RetType, Value, Gas}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Arity), \" \", "
"format_arg(a, ArgType), \" \", "
"format_arg(a, RetType), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
@@ -756,4 +764,3 @@ format_arg_doc({is,_N}) -> "Identifier";
format_arg_doc({ii,_N}) -> "Integer";
format_arg_doc({li,_N}) -> "[Integers]";
format_arg_doc({t,_N}) -> "Type".
+11 -7
View File
@@ -28,17 +28,19 @@
-define(STORE_MAP_THRESHOLD, 500).
-type fate_value() :: aeb_fate_data:fate_type().
-type fate_value_or_tombstone() :: fate_value() | ?FATE_MAP_TOMBSTONE.
-type id() :: integer().
-type used_ids() :: list(id()).
-type maps() :: #{ id() => aeb_fate_data:fate_map() | aeb_fate_data:fate_store_map() }.
%% -- Allocating store maps --------------------------------------------------
-spec allocate_store_maps(used_ids(), [fate_value()]) -> {[fate_value()], maps()}.
-spec allocate_store_maps(used_ids(), [fate_value_or_tombstone()]) -> {[fate_value_or_tombstone()], maps()}.
allocate_store_maps(Used, Vals) ->
{_Used, Vals1, Maps} = allocate_store_maps_l(Used, Vals, #{}),
{Vals1, Maps}.
allocate_store_maps(Used, ?FATE_MAP_TOMBSTONE = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_TRUE = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_FALSE = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_UNIT = Val, Maps) -> {Used, Val, Maps};
@@ -74,8 +76,8 @@ allocate_store_maps(Used, ?FATE_STORE_MAP(Cache, _Id) = Val, Maps) when Cache =:
{Used, Val, Maps};
allocate_store_maps(Used, ?FATE_STORE_MAP(Cache, Id), Maps) ->
{NewId, Used1} = next_id(Used),
{Used1, Cache1, Maps1} = allocate_store_maps_m(Used1, Cache, Maps),
{Used1, ?FATE_STORE_MAP(#{}, NewId), Maps1#{NewId => ?FATE_STORE_MAP(Cache1, Id)}}.
{Used2, Cache1, Maps1} = allocate_store_maps_m(Used1, Cache, Maps),
{Used2, ?FATE_STORE_MAP(#{}, NewId), Maps1#{NewId => ?FATE_STORE_MAP(Cache1, Id)}}.
allocate_store_maps_l(Used, [], Maps) -> {Used, [], Maps};
allocate_store_maps_l(Used, [H | T], Maps) ->
@@ -93,7 +95,8 @@ allocate_store_maps_m(Used, Val, Maps) ->
-type unfold_fun() :: fun((id()) -> aeb_fate_data:fate_map()).
-spec unfold_store_maps(unfold_fun(), fate_value()) -> fate_value().
-spec unfold_store_maps(unfold_fun(), fate_value_or_tombstone()) -> fate_value_or_tombstone().
unfold_store_maps(_Unfold, ?FATE_MAP_TOMBSTONE = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_TRUE = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_FALSE = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_UNIT = Val) -> Val;
@@ -119,7 +122,8 @@ unfold_store_maps(Unfold, Val) when ?IS_FATE_MAP(Val) ->
?MAKE_FATE_MAP(unfold_store_maps_m(Unfold, ?FATE_MAP_VALUE(Val)));
unfold_store_maps(Unfold, ?FATE_STORE_MAP(Cache, Id)) ->
StoreMap = Unfold(Id),
maps:fold(fun write_cache/3, unfold_store_maps(Unfold, StoreMap), Cache).
maps:fold(fun write_cache/3, unfold_store_maps(Unfold, StoreMap),
unfold_store_maps_m(Unfold, Cache)).
unfold_store_maps_l(Unfold, Vals) ->
[ unfold_store_maps(Unfold, Val) || Val <- Vals ].
@@ -134,7 +138,7 @@ write_cache(Key, Val, Map) ->
%% -- Reference counting -----------------------------------------------------
-type refcount() :: #{id() => pos_integer()}.
-type refcount() :: #{id() => integer()}.
-spec refcount_zero() -> refcount().
refcount_zero() -> #{}.
@@ -159,7 +163,7 @@ has_store_maps(Val) ->
-spec refcount(fate_value()) -> refcount().
refcount(Val) -> refcount(Val, #{}).
-spec refcount(fate_value(), refcount()) -> refcount().
-spec refcount(fate_value_or_tombstone(), refcount()) -> refcount().
refcount(?FATE_MAP_TOMBSTONE, Count) -> Count;
refcount(?FATE_TRUE, Count) -> Count;
refcount(?FATE_FALSE, Count) -> Count;
+14 -2
View File
@@ -1,7 +1,7 @@
;; CONTRACT all_instructions
;; Dont expect this contract to typecheck or run.
;; Just used to check assembler rountrip of all instructions.
;; Just used to check assembler roundtrip of all instructions.
FUNCTION foo () : {tuple, []}
RETURN
@@ -224,7 +224,13 @@ FUNCTION foo () : {tuple, []}
AENS_REVOKE
ECVERIFY
ECRECOVER_SECP256K1
VERIFY_SIG
VERIFY_SIG_SECP256K1
ECVERIFY_SECP256K1
SHA3 a
@@ -247,3 +253,9 @@ FUNCTION foo () : {tuple, []}
AUTH_TX_HASH
CONTRACT_TO_ADDRESS @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
IS_ORACLE @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
IS_CONTRACT @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
IS_PAYABLE @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
+5 -5
View File
@@ -28,11 +28,11 @@ FUNCTION tailcall(integer) -> integer
INCA
CALL_T "inc"
FUNCTION remote_call(integer) : integer
PUSH arg0
CALL_R remote.add_five 1 0
INCA
RETURN
;; FUNCTION remote_call(integer) : integer
;; PUSH arg0
;; CALL_R remote.add_five {tuple, [integer]} integer 0 ;; typereps don't parse
;; INCA
;; RETURN
;; Test the code from the shell
;; _build/default/rel/aessembler/bin/aessembler console