Compare commits

...

48 Commits

Author SHA1 Message Date
sennui cee6316e84 Add argument to claim tx to enable bidding 2019-08-14 11:19:50 +02:00
Ulf Norell af6224cb3b Merge pull request #66 from aeternity/PT-166788647-fate-efficient-maps
PT-166788647 FATE efficient maps
2019-08-13 15:51:13 +02:00
Ulf Norell 087ec31698 Fix bad type spec 2019-08-13 13:17:41 +02:00
Ulf Norell c49140fd5d Fix type and some code cleanup 2019-08-13 11:33:21 +02:00
Ulf Norell 414c45fbf7 Export refcount type 2019-08-13 11:25:54 +02:00
Ulf Norell f5a9be67d9 Remove CALL_TR and CALL_GTR 2019-08-13 09:20:29 +02:00
Ulf Norell 15095a74ab Add has_store_maps function 2019-08-12 14:40:34 +02:00
Ulf Norell 5aee70b8ff Add arity to CALL_R and CALL_GR
and deprecate CALL_TR and CALL_GTR
2019-08-12 14:38:10 +02:00
Ulf Norell 54dcf364e5 Store map reference counting 2019-08-12 10:59:30 +02:00
Ulf Norell c30bfd7b1c Unfolding store maps 2019-08-09 12:52:31 +02:00
Ulf Norell e184028261 Code for allocating store maps 2019-08-08 12:42:03 +02:00
Ulf Norell 986a7e6734 Add a store map fate value.
A store map is a (contract local) unique id pointing to a map saved in the contract store, plus a cache of updates.
2019-08-08 12:41:42 +02:00
Thomas Arts 2a9035d5ef Merge pull request #63 from aeternity/PT-167126818
Pt 167126818
2019-08-06 13:15:39 +02:00
Thomas Arts 8a50d20a67 Bring tests up-to-date 2019-08-06 13:04:25 +02:00
Thomas Arts 0b0cc38444 Provide API for querying implemented abi version 2019-08-06 12:21:51 +02:00
Tobias Lindahl 17c2a93e72 Merge pull request #62 from aeternity/PT-167164508-auto-generate-offchain-capabilities
Add information on offchain capabilities of operation
2019-07-10 11:10:49 +02:00
Tobias Lindahl e62cedb22c Add information on offchain capabilities of operation 2019-07-10 10:09:17 +02:00
Tobias Lindahl 76ae61b66c Merge pull request #61 from aeternity/PT-166927306-names-as-strings
Pt 166927306 names as strings
2019-06-28 13:48:42 +02:00
Tobias Lindahl edea526f38 Renum ordinals 2019-06-28 11:22:33 +02:00
Tobias Lindahl 487e087287 Use string instead of name hash for transfer and revoke 2019-06-26 15:33:24 +02:00
Tobias Lindahl c63ac888dd Pt 166233700 fate nameservice (#60)
* Introduce AENS instructions in FATE

* Remove name object and fixup some documentation
2019-06-26 13:19:44 +02:00
Hans Svensson 4d12b124f3 Merge pull request #59 from aeternity/PT-164629640-limit_fate_in_auth_context
Add in_auth field to aeb_fate_generate_ops
2019-06-26 12:29:17 +02:00
Hans Svensson 35ce283736 Add in_auth field to aeb_fate_generate_ops 2019-06-26 11:31:10 +02:00
Ulf Norell 677712b0b8 Merge pull request #58 from aeternity/PT-166233670-fate-events
Add FATE oracle check instructions (and others)
2019-06-25 19:56:56 +02:00
Ulf Norell 5171b800cc Add FATE oracle check instructions (and others) 2019-06-25 10:01:22 +02:00
Ulf Norell bf05e14661 Merge pull request #57 from aeternity/bytes-to-x
Add bytes_to_int and bytes_to_str instructions to FATE
2019-06-24 14:24:28 +02:00
Ulf Norell e3a00905de Add bytes_to_int and bytes_to_str instructions to FATE 2019-06-24 10:55:12 +02:00
Hans Svensson 59af12bf34 Merge pull request #56 from aeternity/PT-166788837-bytes
PT-166788837 bytes
2019-06-20 15:37:12 +02:00
Ulf Norell f7f0dfde51 Update and fix quickcheck fuzz test 2019-06-20 15:27:16 +02:00
Ulf Norell 33a1d5f4fb Perform sanity checks both in serialize and deserialize 2019-06-20 14:30:04 +02:00
Ulf Norell eeaf646a86 Allow variants as map keys 2019-06-20 14:30:04 +02:00
Ulf Norell 7fdc7a6cee Update asm tests 2019-06-20 14:30:04 +02:00
Ulf Norell f13ba67a2c Update and clean up quickcheck tests 2019-06-20 14:29:34 +02:00
Ulf Norell f421c1e361 Add bytes type and values
and remove hash and signature
2019-06-20 14:27:18 +02:00
Thomas Arts f91c8fabdd Merge pull request #51 from aeternity/PT-166696064-decode-calldata-fate
Pt 166696064 decode calldata fate
2019-06-20 13:05:44 +02:00
Tobias Lindahl 9dfc5f4f1d Merge pull request #55 from aeternity/PT-166786424-check-oracle-types
Add oracle types to some oracle instructions
2019-06-20 09:21:23 +02:00
Thomas Arts 1fda6912da Fix error 2019-06-19 17:41:47 +02:00
Thomas Arts cb83224c60 Add query to generate QuickCheck data 2019-06-19 17:41:47 +02:00
Thomas Arts 9840b22546 Add decoding function 2019-06-19 17:41:47 +02:00
Thomas Arts e3f843fd91 Do not return types, create_calldata does not need those 2019-06-19 17:41:47 +02:00
Thomas Arts 803ebc0854 Three new opcodes had been added 2019-06-19 17:41:47 +02:00
Hans Svensson 7e96e3baef Merge pull request #54 from aeternity/fix-bad-spec
Fix incorrect type spec
2019-06-19 13:26:54 +02:00
Hans Svensson 768e0d4fbb Fix incorrect type spec 2019-06-19 13:25:05 +02:00
Tobias Lindahl f92e23c955 Add oracle types to some oracle instructions 2019-06-19 12:11:22 +02:00
Tobias Lindahl e321882b98 Pt 166233685 fate oracles (#52)
* Introduce typereps

* Use typereps for registering oracles

* Add TTL to oracle register

* Introduce oracle query object and ORACLE_QUERY operation

* Stub the remaining oracle instructions

* Adapt oracle respond and extend

* Document oracle ops

* Add unit tests for all oracle instructions
2019-06-18 13:56:48 +02:00
Hans Svensson b45509962e Merge pull request #53 from aeternity/fate_abi_fun
Auth.tx_hash + new functions in aeb_fate_abi
2019-06-18 13:54:21 +02:00
Hans Svensson c1fb3a47c7 More functions in aeb_fate_abi 2019-06-18 13:51:06 +02:00
Hans Svensson a0c3a990ed Add function_name_from_function_hash to aeb_fate_abi 2019-06-18 11:59:54 +02:00
20 changed files with 850 additions and 398 deletions
+4 -6
View File
@@ -49,7 +49,7 @@ or start with stack followed by an integer
`stack1`
`a`
Immediate values can be of 11 types:
Immediate values can be of 10 types:
1. Integers as decimals: {Digits} or -{Digits}
`42`
@@ -70,8 +70,8 @@ Immediate values can be of 11 types:
2c. Oracle address: @ok_{base58char}+
`@ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2d. Name address: @nm_{base58char}+
`@nm_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2d. Oracle query: @oq_{base58char}+
`@oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2e. Channel address: @ch_{base58char}+
`@ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
@@ -104,11 +104,9 @@ Immediate values can be of 11 types:
9. Variants: (| [Arities] | Tag | ( Elements ) |)
`(| [1,3,5,2] | 3 | ( "foo", 12) |)`
10. Hashes: #{base64char}+
10. Bytes: #{base64char}+
`#AQIDCioLFQ==`
11. Signatures: $sg_{base58char}+
Where
+19 -13
View File
@@ -5,13 +5,13 @@
-define(FATE_LIST_T, list()).
-define(FATE_UNIT_T, {tuple, {}}).
-define(FATE_MAP_T, #{ fate_type() => fate_type() }).
-define(FATE_STORE_MAP_T, {store_map, #{ fate_type() => fate_type() | ?FATE_MAP_TOMBSTONE }, integer()}).
-define(FATE_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_HASH_T, {hash, binary()}).
-define(FATE_SIGNATURE_T, {signature, binary()}).
-define(FATE_BYTES_T(N), {bytes, binary()}).
-define(FATE_CONTRACT_T, {contract, <<_:256>>}).
-define(FATE_ORACLE_T, {oracle, <<_:256>>}).
-define(FATE_NAME_T, {name, <<_:256>>}).
-define(FATE_ORACLE_Q_T, {oracle_query, <<_:256>>}).
-define(FATE_CHANNEL_T, {channel, <<_:256>>}).
-define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}).
-define(FATE_VOID_T, void).
@@ -21,14 +21,18 @@
-define(IS_FATE_INTEGER(X), (is_integer(X))).
-define(IS_FATE_LIST(X), (is_list(X))).
-define(IS_FATE_STRING(X), (is_binary(X))).
-define(IS_FATE_STORE_MAP(X), (is_tuple(X) andalso tuple_size(X) == 3
andalso store_map == element(1, X)
andalso is_map(element(2, X))
andalso is_integer(element(3, X)))).
-define(IS_FATE_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_HASH(X), (is_tuple(X) andalso (hash == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_SIGNATURE(X), (is_tuple(X) andalso (signature == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_BYTES(X), (is_tuple(X) andalso (bytes == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_BYTES(N, X), (?IS_FATE_BYTES(X) andalso byte_size(element(2, X)) == (N))).
-define(IS_FATE_CONTRACT(X), (is_tuple(X) andalso (contract == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_ORACLE(X), (is_tuple(X) andalso (oracle == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_NAME(X), (is_tuple(X) andalso (name == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_ORACLE_Q(X), (is_tuple(X) andalso (oracle_query == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_CHANNEL(X), (is_tuple(X) andalso (channel == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_BITS(X), (is_tuple(X) andalso (bits == element(1, X) andalso is_integer(element(2, X))))).
-define(IS_FATE_VARIANT(X), (is_tuple(X)
@@ -39,18 +43,20 @@
andalso is_tuple(element(4, X))
))).
-define(IS_FATE_BOOLEAN(X), is_boolean(X)).
-define(IS_FATE_TYPEREP(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= typerep)).
-define(FATE_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}).
-define(FATE_ADDRESS(A), {address, A}).
-define(FATE_HASH(X), {hash, X}).
-define(FATE_SIGNATURE(S), {signature, S}).
-define(FATE_BYTES(X), {bytes, X}).
-define(FATE_CONTRACT(X), {contract, X}).
-define(FATE_ORACLE(X), {oracle, X}).
-define(FATE_NAME(X), {name, X}).
-define(FATE_ORACLE_Q(X), {oracle_query, X}).
-define(FATE_CHANNEL(X), {channel, X}).
-define(FATE_BITS(B), {bits, B}).
-define(FATE_TYPEREP(T), {typerep, T}).
-define(FATE_STORE_MAP(Cache, Id), {store_map, Cache, Id}).
-define(FATE_MAP_TOMBSTONE, '__DELETED__').
-define(FATE_INTEGER_VALUE(X), (X)).
-define(FATE_BOOLEAN_VALUE(X), (X)).
@@ -58,14 +64,14 @@
-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))).
-define(FATE_SIGNATURE_VALUE(X), (element(2, X))).
-define(FATE_BYTES_VALUE(X), (element(2, X))).
-define(FATE_CONTRACT_VALUE(X), (element(2, X))).
-define(FATE_ORACLE_VALUE(X), (element(2, X))).
-define(FATE_NAME_VALUE(X), (element(2, X))).
-define(FATE_CHANNEL_VALUE(X), (element(2, X))).
-define(FATE_BITS_VALUE(X), (element(2, X))).
-define(FATE_MAP_VALUE(X), (X)).
-define(FATE_STORE_MAP_CACHE(X), (element(2, X))).
-define(FATE_STORE_MAP_ID(X), (element(3, X))).
-define(FATE_MAP_SIZE(X), (map_size(X))).
-define(FATE_STRING_SIZE(X), (byte_size(X))).
-define(FATE_TRUE, true).
+63 -30
View File
@@ -27,36 +27,42 @@
prop_serializes() ->
in_parallel(
?FORALL(FateCode, fate_code(0),
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateCode]),
begin
Binary = aeb_fate_code:serialize(FateCode),
?WHENFAIL(eqc:format("serialized: ~p~n", [Binary]),
begin
Decoded = aeb_fate_code:deserialize(Binary),
measure(binary_size, size(Binary),
equals(Decoded, FateCode))
end)
end))).
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(aeb_fate_code:serialize(FateCode)))))
=/= true} || Failure <- [1,2,3,4, 5] ]).
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(InjectedBin, injection(Binary),
try Org = aeb_fate_code:deserialize(InjectedBin),
NewBin = aeb_fate_code:serialize(Org),
NewOrg = aeb_fate_code:deserialize(NewBin),
?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]),
equals(NewBin, InjectedBin))
catch _:_ ->
true
end))).
?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),
@@ -71,7 +77,7 @@ prop_opcodes() ->
valid_opcodes() ->
lists:seq(0, 16#72) ++ lists:seq(16#fa, 16#fd).
lists:seq(0, 16#7c) ++ lists:seq(16#fa, 16#fd).
fate_code(Failure) ->
@@ -80,16 +86,23 @@ fate_code(Failure) ->
{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)})),
map(small_fate_data_key(5), small_fate_data(4)),
map(small_fate_data_key(5), small_fate_data(4))},
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, list(bb_code(Failure)),
{10, ?LET(BBs, short_list(6, bb_code(Failure)),
maps:from_list(
lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]).
@@ -97,9 +110,9 @@ 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(list(elements(NonEndBB))), bblock(Failure, Ops))},
{if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {list(elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))},
{10, ?LET({Ops, Op}, {list(elements(NonEndBB)), elements(EndBB)},
[{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) ->
@@ -118,14 +131,34 @@ bblock(Failure, Ops) ->
end
end || Op <- Ops ].
injection(Binary) ->
?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0,255)},
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())).
+54 -68
View File
@@ -32,21 +32,15 @@ prop_format_scan() ->
end)).
prop_serializes() ->
?FORALL(FateDatas, non_empty(?SIZED(Size, resize(Size div 2, list(fate_data())))),
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateDatas]),
?FORALL({Data, Garbage}, {fate_data(), binary()},
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]),
begin
{T1, Binary} =
timer:tc( fun() ->
<< begin B = aeb_fate_encoding:serialize(Data),
<<B/binary>> end || Data <- FateDatas >>
end),
{T2, {FateData, _}} =
timer:tc(fun() -> aeb_fate_encoding:deserialize_one(Binary) end),
Binary = <<(aeb_fate_encoding:serialize(Data))/binary, Garbage/binary>>,
{FateData, Rest} = aeb_fate_encoding:deserialize_one(Binary),
measure(binary_size, size(Binary),
measure(encode, T1,
measure(decode, T2,
conjunction([{equal, equals(hd(FateDatas), FateData)},
{size, size(Binary) < 500000}]))))
conjunction([{equal, equals(Data, FateData)},
{rest, equals(Garbage, Rest)},
{size, size(Binary) < 500000}]))
end)).
prop_fuzz() ->
@@ -98,72 +92,64 @@ fate_data() ->
?SIZED(Size, ?LET(Data, fate_data(Size, [map, variant]), eqc_symbolic:eval(Data))).
fate_data_key() ->
?SIZED(Size, ?LET(Data, fate_data(Size div 4, []), eqc_symbolic:eval(Data))).
?SIZED(Size, ?LET(Data, fate_data(Size div 4, [variant]), eqc_symbolic:eval(Data))).
fate_data(0, _Options) ->
?LAZY(
oneof([fate_integer(),
fate_boolean(),
fate_nil(),
fate_unit(),
fate_string(),
fate_address(),
fate_hash(),
fate_signature(),
fate_contract(),
fate_oracle(),
fate_name(),
fate_bits(),
fate_channel()]));
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()])}]));
fate_data(Size, Options) ->
oneof([?LAZY(fate_data(Size - 1, Options)),
?LAZY(fate_list( fate_data(Size div 5, Options) )),
?LAZY(fate_tuple( list(fate_data(Size div 5, Options)) ))] ++
[?LAZY(fate_variant( list(fate_data(Size div 5, Options))))
|| lists:member(variant, Options)
] ++
[
?LAZY(fate_map( fate_data(Size div 8, Options -- [map, variant]),
fate_data(Size div 5, Options)))
|| lists:member(map, 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() -> {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,
[frequency([{10, non_quote_string()}, {2, list(non_quote_string())},
{1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}])]}.
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)]}.
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(ListGen) ->
{call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}.
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(ListGen) ->
?LET({L1, L2, TupleAsList}, {list(choose(0, 255)), list(choose(0,255)), ListGen},
{call, aeb_fate_data, make_variant,
[L1 ++ [length(TupleAsList)] ++ L2, length(L1), list_to_tuple(TupleAsList)]}).
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(Gen) ->
{call, aeb_fate_data, make_list, [frequency([{20, list(Gen)}, {1, ?LET(N, choose(64-3, 64+3), vector(N, Gen))}])]}.
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(KeyGen, ValGen) ->
{call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}.
non_zero_binary(N) ->
Bits = N*8,
?SUCHTHAT(Bin, binary(N), begin <<V:Bits>> = Bin, V =/= 0 end).
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])),
return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
non_quote_string() ->
?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []).
+17 -10
View File
@@ -11,9 +11,12 @@
-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(FateType,
collect(kind(FateType),
begin
Serialized = aeb_fate_encoding:serialize_type(FateType),
BinSerialized = list_to_binary(Serialized),
@@ -32,18 +35,22 @@ fate_type(0) ->
oneof([integer,
boolean,
address,
hash,
signature,
{bytes, nat()},
contract,
oracle,
name,
channel,
bits,
string]);
fate_type(Size) ->
oneof([?LAZY(fate_type(Size div 2)),
{list, ?LAZY(fate_type(Size div 2))},
{tuple, list(?LAZY(fate_type(Size div 2)))},
{variant, list(?LAZY(fate_type(Size div 2)))},
?LETSHRINK([T1, T2], [?LAZY(fate_type(Size div 2)), ?LAZY(fate_type(Size div 2))],
{map, T1, T2})]).
?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)))).
+7 -1
View File
@@ -19,6 +19,7 @@
, typereps_from_type_hash/2
, function_name_from_type_hash/2
, get_function_hash_from_calldata/1
, abi_version/0
]).
-type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8.
@@ -35,6 +36,11 @@
%%% API
%%%===================================================================
%% Shall match ?ABI_AEVM_SOPHIA_1
-spec abi_version() -> integer().
abi_version() ->
1.
%%%===================================================================
%%% Handle calldata
@@ -43,7 +49,7 @@ create_calldata(FunName, Args, ArgTypes0, RetType) ->
<<TypeHashInt:?HASH_SIZE/unit:8>> =
function_type_hash(list_to_binary(FunName), ArgTypes, RetType),
Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}),
{ok, Data, {tuple, [word, ArgTypes]}, RetType}.
{ok, Data}.
-spec check_calldata(binary(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', atom()}.
+55 -1
View File
@@ -9,15 +9,69 @@
%%%-------------------------------------------------------------------
-module(aeb_fate_abi).
-export([ create_calldata/2 ]).
-export([ create_calldata/2
, decode_calldata/2
, get_function_hash_from_calldata/1
, get_function_name_from_function_hash/2
, get_function_type_from_function_hash/2
, abi_version/0 ]).
-include("../include/aeb_fate_data.hrl").
%%%===================================================================
%%% API
%%%===================================================================
%% Shall match ?ABI_FATE_SOPHIA_1
-spec abi_version() -> integer().
abi_version() ->
3.
-spec create_calldata(list(), [term()]) -> {ok, binary()}.
create_calldata(FunName, Args) ->
FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)),
{ok, aeb_fate_encoding:serialize(
aeb_fate_data:make_tuple({FunctionId,
aeb_fate_data:make_tuple(list_to_tuple(Args))}))}.
-spec decode_calldata(list(), binary()) -> {ok, term()} | {error, term()}.
decode_calldata(FunName, Calldata) ->
FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)),
case ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(Calldata)) of
[FunctionId, FateArgs] -> {ok, ?FATE_TUPLE_ELEMENTS(FateArgs)};
_ -> {error, decode_error}
end.
-spec get_function_name_from_function_hash(binary(), aeb_fate_code:fcode()) ->
{ok, term()} | {error, term()}.
get_function_name_from_function_hash(<<SymbolHash:4/binary, _:28/binary>>, FateCode) ->
get_function_name_from_function_hash(SymbolHash, FateCode);
get_function_name_from_function_hash(SymbolHash = <<_:4/binary>>, FateCode) ->
Symbols = aeb_fate_code:symbols(FateCode),
case maps:get(SymbolHash, Symbols, undefined) of
undefined -> {error, no_function_matching_function_hash};
Function -> {ok, Function}
end.
-spec get_function_hash_from_calldata(binary()) ->
{ok, binary()} | {error, term()}.
get_function_hash_from_calldata(CallData) ->
try ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(CallData)) of
[FunHash, _Args] -> {ok, FunHash};
_ -> {error, bad_calldata}
catch _:_ ->
{error, bad_calldata}
end.
-spec get_function_type_from_function_hash(binary(), aeb_fate_code:fcode()) ->
{ok, term(), term()} | {error, term()}.
get_function_type_from_function_hash(<<SymbolHash:4/binary, _:28/binary>>, FateCode) ->
get_function_type_from_function_hash(SymbolHash, FateCode);
get_function_type_from_function_hash(SymbolHash, FateCode) ->
Functions = aeb_fate_code:functions(FateCode),
case maps:get(SymbolHash, Functions, undefined) of
undefined ->
{error, no_function_matching_function_hash};
{{ArgTypes, RetType}, _Code} ->
{ok, ArgTypes, RetType}
end.
+20 -16
View File
@@ -33,7 +33,7 @@
%%% References to the top of the stack is the letter a (for accumulator)
%%% a
%%%
%%% Immediate values can be of 11 types:
%%% Immediate values can be of 10 types:
%%% 1a. Integers as decimals: {Digits} or -{Digits}
%%% 42
%%% -2374683271468723648732648736498712634876147
@@ -42,7 +42,7 @@
%%% 2a. account addresses, a base58c encoded string prefixed with @ak_
%%% 2b. contract address: @ct_{base58char}+
%%% 2c. oracle address: @ok_{base58char}+
%%% 2d. name address: @nm_{base58char}+
%%% 2d. oracle query id: @oq_{base58char}+
%%% 2e. channel address: @ch_{base58char}+
%%% 3. Boolean true or false
%%% true
@@ -65,9 +65,8 @@
%%% (1, "foo")
%%% 9. Variants: (| [Arities] | Tag | ( Elements ) |)
%%% (| [0,1,2] | 2 | ( "foo", 12) |)
%%% 10. Hashes: #{base64char}+
%%% 10. Bytes: #{base64char}+
%%% #AQIDCioLFQ==
%%% 11. Signatures: $sg_{base58char}+
%%%
%%% Where Digits: [0123456789]
%%% Hexdigits: [0123456789abcdef]
@@ -267,25 +266,20 @@ to_bytecode([{object,_line, {oracle, Value}}|Rest],
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_oracle(Value)}|Code],
Opts);
to_bytecode([{object,_line, {name, Value}}|Rest],
to_bytecode([{object,_line, {oracle_query, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_name(Value)}|Code],
[{immediate, aeb_fate_data:make_oracle_query(Value)}|Code],
Opts);
to_bytecode([{object,_line, {channel, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_contract(Value)}|Code],
Opts);
to_bytecode([{hash,_line, Value}|Rest],
to_bytecode([{bytes,_line, Value}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_hash(Value)}|Code],
Opts);
to_bytecode([{signature,_line, {signature, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_signature(Value)}|Code],
[{immediate, aeb_fate_data:make_bytes(Value)}|Code],
Opts);
to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) ->
{Env2, Id} = insert_symbol(list_to_binary(ID), Env),
@@ -304,6 +298,10 @@ to_bytecode([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) ->
{Arities, Tag, Values, Rest} = parse_variant(Tokens),
Variant = aeb_fate_data:make_variant(Arities, Tag, Values),
to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts);
to_bytecode([{typerep,_line}|Rest], Address, Env, Code, Opts) ->
{Type, Rest1} = to_type(Rest),
TypeRep = aeb_fate_data:make_typerep(Type),
to_bytecode(Rest1, Address, Env, [{immediate, TypeRep}|Code], Opts);
to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts);
@@ -396,14 +394,16 @@ parse_value([{object,_line, {contract, Address}} | Rest]) ->
{aeb_fate_data:make_contract(Address), Rest};
parse_value([{object,_line, {oracle, Address}} | Rest]) ->
{aeb_fate_data:make_oracle(Address), Rest};
parse_value([{object,_line, {name, Address}} | Rest]) ->
{aeb_fate_data:make_name(Address), Rest};
parse_value([{object,_line, {oracle_query, Address}} | Rest]) ->
{aeb_fate_data:make_oracle_query(Address), Rest};
parse_value([{object,_line, {channel, Address}} | Rest]) ->
{aeb_fate_data:make_channel(Address), Rest};
parse_value([{hash,_line, Hash} | Rest]) ->
{aeb_fate_data:make_hash(Hash), Rest};
parse_value([{signature,_line, Hash} | Rest]) ->
{aeb_fate_data:make_signature(Hash), Rest}.
{aeb_fate_data:make_signature(Hash), Rest};
parse_value([{typerep,_line} | Rest]) ->
to_type(Rest).
to_fun_def([{id, _, Name}, {'(', _} | Rest]) ->
{ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest),
@@ -429,6 +429,7 @@ to_type([{id, _, "string"} | Rest]) -> {string, Rest};
to_type([{id, _, "address"} | Rest]) -> {address, Rest};
to_type([{id, _, "contract"} | Rest]) -> {contract, Rest};
to_type([{id, _, "oracle"} | Rest]) -> {oracle, Rest};
to_type([{id, _, "oracle_query"} | Rest]) -> {oracle_query, Rest};
to_type([{id, _, "name"} | Rest]) -> {name, Rest};
to_type([{id, _, "channel"} | Rest]) -> {channel, Rest};
to_type([{id, _, "hash"} | Rest]) -> {hash, Rest};
@@ -447,6 +448,9 @@ to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) ->
{KeyType, [{',', _}| Rest2]} = to_type(Rest),
{ValueType, [{'}', _}| Rest3]} = to_type(Rest2),
{{map, KeyType, ValueType}, Rest3};
to_type([{'{', _}, {id, _, "bytes"}, {',', _}, {int, _, Size}, {'}', _} | Rest]) ->
%% TODO: Error handling
{{bytes, Size}, Rest};
to_type([{'{', _}
, {id, _, "variant"}
, {',', _}
+5 -7
View File
@@ -17,8 +17,7 @@ BASE64 = [A-Za-z0-9+/=]
INT = {DIGIT}+
HEX = 0x{HEXDIGIT}+
OBJECT = @[a-z][a-z]_{BASE58}+
HASH = #{BASE64}+
SIG = \$sg_{BASE58}+
BYTES = #{BASE64}+
WS = [\000-\s]
ID = {LOWER}[a-zA-Z0-9_]*
STRING = "[^"]*"
@@ -36,10 +35,8 @@ false : {token, {boolean, TokenLine, false}}.
FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
{HASH} :
{token, {hash, TokenLine, parse_hash(TokenChars)}}.
{SIG} :
{token, {signature, TokenLine, parse_object(TokenChars)}}.
{BYTES} :
{token, {bytes, TokenLine, parse_hash(TokenChars)}}.
{OBJECT} :
{token, {object, TokenLine, parse_object(TokenChars)}}.
{ID} :
@@ -75,6 +72,7 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
\{ : {token, {'{', TokenLine}}.
\} : {token, {'}', TokenLine}}.
\| : {token, {'|', TokenLine}}.
\' : {token, {typerep, TokenLine}}.
;;.* :
{token, {comment, TokenLine, drop_prefix($;, TokenChars)}}.
@@ -118,7 +116,7 @@ parse_object([_|Chars]) ->
{account_pubkey, Bin} -> {address, Bin};
{contract_pubkey, Bin} -> {contract, Bin};
{oracle_pubkey, Bin} -> {oracle, Bin};
{name, Bin} -> {name, Bin};
{oracle_query_id, Bin} -> {oracle_query, Bin};
{channel, Bin} -> {channel, Bin};
{signature, Bin} -> {signature, Bin}
end.
+64 -35
View File
@@ -27,7 +27,7 @@
-include("../include/aeb_fate_data.hrl").
-ifdef(EQC).
-export([update_annotations/2
-export([ update_annotations/2
, update_functions/2
, update_symbols/2]).
-endif.
@@ -39,6 +39,9 @@
-define(HASH_BYTES, 32).
-type fcode() :: #fcode{}.
-export_type([fcode/0]).
%%%===================================================================
%%% API
%%%===================================================================
@@ -97,6 +100,7 @@ serialize(#fcode{} = F) ->
serialize(F, []).
serialize(#fcode{} = F, Options) ->
sanity_check(F),
serialize(F, serialize_functions(F), Options).
serialize(#fcode{} = F, Functions, Options) ->
@@ -124,10 +128,8 @@ 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) when byte_size(Id) == 4 ->
[[?FUNCTION, Id, serialize_signature(Sig), serialize_bbs(C)] | Acc];
({Id, _}, _) ->
error({illegal_function_id, Id})
lists:foldr(fun({Id, {Sig, C}}, Acc) ->
[[?FUNCTION, Id, serialize_signature(Sig), serialize_bbs(C)] | Acc]
end, [], lists:sort(maps:to_list(Functions)))).
serialize_signature({Args, RetType}) ->
@@ -145,46 +147,70 @@ serialize_bbs(#{} = BBs) ->
serialize_bbs(BBs, N, Acc) ->
case maps:get(N, BBs, none) of
none ->
%% Assert that the BBs were contiguous
Size = maps:size(BBs),
case Size =:= N of
true ->
lists:reverse(Acc);
false ->
error({not_contiguous_labels, lists:sort(maps:keys(BBs))})
end;
[] ->
error({empty_code_block, N});
BB ->
serialize_bbs(BBs, N + 1, [serialize_bb(BB, [])|Acc])
none -> lists:reverse(Acc);
BB -> serialize_bbs(BBs, N + 1, [serialize_bb(BB, [])|Acc])
end.
serialize_bb([Op], Acc) ->
lists:reverse([serialize_op(true, Op)|Acc]);
lists:reverse([serialize_op(Op)|Acc]);
serialize_bb([Op|Rest], Acc) ->
serialize_bb(Rest, [serialize_op(false, Op)|Acc]).
%% serialize_bb([], Acc) ->
%% lists:reverse(Acc).
serialize_bb(Rest, [serialize_op(Op)|Acc]).
serialize_op(Kind, Op) ->
serialize_op(Op) ->
[Mnemonic|Args] =
case is_tuple(Op) of
true -> tuple_to_list(Op);
false -> [Op]
end,
safe_serialize(Kind, aeb_fate_opcodes:m_to_op(Mnemonic), Args).
[aeb_fate_opcodes:m_to_op(Mnemonic) | serialize_code(Args)].
safe_serialize(Last, Op, Args) ->
sanity_check(#fcode{ functions = Funs }) ->
_ = [ case Def of
{_, BBs} when byte_size(Id) == 4 -> sanity_check_bbs(BBs);
_ -> error({illegal_function_id, Id})
end || {Id, Def} <- maps:to_list(Funs) ],
ok.
sanity_check_bbs(#{} = BBs) ->
sanity_check_bbs(BBs, 0).
sanity_check_bbs(BBs, N) ->
case maps:get(N, BBs, none) of
none ->
%% Assert that the BBs were contiguous
case maps:size(BBs) =:= N of
true -> ok;
false -> error({not_contiguous_labels, lists:sort(maps:keys(BBs))})
end;
[] ->
error({empty_code_block, N});
BB ->
sanity_check_bb(BB),
sanity_check_bbs(BBs, N + 1)
end.
sanity_check_bb([Op]) ->
sanity_check_op(true, Op);
sanity_check_bb([Op|Rest]) ->
sanity_check_op(false, Op),
sanity_check_bb(Rest).
sanity_check_op(IsLast, Op) ->
[Mnemonic|Args] =
case is_tuple(Op) of
true -> tuple_to_list(Op);
false -> [Op]
end,
safe_sanity_check(IsLast, aeb_fate_opcodes:m_to_op(Mnemonic), Args).
safe_sanity_check(IsLast, Op, Args) ->
case length(Args) == aeb_fate_opcodes:args(Op) of
true ->
case Last == aeb_fate_opcodes:end_bb(Op) of
true -> [Op|serialize_code(Args)];
false ->
error({wrong_opcode_in_bb, Op})
case IsLast == aeb_fate_opcodes:end_bb(Op) of
true -> ok;
false -> error({wrong_opcode_in_bb, Op})
end;
false ->
error({wrong_nr_args_opcode, Op})
false -> error({wrong_nr_args_opcode, Op})
end.
@@ -263,10 +289,13 @@ deserialize(Bytes) ->
, functions => #{}
, code => #{}
},
#fcode{ functions = deserialize_functions(ByteCode, Env)
, annotations = deserialize_annotations(Annotations)
, symbols = deserialize_symbols(SymbolTable)
}.
Fcode =
#fcode{ functions = deserialize_functions(ByteCode, Env)
, annotations = deserialize_annotations(Annotations)
, symbols = deserialize_symbols(SymbolTable)
},
sanity_check(Fcode),
Fcode.
deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
+62 -42
View File
@@ -10,14 +10,14 @@
-type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T.
-type fate_store_map() :: ?FATE_STORE_MAP_T.
-type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T.
-type fate_hash() :: ?FATE_HASH_T.
-type fate_hash() :: ?FATE_BYTES_T(32).
-type fate_signature() :: ?FATE_BYTES_T(64).
-type fate_contract() :: ?FATE_CONTRACT_T.
-type fate_oracle() :: ?FATE_ORACLE_T.
-type fate_name() :: ?FATE_NAME_T.
-type fate_channel() :: ?FATE_CHANNEL_T.
-type fate_signature() :: ?FATE_SIGNATURE_T.
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_bits() :: ?FATE_BITS_T.
@@ -32,7 +32,6 @@
| signature
| contract
| oracle
| name
| channel
| bits
| string
@@ -52,7 +51,6 @@
| fate_signature()
| fate_contract()
| fate_oracle()
| fate_name()
| fate_channel()
| fate_variant()
| fate_map()
@@ -71,10 +69,10 @@
, fate_signature/0
, fate_contract/0
, fate_oracle/0
, fate_name/0
, fate_channel/0
, fate_variant/0
, fate_map/0
, fate_store_map/0
, fate_bits/0
, fate_type_type/0
]).
@@ -86,15 +84,19 @@
, make_tuple/1
, make_string/1
, make_map/1
, make_store_map/1
, make_store_map/2
, make_address/1
, make_bytes/1
, make_hash/1
, make_signature/1
, make_contract/1
, make_oracle/1
, make_name/1
, make_oracle_query/1
, make_channel/1
, make_bits/1
, make_unit/0
, make_typerep/1
]).
-export([
elt/2
@@ -110,18 +112,22 @@ make_list(L) -> ?MAKE_FATE_LIST(L).
make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M).
make_store_map(Id) -> make_store_map(#{}, Id).
make_store_map(Cache, Id) -> ?FATE_STORE_MAP(Cache, Id).
make_address(X) -> ?FATE_ADDRESS(X).
make_hash(X) -> ?FATE_HASH(X).
make_signature(X) -> ?FATE_SIGNATURE(X).
make_bytes(X) -> ?FATE_BYTES(X).
make_hash(X) -> make_bytes(X).
make_signature(X) -> make_bytes(X).
make_contract(X) -> ?FATE_CONTRACT(X).
make_oracle(X) -> ?FATE_ORACLE(X).
make_name(X) -> ?FATE_NAME(X).
make_oracle_query(X) -> ?FATE_ORACLE_Q(X).
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(iolist_to_binary(S));
make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_typerep(T) -> ?FATE_TYPEREP(T).
%% Tag points to the selected variant (zero based)
%% The arity of this variant is read from the list of provided arities
@@ -164,19 +170,19 @@ format(?FATE_VARIANT(Arities, Tag, T)) ->
" |)"];
format(M) when ?IS_FATE_MAP(M) ->
["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
format(?FATE_HASH(X)) -> ["#", base64:encode(X)];
format(?FATE_BYTES(X)) -> ["#", base64:encode(X)];
format(?FATE_ADDRESS(X)) ->
["@", aeser_api_encoder:encode(account_pubkey, X)];
format(?FATE_SIGNATURE(X)) ->
["$", aeser_api_encoder:encode(signature, X)];
format(?FATE_CONTRACT(X)) ->
["@", aeser_api_encoder:encode(contract_pubkey, X)];
format(?FATE_ORACLE(X)) ->
["@", aeser_api_encoder:encode(oracle_pubkey, X)];
format(?FATE_NAME(X)) ->
["@", aeser_api_encoder:encode(name, X)];
format(?FATE_ORACLE_Q(X)) ->
["@", aeser_api_encoder:encode(oracle_query_id, X)];
format(?FATE_CHANNEL(X)) ->
["@", aeser_api_encoder:encode(channel, X)];
format(?FATE_TYPEREP(X)) ->
["'", io_lib:format("~p", [X])];
format(V) -> exit({not_a_fate_type, V}).
format_bits(0, Acc) -> Acc;
@@ -200,24 +206,38 @@ format_kvs(List) ->
%% Total order of FATE terms.
%% Integers < Booleans < Address < Channel < Contract < Name < Oracle
%% Integers < Booleans < Address < Channel < Contract < Oracle
%% < Hash < Signature < Bits < String < Tuple < Map < List < Variant
-define(ORD_INTEGER , 0).
-define(ORD_BOOLEAN , 1).
-define(ORD_ADDRESS , 2).
-define(ORD_CHANNEL , 3).
-define(ORD_CONTRACT , 4).
-define(ORD_ORACLE , 5).
-define(ORD_BYTES , 6).
-define(ORD_BITS , 7).
-define(ORD_STRING , 8).
-define(ORD_TUPLE , 9).
-define(ORD_MAP , 10).
-define(ORD_LIST , 11).
-define(ORD_VARIANT , 12).
-define(ORD_ORACLE_Q , 13).
-spec ordinal(fate_type()) -> integer().
ordinal(T) when ?IS_FATE_INTEGER(T) -> 0;
ordinal(T) when ?IS_FATE_BOOLEAN(T) -> 1;
ordinal(T) when ?IS_FATE_ADDRESS(T) -> 2;
ordinal(T) when ?IS_FATE_CHANNEL(T) -> 3;
ordinal(T) when ?IS_FATE_CONTRACT(T) -> 4;
ordinal(T) when ?IS_FATE_NAME(T) -> 5;
ordinal(T) when ?IS_FATE_ORACLE(T) -> 6;
ordinal(T) when ?IS_FATE_HASH(T) -> 7;
ordinal(T) when ?IS_FATE_SIGNATURE(T) -> 8;
ordinal(T) when ?IS_FATE_BITS(T) -> 9;
ordinal(T) when ?IS_FATE_STRING(T) -> 10;
ordinal(T) when ?IS_FATE_TUPLE(T) -> 11;
ordinal(T) when ?IS_FATE_MAP(T) -> 12;
ordinal(T) when ?IS_FATE_LIST(T) -> 13;
ordinal(T) when ?IS_FATE_VARIANT(T) -> 14.
ordinal(T) when ?IS_FATE_INTEGER(T) -> ?ORD_INTEGER;
ordinal(T) when ?IS_FATE_BOOLEAN(T) -> ?ORD_BOOLEAN;
ordinal(T) when ?IS_FATE_ADDRESS(T) -> ?ORD_ADDRESS;
ordinal(T) when ?IS_FATE_CHANNEL(T) -> ?ORD_CHANNEL;
ordinal(T) when ?IS_FATE_CONTRACT(T) -> ?ORD_CONTRACT;
ordinal(T) when ?IS_FATE_ORACLE(T) -> ?ORD_ORACLE;
ordinal(T) when ?IS_FATE_BYTES(T) -> ?ORD_BYTES;
ordinal(T) when ?IS_FATE_BITS(T) -> ?ORD_BITS;
ordinal(T) when ?IS_FATE_STRING(T) -> ?ORD_STRING;
ordinal(T) when ?IS_FATE_TUPLE(T) -> ?ORD_TUPLE;
ordinal(T) when ?IS_FATE_MAP(T) -> ?ORD_MAP;
ordinal(T) when ?IS_FATE_LIST(T) -> ?ORD_LIST;
ordinal(T) when ?IS_FATE_VARIANT(T) -> ?ORD_VARIANT;
ordinal(T) when ?IS_FATE_ORACLE_Q(T) -> ?ORD_ORACLE_Q.
-spec lt(fate_type(), fate_type()) -> boolean().
@@ -229,12 +249,12 @@ lt(A, B) ->
end.
%% Integers are ordered as usual.
lt(0, A, B) when ?IS_FATE_INTEGER(A), ?IS_FATE_INTEGER(B) ->
lt(?ORD_INTEGER, A, B) when ?IS_FATE_INTEGER(A), ?IS_FATE_INTEGER(B) ->
?FATE_INTEGER_VALUE(A) < ?FATE_INTEGER_VALUE(B);
%% false is smaller than true (true also for erlang booleans).
lt(1, A, B) when ?IS_FATE_BOOLEAN(A), ?IS_FATE_BOOLEAN(B) ->
lt(?ORD_BOOLEAN, A, B) when ?IS_FATE_BOOLEAN(A), ?IS_FATE_BOOLEAN(B) ->
?FATE_BOOLEAN_VALUE(A) < ?FATE_BOOLEAN_VALUE(B);
lt(9, A, B) when ?IS_FATE_BITS(A), ?IS_FATE_BITS(B) ->
lt(?ORD_BITS, A, B) when ?IS_FATE_BITS(A), ?IS_FATE_BITS(B) ->
BitsA = ?FATE_BITS_VALUE(A),
BitsB = ?FATE_BITS_VALUE(B),
if BitsA < 0 ->
@@ -245,7 +265,7 @@ lt(9, A, B) when ?IS_FATE_BITS(A), ?IS_FATE_BITS(B) ->
true;
true -> BitsA < BitsB
end;
lt(10,?FATE_STRING(A), ?FATE_STRING(B)) ->
lt(?ORD_STRING,?FATE_STRING(A), ?FATE_STRING(B)) ->
SizeA = size(A),
SizeB = size(B),
case SizeA - SizeB of
@@ -253,23 +273,23 @@ lt(10,?FATE_STRING(A), ?FATE_STRING(B)) ->
N -> N < 0
end;
lt(11,?FATE_TUPLE(A), ?FATE_TUPLE(B)) ->
lt(?ORD_TUPLE,?FATE_TUPLE(A), ?FATE_TUPLE(B)) ->
SizeA = size(A),
SizeB = size(B),
case SizeA - SizeB of
0 -> tuple_elements_lt(0, A, B, SizeA);
N -> N < 0
end;
lt(12, ?FATE_MAP_VALUE(A), ?FATE_MAP_VALUE(B)) ->
lt(?ORD_MAP, ?FATE_MAP_VALUE(A), ?FATE_MAP_VALUE(B)) ->
SizeA = maps:size(A),
SizeB = maps:size(B),
case SizeA - SizeB of
0 -> maps_lt(A, B);
N -> N < 0
end;
lt(13, ?FATE_LIST_VALUE(_), ?FATE_LIST_VALUE([])) -> false;
lt(13, ?FATE_LIST_VALUE([]), ?FATE_LIST_VALUE(_)) -> true;
lt(13, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) ->
lt(?ORD_LIST, ?FATE_LIST_VALUE(_), ?FATE_LIST_VALUE([])) -> false;
lt(?ORD_LIST, ?FATE_LIST_VALUE([]), ?FATE_LIST_VALUE(_)) -> true;
lt(?ORD_LIST, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) ->
O1 = ordinal(A),
O2 = ordinal(B),
if O1 == O2 ->
@@ -278,8 +298,8 @@ lt(13, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) ->
end;
true -> O1 < O2
end;
lt(14, ?FATE_VARIANT(AritiesA, TagA, TA),
?FATE_VARIANT(AritiesB, TagB, TB)) ->
lt(?ORD_VARIANT, ?FATE_VARIANT(AritiesA, TagA, TA),
?FATE_VARIANT(AritiesB, TagB, TB)) ->
if length(AritiesA) < length(AritiesB) -> true;
length(AritiesA) > length(AritiesB) -> false;
true ->
+50 -26
View File
@@ -73,7 +73,7 @@
-define(TYPE_MAP , 2#01100111). %% 0110 0111 | Type | Type
-define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef
-define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type]
%% 1001 0111
-define(TYPE_BYTES , 2#10010111). %% 1001 0111 - Bytes typedef
%% 1010 0111
%% 1011 0111
%% 1100 0111
@@ -93,6 +93,7 @@
%% %% 1000 1111 - FREE (Possibly for bytecode in the future.)
-define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array
-define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values]
-define(MAP_ID , 2#10111111). %% 1011 1111 | RLP encoded integer (store map id)
-define(NEG_BITS , 2#11001111). %% 1100 1111 | RLP encoded integer (infinite 1:s bitfield)
-define(EMPTY_MAP , 2#11011111). %% 1101 1111
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 | RLP encoded (integer - 64)
@@ -108,12 +109,23 @@
%% Object types
-define(OTYPE_ADDRESS, 0).
-define(OTYPE_HASH, 1).
-define(OTYPE_SIGNATURE, 2).
-define(OTYPE_CONTRACT, 3).
-define(OTYPE_ORACLE, 4).
-define(OTYPE_NAME, 5).
-define(OTYPE_CHANNEL, 6).
-define(OTYPE_BYTES, 1).
-define(OTYPE_CONTRACT, 2).
-define(OTYPE_ORACLE, 3).
-define(OTYPE_ORACLE_Q, 4).
-define(OTYPE_CHANNEL, 5).
-define(IS_TYPE_TAG(X), (X =:= ?TYPE_INTEGER orelse
X =:= ?TYPE_BOOLEAN orelse
X =:= ?TYPE_ANY orelse
X =:= ?TYPE_VAR orelse
X =:= ?TYPE_LIST orelse
X =:= ?TYPE_TUPLE orelse
X =:= ?TYPE_OBJECT orelse
X =:= ?TYPE_BITS orelse
X =:= ?TYPE_MAP orelse
X =:= ?TYPE_STRING orelse
X =:= ?TYPE_VARIANT)).
%% --------------------------------------------------
%% Serialize
@@ -140,18 +152,16 @@ serialize(String) when ?IS_FATE_STRING(String),
<<?LONG_STRING,
(serialize_integer(?FATE_STRING_SIZE(String) - ?SHORT_STRING_SIZE))/binary
, Bytes/binary>>;
serialize(?FATE_BYTES(Bytes)) when is_binary(Bytes) ->
<<?OBJECT, ?OTYPE_BYTES, (serialize(?FATE_STRING(Bytes)))/binary>>;
serialize(?FATE_ADDRESS(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ADDRESS, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_HASH(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_HASH, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_SIGNATURE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_SIGNATURE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CONTRACT(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CONTRACT, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_NAME(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_NAME, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE_Q(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE_Q, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CHANNEL(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CHANNEL, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
@@ -184,6 +194,9 @@ serialize(Map) when ?IS_FATE_MAP(Map) ->
<<?MAP,
(rlp_encode_int(Size))/binary,
(Elements)/binary>>;
serialize(?FATE_STORE_MAP(Cache, Id)) when Cache =:= #{} ->
%% We should never get to serialization without having flushed the caches.
<<?MAP_ID, (rlp_encode_int(Id))/binary>>;
serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities),
@@ -200,7 +213,9 @@ serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
(serialize(?FATE_TUPLE(Values)))/binary
>>
end
end.
end;
serialize(?FATE_TYPEREP(T)) ->
iolist_to_binary(serialize_type(T)).
%% -----------------------------------------------------
@@ -216,12 +231,12 @@ serialize_type({tuple, Ts}) ->
N when N =< 255 ->
[?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]]
end;
serialize_type({bytes, N}) when 0 =< N ->
[?TYPE_BYTES | binary_to_list(serialize_integer(N))];
serialize_type(address) -> [?TYPE_OBJECT, ?OTYPE_ADDRESS];
serialize_type(hash) -> [?TYPE_OBJECT, ?OTYPE_HASH];
serialize_type(signature) -> [?TYPE_OBJECT, ?OTYPE_SIGNATURE];
serialize_type(contract) -> [?TYPE_OBJECT, ?OTYPE_CONTRACT];
serialize_type(oracle) -> [?TYPE_OBJECT, ?OTYPE_ORACLE];
serialize_type(name) -> [?TYPE_OBJECT, ?OTYPE_NAME];
serialize_type(oracle_query)-> [?TYPE_OBJECT, ?OTYPE_ORACLE_Q];
serialize_type(channel) -> [?TYPE_OBJECT, ?OTYPE_CHANNEL];
serialize_type(bits) -> [?TYPE_BITS];
serialize_type({map, K, V}) -> [?TYPE_MAP
@@ -245,14 +260,16 @@ deserialize_type(<<?TYPE_LIST, Rest/binary>>) ->
deserialize_type(<<?TYPE_TUPLE, N, Rest/binary>>) ->
{Ts, Rest2} = deserialize_types(N, Rest, []),
{{tuple, Ts}, Rest2};
deserialize_type(<<?TYPE_BYTES, Rest/binary>>) ->
{N, Rest2} = deserialize_one(Rest),
true = is_integer(N) andalso N >= 0,
{{bytes, N}, Rest2};
deserialize_type(<<?TYPE_OBJECT, ObjectType, Rest/binary>>) ->
case ObjectType of
?OTYPE_ADDRESS -> {address, Rest};
?OTYPE_HASH -> {hash, Rest};
?OTYPE_SIGNATURE -> {signature, Rest};
?OTYPE_CONTRACT -> {contract, Rest};
?OTYPE_ORACLE -> {oracle, Rest};
?OTYPE_NAME -> {name, Rest};
?OTYPE_ORACLE_Q -> {oracle_query, Rest};
?OTYPE_CHANNEL -> {channel, Rest}
end;
deserialize_type(<<?TYPE_BITS, Rest/binary>>) -> {bits, Rest};
@@ -354,6 +371,7 @@ deserialize2(<<?POS_BITS, Rest/binary>>) ->
{?FATE_BITS(Bint), Rest2};
deserialize2(<<?LONG_STRING, Rest/binary>>) ->
{S, Rest2} = deserialize_one(Rest),
true = is_integer(S) andalso S >= 0,
Size = S + ?SHORT_STRING_SIZE,
String = binary:part(Rest2, 0, Size),
Rest3 = binary:part(Rest2, byte_size(Rest2), - (byte_size(Rest2) - Size)),
@@ -362,16 +380,18 @@ deserialize2(<<S:6, ?SHORT_STRING:2, Rest/binary>>) ->
String = binary:part(Rest, 0, S),
Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)),
{?MAKE_FATE_STRING(String), Rest2};
deserialize2(<<?OBJECT, ?OTYPE_BYTES, Rest/binary>>) ->
{String, Rest2} = deserialize_one(Rest),
true = ?IS_FATE_STRING(String),
{?FATE_BYTES(?FATE_STRING_VALUE(String)), Rest2};
deserialize2(<<?OBJECT, ObjectType, Rest/binary>>) ->
{A, Rest2} = aeser_rlp:decode_one(Rest),
Val =
case ObjectType of
?OTYPE_ADDRESS -> ?FATE_ADDRESS(A);
?OTYPE_HASH -> ?FATE_HASH(A);
?OTYPE_SIGNATURE -> ?FATE_SIGNATURE(A);
?OTYPE_CONTRACT -> ?FATE_CONTRACT(A);
?OTYPE_ORACLE -> ?FATE_ORACLE(A);
?OTYPE_NAME -> ?FATE_NAME(A);
?OTYPE_ORACLE_Q -> ?FATE_ORACLE_Q(A);
?OTYPE_CHANNEL -> ?FATE_CHANNEL(A)
end,
{Val, Rest2};
@@ -410,6 +430,9 @@ deserialize2(<<?MAP, Rest/binary>>) ->
false ->
error({unknown_map_serialization_format, KVList})
end;
deserialize2(<<?MAP_ID, Rest/binary>>) ->
{Id, Rest1} = rlp_decode_int(Rest),
{?FATE_STORE_MAP(#{}, Id), Rest1};
deserialize2(<<?VARIANT, Rest/binary>>) ->
{AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest),
Arities = binary_to_list(AritiesBin),
@@ -424,7 +447,10 @@ deserialize2(<<?VARIANT, Rest/binary>>) ->
true ->
{?FATE_VARIANT(Arities, Tag, T), Rest3}
end
end.
end;
deserialize2(<<TypeTag, _/binary>> = Bin) when ?IS_TYPE_TAG(TypeTag) ->
{Type, Rest} = deserialize_type(Bin),
{?FATE_TYPEREP(Type), Rest}.
insert_kv([]) -> [];
insert_kv([K, V | R]) -> [{K, V} | insert_kv(R)].
@@ -461,7 +487,5 @@ sort(KVList) ->
valid_key_type(K) when ?IS_FATE_MAP(K) ->
error({map_as_key_in_map, K});
valid_key_type(K) when ?IS_FATE_VARIANT(K) ->
error({variant_as_key_in_map, K});
valid_key_type(_K) ->
true.
+164 -134
View File
@@ -42,136 +42,145 @@ check_numbering(_, []) -> true.
%% TODO: Some real gas numbers...
ops_defs() ->
%% Opname, Opcode, end_bb, gas, format, Constructor, ArgType, ResType, Documentation
[ { 'RETURN', 16#00, 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, 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, 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, 8, [a,is,a], call_r, {contract, string, integer}, any, "Remote call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function."}
, { 'CALL_T', 16#04, 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."}
, { 'CALL_TR', 16#05, true, 8, [a,is,a], call_tr, {contract, string, integer}, any, "Remote tail call to contract Arg0 and function Arg1 with value Arg2. 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."}
, { 'CALL_GR', 16#06, true, 8, [a,is,a,a], call_gr, {contract, string, integer, integer}, any, "Remote call with gas cap in Arg3. Otherwise as CALL_R."}
, { 'CALL_GTR', 16#07, true, 8, [a,is,a,a], call_gtr, {contract, string, integer, integer}, any, "Remote tail call with gas cap in Arg3. Otherwise as CALL_TR."}
, { 'JUMP', 16#08, 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, 4, [a,ii], jumpif, {boolean, integer}, none, "Conditional jump to a basic block. If Arg0 then jump to Arg1."}
, { 'SWITCH_V2', 16#0a, true, 4, [a,ii,ii], switch, {variant, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_V3', 16#0b, true, 4, [a,ii,ii,ii], switch, {variant, integer, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_VN', 16#0c, true, 4, [a, li], switch, {variant, {list, integer}}, none, "Conditional jump to a basic block on variant tag."}
, { 'CALL_VALUE', 16#0d, false, 3, [a], call_value, {}, integer, "The value sent in the current remote call."}
, { 'PUSH', 16#0e, false, 2, [a], push, {any}, any, "Push argument to stack."}
, { 'DUPA', 16#0f, false, 3, [], dup, {any}, any, "Duplicate top of stack."}
, { 'DUP', 16#10, false, 3, [a], dup, {any}, any, "push Arg0 stack pos on top of stack."}
, { 'POP', 16#11, false, 3, [a], pop, {integer}, integer, "Arg0 := top of stack."}
, { 'INCA', 16#12, false, 2, [], inc, {integer}, integer, "Increment accumulator."}
, { 'INC', 16#13, false, 2, [a], inc, {integer}, integer, "Increment argument."}
, { 'DECA', 16#14, false, 2, [], dec, {integer}, integer, "Decrement accumulator."}
, { 'DEC', 16#15, false, 2, [a], dec, {integer}, integer, "Decrement argument."}
, { 'ADD', 16#16, false, 3, [a,a,a], add, {integer, integer}, integer, "Arg0 := Arg1 + Arg2."}
, { 'SUB', 16#17, false, 3, [a,a,a], sub, {integer, integer}, integer, "Arg0 := Arg1 - Arg2."}
, { 'MUL', 16#18, false, 3, [a,a,a], mul, {integer, integer}, integer, "Arg0 := Arg1 * Arg2."}
, { 'DIV', 16#19, false, 3, [a,a,a], divide, {integer, integer}, integer, "Arg0 := Arg1 / Arg2."}
, { 'MOD', 16#1a, false, 3, [a,a,a], modulo, {integer, integer}, integer, "Arg0 := Arg1 mod Arg2."}
, { 'POW', 16#1b, false, 3, [a,a,a], pow, {integer, integer}, integer, "Arg0 := Arg1 ^ Arg2."}
, { 'STORE', 16#1c, false, 3, [a,a], store, {any}, any, "Arg0 := Arg1."}
, { 'SHA3', 16#1d, false, 30, [a,a], sha3, {any}, hash, "Arg0 := sha3(Arg1)."}
, { 'SHA256', 16#1e, false, 30, [a,a], sha256, {any}, hash, "Arg0 := sha256(Arg1)."}
, { 'BLAKE2B', 16#1f, false, 30, [a,a], blake2b, {any}, hash, "Arg0 := blake2b(Arg1)."}
, { 'LT', 16#20, false, 3, [a,a,a], lt, {integer, integer}, boolean, "Arg0 := Arg1 < Arg2."}
, { 'GT', 16#21, false, 3, [a,a,a], gt, {integer, integer}, boolean, "Arg0 := Arg1 > Arg2."}
, { 'EQ', 16#22, false, 3, [a,a,a], eq, {integer, integer}, boolean, "Arg0 := Arg1 = Arg2."}
, { 'ELT', 16#23, false, 3, [a,a,a], elt, {integer, integer}, boolean, "Arg0 := Arg1 =< Arg2."}
, { 'EGT', 16#24, false, 3, [a,a,a], egt, {integer, integer}, boolean, "Arg0 := Arg1 >= Arg2."}
, { 'NEQ', 16#25, false, 3, [a,a,a], neq, {integer, integer}, boolean, "Arg0 := Arg1 /= Arg2."}
, { 'AND', 16#26, false, 3, [a,a,a], and_op, {boolean, boolean}, boolean, "Arg0 := Arg1 and Arg2."}
, { 'OR', 16#27, false, 3, [a,a,a], or_op, {boolean, boolean}, boolean, "Arg0 := Arg1 or Arg2."}
, { 'NOT', 16#28, false, 3, [a,a], not_op, {boolean}, boolean, "Arg0 := not Arg1."}
, { 'TUPLE', 16#29, false, 3, [a,ii], tuple, {integer}, tuple, "Arg0 := tuple of size = Arg1. Elements on stack."}
, { 'ELEMENT', 16#2a, false, 3, [a,a,a], element_op, {integer, tuple}, any, "Arg1 := element(Arg2, Arg3)."}
, { 'SETELEMENT', 16#2b, false, 3, [a,a,a,a], setelement, {integer, tuple, any}, tuple, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
, { 'MAP_EMPTY', 16#2c, false, 3, [a], map_empty, {}, map, "Arg0 := #{}."}
, { 'MAP_LOOKUP', 16#2d, false, 3, [a,a,a], map_lookup, {map, any}, any, "Arg0 := lookup key Arg2 in map Arg1."}
, { 'MAP_LOOKUPD', 16#2e, false, 3, [a,a,a,a], map_lookup, {map, any, any}, any, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."}
, { 'MAP_UPDATE', 16#2f, false, 3, [a,a,a,a], map_update, {map, any, any}, map, "Arg0 := update key Arg2 in map Arg1 with value Arg3."}
, { 'MAP_DELETE', 16#30, false, 3, [a,a,a], map_delete, {map, any}, map, "Arg0 := delete key Arg2 from map Arg1."}
, { 'MAP_MEMBER', 16#31, false, 3, [a,a,a], map_member, {map, any}, boolean, "Arg0 := true if key Arg2 is in map Arg1."}
, { 'MAP_FROM_LIST', 16#32, false, 3, [a,a], map_from_list, {{list, {tuple, [any, any]}}}, map, "Arg0 := make a map from (key, value) list in Arg1."}
, { 'IS_NIL', 16#33, false, 3, [a,a], is_nil, {list}, boolean, "Arg0 := true if Arg1 == []."}
, { 'CONS', 16#34, false, 3, [a,a,a], cons, {any, list}, list, "Arg0 := [Arg1|Arg2]."}
, { 'HD', 16#35, false, 3, [a,a], hd, {list}, any, "Arg0 := head of list Arg1."}
, { 'TL', 16#36, false, 3, [a,a], tl, {list}, list, "Arg0 := tail of list Arg1."}
, { 'LENGTH', 16#37, false, 3, [a,a], length, {list}, integer, "Arg0 := length of list Arg1."}
, { 'NIL', 16#38, false, 3, [a], nil, {}, list, "Arg0 := []."}
, { 'STR_JOIN', 16#39, false, 3, [a,a,a], str_join, {string, string}, string, "Arg0 := string Arg1 followed by string Arg2."}
, { 'INT_TO_STR', 16#3a, false, 3, [a,a], int_to_str, {integer}, string, "Arg0 := turn integer Arg1 into a string."}
, { 'ADDR_TO_STR', 16#3b, false, 3, [a,a], addr_to_str, {address}, string, "Arg0 := turn address Arg1 into a string."}
, { 'STR_REVERSE', 16#3c, false, 3, [a,a], str_reverse, {string}, string, "Arg0 := the reverse of string Arg1."}
, { 'APPEND', 16#3d, false, 3, [a,a,a], append, {list, list}, list, "Arg0 := Arg1 ++ Arg2."}
, { 'INT_TO_ADDR', 16#3e, false, 3, [a,a], int_to_addr, {integer}, address, "Arg0 := turn integer Arg1 into an address."}
, { 'VARIANT', 16#3f, false, 3, [a,a,a,a], variant, {integer, integer, integer}, variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."}
, { 'VARIANT_TEST', 16#40, false, 3, [a,a,a], variant_test, {variant, integer}, boolean, "Arg0 := true if variant Arg1 has the tag Arg2."}
, { 'VARIANT_ELEMENT', 16#41, false, 3, [a,a,a], variant_element, {variant, integer}, any, "Arg0 := element number Arg2 from variant Arg1."}
, { 'BITS_NONEA', 16#42, false, 3, [], bits_none, {}, bits, "push an empty bitmap on the stack."}
, { 'BITS_NONE', 16#43, false, 3, [a], bits_none, {}, bits, "Arg0 := empty bitmap."}
, { 'BITS_ALLA', 16#44, false, 3, [], bits_all, {}, bits, "push a full bitmap on the stack."}
, { 'BITS_ALL', 16#45, false, 3, [a], bits_all, {}, bits, "Arg0 := full bitmap."}
, { 'BITS_ALL_N', 16#46, false, 3, [a,a], bits_all_n, {integer}, bits, "Arg0 := bitmap with Arg1 bits set."}
, { 'BITS_SET', 16#47, false, 3, [a,a,a], bits_set, {bits, integer}, bits, "Arg0 := set bit Arg2 of bitmap Arg1."}
, { 'BITS_CLEAR', 16#48, false, 3, [a,a,a], bits_clear, {bits, integer}, bits, "Arg0 := clear bit Arg2 of bitmap Arg1."}
, { 'BITS_TEST', 16#49, false, 3, [a,a,a], bits_test, {bits, integer}, boolean, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."}
, { 'BITS_SUM', 16#4a, false, 3, [a,a], bits_sum, {bits}, integer, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."}
, { 'BITS_OR', 16#4b, false, 3, [a,a,a], bits_or, {bits, bits}, bits, "Arg0 := Arg1 v Arg2."}
, { 'BITS_AND', 16#4c, false, 3, [a,a,a], bits_and, {bits, bits}, bits, "Arg0 := Arg1 ^ Arg2."}
, { 'BITS_DIFF', 16#4d, false, 3, [a,a,a], bits_diff, {bits, bits}, bits, "Arg0 := Arg1 - Arg2."}
, { 'BALANCE', 16#4e, false, 3, [a], balance, {}, integer, "Arg0 := The current contract balance."}
, { 'ORIGIN', 16#4f, false, 3, [a], origin, {}, address, "Arg0 := Address of contract called by the call transaction."}
, { 'CALLER', 16#50, false, 3, [a], caller, {}, address, "Arg0 := The address that signed the call transaction."}
, { 'GASPRICE', 16#51, false, 3, [a], gasprice, {}, integer, "Arg0 := The current gas price."}
, { 'BLOCKHASH', 16#52, false, 3, [a,a], blockhash, {integer}, hash, "Arg0 := The blockhash at height."}
, { 'BENEFICIARY', 16#53, false, 3, [a], beneficiary, {}, address, "Arg0 := The address of the current beneficiary."}
, { 'TIMESTAMP', 16#54, false, 3, [a], timestamp, {}, integer, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
, { 'GENERATION', 16#55, false, 3, [a], generation, {}, integer, "Arg0 := The block height of the cureent generation."}
, { 'MICROBLOCK', 16#56, false, 3, [a], microblock, {}, integer, "Arg0 := The current micro block number."}
, { 'DIFFICULTY', 16#57, false, 3, [a], difficulty, {}, integer, "Arg0 := The current difficulty."}
, { 'GASLIMIT', 16#58, false, 3, [a], gaslimit, {}, integer, "Arg0 := The current gaslimit."}
, { 'GAS', 16#59, false, 3, [a], gas, {}, integer, "Arg0 := The amount of gas left."}
, { 'ADDRESS', 16#5a, false, 3, [a], address, {}, address, "Arg0 := The current contract address."}
%% Opname, Opcode, end_bb, in_auth offchain, gas, format, Constructor, ArgType, ResType, Documentation
[ { '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_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."}
, { '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."}
, { 'SWITCH_V2', 16#0a, true, true, true, 4, [a,ii,ii], switch, {variant, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_V3', 16#0b, true, true, true, 4, [a,ii,ii,ii], switch, {variant, integer, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_VN', 16#0c, true, true, true, 4, [a, li], switch, {variant, {list, integer}}, none, "Conditional jump to a basic block on variant tag."}
, { 'CALL_VALUE', 16#0d, false, true, true, 3, [a], call_value, {}, integer, "The value sent in the current remote call."}
, { 'PUSH', 16#0e, false, true, true, 2, [a], push, {any}, any, "Push argument to stack."}
, { 'DUPA', 16#0f, false, true, true, 3, [], dup, {any}, any, "Duplicate top of stack."}
, { 'DUP', 16#10, false, true, true, 3, [a], dup, {any}, any, "push Arg0 stack pos on top of stack."}
, { 'POP', 16#11, false, true, true, 3, [a], pop, {integer}, integer, "Arg0 := top of stack."}
, { 'INCA', 16#12, false, true, true, 2, [], inc, {integer}, integer, "Increment accumulator."}
, { 'INC', 16#13, false, true, true, 2, [a], inc, {integer}, integer, "Increment argument."}
, { 'DECA', 16#14, false, true, true, 2, [], dec, {integer}, integer, "Decrement accumulator."}
, { 'DEC', 16#15, false, true, true, 2, [a], dec, {integer}, integer, "Decrement argument."}
, { 'ADD', 16#16, false, true, true, 3, [a,a,a], add, {integer, integer}, integer, "Arg0 := Arg1 + Arg2."}
, { 'SUB', 16#17, false, true, true, 3, [a,a,a], sub, {integer, integer}, integer, "Arg0 := Arg1 - Arg2."}
, { 'MUL', 16#18, false, true, true, 3, [a,a,a], mul, {integer, integer}, integer, "Arg0 := Arg1 * Arg2."}
, { 'DIV', 16#19, false, true, true, 3, [a,a,a], divide, {integer, integer}, integer, "Arg0 := Arg1 / Arg2."}
, { 'MOD', 16#1a, false, true, true, 3, [a,a,a], modulo, {integer, integer}, integer, "Arg0 := Arg1 mod Arg2."}
, { 'POW', 16#1b, false, true, true, 3, [a,a,a], pow, {integer, integer}, integer, "Arg0 := Arg1 ^ Arg2."}
, { 'STORE', 16#1c, false, true, true, 3, [a,a], store, {any}, any, "Arg0 := Arg1."}
, { 'SHA3', 16#1d, false, true, true, 30, [a,a], sha3, {any}, hash, "Arg0 := sha3(Arg1)."}
, { 'SHA256', 16#1e, false, true, true, 30, [a,a], sha256, {any}, hash, "Arg0 := sha256(Arg1)."}
, { 'BLAKE2B', 16#1f, false, true, true, 30, [a,a], blake2b, {any}, hash, "Arg0 := blake2b(Arg1)."}
, { 'LT', 16#20, false, true, true, 3, [a,a,a], lt, {integer, integer}, boolean, "Arg0 := Arg1 < Arg2."}
, { 'GT', 16#21, false, true, true, 3, [a,a,a], gt, {integer, integer}, boolean, "Arg0 := Arg1 > Arg2."}
, { 'EQ', 16#22, false, true, true, 3, [a,a,a], eq, {integer, integer}, boolean, "Arg0 := Arg1 = Arg2."}
, { 'ELT', 16#23, false, true, true, 3, [a,a,a], elt, {integer, integer}, boolean, "Arg0 := Arg1 =< Arg2."}
, { 'EGT', 16#24, false, true, true, 3, [a,a,a], egt, {integer, integer}, boolean, "Arg0 := Arg1 >= Arg2."}
, { 'NEQ', 16#25, false, true, true, 3, [a,a,a], neq, {integer, integer}, boolean, "Arg0 := Arg1 /= Arg2."}
, { 'AND', 16#26, false, true, true, 3, [a,a,a], and_op, {boolean, boolean}, boolean, "Arg0 := Arg1 and Arg2."}
, { 'OR', 16#27, false, true, true, 3, [a,a,a], or_op, {boolean, boolean}, boolean, "Arg0 := Arg1 or Arg2."}
, { 'NOT', 16#28, false, true, true, 3, [a,a], not_op, {boolean}, boolean, "Arg0 := not Arg1."}
, { 'TUPLE', 16#29, false, true, true, 3, [a,ii], tuple, {integer}, tuple, "Arg0 := tuple of size = Arg1. Elements on stack."}
, { 'ELEMENT', 16#2a, false, true, true, 3, [a,a,a], element_op, {integer, tuple}, any, "Arg1 := element(Arg2, Arg3)."}
, { 'SETELEMENT', 16#2b, false, true, true, 3, [a,a,a,a], setelement, {integer, tuple, any}, tuple, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
, { 'MAP_EMPTY', 16#2c, false, true, true, 3, [a], map_empty, {}, map, "Arg0 := #{}."}
, { 'MAP_LOOKUP', 16#2d, false, true, true, 3, [a,a,a], map_lookup, {map, any}, any, "Arg0 := lookup key Arg2 in map Arg1."}
, { 'MAP_LOOKUPD', 16#2e, false, true, true, 3, [a,a,a,a], map_lookup, {map, any, any}, any, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."}
, { 'MAP_UPDATE', 16#2f, false, true, true, 3, [a,a,a,a], map_update, {map, any, any}, map, "Arg0 := update key Arg2 in map Arg1 with value Arg3."}
, { 'MAP_DELETE', 16#30, false, true, true, 3, [a,a,a], map_delete, {map, any}, map, "Arg0 := delete key Arg2 from map Arg1."}
, { 'MAP_MEMBER', 16#31, false, true, true, 3, [a,a,a], map_member, {map, any}, boolean, "Arg0 := true if key Arg2 is in map Arg1."}
, { 'MAP_FROM_LIST', 16#32, false, true, true, 3, [a,a], map_from_list, {{list, {tuple, [any, any]}}}, map, "Arg0 := make a map from (key, value) list in Arg1."}
, { 'IS_NIL', 16#33, false, true, true, 3, [a,a], is_nil, {list}, boolean, "Arg0 := true if Arg1 == []."}
, { 'CONS', 16#34, false, true, true, 3, [a,a,a], cons, {any, list}, list, "Arg0 := [Arg1|Arg2]."}
, { 'HD', 16#35, false, true, true, 3, [a,a], hd, {list}, any, "Arg0 := head of list Arg1."}
, { 'TL', 16#36, false, true, true, 3, [a,a], tl, {list}, list, "Arg0 := tail of list Arg1."}
, { 'LENGTH', 16#37, false, true, true, 3, [a,a], length, {list}, integer, "Arg0 := length of list Arg1."}
, { 'NIL', 16#38, false, true, true, 3, [a], nil, {}, list, "Arg0 := []."}
, { 'STR_JOIN', 16#39, false, true, true, 3, [a,a,a], str_join, {string, string}, string, "Arg0 := string Arg1 followed by string Arg2."}
, { 'INT_TO_STR', 16#3a, false, true, true, 3, [a,a], int_to_str, {integer}, string, "Arg0 := turn integer Arg1 into a string."}
, { 'ADDR_TO_STR', 16#3b, false, true, true, 3, [a,a], addr_to_str, {address}, string, "Arg0 := turn address Arg1 into a string."}
, { 'STR_REVERSE', 16#3c, false, true, true, 3, [a,a], str_reverse, {string}, string, "Arg0 := the reverse of string Arg1."}
, { 'APPEND', 16#3d, false, true, true, 3, [a,a,a], append, {list, list}, list, "Arg0 := Arg1 ++ Arg2."}
, { 'INT_TO_ADDR', 16#3e, false, true, true, 3, [a,a], int_to_addr, {integer}, address, "Arg0 := turn integer Arg1 into an address."}
, { 'VARIANT', 16#3f, false, true, true, 3, [a,a,a,a], variant, {integer, integer, integer}, variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."}
, { 'VARIANT_TEST', 16#40, false, true, true, 3, [a,a,a], variant_test, {variant, integer}, boolean, "Arg0 := true if variant Arg1 has the tag Arg2."}
, { 'VARIANT_ELEMENT', 16#41, false, true, true, 3, [a,a,a], variant_element, {variant, integer}, any, "Arg0 := element number Arg2 from variant Arg1."}
, { 'BITS_NONEA', 16#42, false, true, true, 3, [], bits_none, {}, bits, "push an empty bitmap on the stack."}
, { 'BITS_NONE', 16#43, false, true, true, 3, [a], bits_none, {}, bits, "Arg0 := empty bitmap."}
, { 'BITS_ALLA', 16#44, false, true, true, 3, [], bits_all, {}, bits, "push a full bitmap on the stack."}
, { 'BITS_ALL', 16#45, false, true, true, 3, [a], bits_all, {}, bits, "Arg0 := full bitmap."}
, { 'BITS_ALL_N', 16#46, false, true, true, 3, [a,a], bits_all_n, {integer}, bits, "Arg0 := bitmap with Arg1 bits set."}
, { 'BITS_SET', 16#47, false, true, true, 3, [a,a,a], bits_set, {bits, integer}, bits, "Arg0 := set bit Arg2 of bitmap Arg1."}
, { 'BITS_CLEAR', 16#48, false, true, true, 3, [a,a,a], bits_clear, {bits, integer}, bits, "Arg0 := clear bit Arg2 of bitmap Arg1."}
, { 'BITS_TEST', 16#49, false, true, true, 3, [a,a,a], bits_test, {bits, integer}, boolean, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."}
, { 'BITS_SUM', 16#4a, false, true, true, 3, [a,a], bits_sum, {bits}, integer, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."}
, { 'BITS_OR', 16#4b, false, true, true, 3, [a,a,a], bits_or, {bits, bits}, bits, "Arg0 := Arg1 v Arg2."}
, { 'BITS_AND', 16#4c, false, true, true, 3, [a,a,a], bits_and, {bits, bits}, bits, "Arg0 := Arg1 ^ Arg2."}
, { 'BITS_DIFF', 16#4d, false, true, true, 3, [a,a,a], bits_diff, {bits, bits}, bits, "Arg0 := Arg1 - Arg2."}
, { 'BALANCE', 16#4e, false, true, true, 3, [a], balance, {}, integer, "Arg0 := The current contract balance."}
, { 'ORIGIN', 16#4f, false, true, true, 3, [a], origin, {}, address, "Arg0 := Address of contract called by the call transaction."}
, { 'CALLER', 16#50, false, true, true, 3, [a], caller, {}, address, "Arg0 := The address that signed the call transaction."}
, { 'GASPRICE', 16#51, false, true, true, 3, [a], gasprice, {}, integer, "Arg0 := The current gas price."}
, { 'BLOCKHASH', 16#52, false, true, true, 3, [a,a], blockhash, {integer}, hash, "Arg0 := The blockhash at height."}
, { 'BENEFICIARY', 16#53, false, true, true, 3, [a], beneficiary, {}, address, "Arg0 := The address of the current beneficiary."}
, { 'TIMESTAMP', 16#54, false, true, true, 3, [a], timestamp, {}, integer, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
, { 'GENERATION', 16#55, false, true, true, 3, [a], generation, {}, integer, "Arg0 := The block height of the cureent generation."}
, { 'MICROBLOCK', 16#56, false, true, true, 3, [a], microblock, {}, integer, "Arg0 := The current micro block number."}
, { 'DIFFICULTY', 16#57, false, true, true, 3, [a], difficulty, {}, integer, "Arg0 := The current difficulty."}
, { 'GASLIMIT', 16#58, false, true, true, 3, [a], gaslimit, {}, integer, "Arg0 := The current gaslimit."}
, { 'GAS', 16#59, false, true, true, 3, [a], gas, {}, integer, "Arg0 := The amount of gas left."}
, { 'ADDRESS', 16#5a, false, true, true, 3, [a], address, {}, address, "Arg0 := The current contract address."}
, { 'LOG0', 16#5b, false, 3, [a], log, {string}, none, "Create a log message in the call object."}
, { 'LOG1', 16#5c, false, 3, [a,a], log, {integer, string}, none, "Create a log message with one topic in the call object."}
, { 'LOG2', 16#5d, false, 3, [a,a,a], log, {integer, integer, string}, none, "Create a log message with two topics in the call object."}
, { 'LOG3', 16#5e, false, 3, [a,a,a,a], log, {integer, integer, integer, string}, none, "Create a log message with three topics in the call object."}
, { 'LOG4', 16#5f, false, 3, [a,a,a,a,a], log, {integer, integer, integer, integer, string}, none, "Create a log message with four topics in the call object."}
, { 'LOG0', 16#5b, false, true, true, 3, [a], log, {string}, none, "Create a log message in the call object."}
, { 'LOG1', 16#5c, false, true, true, 3, [a,a], log, {integer, string}, none, "Create a log message with one topic in the call object."}
, { 'LOG2', 16#5d, false, true, true, 3, [a,a,a], log, {integer, integer, string}, none, "Create a log message with two topics in the call object."}
, { 'LOG3', 16#5e, false, true, true, 3, [a,a,a,a], log, {integer, integer, integer, string}, none, "Create a log message with three topics in the call object."}
, { 'LOG4', 16#5f, false, true, true, 3, [a,a,a,a,a], log, {integer, integer, integer, integer, string}, none, "Create a log message with four topics in the call object."}
%% Transaction ops
, { 'SPEND', 16#60, false, 3, [a,a], spend, {address, integer}, none, "Transfer Arg1 tokens to account Arg0. (If the contract account has at least that many tokens."}
, { 'ORACLE_REGISTER', 16#61, false, 3, [a,a,a,a,a,a], oracle_register, {any,any,any,any,any}, any, "NYI"}
%% TODO:
, { 'ORACLE_QUERY', 16#62, false, 3, [], oracle_query, {}, none, "NYI"}
, { 'ORACLE_RESPOND', 16#63, false, 3, [], oracle_respond, {}, none, "NYI"}
, { 'ORACLE_EXTEND', 16#64, false, 3, [], oracle_extend, {}, none, "NYI"}
, { 'ORACLE_GET_ANSWER', 16#65, false, 3, [], oracle_get_answer, {}, none, "NYI"}
, { 'ORACLE_GET_QUESTION', 16#66, false, 3, [], oracle_get_question, {}, none, "NYI"}
, { 'ORACLE_QUERY_FEE', 16#67, false, 3, [], oracle_query_fee, {}, none, "NYI"}
, { 'AENS_RESOLVE', 16#68, false, 3, [], aens_resolve, {}, none, "NYI"}
, { 'AENS_PRECLAIM', 16#69, false, 3, [], aens_preclaim, {}, none, "NYI"}
, { 'AENS_CLAIM', 16#6a, false, 3, [], aens_claim, {}, none, "NYI"}
, { 'AENS_UPDATE', 16#6b, false, 3, [], aend_update, {}, none, "NYI"}
, { 'AENS_TRANSFER', 16#6c, false, 3, [], aens_transfer, {}, none, "NYI"}
, { 'AENS_REVOKE', 16#6d, false, 3, [], aens_revoke, {}, none, "NYI"}
, { 'BALANCE_OTHER', 16#6e, false, 3, [a,a], balance_other, {address}, integer, "Arg0 := The balance of address Arg1."}
, { 'SPEND', 16#60, false, false, true, 3, [a,a], spend, {address, integer}, none, "Transfer Arg1 tokens to account Arg0. (If the contract account has at least that many tokens."}
, { 'ORACLE_REGISTER', 16#61, false, false, false, 3, [a,a,a,a,a,a,a], oracle_register, {signature, address, integer, variant, typerep, typerep}, oracle, "Arg0 := New oracle with address Arg2, query fee Arg3, TTL Arg4, query type Arg5 and response type Arg6. Arg0 contains delegation signature."}
, { 'ORACLE_QUERY', 16#62, false, false, false, 3, [a,a,a,a,a,a,a,a], oracle_query, {oracle, any, integer, variant, variant, typerep, typerep}, oracle_query, "Arg0 := New oracle query for oracle Arg1, question in Arg2, query fee in Arg3, query TTL in Arg4, response TTL in Arg5. Typereps for checking oracle type is in Arg6 and Arg7."}
, { 'ORACLE_RESPOND', 16#63, false, false, false, 3, [a,a,a,a,a,a], oracle_respond, {signature, oracle, oracle_query,any, typerep, typerep}, none, "Respond as oracle Arg1 to query in Arg2 with response Arg3. Arg0 contains delegation signature. Typereps for checking oracle type is in Arg4 and Arg5."}
, { 'ORACLE_EXTEND', 16#64, false, false, false, 3, [a,a,a], oracle_extend, {signature, oracle, variant}, none, "Extend oracle in Arg1 with TTL in Arg2. Arg0 contains delegation signature."}
, { 'ORACLE_GET_ANSWER', 16#65, false, false, true, 3, [a,a,a,a,a], oracle_get_answer, {oracle, oracle_query, typerep, typerep}, any, "Arg0 := option variant with answer (if any) from oracle query in Arg1 given by oracle Arg0. Typereps for checking oracle type is in Arg3 and Arg4."}
, { 'ORACLE_GET_QUESTION', 16#66, false, false, true, 3, [a,a,a,a,a], oracle_get_question, {oracle, oracle_query, typerep, typerep}, any, "Arg0 := question in oracle query Arg2 given to oracle Arg1. Typereps for checking oracle type is in Arg3 and Arg4."}
, { 'ORACLE_QUERY_FEE', 16#67, false, false, true, 3, [a,a], oracle_query_fee, {oracle}, integer, "Arg0 := query fee for oracle Arg1"}
, { 'AENS_RESOLVE', 16#68, false, false, true, 3, [a,a,a,a], aens_resolve, {string, string, typerep}, variant, "Resolve name in Arg0 with tag Arg1. Arg2 describes the type parameter of the resolved name."}
, { 'AENS_PRECLAIM', 16#69, false, false, false, 3, [a,a,a], aens_preclaim, {signature, address, hash}, none, "Preclaim the hash in Arg2 for address in Arg1. Arg0 contains delegation signature."}
, { 'AENS_CLAIM', 16#6a, false, false, false, 3, [a,a,a,a,a], aens_claim, {signature, address, string, integer, integer}, none, "Attempt to claim the name in Arg2 for address in Arg1 at a price in Arg4. Arg3 contains the salt used to hash the preclaim. Arg0 contains delegation signature."}
, { 'AENS_UPDATE', 16#6b, false, false, false, 3, [], aens_update, {}, none, "NYI"}
, { 'AENS_TRANSFER', 16#6c, false, false, false, 3, [a,a,a,a], aens_transfer,{signature, address, address, string}, none, "Transfer ownership of name Arg3 from account Arg1 to Arg2. Arg0 contains delegation signature."}
, { 'AENS_REVOKE', 16#6d, false, false, false, 3, [a,a,a], aens_revoke, {signature, address, string}, none, "Revoke the name in Arg2 from owner Arg1. Arg0 contains delegation signature."}
, { 'BALANCE_OTHER', 16#6e, false, true, true, 3, [a,a], balance_other, {address}, integer, "Arg0 := The balance of address Arg1."}
%% TODO: Reorder these before documenting the specification
, { 'MAP_SIZE', 16#6f, false, 3, [a,a], map_size_, {map}, integer, "Arg0 := The size of the map Arg1."}
, { 'MAP_TO_LIST', 16#70, false, 3, [a,a], map_to_list, {map}, list, "Arg0 := The tuple list representation of the map Arg1."}
, { 'STR_LENGTH', 16#71, false, 3, [a,a], str_length, {string}, integer, "Arg0 := The length of the string Arg1."}
, { 'MAP_SIZE', 16#6f, false, true, true, 3, [a,a], map_size_, {map}, integer, "Arg0 := The size of the map Arg1."}
, { '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, 1300, [a,a,a,a], ecverify, {hash, address, signature}, boolean, "Arg0 := ecverify(Hash, PubKey, Signature)"}
, { 'ECVERIFY_SECP256K1', 16#73, false, 1300, [a,a,a,a], ecverify_secp256k1, {hash, signature, signature}, boolean, "Arg0 := ecverify_secp256k1(Hash, PubKey, Signature)"}
, { '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)"}
, { 'CONTRACT_TO_ADDRESS', 16#74, false, 3, [a,a], contract_to_address, {contract}, address, "Arg0 := Arg1 - A no-op type conversion"}
, { 'AUTH_TX_HASH', 16#75, false, 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."}
, { 'DEACTIVATE', 16#fa, false, 3, [], deactivate, {}, none, "Mark the current contract for deactivation."}
, { 'ABORT', 16#fb, true, 3, [a], abort, {string}, none, "Abort execution (dont use all gas) with error message in Arg0."}
, { 'EXIT', 16#fc, true, 3, [a], exit, {string}, none, "Abort execution (use upp all gas) with error message in Arg0."}
, { 'NOP', 16#fd, false, 1, [], nop, {}, none, "The no op. does nothing."}
, { '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)"}
, { '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"}
, { '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."}
, { 'NOP', 16#fd, false, true, true, 1, [], nop, {}, none, "The no op. does nothing."}
%% FUNCTION 16#fe "Function declaration and entrypoint."
%% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes."
].
@@ -195,11 +204,15 @@ generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) ->
ToOp = lists:flatten([gen_m_to_op(Op) || Op <- Ops]),
Args = lists:flatten([gen_args(Op) || Op <- Ops]),
EndBB = lists:flatten([gen_bb(Op) || Op <- Ops]),
InAuth = lists:flatten([gen_in_auth(Op) || Op <- Ops]),
Offchain = lists:flatten([gen_allowed_offchain(Op) || Op <- Ops]),
io:format(File, "~s", [prelude("Provides opcode primitives.\n")]),
io:format(File, "~s", [ops_exports(Modulename, HrlFile,
["args/1\n"
" , end_bb/1\n"
" , in_auth/1\n"
" , allowed_offchain/1\n"
" , mnemonic/1\n"
" , m_to_op/1\n"
])]),
@@ -216,6 +229,12 @@ generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) ->
io:format(File, "%% Does FATE Op end a Basic Block?\n~s", [EndBB]),
io:format(File, "end_bb(_) -> false.\n\n", []),
io:format(File, "%% Is FATE Op allowed in GA Authentication context?\n~s", [InAuth]),
io:format(File, "in_auth(_) -> false.\n\n", []),
io:format(File, "%% Is FATE Op allowed in a state channel offchain context?\n~s", [Offchain]),
io:format(File, "allowed_offchain(_) -> false.\n\n", []),
file:close(File).
generate_code_ops(Modulename, SrcDir, Ops) ->
@@ -339,7 +358,6 @@ gen_mnemonic(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("mnemonic(~21s) -> ~21w ;\n",
[Macro, Name])).
gen_m_to_op(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n",
[Name, Macro])).
@@ -352,6 +370,13 @@ gen_bb(#{macro := Macro, end_bb := EndBB}) ->
lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n",
[Macro, EndBB])).
gen_in_auth(#{macro := Macro, in_auth := InAuth}) ->
lists:flatten(io_lib:format("in_auth(~21s) -> ~w ;\n",
[Macro, InAuth])).
gen_allowed_offchain(#{macro := Macro, offchain := Offchain}) ->
lists:flatten(io_lib:format("allowed_offchain(~21s) -> ~w ;\n",
[Macro, Offchain])).
prelude(Doc) ->
"%%%-------------------------------------------------------------------\n"
@@ -371,7 +396,7 @@ gen_defines(#{opname := Name, opcode := OpCode}) ->
gen([]) ->
[];
gen([{OpName, OpCode, EndBB, Gas, FateFormat, Constructor, ArgTypes, ResType, Doc} | Rest]) ->
gen([{OpName, OpCode, EndBB, InAuth, AllowedOffchain, Gas, FateFormat, Constructor, ArgTypes, ResType, Doc} | Rest]) ->
Arity = length(FateFormat),
Name = atom_to_list(OpName),
LowerName = string:to_lower(Name),
@@ -388,6 +413,8 @@ gen([{OpName, OpCode, EndBB, Gas, FateFormat, Constructor, ArgTypes, ResType, Do
, opcode => OpCode
, arity => Arity
, end_bb => EndBB
, in_auth => InAuth
, offchain => AllowedOffchain
, format => FateFormat
, macro => Macro
, type_name => TypeName
@@ -461,25 +488,29 @@ gen_asm_pp(Module, Path, Ops) ->
io:format(File, "format_op(Op, _Symbols) -> io_lib:format(\";; Bad Op: ~~w\\n\", [Op]).\n", []),
file:close(File).
gen_format(#{opname := Name}) when (Name =:= 'CALL_R') or (Name =:= 'CALL_TR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Value}, Symbols) ->\n"
gen_format(#{opname := Name}) when (Name =:= 'CALL_R') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Arity, Value}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Arity), \" \", "
"format_arg(a, Value)];\n"
"format_op({~w, Contract, {immediate, Function}, Value}, Symbols) ->\n"
"format_op({~w, Contract, {immediate, Function}, Arity, Value}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Arity), \" \", "
"format_arg(a, Value)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name}) when (Name =:= 'CALL_GR') or (Name =:= 'CALL_GTR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Value, Gas}, Symbols) ->\n"
gen_format(#{opname := Name}) when (Name =:= 'CALL_GR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Arity, Value, Gas}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Arity), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n"
"format_op({~w, Contract, {immediate, Function}, Value, Gas}, Symbols) ->\n"
"format_op({~w, Contract, {immediate, Function}, Arity, Value, Gas}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Arity), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
@@ -679,7 +710,7 @@ gen_variant() ->
end.
%% TODO: add gas cost.
%% 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()]),
@@ -725,4 +756,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".
+210
View File
@@ -0,0 +1,210 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Functions for manipulating FATE maps. In particular for mediating
%%% between plain map values (represented by Erlang maps) and maps that are
%%% fully or partially saved in the contract store.
%%% @end
%%% -------------------------------------------------------------------
-module(aeb_fate_maps).
-include("aeb_fate_data.hrl").
-export([ allocate_store_maps/2
, has_store_maps/1
, unfold_store_maps/2
, refcount/1
, refcount_zero/0
, refcount_diff/2
, refcount_union/1
, refcount_union/2
, no_used_ids/0 ]).
-export_type([used_ids/0, maps/0, refcount/0]).
%% Size in bytes of serialization of a map for which we turn it into a store
%% map. It's not worth turning small maps into store maps.
%% Under consensus!
-define(STORE_MAP_THRESHOLD, 500).
-type fate_value() :: aeb_fate_data:fate_type().
-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()}.
allocate_store_maps(Used, Vals) ->
{_Used, Vals1, Maps} = allocate_store_maps_l(Used, Vals, #{}),
{Vals1, 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};
allocate_store_maps(Used, ?FATE_BITS(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_BYTES(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_ADDRESS(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_CONTRACT(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_ORACLE(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_ORACLE_Q(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_CHANNEL(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_TYPEREP(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, Val, Maps) when ?IS_FATE_INTEGER(Val) -> {Used, Val, Maps};
allocate_store_maps(Used, Val, Maps) when ?IS_FATE_STRING(Val) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_TUPLE(Val), Maps) ->
{Used1, Vals, Maps1} = allocate_store_maps_l(Used, tuple_to_list(Val), Maps),
{Used1, ?FATE_TUPLE(list_to_tuple(Vals)), Maps1};
allocate_store_maps(Used, Val, Maps) when ?IS_FATE_LIST(Val) ->
{Used1, Vals, Maps1} = allocate_store_maps_l(Used, ?FATE_LIST_VALUE(Val), Maps),
{Used1, ?MAKE_FATE_LIST(Vals), Maps1};
allocate_store_maps(Used, ?FATE_VARIANT(Arities, Tag, Vals), Maps) ->
{Used1, Vals1, Maps1} = allocate_store_maps_l(Used, tuple_to_list(Vals), Maps),
{Used1, ?FATE_VARIANT(Arities, Tag, list_to_tuple(Vals1)), Maps1};
allocate_store_maps(Used, Val, Maps) when ?IS_FATE_MAP(Val) ->
{Used1, KVs, Maps1} = allocate_store_maps_m(Used, ?FATE_MAP_VALUE(Val), Maps),
Val1 = ?MAKE_FATE_MAP(KVs),
case byte_size(aeb_fate_encoding:serialize(Val1)) < ?STORE_MAP_THRESHOLD of
true -> {Used1, Val1, Maps1};
false ->
{Id, Used2} = next_id(Used1),
{Used2, ?FATE_STORE_MAP(#{}, Id), Maps1#{Id => Val1}}
end;
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)}}.
allocate_store_maps_l(Used, [], Maps) -> {Used, [], Maps};
allocate_store_maps_l(Used, [H | T], Maps) ->
{Used1, H1, Maps1} = allocate_store_maps(Used, H, Maps),
{Used2, T1, Maps2} = allocate_store_maps(Used1, T, Maps1),
{Used2, [H1 | T1], Maps2}.
allocate_store_maps_m(Used, Val, Maps) ->
maps:fold(fun(K, V, {Us, M, Ms}) ->
{Us1, V1, Ms1} = allocate_store_maps(Us, V, Ms),
{Us1, M#{ K => V1 }, Ms1}
end, {Used, #{}, Maps}, Val).
%% -- Unfolding store maps ---------------------------------------------------
-type unfold_fun() :: fun((id()) -> aeb_fate_data:fate_map()).
-spec unfold_store_maps(unfold_fun(), fate_value()) -> fate_value().
unfold_store_maps(_Unfold, ?FATE_TRUE = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_FALSE = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_UNIT = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_BITS(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_BYTES(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_ADDRESS(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_CONTRACT(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_ORACLE(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_ORACLE_Q(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_CHANNEL(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_TYPEREP(_) = Val) -> Val;
unfold_store_maps(_Unfold, Val) when ?IS_FATE_INTEGER(Val) -> Val;
unfold_store_maps(_Unfold, Val) when ?IS_FATE_STRING(Val) -> Val;
unfold_store_maps(Unfold, ?FATE_TUPLE(Val)) ->
Vals = unfold_store_maps_l(Unfold, tuple_to_list(Val)),
?FATE_TUPLE(list_to_tuple(Vals));
unfold_store_maps(Unfold, Val) when ?IS_FATE_LIST(Val) ->
?MAKE_FATE_LIST(unfold_store_maps_l(Unfold, ?FATE_LIST_VALUE(Val)));
unfold_store_maps(Unfold, ?FATE_VARIANT(Arities, Tag, Vals)) ->
Vals1 = unfold_store_maps_l(Unfold, tuple_to_list(Vals)),
?FATE_VARIANT(Arities, Tag, list_to_tuple(Vals1));
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).
unfold_store_maps_l(Unfold, Vals) ->
[ unfold_store_maps(Unfold, Val) || Val <- Vals ].
unfold_store_maps_m(Unfold, Val) ->
maps:map(fun(_, V) -> unfold_store_maps(Unfold, V) end, Val).
write_cache(Key, ?FATE_MAP_TOMBSTONE, Map) ->
maps:remove(Key, Map);
write_cache(Key, Val, Map) ->
Map#{ Key => Val }.
%% -- Reference counting -----------------------------------------------------
-type refcount() :: #{id() => pos_integer()}.
-spec refcount_zero() -> refcount().
refcount_zero() -> #{}.
-spec refcount_diff(refcount(), refcount()) -> refcount().
refcount_diff(New, Old) ->
maps:fold(fun(K, N, C) -> maps:update_with(K, fun(M) -> M - N end, -N, C) end,
New, Old).
-spec refcount_union([refcount()]) -> refcount().
refcount_union(Counts) -> lists:foldl(fun refcount_union/2, #{}, Counts).
-spec refcount_union(refcount(), refcount()) -> refcount().
refcount_union(A, B) ->
maps:fold(fun(K, N, C) -> maps:update_with(K, fun(M) -> M + N end, N, C) end,
B, A).
-spec has_store_maps(fate_value()) -> boolean().
has_store_maps(Val) ->
refcount_zero() /= refcount(Val).
-spec refcount(fate_value()) -> refcount().
refcount(Val) -> refcount(Val, #{}).
-spec refcount(fate_value(), refcount()) -> refcount().
refcount(?FATE_MAP_TOMBSTONE, Count) -> Count;
refcount(?FATE_TRUE, Count) -> Count;
refcount(?FATE_FALSE, Count) -> Count;
refcount(?FATE_UNIT, Count) -> Count;
refcount(?FATE_BITS(_), Count) -> Count;
refcount(?FATE_BYTES(_), Count) -> Count;
refcount(?FATE_ADDRESS(_), Count) -> Count;
refcount(?FATE_CONTRACT(_), Count) -> Count;
refcount(?FATE_ORACLE(_), Count) -> Count;
refcount(?FATE_ORACLE_Q(_), Count) -> Count;
refcount(?FATE_CHANNEL(_), Count) -> Count;
refcount(?FATE_TYPEREP(_), Count) -> Count;
refcount(Val, Count) when ?IS_FATE_INTEGER(Val) -> Count;
refcount(Val, Count) when ?IS_FATE_STRING(Val) -> Count;
refcount(?FATE_TUPLE(Val), Count) ->
refcount_l(tuple_to_list(Val), Count);
refcount(Val, Count) when ?IS_FATE_LIST(Val) ->
refcount_l(?FATE_LIST_VALUE(Val), Count);
refcount(?FATE_VARIANT(_Arities, _Tag, Vals), Count) ->
refcount_l(tuple_to_list(Vals), Count);
refcount(Val, Count) when ?IS_FATE_MAP(Val) ->
refcount_m(?FATE_MAP_VALUE(Val), Count);
refcount(?FATE_STORE_MAP(Cache, Id), Count) ->
refcount_m(Cache, maps:update_with(Id, fun(N) -> N + 1 end, 1, Count)).
refcount_l(Vals, Count) ->
lists:foldl(fun refcount/2, Count, Vals).
refcount_m(Val, Count) ->
%% No maps in map keys
maps:fold(fun(_, ?FATE_MAP_TOMBSTONE, C) -> C;
(_, V, C) -> refcount(V, C) end, Count, Val).
%% -- Map id allocation ------------------------------------------------------
-spec no_used_ids() -> used_ids().
no_used_ids() -> [].
-spec next_id(used_ids()) -> {id(), used_ids()}.
next_id(UsedIds) ->
next_id(UsedIds, 0, []).
next_id(Used, J, Acc) when Used == []; J < hd(Used) ->
{J, lists:reverse(Acc) ++ [J | Used]};
next_id([I | Used], I, Acc) ->
next_id(Used, I + 1, [I | Acc]);
next_id([I | Used], J, Acc) when J > I ->
next_id(Used, J, [I | Acc]).
+2
View File
@@ -47,6 +47,8 @@ sources() ->
, "tuple"
, "mapofmap"
, "immediates"
, "names"
, "oracles"
%% , "all_instructions"
].
-1
View File
@@ -50,7 +50,6 @@ sources() ->
aeb_fate_data:make_hash(<<1,2,3,4,5>>),
aeb_fate_data:make_signature(<<1,2,3,4,5>>),
aeb_fate_data:make_contract(<<1,2,3,4,5>>),
aeb_fate_data:make_name(<<1,2,3,4,5>>),
aeb_fate_data:make_channel(<<1,2,3,4,5>>),
aeb_fate_data:make_list([True]),
aeb_fate_data:make_address(
-3
View File
@@ -74,9 +74,6 @@ FUNCTION oracle() : oracle
FUNCTION contract() : contract
RETURNR @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION name() : name
RETURNR @nm_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION channel() : channel
RETURNR @ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
+21
View File
@@ -0,0 +1,21 @@
;; CONTRACT names
FUNCTION preclaim(address, {bytes, 32}) : {tuple, []}
AENS_PRECLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1
RETURNR {}
FUNCTION claim(address, string, integer, integer) : {tuple, []}
AENS_CLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 arg3
RETURNR {}
FUNCTION transfer(address, address, {bytes, 32}) : {tuple, []}
AENS_TRANSFER #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2
RETURNR {}
FUNCTION revoke(address, {bytes, 32}) : {tuple, []}
AENS_REVOKE #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1
RETURNR {}
FUNCTION resolve(string, string) : {variant, [{tuple, []}, {tuple, [address]}]}
AENS_RESOLVE a arg0 arg1 'address
RETURN
+32
View File
@@ -0,0 +1,32 @@
;; CONTRACT oracles
FUNCTION register (address, integer, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : oracle
ORACLE_REGISTER a #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURN
FUNCTION query (oracle, integer, string) : oracle_query
ORACLE_QUERY a arg0 arg1 arg2 (| [1,1] | 0 | (100) |) (| [1,1] | 0 | (100) |) 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURN
FUNCTION bogus_query () : oracle_query
RETURNR @oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION respond (oracle, integer, string) : {tuple, []}
ORACLE_RESPOND #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR {}
FUNCTION extend (oracle, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : {tuple, []}
ORACLE_EXTEND #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1
RETURNR {}
FUNCTION get_question (oracle, oracle_query) : string
ORACLE_GET_QUESTION a arg0 arg1 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURN
FUNCTION get_answer (oracle, oracle_query) : {variant, [{tuple, []}, {tuple, [string]}]}
ORACLE_GET_ANSWER a arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURN
FUNCTION query_fee (oracle) : integer
ORACLE_QUERY_FEE a arg0
RETURN
+1 -5
View File
@@ -30,14 +30,10 @@ FUNCTION tailcall(integer) -> integer
FUNCTION remote_call(integer) : integer
PUSH arg0
CALL_R remote.add_five 0
CALL_R remote.add_five 1 0
INCA
RETURN
FUNCTION remote_tailcall(integer) : integer
PUSH arg0
CALL_TR remote add_five 0
;; Test the code from the shell
;; _build/default/rel/aessembler/bin/aessembler console