From e321882b9830eea5ce5ab8801e8b19fd350a4e00 Mon Sep 17 00:00:00 2001 From: Tobias Lindahl Date: Tue, 18 Jun 2019 13:56:48 +0200 Subject: [PATCH] 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 --- include/aeb_fate_data.hrl | 6 +++++- src/aeb_fate_asm.erl | 16 +++++++++++++++- src/aeb_fate_asm_scan.template | 2 ++ src/aeb_fate_data.erl | 11 ++++++++++- src/aeb_fate_encoding.erl | 31 +++++++++++++++++++++++++++---- src/aeb_fate_generate_ops.erl | 17 ++++++++--------- test/aeb_fate_asm_test.erl | 1 + test/asm_code/oracles.fate | 32 ++++++++++++++++++++++++++++++++ 8 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 test/asm_code/oracles.fate diff --git a/include/aeb_fate_data.hrl b/include/aeb_fate_data.hrl index 6218244..8ef98dd 100644 --- a/include/aeb_fate_data.hrl +++ b/include/aeb_fate_data.hrl @@ -11,6 +11,7 @@ -define(FATE_SIGNATURE_T, {signature, binary()}). -define(FATE_CONTRACT_T, {contract, <<_:256>>}). -define(FATE_ORACLE_T, {oracle, <<_:256>>}). +-define(FATE_ORACLE_Q_T, {oracle_query, <<_:256>>}). -define(FATE_NAME_T, {name, <<_:256>>}). -define(FATE_CHANNEL_T, {channel, <<_:256>>}). -define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}). @@ -28,6 +29,7 @@ -define(IS_FATE_SIGNATURE(X), (is_tuple(X) andalso (signature == element(1, X) andalso is_binary(element(2, X))))). -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_ORACLE_Q(X), (is_tuple(X) andalso (oracle_query == 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_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))))). @@ -39,6 +41,7 @@ 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}). @@ -47,10 +50,11 @@ -define(FATE_SIGNATURE(S), {signature, S}). -define(FATE_CONTRACT(X), {contract, X}). -define(FATE_ORACLE(X), {oracle, X}). +-define(FATE_ORACLE_Q(X), {oracle_query, X}). -define(FATE_NAME(X), {name, X}). -define(FATE_CHANNEL(X), {channel, X}). -define(FATE_BITS(B), {bits, B}). - +-define(FATE_TYPEREP(T), {typerep, T}). -define(FATE_INTEGER_VALUE(X), (X)). -define(FATE_BOOLEAN_VALUE(X), (X)). diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index e9d70ee..68eab63 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -267,6 +267,11 @@ 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, {oracle_query, Value}}|Rest], + Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, + [{immediate, aeb_fate_data:make_oracle_query(Value)}|Code], + Opts); to_bytecode([{object,_line, {name, Value}}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, @@ -304,6 +309,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,6 +405,8 @@ 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, {oracle_query, Address}} | Rest]) -> + {aeb_fate_data:make_oracle_query(Address), Rest}; parse_value([{object,_line, {name, Address}} | Rest]) -> {aeb_fate_data:make_name(Address), Rest}; parse_value([{object,_line, {channel, Address}} | Rest]) -> @@ -403,7 +414,9 @@ parse_value([{object,_line, {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 +442,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}; diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template index bd67d28..48639a7 100644 --- a/src/aeb_fate_asm_scan.template +++ b/src/aeb_fate_asm_scan.template @@ -75,6 +75,7 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. \{ : {token, {'{', TokenLine}}. \} : {token, {'}', TokenLine}}. \| : {token, {'|', TokenLine}}. +\' : {token, {typerep, TokenLine}}. ;;.* : {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. @@ -118,6 +119,7 @@ parse_object([_|Chars]) -> {account_pubkey, Bin} -> {address, Bin}; {contract_pubkey, Bin} -> {contract, Bin}; {oracle_pubkey, Bin} -> {oracle, Bin}; + {oracle_query_id, Bin} -> {oracle_query, Bin}; {name, Bin} -> {name, Bin}; {channel, Bin} -> {channel, Bin}; {signature, Bin} -> {signature, Bin} diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 1b6f6ae..25e7c87 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -91,10 +91,12 @@ , make_signature/1 , make_contract/1 , make_oracle/1 + , make_oracle_query/1 , make_name/1 , make_channel/1 , make_bits/1 , make_unit/0 + , make_typerep/1 ]). -export([ elt/2 @@ -115,6 +117,7 @@ make_hash(X) -> ?FATE_HASH(X). make_signature(X) -> ?FATE_SIGNATURE(X). make_contract(X) -> ?FATE_CONTRACT(X). make_oracle(X) -> ?FATE_ORACLE(X). +make_oracle_query(X) -> ?FATE_ORACLE_Q(X). make_name(X) -> ?FATE_NAME(X). make_channel(X) -> ?FATE_CHANNEL(X). make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I). @@ -122,6 +125,7 @@ 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 @@ -173,10 +177,14 @@ format(?FATE_CONTRACT(X)) -> ["@", aeser_api_encoder:encode(contract_pubkey, X)]; format(?FATE_ORACLE(X)) -> ["@", aeser_api_encoder:encode(oracle_pubkey, X)]; +format(?FATE_ORACLE_Q(X)) -> + ["@", aeser_api_encoder:encode(oracle_query_id, X)]; format(?FATE_NAME(X)) -> ["@", aeser_api_encoder:encode(name, 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; @@ -217,7 +225,8 @@ 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_VARIANT(T) -> 14; +ordinal(T) when ?IS_FATE_ORACLE_Q(T) -> 15. -spec lt(fate_type(), fate_type()) -> boolean(). diff --git a/src/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl index c324124..5e48113 100644 --- a/src/aeb_fate_encoding.erl +++ b/src/aeb_fate_encoding.erl @@ -112,8 +112,21 @@ -define(OTYPE_SIGNATURE, 2). -define(OTYPE_CONTRACT, 3). -define(OTYPE_ORACLE, 4). --define(OTYPE_NAME, 5). --define(OTYPE_CHANNEL, 6). +-define(OTYPE_ORACLE_Q, 5). +-define(OTYPE_NAME, 6). +-define(OTYPE_CHANNEL, 7). + +-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 @@ -150,6 +163,8 @@ serialize(?FATE_CONTRACT(Address)) when is_binary(Address) -> <>; serialize(?FATE_ORACLE(Address)) when is_binary(Address) -> <>; +serialize(?FATE_ORACLE_Q(Address)) when is_binary(Address) -> + <>; serialize(?FATE_NAME(Address)) when is_binary(Address) -> <>; serialize(?FATE_CHANNEL(Address)) when is_binary(Address) -> @@ -200,7 +215,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)). %% ----------------------------------------------------- @@ -221,6 +238,7 @@ 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(oracle_query)-> [?TYPE_OBJECT, ?OTYPE_ORACLE_Q]; serialize_type(name) -> [?TYPE_OBJECT, ?OTYPE_NAME]; serialize_type(channel) -> [?TYPE_OBJECT, ?OTYPE_CHANNEL]; serialize_type(bits) -> [?TYPE_BITS]; @@ -252,6 +270,7 @@ deserialize_type(<>) -> ?OTYPE_SIGNATURE -> {signature, Rest}; ?OTYPE_CONTRACT -> {contract, Rest}; ?OTYPE_ORACLE -> {oracle, Rest}; + ?OTYPE_ORACLE_Q -> {oracle_query, Rest}; ?OTYPE_NAME -> {name, Rest}; ?OTYPE_CHANNEL -> {channel, Rest} end; @@ -371,6 +390,7 @@ deserialize2(<>) -> ?OTYPE_SIGNATURE -> ?FATE_SIGNATURE(A); ?OTYPE_CONTRACT -> ?FATE_CONTRACT(A); ?OTYPE_ORACLE -> ?FATE_ORACLE(A); + ?OTYPE_ORACLE_Q -> ?FATE_ORACLE_Q(A); ?OTYPE_NAME -> ?FATE_NAME(A); ?OTYPE_CHANNEL -> ?FATE_CHANNEL(A) end, @@ -424,7 +444,10 @@ deserialize2(<>) -> true -> {?FATE_VARIANT(Arities, Tag, T), Rest3} end - end. + end; +deserialize2(<> = 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)]. diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 1458a34..eb3c47b 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -141,15 +141,14 @@ ops_defs() -> , { '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."} %% 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"} + , { '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,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, 3, [a,a,a,a,a,a], oracle_query, {oracle, any, integer, variant, variant}, oracle_query, "Arg0 := New oracle query for oracle Arg1, question in Arg2, query fee in Arg3, query TTL in Arg4, response TTL in Arg5"} + , { 'ORACLE_RESPOND', 16#63, false, 3, [a,a,a,a], oracle_respond, {signature, oracle, oracle_query,any}, none, "Respond as oracle Arg1 to query in Arg2 with response Arg3. Arg0 contains delegation signature."} + , { 'ORACLE_EXTEND', 16#64, 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, 3, [a,a,a], oracle_get_answer, {oracle, oracle_query}, any, "Arg0 := option variant with answer (if any) from oracle query in Arg1 given by oracle Arg0"} + , { 'ORACLE_GET_QUESTION', 16#66, false, 3, [a,a,a], oracle_get_question, {oracle, oracle_query}, any, "Arg0 := question in oracle query Arg2 given to oracle Arg1"} + , { 'ORACLE_QUERY_FEE', 16#67, false, 3, [a,a], oracle_query_fee, {oracle}, integer, "Arg0 := query fee for oracle Arg1"} , { '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"} diff --git a/test/aeb_fate_asm_test.erl b/test/aeb_fate_asm_test.erl index 64fe2a1..42df184 100644 --- a/test/aeb_fate_asm_test.erl +++ b/test/aeb_fate_asm_test.erl @@ -47,6 +47,7 @@ sources() -> , "tuple" , "mapofmap" , "immediates" + , "oracles" %% , "all_instructions" ]. diff --git a/test/asm_code/oracles.fate b/test/asm_code/oracles.fate new file mode 100644 index 0000000..6e5a953 --- /dev/null +++ b/test/asm_code/oracles.fate @@ -0,0 +1,32 @@ +;; CONTRACT oracles + +FUNCTION register (address, integer, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : oracle + ORACLE_REGISTER a $sg_11111111111111111111111111111111111111111111111111111111111111116oUsJe 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) |) + RETURN + +FUNCTION bogus_query () : oracle_query + RETURNR @oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv + +FUNCTION respond (oracle, integer, string) : {tuple, []} + ORACLE_RESPOND $sg_11111111111111111111111111111111111111111111111111111111111111116oUsJe arg0 arg1 arg2 + RETURNR {} + +FUNCTION extend (oracle, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : {tuple, []} + ORACLE_EXTEND $sg_11111111111111111111111111111111111111111111111111111111111111116oUsJe arg0 arg1 + RETURNR {} + +FUNCTION get_question (oracle, oracle_query) : string + ORACLE_GET_QUESTION arg0 arg1 + RETURN + +FUNCTION get_answer (oracle, oracle_query) : {variant, [{tuple, []}, {tuple, [string]}]} + ORACLE_GET_ANSWER arg0 arg1 + RETURN + +FUNCTION query_fee (oracle) : integer + ORACLE_QUERY_FEE arg0 + RETURN