From ab812e428594930dfcedeee5d7eab708bce8ccf2 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Fri, 21 Apr 2023 11:49:14 +0900 Subject: [PATCH 1/8] Add zomp.meta, fix "key_block " typo. --- bindings/erlang/src/vanillae.erl | 44 ++++++++++++++++++-------------- bindings/erlang/zomp.meta | 7 ++++- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/bindings/erlang/src/vanillae.erl b/bindings/erlang/src/vanillae.erl index 6703a2f..70ab4ae 100644 --- a/bindings/erlang/src/vanillae.erl +++ b/bindings/erlang/src/vanillae.erl @@ -75,8 +75,8 @@ % OTP Application Interface -%-export([start/0, stop/0]). -%-export([start/2, stop/1]). +-export([start/0, stop/0]). +-export([start/2, stop/1]). %%% Types @@ -277,7 +277,7 @@ timeout(MS) -> top_height() -> case top_block() of {ok, #{"micro_block" := #{"height" := Height}}} -> {ok, Height}; - {ok, #{"key_block " := #{"height" := Height}}} -> {ok, Height}; + {ok, #{"key_block" := #{"height" := Height}}} -> {ok, Height}; Error -> Error end. @@ -1478,19 +1478,25 @@ eu(N, Size) -> % /v2/debug/crash -%-spec start() -> ok | {error, Reason :: term()}. -% -%start() -> -% application:start(vanillae). -% -% -%-spec start(normal, term()) -> {ok, pid()}. -% -%start(normal, _Args) -> -% vanillae_sup:start_link(). -% -% -%-spec stop(term()) -> ok. -% -%stop(_State) -> -% ok. +-spec start() -> ok | {error, Reason :: term()}. + +start() -> + application:start(vanillae). + + +-spec stop() -> ok | {error, Reason :: term()}. + +stop() -> + application:stop(vanillae). + + +-spec start(normal, term()) -> {ok, pid()}. + +start(normal, _Args) -> + vanillae_sup:start_link(). + + +-spec stop(term()) -> ok. + +stop(_State) -> + ok. diff --git a/bindings/erlang/zomp.meta b/bindings/erlang/zomp.meta index bb0aac5..7f89e08 100644 --- a/bindings/erlang/zomp.meta +++ b/bindings/erlang/zomp.meta @@ -2,7 +2,12 @@ {author,"Craig Everett"}. {c_email,"ceverett@tsuriai.jp"}. {copyright,"Craig Everett"}. -{deps,[]}. +{deps,[{"otpr","aeserialization",{0,1,0}}, + {"otpr","aesophia",{7,1,0}}, + {"otpr","zj",{1,1,0}}, + {"otpr","eblake2",{1,0,0}}, + {"otpr","getopt",{1,0,2}}, + {"otpr","aebytecode",{3,2,0}}]}. {desc,"Erlang library for ecommerce use of the Aeternity blockchain"}. {file_exts,[]}. {key_name,none}. From 1b4b810936f7e0a82b26acdb76856bb2983b6c0f Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Fri, 21 Apr 2023 11:51:09 +0900 Subject: [PATCH 2/8] Add zomp.meta and fix key_block typo --- bindings/erlang/src/vanillae.erl | 44 ++++++++++++++++++-------------- bindings/erlang/zomp.meta | 7 ++++- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/bindings/erlang/src/vanillae.erl b/bindings/erlang/src/vanillae.erl index 6703a2f..70ab4ae 100644 --- a/bindings/erlang/src/vanillae.erl +++ b/bindings/erlang/src/vanillae.erl @@ -75,8 +75,8 @@ % OTP Application Interface -%-export([start/0, stop/0]). -%-export([start/2, stop/1]). +-export([start/0, stop/0]). +-export([start/2, stop/1]). %%% Types @@ -277,7 +277,7 @@ timeout(MS) -> top_height() -> case top_block() of {ok, #{"micro_block" := #{"height" := Height}}} -> {ok, Height}; - {ok, #{"key_block " := #{"height" := Height}}} -> {ok, Height}; + {ok, #{"key_block" := #{"height" := Height}}} -> {ok, Height}; Error -> Error end. @@ -1478,19 +1478,25 @@ eu(N, Size) -> % /v2/debug/crash -%-spec start() -> ok | {error, Reason :: term()}. -% -%start() -> -% application:start(vanillae). -% -% -%-spec start(normal, term()) -> {ok, pid()}. -% -%start(normal, _Args) -> -% vanillae_sup:start_link(). -% -% -%-spec stop(term()) -> ok. -% -%stop(_State) -> -% ok. +-spec start() -> ok | {error, Reason :: term()}. + +start() -> + application:start(vanillae). + + +-spec stop() -> ok | {error, Reason :: term()}. + +stop() -> + application:stop(vanillae). + + +-spec start(normal, term()) -> {ok, pid()}. + +start(normal, _Args) -> + vanillae_sup:start_link(). + + +-spec stop(term()) -> ok. + +stop(_State) -> + ok. diff --git a/bindings/erlang/zomp.meta b/bindings/erlang/zomp.meta index bb0aac5..7f89e08 100644 --- a/bindings/erlang/zomp.meta +++ b/bindings/erlang/zomp.meta @@ -2,7 +2,12 @@ {author,"Craig Everett"}. {c_email,"ceverett@tsuriai.jp"}. {copyright,"Craig Everett"}. -{deps,[]}. +{deps,[{"otpr","aeserialization",{0,1,0}}, + {"otpr","aesophia",{7,1,0}}, + {"otpr","zj",{1,1,0}}, + {"otpr","eblake2",{1,0,0}}, + {"otpr","getopt",{1,0,2}}, + {"otpr","aebytecode",{3,2,0}}]}. {desc,"Erlang library for ecommerce use of the Aeternity blockchain"}. {file_exts,[]}. {key_name,none}. From e8c0242d19bffb67b030928ff7e8af3933af5231 Mon Sep 17 00:00:00 2001 From: Spivee Date: Mon, 1 May 2023 14:37:53 +1000 Subject: [PATCH 3/8] Fix handle_down error message (#2) * Fix handle_down error message It was printing a tuple with `~s`, and also tried to pretty print a fat call stack more than a hundred columns into the first line, which put together made for some confusing exception-during-error-handling messages in the erlang console. * Switch to unicode formatting in handle_down --- bindings/erlang/src/vanillae_man.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bindings/erlang/src/vanillae_man.erl b/bindings/erlang/src/vanillae_man.erl index 53a6e68..eea3516 100644 --- a/bindings/erlang/src/vanillae_man.erl +++ b/bindings/erlang/src/vanillae_man.erl @@ -189,9 +189,11 @@ handle_down(PID, Mon, Info, State = #s{fetchers = Fetchers}) -> {value, #fetcher{time = Time, node = Node, from = From, req = R}, Remaining} -> TS = calendar:system_time_to_rfc3339(Time, [{unit, nanosecond}]), Format = - "ERROR ~s: Fetcher process ~p making request to ~p exited with ~p~n" - "Request contents:~n~n" - "~s", + "ERROR ~ts: Fetcher process ~130tp exited while making request to ~130tp~n" + "Exit reason:~n" + "~tp~n" + "Request contents:~n" + "~tp~n~n", Formatted = io_lib:format(Format, [TS, PID, Node, Info, R]), Message = unicode:characters_to_list(Formatted), ok = gen_server:reply(From, {error, Message}), From 5a92682c22ce196d402e61f23d531fa2521b8dcd Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 1 May 2023 13:38:34 +0900 Subject: [PATCH 4/8] Applicationize Vanillae and fix 'ok' assertion --- bindings/erlang/src/vanillae.erl | 38 ++++++++++++++---------- bindings/erlang/src/vanillae_sup.erl | 43 ++++++++++++++++++++++++++++ bindings/erlang/zomp.meta | 8 +++--- 3 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 bindings/erlang/src/vanillae_sup.erl diff --git a/bindings/erlang/src/vanillae.erl b/bindings/erlang/src/vanillae.erl index 70ab4ae..8a1ce46 100644 --- a/bindings/erlang/src/vanillae.erl +++ b/bindings/erlang/src/vanillae.erl @@ -1033,14 +1033,18 @@ read_aci(Path) -> %% for contract_call/10. contract_call(CallerID, AACI, ConID, Fun, Args) -> - {ok, Nonce} = next_nonce(CallerID), - Gas = min_gas(), - GasPrice = min_gas_price(), - Fee = min_fee(), - Amount = 0, - contract_call(CallerID, Nonce, - Gas, GasPrice, Fee, Amount, - AACI, ConID, Fun, Args). + case next_nonce(CallerID) of + {ok, Nonce} -> + Gas = min_gas(), + GasPrice = min_gas_price(), + Fee = min_fee(), + Amount = 0, + contract_call(CallerID, Nonce, + Gas, GasPrice, Fee, Amount, + AACI, ConID, Fun, Args); + Error -> + Error + end. -spec contract_call(CallerID, Gas, AACI, ConID, Fun, Args) -> Result @@ -1061,13 +1065,17 @@ contract_call(CallerID, AACI, ConID, Fun, Args) -> %% for contract_call/10. contract_call(CallerID, Gas, AACI, ConID, Fun, Args) -> - {ok, Nonce} = next_nonce(CallerID), - GasPrice = min_gas_price(), - Fee = min_fee(), - Amount = 0, - contract_call(CallerID, Nonce, - Gas, GasPrice, Fee, Amount, - AACI, ConID, Fun, Args). + case next_nonce(CallerID) of + {ok, Nonce} -> + GasPrice = min_gas_price(), + Fee = min_fee(), + Amount = 0, + contract_call(CallerID, Nonce, + Gas, GasPrice, Fee, Amount, + AACI, ConID, Fun, Args); + Error -> + Error + end. -spec contract_call(CallerID, Nonce, diff --git a/bindings/erlang/src/vanillae_sup.erl b/bindings/erlang/src/vanillae_sup.erl new file mode 100644 index 0000000..08aedd2 --- /dev/null +++ b/bindings/erlang/src/vanillae_sup.erl @@ -0,0 +1,43 @@ +%%% @doc +%%% Vanillae Erlang Aeternity application supervisor +%%% +%%% The very top level supervisor in the system. It only has one service branch: the +%%% "vanillae_man" (Vanillae Manager). +%%% +%%% See: http://erlang.org/doc/design_principles/applications.html +%%% See: http://zxq9.com/archives/1311 +%%% @end + +-module(vanillae_sup). +-vsn("0.1.0"). +-behaviour(supervisor). +-author("Craig Everett "). +-copyright("Craig Everett "). +-license("GPL-3.0-or-later"). + +-export([start_link/0]). +-export([init/1]). + + +-spec start_link() -> {ok, pid()}. +%% @private +%% This supervisor's own start function. + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + + +-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. +%% @private +%% The OTP init/1 function. + +init([]) -> + RestartStrategy = {one_for_one, 0, 60}, + Manager = {vanillae_man, + {vanillae_man, start_link, []}, + permanent, + 5000, + worker, + [vanillae_man]}, + Children = [Manager], + {ok, {RestartStrategy, Children}}. diff --git a/bindings/erlang/zomp.meta b/bindings/erlang/zomp.meta index 7f89e08..bb0d37c 100644 --- a/bindings/erlang/zomp.meta +++ b/bindings/erlang/zomp.meta @@ -2,12 +2,12 @@ {author,"Craig Everett"}. {c_email,"ceverett@tsuriai.jp"}. {copyright,"Craig Everett"}. -{deps,[{"otpr","aeserialization",{0,1,0}}, - {"otpr","aesophia",{7,1,0}}, +{deps,[{"otpr","aebytecode",{3,2,1}}, + {"otpr","aesophia",{7,1,2}}, + {"otpr","aeserialization",{0,1,0}}, {"otpr","zj",{1,1,0}}, {"otpr","eblake2",{1,0,0}}, - {"otpr","getopt",{1,0,2}}, - {"otpr","aebytecode",{3,2,0}}]}. + {"otpr","getopt",{1,0,2}}]}. {desc,"Erlang library for ecommerce use of the Aeternity blockchain"}. {file_exts,[]}. {key_name,none}. From c8d1c8ae60c333a22025baabdbf721adfbb6735f Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 1 May 2023 13:55:18 +0900 Subject: [PATCH 5/8] Version increase --- bindings/erlang/ebin/vanillae.app | 2 +- bindings/erlang/src/vanillae.erl | 2 +- bindings/erlang/src/vanillae_fetcher.erl | 2 +- bindings/erlang/src/vanillae_man.erl | 2 +- bindings/erlang/src/vanillae_sup.erl | 2 +- bindings/erlang/zomp.meta | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bindings/erlang/ebin/vanillae.app b/bindings/erlang/ebin/vanillae.app index cdb3716..3d714fc 100644 --- a/bindings/erlang/ebin/vanillae.app +++ b/bindings/erlang/ebin/vanillae.app @@ -3,6 +3,6 @@ {registered,[]}, {included_applications,[]}, {applications,[stdlib,kernel]}, - {vsn,"0.1.0"}, + {vsn,"0.2.0"}, {modules,[vanillae,vanillae_fetcher,vanillae_man]}, {mod,{vanillae,[]}}]}. diff --git a/bindings/erlang/src/vanillae.erl b/bindings/erlang/src/vanillae.erl index 8a1ce46..f7247bd 100644 --- a/bindings/erlang/src/vanillae.erl +++ b/bindings/erlang/src/vanillae.erl @@ -26,7 +26,7 @@ %%% @end -module(vanillae). --vsn("0.1.0"). +-vsn("0.2.0"). %-behavior(application). -author("Craig Everett "). -copyright("Craig Everett "). diff --git a/bindings/erlang/src/vanillae_fetcher.erl b/bindings/erlang/src/vanillae_fetcher.erl index 571de3f..523f122 100644 --- a/bindings/erlang/src/vanillae_fetcher.erl +++ b/bindings/erlang/src/vanillae_fetcher.erl @@ -1,5 +1,5 @@ -module(vanillae_fetcher). --vsn("0.1.0"). +-vsn("0.2.0"). -author("Craig Everett "). -copyright("Craig Everett "). -license("MIT"). diff --git a/bindings/erlang/src/vanillae_man.erl b/bindings/erlang/src/vanillae_man.erl index eea3516..5b75611 100644 --- a/bindings/erlang/src/vanillae_man.erl +++ b/bindings/erlang/src/vanillae_man.erl @@ -9,7 +9,7 @@ %%% @end -module(vanillae_man). --vsn("0.1.0"). +-vsn("0.2.0"). -behavior(gen_server). -author("Craig Everett "). -copyright("Craig Everett "). diff --git a/bindings/erlang/src/vanillae_sup.erl b/bindings/erlang/src/vanillae_sup.erl index 08aedd2..2874768 100644 --- a/bindings/erlang/src/vanillae_sup.erl +++ b/bindings/erlang/src/vanillae_sup.erl @@ -9,7 +9,7 @@ %%% @end -module(vanillae_sup). --vsn("0.1.0"). +-vsn("0.2.0"). -behaviour(supervisor). -author("Craig Everett "). -copyright("Craig Everett "). diff --git a/bindings/erlang/zomp.meta b/bindings/erlang/zomp.meta index bb0d37c..d1148a6 100644 --- a/bindings/erlang/zomp.meta +++ b/bindings/erlang/zomp.meta @@ -14,7 +14,7 @@ {license,"MIT"}. {modules,[]}. {name,"Vanillae for Erlang"}. -{package_id,{"otpr","vanillae",{0,1,0}}}. +{package_id,{"otpr","vanillae",{0,2,0}}}. {prefix,"v"}. {repo_url,"https://github.com/aeternity/Vanillae"}. {tags,[]}. From 21040bf4b868194e1056715948b3e14e6ae749c2 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Sun, 7 May 2023 12:32:19 +0900 Subject: [PATCH 6/8] minor --- bindings/erlang/ebin/vanillae.app | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/erlang/ebin/vanillae.app b/bindings/erlang/ebin/vanillae.app index 3d714fc..036c5fb 100644 --- a/bindings/erlang/ebin/vanillae.app +++ b/bindings/erlang/ebin/vanillae.app @@ -4,5 +4,5 @@ {included_applications,[]}, {applications,[stdlib,kernel]}, {vsn,"0.2.0"}, - {modules,[vanillae,vanillae_fetcher,vanillae_man]}, + {modules,[vanillae,vanillae_fetcher,vanillae_man,vanillae_sup]}, {mod,{vanillae,[]}}]}. From 80e80f9f9eae805a64ac8b4ef1d58bf027cbf59c Mon Sep 17 00:00:00 2001 From: Spivee Date: Tue, 9 May 2023 21:11:31 +1000 Subject: [PATCH 7/8] Complex AACI type coercion (#3) * build complex and indirected types into AACI * break up coerce and coerce_step coerce_step handles the looping and accumulation logic, whereas coerce just handles one term, and returns {ok, _} or {error, _}. This way coerce can become recursive, and even without recursion becomes easier to read, due to fewer nested tuples. Pretty insignificant change, but it'll be nice to have something to diff off of more cleanly. * coerce some more types Still haven't covered lists or tuples, but these were enough to cover a real contract that I want to test properly, once I have a node to test on. * process types from all contracts in ACI This includes the contract itself, which is added as an alias for `contract`. This way the ACI can describe and use contract interfaces, and `ct_*` contracts can be passed into vanillae. * coerce lists and tuples --- bindings/erlang/src/vanillae.erl | 491 +++++++++++++++++++++++++++---- 1 file changed, 435 insertions(+), 56 deletions(-) diff --git a/bindings/erlang/src/vanillae.erl b/bindings/erlang/src/vanillae.erl index f7247bd..da1e308 100644 --- a/bindings/erlang/src/vanillae.erl +++ b/bindings/erlang/src/vanillae.erl @@ -1276,69 +1276,462 @@ prepare_contract(File) -> end. prepare_aaci(ACI) -> + % NOTE this will also pick up the main contract; as a result the main + % contract extraction later on shouldn't bother with typedefs. + Contracts = [{N, T} || #{contract := #{name := N, + typedefs := T}} <- ACI], + Types = simplify_contract_types(Contracts, #{}), + [{NameBin, SpecDefs}] = [{N, F} || #{contract := #{kind := contract_main, functions := F, name := N}} <- ACI], Name = binary_to_list(NameBin), - Specs = lists:foldl(fun simplify_specs/2, #{}, SpecDefs), - {aaci, Name, Specs}. + Specs = simplify_specs(SpecDefs, #{}, Types), + {aaci, Name, Specs, Types}. -simplify_specs(#{name := NameBin, arguments := ArgDefs}, Specs) -> +simplify_contract_types([], Types) -> Types; +simplify_contract_types([{NameBin, TypeDefs} | Rest], Types) -> Name = binary_to_list(NameBin), - ArgTypes = lists:map(fun simplify_args/1, ArgDefs), - maps:put(Name, ArgTypes, Specs). + Types2 = maps:put(Name, {[], contract}, Types), + Types3 = simplify_typedefs(TypeDefs, Types2, Name ++ "."), + simplify_contract_types(Rest, Types3). -simplify_args(#{name := NameBin, type := TypeBin}) -> +simplify_typedefs([], Types, _NamePrefix) -> Types; +simplify_typedefs([Next | Rest], Types, NamePrefix) -> + #{name := NameBin, vars := ParamDefs, typedef := T} = Next, + Name = NamePrefix ++ binary_to_list(NameBin), + Params = [binary_to_list(Param) || #{name := Param} <- ParamDefs], + Type = opaque_type(Params, T), + NewTypes = maps:put(Name, {Params, Type}, Types), + simplify_typedefs(Rest, NewTypes, NamePrefix). + +simplify_specs([], Specs, _Types) -> Specs; +simplify_specs([#{name := NameBin, arguments := ArgDefs} | Rest], Specs, Types) -> Name = binary_to_list(NameBin), - Type = type(TypeBin), + ArgTypes = [simplify_args(Arg, Types) || Arg <- ArgDefs], + NewSpecs = maps:put(Name, ArgTypes, Specs), + simplify_specs(Rest, NewSpecs, Types). + +simplify_args(#{name := NameBin, type := TypeDef}, Types) -> + Name = binary_to_list(NameBin), + % FIXME We should make this error more informative, and continue + % propogating it up, so that the user can provide their own ACI and find + % out whether it worked or not. At that point ACI -> AACI could almost be a + % module or package of its own. + {ok, Type} = type(TypeDef, Types), {Name, Type}. -type(<<"int">>) -> integer; -type(<<"address">>) -> address; -type(<<"contract">>) -> contract; -type(<<"bool">>) -> boolean; -type(Name) -> binary_to_list(Name). -%type(#{<<"list">> := T}) -> {list, type(T)}; -%type(#{<<"tuple">> := T}) -> {tuple, type(T)}; -%type(#{<<"map">> := {K, V}} -> {map, type(K), type(V)}; -%type(<<"string">>) -> string; +% Type preparation has two goals. First, we need a data structure that can be +% traversed quickly, to take sophia-esque erlang expressions and turn them into +% fate-esque erlang expressions that aebytecode can serialize. Second, we need +% partially substituted names, so that error messages can be generated for why +% "foobar" is not valid as the third field of a `bazquux`, because the third +% field is supposed to be `option(integer)`, not `string`. +% +% To achieve this we need three representations of each type expression, which +% together form an 'annotated type'. First, we need the fully opaque name, +% "bazquux", then we need the normalized name, which is an opaque name with the +% bare-minimum substitution needed to make the outer-most type-constructor an +% identifiable built-in, ADT, or record type, and then we need the flattened +% type, which is the raw {variant, [{Name, Fields}, ...]} or +% {record, [{Name, Type}]} expression that can be used in actual Sophia->FATE +% coercion. The type sub-expressions in these flattened types will each be +% fully annotated as well, i.e. they will each contain *all three* of the above +% representations, so that coercion of subexpressions remains fast AND +% informative. +% +% In a lot of cases the opaque type given will already be normalized, in which +% case either the normalized field or the non-normalized field of an annotated +% type can simple be the atom `already_normalized`, which means error messages +% can simply render the normalized type expression and know that the error will +% make sense. -coerce({{ArgName, integer}, S}, {Good, Broken}) -> - try - N = list_to_integer(S), - {[N | Good], Broken} - catch - error:Reason -> {Good, [{ArgName, Reason} | Broken]} +type(T, Types) -> + O = opaque_type([], T), + flatten_opaque_type(O, Types). + +opaque_type(Params, NameBin) when is_binary(NameBin) -> + Name = opaque_type_name(NameBin), + case not is_atom(Name) and lists:member(Name, Params) of + false -> Name; + true -> {var, Name} end; -coerce({{ArgName, address}, S}, {Good, Broken}) -> +opaque_type(Params, #{record := FieldDefs}) -> + Fields = [{binary_to_list(Name), opaque_type(Params, Type)} + || #{name := Name, type := Type} <- FieldDefs], + {record, Fields}; +opaque_type(Params, #{variant := VariantDefs}) -> + ConvertVariant = fun(Pair) -> + [{Name, Types}] = maps:to_list(Pair), + {binary_to_list(Name), [opaque_type(Params, Type) || Type <- Types]} + end, + Variants = lists:map(ConvertVariant, VariantDefs), + {variant, Variants}; +opaque_type(Params, #{tuple := TypeDefs}) -> + {tuple, [opaque_type(Params, Type) || Type <- TypeDefs]}; +opaque_type(Params, Pair) when is_map(Pair) -> + [{Name, TypeArgs}] = maps:to_list(Pair), + {opaque_type_name(Name), [opaque_type(Params, Arg) || Arg <- TypeArgs]}. + +% atoms for builtins, lists for user defined types +opaque_type_name(<<"int">>) -> integer; +opaque_type_name(<<"address">>) -> address; +opaque_type_name(<<"contract">>) -> contract; +opaque_type_name(<<"bool">>) -> boolean; +opaque_type_name(<<"option">>) -> option; +opaque_type_name(<<"list">>) -> list; +opaque_type_name(<<"map">>) -> map; +opaque_type_name(<<"string">>) -> string; +opaque_type_name(Name) -> binary_to_list(Name). + +flatten_opaque_type(T, Types) -> + case normalize_opaque_type(T, Types) of + {ok, AlreadyNormalized, NOpaque, NExpanded} -> + flatten_opaque_type2(T, AlreadyNormalized, NOpaque, NExpanded, + Types); + Error -> Error + end. + +flatten_opaque_type2(T, AlreadyNormalized, NOpaque, NExpanded, Types) -> + case flatten_normalized_type(NExpanded, Types) of + {ok, Flat} -> + case AlreadyNormalized of + true -> {ok, {T, already_normalized, Flat}}; + false -> {ok, {T, NOpaque, Flat}} + end; + Error -> Error + end. + +flatten_opaque_types([T | Rest], Types, Acc) -> + case flatten_opaque_type(T, Types) of + {ok, Type} -> flatten_opaque_types(Rest, Types, [Type | Acc]); + Error -> Error + end; +flatten_opaque_types([], _Types, Acc) -> + {ok, lists:reverse(Acc)}. + +flatten_opaque_bindings([{Name, T} | Rest], Types, Acc) -> + case flatten_opaque_type(T, Types) of + {ok, Type} -> flatten_opaque_bindings(Rest, Types, [{Name, Type} | Acc]); + Error -> Error + end; +flatten_opaque_bindings([], _Types, Acc) -> + {ok, lists:reverse(Acc)}. + +flatten_opaque_variants([{Name, Elems} | Rest], Types, Acc) -> + case flatten_opaque_types(Elems, Types, []) of + {ok, ElemsFlat} -> + flatten_opaque_variants(Rest, Types, [{Name, ElemsFlat} | Acc]); + Error -> Error + end; +flatten_opaque_variants([], _Types, Acc) -> + {ok, lists:reverse(Acc)}. + +flatten_normalized_type(PrimitiveType, _Types) when is_atom(PrimitiveType) -> + {ok, PrimitiveType}; +flatten_normalized_type({variant, VariantsOpaque}, Types) -> + case flatten_opaque_variants(VariantsOpaque, Types, []) of + {ok, Variants} -> {ok, {variant, Variants}}; + Error -> Error + end; +flatten_normalized_type({record, FieldsOpaque}, Types) -> + case flatten_opaque_bindings(FieldsOpaque, Types, []) of + {ok, Fields} -> {ok, {record, Fields}}; + Error -> Error + end; +flatten_normalized_type({T, ElemsOpaque}, Types) -> + case flatten_opaque_types(ElemsOpaque, Types, []) of + {ok, Elems} -> {ok, {T, Elems}}; + Error -> Error + end. + +normalize_opaque_type(T, Types) -> + case type_is_expanded(T) of + false -> normalize_opaque_type(T, Types, true); + true -> {ok, true, T, T} + end. + +% FIXME detect infinite loops +% FIXME detect builtins with the wrong number of arguments +% FIXME should nullary types have an empty list of arguments added before now? +normalize_opaque_type({option, [T]}, _Types, IsFirst) -> + % Just like user-made ADTs, 'option' is considered part of the type, and so + % options are considered normalised. + {ok, IsFirst, {option, [T]}, {variant, [{"None", []}, {"Some", [T]}]}}; +normalize_opaque_type(T, Types, IsFirst) when is_list(T) -> + normalize_opaque_type({T, []}, Types, IsFirst); +normalize_opaque_type({T, TypeArgs}, Types, IsFirst) when is_list(T) -> + case maps:get(T, Types, not_found) of + %{error, invalid_aci}; % FIXME more info + % FIXME We don't understand lookups from other scopes, so we can't + % really prove that the user is wrong, so just assume it is a type that + % we don't understand. + not_found -> {ok, IsFirst, {T, TypeArgs}, {unknown_type, TypeArgs}}; + {TypeParamNames, Definition} -> + Bindings = lists:zip(TypeParamNames, TypeArgs), + normalize_opaque_type2(T, TypeArgs, Types, IsFirst, Bindings, Definition) + end. + +normalize_opaque_type2(T, TypeArgs, Types, IsFirst, Bindings, Definition) -> + SubResult = case Bindings of + [] -> {ok, Definition}; + _ -> substitute_opaque_type(Bindings, Definition) + end, + case SubResult of + % Type names were already normalized if they were ADTs or records, + % since for those connectives the name is considered part of the type. + {ok, NextT = {variant, _}} -> {ok, IsFirst, {T, TypeArgs}, NextT}; + {ok, NextT = {record, _}} -> {ok, IsFirst, {T, TypeArgs}, NextT}; + % Everything else has to be substituted down to a built-in connective + % to be considered normalized. + {ok, NextT} -> normalize_opaque_type3(NextT, Types); + Error -> Error + end. + +% while this does look like normalize_opaque_type/2, it sets IsFirst to false +% instead of true, and is part of the loop, instead of being an initial +% condition for the loop. +normalize_opaque_type3(NextT, Types) -> + case type_is_expanded(NextT) of + false -> normalize_opaque_type(NextT, Types, false); + true -> {ok, false, NextT, NextT} + end. + +% Strings indicate names that should be substituted. Atoms indicate built in +% types, which don't need to be expanded, except for option. +type_is_expanded({option, _}) -> false; +type_is_expanded(X) when is_atom(X) -> true; +type_is_expanded({X, _}) when is_atom(X) -> true; +type_is_expanded(_) -> false. + +% Skip traversal if there is nothing to substitute. This will often be the +% most common case. +substitute_opaque_type(Bindings, {var, VarName}) -> + case lists:keyfind(VarName, 1, Bindings) of + false -> {error, invalid_aci}; + {_, TypeArg} -> {ok, TypeArg} + end; +substitute_opaque_type(Bindings, {Connective, Args}) -> + case substitute_opaque_types(Bindings, Args, []) of + {ok, Result} -> {ok, {Connective, Result}}; + Error -> Error + end; +substitute_opaque_type(_Bindings, Type) -> {ok, Type}. + +substitute_opaque_types(Bindings, [Next | Rest], Acc) -> + case substitute_opaque_type(Bindings, Next) of + {ok, Result} -> substitute_opaque_types(Bindings, Rest, [Result | Acc]); + Error -> Error + end; +substitute_opaque_types(_Bindings, [], Acc) -> + {ok, lists:reverse(Acc)}. + +coerce_bindings(VarTypes, Terms) -> + DefLength = length(VarTypes), + ArgLength = length(Terms), + if + DefLength =:= ArgLength -> coerce_zipped_bindings(lists:zip(VarTypes, Terms)); + DefLength > ArgLength -> {error, too_few_args}; + DefLength < ArgLength -> {error, too_many_args} + end. + +coerce_zipped_bindings(Bindings) -> + case lists:foldl(fun coerce_step/2, {[], []}, Bindings) of + {Coerced, []} -> + {ok, lists:reverse(Coerced)}; + {_, Errors} -> + {error, {args, lists:reverse(Errors)}} + end. + +coerce_step({{ArgName, AnnotatedType}, Term}, {Good, Broken}) -> + case coerce(AnnotatedType, Term) of + {ok, FATETerm} -> {[FATETerm | Good], Broken}; + {error, Error} -> {Good, [{ArgName, Error} | Broken]} + end. + +coerce({_, _, integer}, S) when is_integer(S) -> + {ok, S}; +coerce({O, N, integer}, S) when is_list(S) -> + try + Val = list_to_integer(S), + {ok, Val} + catch + error:badarg -> {error, {invalid, O, N, S}} + end; +coerce({O, N, address}, S) -> try case aeser_api_encoder:decode(unicode:characters_to_binary(S)) of - {account_pubkey, Key} -> {[{address, Key} | Good], Broken}; - _ -> {Good, [{ArgName, bad_pubkey} | Broken]} + {account_pubkey, Key} -> {ok, {address, Key}}; + _ -> {error, bad_pubkey} end catch - error:Reason -> {Good, [{ArgName, Reason} | Broken]} + error:_ -> {error, {invalid, O, N, S}} end; -coerce({{ArgName, contract}, S}, {Good, Broken}) -> +coerce({O, N, contract}, S) -> try case aeser_api_encoder:decode(unicode:characters_to_binary(S)) of - R = {contract_bytearray, _} -> {[R | Good], Broken}; - _ -> {Good, [{ArgName, bad_contract} | Broken]} + {contract_pubkey, Key} -> {ok, {contract, Key}}; + _ -> {error, bad_contract} end catch - error:Reason -> {Good, [{ArgName, Reason} | Broken]} + error:_ -> {error, {invalid, O, N, S}} end; -coerce({{_, bool}, true}, {Good, Broken}) -> - {[true | Good], Broken}; -coerce({{_, bool}, false}, {Good, Broken}) -> - {[false | Good], Broken}; -coerce({{ArgName, bool}, _}, {Good, Broken}) -> - {Good, [{ArgName, not_bool} | Broken]}; -coerce({_, S}, {Good, Broken}) -> - {[S | Good], Broken}. +coerce({_, _, boolean}, true) -> + {ok, true}; +coerce({_, _, boolean}, false) -> + {ok, false}; +coerce({_, _, boolean}, _) -> + {error, not_bool}; +coerce({O, N, string}, Str) -> + case unicode:characters_to_binary(Str) of + {error, _, _} -> + {error, invalid_string, O, N, Str}; + {incomplete, _, _} -> + {error, invalid_string, O, N, Str}; + StrBin -> + {ok, StrBin} + end; +coerce({_, _, {list, [Type]}}, Data) when is_list(Data) -> + coerce_list(Type, Data, []); +coerce({_, _, {map, [KeyType, ValType]}}, Data) when is_map(Data) -> + coerce_map(KeyType, ValType, maps:iterator(Data), #{}); +coerce({O, N, {tuple, ElementTypes}}, Data) when is_tuple(Data) -> + ElementList = tuple_to_list(Data), + coerce_tuple(O, N, ElementTypes, ElementList); +coerce({O, N, {variant, Variants}}, Data) when is_tuple(Data), tuple_size(Data) > 0 -> + [Name | Fields] = tuple_to_list(Data), + case lookup_variant(Name, Variants) of + {Tag, FieldTypes} -> + coerce_variant2(O, N, Variants, Name, Tag, FieldTypes, Fields); + not_found -> + ValidNames = [Valid || {Valid, _} <- Variants], + {error, {adt_invalid, O, N, Name, ValidNames}} + end; +coerce({O, N, {variant, Variants}}, Name) when is_list(Name) -> + coerce({O, N, {variant, Variants}}, {Name}); +coerce({O, N, {record, Fields}}, Map) when is_map(Map) -> + coerce_map_to_record(O, N, Fields, Map); +coerce({O, N, {unknown_type, _}}, Data) -> + case N of + already_normalized -> + io:format("Warning: Unknown type ~p. Using term ~p as is.~n", [O, Data]); + _ -> + io:format("Warning: Unknown type ~p (i.e. ~p). Using term ~p as is.~n", [O, N, Data]) + end, + {ok, Data}; +coerce({O, N, _}, Data) -> {error, {invalid, O, N, Data}}. +coerce_list(Type, [Next | Rest], Acc) -> + case coerce(Type, Next) of + {ok, Coerced} -> coerce_list(Type, Rest, [Coerced | Acc]); + Error -> Error + end; +coerce_list(_Type, [], Acc) -> + {ok, lists:reverse(Acc)}. + +coerce_map(KeyType, ValType, Remaining, Acc) -> + case maps:next(Remaining) of + {K, V, RemainingAfter} -> + coerce_map2(KeyType, ValType, RemainingAfter, Acc, K, V); + none -> {ok, Acc} + end. + +coerce_map2(KeyType, ValType, Remaining, Acc, K, V) -> + case coerce(KeyType, K) of + {ok, KFATE} -> + coerce_map3(KeyType, ValType, Remaining, Acc, KFATE, V); + Error -> Error + end. + +coerce_map3(KeyType, ValType, Remaining, Acc, KFATE, V) -> + case coerce(ValType, V) of + {ok, VFATE} -> + NewAcc = Acc#{KFATE => VFATE}, + coerce_map(KeyType, ValType, Remaining, NewAcc); + Error -> Error + end. + +lookup_variant(Name, Variants) -> lookup_variant(Name, Variants, 0). + +lookup_variant(Name, [{Name, Fields} | _], Tag) -> {Tag, Fields}; +lookup_variant(Name, [_ | Rest], Tag) -> + lookup_variant(Name, Rest, Tag + 1); +lookup_variant(_Name, [], _Tag) -> + not_found. + +coerce_tuple(O, N, FieldTypes, Fields) -> + case coerce_tuple_elements(FieldTypes, Fields, []) of + {ok, FATETuple} -> + {ok, {tuple, FATETuple}}; + {error, too_few_terms} -> + {error, {tuple_too_few_terms, O, N, FieldTypes, Fields}}; + {error, too_many_terms} -> + {error, {tuple_too_many_terms, O, N, FieldTypes, Fields}}; + Error -> Error + end. + +coerce_variant2(O, N, Variants, Name, Tag, FieldTypes, Fields) -> + case coerce_tuple_elements(FieldTypes, Fields, []) of + {ok, FATETuple} -> + Arities = [length(VariantTerms) + || {_, VariantTerms} <- Variants], + {ok, {variant, Arities, Tag, FATETuple}}; + {error, too_few_terms} -> + {error, {adt_too_few_terms, O, N, Name, FieldTypes, Fields}}; + {error, too_many_terms} -> + {error, {adt_too_many_terms, O, N, Name, FieldTypes, Fields}}; + Error -> Error + end. + +coerce_tuple_elements([Type | Types], [Field | Fields], Acc) -> + case coerce(Type, Field) of + {ok, Value} -> coerce_tuple_elements(Types, Fields, [Value | Acc]); + Error -> Error + end; +coerce_tuple_elements([], [], Acc) -> + {ok, list_to_tuple(lists:reverse(Acc))}; +coerce_tuple_elements(_, [], _) -> + {error, too_few_terms}; +coerce_tuple_elements([], _, _) -> + {error, too_many_terms}. + +coerce_map_to_record(O, N, Fields, Map) -> + case zip_record_fields(Fields, Map) of + {ok, Zipped} -> + case coerce_zipped_bindings(Zipped) of + {ok, FATEFields} -> + {ok, {tuple, list_to_tuple(FATEFields)}}; + Error -> Error % FIXME when do we wrap errors vs propogate + % them? Hard to say until we actually render + % them. + end; + {error, {missing_fields, Missing}} -> + {error, {missing_fields, O, N, Missing}}; + {error, {unexpected_fields, Unexpected}} -> + Names = [Name || {Name, _} <- maps:to_list(Unexpected)], + {error, {unexpected_fields, O, N, Names}} + end. + +zip_record_fields(Fields, Map) -> + case lists:mapfoldl(fun zip_record_field/2, {Map, []}, Fields) of + {_, {_, Missing = [_|_]}} -> + {error, {missing_fields, lists:reverse(Missing)}}; + {_, {Remaining, _}} when map_size(Remaining) > 0 -> + {error, {unexpected_fields, Remaining}}; + {Zipped, _} -> + {ok, Zipped} + end. + +zip_record_field({Name, Type}, {Remaining, Missing}) -> + case maps:take(Name, Remaining) of + {Term, RemainingAfter} -> + ZippedTerm = {{Name, Type}, Term}, + {ZippedTerm, {RemainingAfter, Missing}}; + error -> + {missing, {Remaining, [Name | Missing]}} + end. -spec min_gas_price() -> integer(). %% @doc @@ -1384,32 +1777,18 @@ min_fee() -> 200000000000000. -encode_call_data({aaci, _, FunDefs}, Fun, Args) -> +encode_call_data({aaci, _ContractName, FunDefs, _TypeDefs}, Fun, Args) -> case maps:find(Fun, FunDefs) of {ok, ArgDef} -> encode_call_data2(ArgDef, Fun, Args); error -> {error, bad_fun_name} end. encode_call_data2(ArgDef, Fun, Args) -> - DefLength = length(ArgDef), - ArgLength = length(Args), - if - DefLength =:= ArgLength -> encode_call_data3(ArgDef, Fun, Args); - DefLength > ArgLength -> {error, too_few_args}; - DefLength < ArgLength -> {error, too_many_args} + case coerce_bindings(ArgDef, Args) of + {ok, Coerced} -> aeb_fate_abi:create_calldata(Fun, Coerced); + Error -> Error end. -encode_call_data3(ArgDef, Fun, Args) -> - Binding = lists:zip(ArgDef, Args), - case lists:foldl(fun coerce/2, {[], []}, Binding) of - {Coerced, []} -> - Reversed = lists:reverse(Coerced), - aeb_fate_abi:create_calldata(Fun, Reversed); - {_, Errors} -> - {error, {args, lists:reverse(Errors)}} - end. - - verify_signature(Sig, Message, PubKey) -> case aeser_api_encoder:decode(PubKey) of {account_pubkey, PK} -> verify_signature2(Sig, Message, PK); From 3677709c89b05cb3494e884b939ec8cc258b96b9 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Thu, 15 Jun 2023 20:20:33 +0900 Subject: [PATCH 8/8] v2 -> v3 --- bindings/erlang/src/vanillae.erl | 136 +++++++++++++++---------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/bindings/erlang/src/vanillae.erl b/bindings/erlang/src/vanillae.erl index da1e308..95fde1c 100644 --- a/bindings/erlang/src/vanillae.erl +++ b/bindings/erlang/src/vanillae.erl @@ -291,7 +291,7 @@ top_height() -> %% Returns the current block height as an integer. top_block() -> - request("/v2/blocks/top"). + request("/v3/blocks/top"). -spec kb_current() -> {ok, CurrentBlock} | {error, Reason} @@ -301,7 +301,7 @@ top_block() -> %% Returns the current keyblock's metadata as a map. kb_current() -> - request("/v2/key-blocks/current"). + request("/v3/key-blocks/current"). -spec kb_current_hash() -> {ok, Hash} | {error, Reason} @@ -316,7 +316,7 @@ kb_current() -> %% ''' kb_current_hash() -> - case request("/v2/key-blocks/current/hash") of + case request("/v3/key-blocks/current/hash") of {ok, #{"reason" := Reason}} -> {error, Reason}; {ok, #{"hash" := Hash}} -> {ok, Hash}; Error -> Error @@ -335,7 +335,7 @@ kb_current_hash() -> %% ''' kb_current_height() -> - case request("/v2/key-blocks/current/height") of + case request("/v3/key-blocks/current/height") of {ok, #{"reason" := Reason}} -> {error, Reason}; {ok, #{"height" := Height}} -> {ok, Height}; Error -> Error @@ -350,7 +350,7 @@ kb_current_height() -> %% `{error, "Beneficiary not configured"}' kb_pending() -> - result(request("/v2/key-blocks/pending")). + result(request("/v3/key-blocks/pending")). -spec kb_by_hash(ID) -> {ok, KeyBlock} | {error, Reason} @@ -361,7 +361,7 @@ kb_pending() -> %% Returns the keyblock identified by the provided hash. kb_by_hash(ID) -> - result(request(["/v2/key-blocks/hash/", ID])). + result(request(["/v3/key-blocks/hash/", ID])). -spec kb_by_height(Height) -> {ok, KeyBlock} | {error, Reason} @@ -373,11 +373,11 @@ kb_by_hash(ID) -> kb_by_height(Height) -> StringN = integer_to_list(Height), - result(request(["/v2/key-blocks/height/", StringN])). + result(request(["/v3/key-blocks/height/", StringN])). %kb_insert(KeyblockData) -> -% request("/v2/key-blocks", KeyblockData). +% request("/v3/key-blocks", KeyblockData). -spec mb_header(ID) -> {ok, MB_Header} | {error, Reason} @@ -388,7 +388,7 @@ kb_by_height(Height) -> %% Returns the header of the microblock indicated by the provided ID (hash). mb_header(ID) -> - result(request(["/v2/micro-blocks/hash/", ID, "/header"])). + result(request(["/v3/micro-blocks/hash/", ID, "/header"])). -spec mb_txs(ID) -> {ok, TXs} | {error, Reason} @@ -399,7 +399,7 @@ mb_header(ID) -> %% Returns a list of transactions included in the microblock. mb_txs(ID) -> - case request(["/v2/micro-blocks/hash/", ID, "/transactions"]) of + case request(["/v3/micro-blocks/hash/", ID, "/transactions"]) of {ok, #{"transactions" := TXs}} -> {ok, TXs}; {ok, #{"reason" := Reason}} -> {error, Reason}; Error -> Error @@ -417,7 +417,7 @@ mb_txs(ID) -> mb_tx_index(ID, Index) -> StrHeight = integer_to_list(Index), - result(request(["/v2/micro-blocks/hash/", ID, "/transactions/index/", StrHeight])). + result(request(["/v3/micro-blocks/hash/", ID, "/transactions/index/", StrHeight])). -spec mb_tx_count(ID) -> {ok, Count} | {error, Reason} @@ -428,7 +428,7 @@ mb_tx_index(ID, Index) -> %% Retrieve the number of transactions contained in the indicated microblock. mb_tx_count(ID) -> - case request(["/v2/micro-blocks/hash/", ID, "/transactions/count"]) of + case request(["/v3/micro-blocks/hash/", ID, "/transactions/count"]) of {ok, #{"count" := Count}} -> {ok, Count}; {ok, #{"reason" := Reason}} -> {error, Reason}; Error -> Error @@ -443,7 +443,7 @@ mb_tx_count(ID) -> %% the current generation. gen_current() -> - result(request("/v2/generations/current")). + result(request("/v3/generations/current")). -spec gen_by_id(ID) -> {ok, Generation} | {error, Reason} @@ -454,7 +454,7 @@ gen_current() -> %% Retrieve generation data (keyblock and list of associated microblocks) by keyhash. gen_by_id(ID) -> - result(request(["/v2/generations/hash/", ID])). + result(request(["/v3/generations/hash/", ID])). -spec gen_by_height(Height) -> {ok, Generation} | {error, Reason} @@ -466,7 +466,7 @@ gen_by_id(ID) -> gen_by_height(Height) -> StrHeight = integer_to_list(Height), - result(request(["/v2/generations/height/", StrHeight])). + result(request(["/v3/generations/height/", StrHeight])). -spec acc(AccountID) -> {ok, Account} | {error, Reason} @@ -477,7 +477,7 @@ gen_by_height(Height) -> %% Retrieve account data by account ID (public key). acc(AccountID) -> - result(request(["/v2/accounts/", AccountID])). + result(request(["/v3/accounts/", AccountID])). -spec acc_at_height(AccountID, Height) -> {ok, Account} | {error, Reason} @@ -490,7 +490,7 @@ acc(AccountID) -> acc_at_height(AccountID, Height) -> StrHeight = integer_to_list(Height), - case request(["/v2/accounts/", AccountID, "/height/", StrHeight]) of + case request(["/v3/accounts/", AccountID, "/height/", StrHeight]) of {ok, #{"reason" := "Internal server error"}} -> {error, gc_out_of_range}; {ok, #{"reason" := Reason}} -> {error, Reason}; Result -> Result @@ -507,7 +507,7 @@ acc_at_height(AccountID, Height) -> %% block represented the current state of the chain. acc_at_block_id(AccountID, BlockID) -> - case request(["/v2/accounts/", AccountID, "/hash/", BlockID]) of + case request(["/v3/accounts/", AccountID, "/hash/", BlockID]) of {ok, #{"reason" := "Internal server error"}} -> {error, gc_out_of_range}; {ok, #{"reason" := Reason}} -> {error, Reason}; Result -> Result @@ -522,7 +522,7 @@ acc_at_block_id(AccountID, BlockID) -> %% Retrieve a list of transactions pending for the given account. acc_pending_txs(AccountID) -> - request(["/v2/accounts/", AccountID, "/transactions/pending"]). + request(["/v3/accounts/", AccountID, "/transactions/pending"]). -spec next_nonce(AccountID) -> {ok, Nonce} | {error, Reason} @@ -533,13 +533,13 @@ acc_pending_txs(AccountID) -> %% Retrieve the next nonce for the given account next_nonce(AccountID) -> -% case request(["/v2/accounts/", AccountID, "/next-nonce"]) of +% case request(["/v3/accounts/", AccountID, "/next-nonce"]) of % {ok, #{"next_nonce" := Nonce}} -> {ok, Nonce}; % {ok, #{"reason" := "Account not found"}} -> {ok, 1}; % {ok, #{"reason" := Reason}} -> {error, Reason}; % Error -> Error % end. - case request(["/v2/accounts/", AccountID]) of + case request(["/v3/accounts/", AccountID]) of {ok, #{"nonce" := Nonce}} -> {ok, Nonce + 1}; {ok, #{"reason" := "Account not found"}} -> {ok, 1}; {ok, #{"reason" := Reason}} -> {error, Reason}; @@ -597,7 +597,7 @@ dry_run(TX, Accounts, KBHash) -> txs => [#{tx => TXB}], tx_events => true}, JSON = zj:binary_encode(DryData), - request("/v2/dry-run", JSON). + request("/v3/dry-run", JSON). to_binary(S) when is_binary(S) -> S; to_binary(S) when is_list(S) -> list_to_binary(S). @@ -611,7 +611,7 @@ to_binary(S) when is_list(S) -> list_to_binary(S). %% Retrieve a transaction by ID. tx(ID) -> - request(["/v2/transactions/", ID]). + request(["/v3/transactions/", ID]). -spec tx_info(ID) -> {ok, Info} | {error, Reason} @@ -622,7 +622,7 @@ tx(ID) -> %% Retrieve TX metadata by ID. tx_info(ID) -> - result(request(["/v2/transactions/", ID, "/info"])). + result(request(["/v3/transactions/", ID, "/info"])). -spec post_tx(Data) -> {ok, Result} | {error, Reason} @@ -634,7 +634,7 @@ tx_info(ID) -> post_tx(Data) -> JSON = zj:binary_encode(#{tx => Data}), - request("/v2/transactions", JSON). + request("/v3/transactions", JSON). -spec contract(ID) -> {ok, ContractData} | {error, Reason} @@ -645,7 +645,7 @@ post_tx(Data) -> %% Retrieve a contract's metadata by ID. contract(ID) -> - result(request(["/v2/contracts/", ID])). + result(request(["/v3/contracts/", ID])). -spec contract_code(ID) -> {ok, Bytecode} | {error, Reason} @@ -654,7 +654,7 @@ contract(ID) -> Reason :: ae_error() | string(). contract_code(ID) -> - case request(["/v2/contracts/", ID, "/code"]) of + case request(["/v3/contracts/", ID, "/code"]) of {ok, #{"bytecode" := Bytecode}} -> {ok, Bytecode}; {ok, #{"reason" := Reason}} -> {error, Reason}; Error -> Error @@ -667,19 +667,19 @@ contract_code(ID) -> Reason :: ae_error() | string(). contract_poi(ID) -> - request(["/v2/contracts/", ID, "/poi"]). + request(["/v3/contracts/", ID, "/poi"]). % TODO %oracle(ID) -> -% request(["/v2/oracles/", ID]). +% request(["/v3/oracles/", ID]). % TODO %oracle_queries(ID) -> -% request(["/v2/oracles/", ID, "/queries"]). +% request(["/v3/oracles/", ID, "/queries"]). % TODO %oracle_queries_by_id(OracleID, QueryID) -> -% request(["/v2/oracles/", OracleID, "/queries/", QueryID]). +% request(["/v3/oracles/", OracleID, "/queries/", QueryID]). -spec name(Name) -> {ok, Info} | {error, Reason} @@ -690,12 +690,12 @@ contract_poi(ID) -> %% Retrieve a name's chain information. name(Name) -> - result(request(["/v2/names/", Name])). + result(request(["/v3/names/", Name])). % TODO %channel(ID) -> -% request(["/v2/channels/", ID]). +% request(["/v3/channels/", ID]). % FIXME: This should take a specific peer address:port otherwise it will be pointlessly @@ -708,7 +708,7 @@ name(Name) -> %% the given address. peer_pubkey() -> - case request("/v2/peers/pubkey") of + case request("/v3/peers/pubkey") of {ok, #{"pubkey" := Pubkey}} -> {ok, Pubkey}; {ok, #{"reason" := Reason}} -> {error, Reason}; Error -> Error @@ -724,7 +724,7 @@ peer_pubkey() -> %% Retrieve the node's status and meta it currently has about the chain. status() -> - request("/v2/status"). + request("/v3/status"). -spec status_chainends() -> {ok, ChainEnds} | {error, Reason} @@ -734,7 +734,7 @@ status() -> %% Retrieve the latest keyblock hashes status_chainends() -> - request("/v2/status/chain-ends"). + request("/v3/status/chain-ends"). request(Path) -> @@ -1830,39 +1830,39 @@ eu(N, Size) -> %%% Debug functionality % debug_network() -> -% request("/v2/debug/network"). +% request("/v3/debug/network"). % -% /v2/debug/contracts/create -% /v2/debug/contracts/call -% /v2/debug/oracles/register -% /v2/debug/oracles/extend -% /v2/debug/oracles/query -% /v2/debug/oracles/respond -% /v2/debug/names/preclaim -% /v2/debug/names/claim -% /v2/debug/names/update -% /v2/debug/names/transfer -% /v2/debug/names/revoke -% /v2/debug/transactions/spend -% /v2/debug/channels/create -% /v2/debug/channels/deposit -% /v2/debug/channels/withdraw -% /v2/debug/channels/snapshot/solo -% /v2/debug/channels/set-delegates -% /v2/debug/channels/close/mutual -% /v2/debug/channels/close/solo -% /v2/debug/channels/slash -% /v2/debug/channels/settle -% /v2/debug/transactions/pending -% /v2/debug/names/commitment-id -% /v2/debug/accounts/beneficiary -% /v2/debug/accounts/node -% /v2/debug/peers -% /v2/debug/transactions/dry-run -% /v2/debug/transactions/paying-for -% /v2/debug/check-tx/pool/{hash} -% /v2/debug/token-supply/height/{height} -% /v2/debug/crash +% /v3/debug/contracts/create +% /v3/debug/contracts/call +% /v3/debug/oracles/register +% /v3/debug/oracles/extend +% /v3/debug/oracles/query +% /v3/debug/oracles/respond +% /v3/debug/names/preclaim +% /v3/debug/names/claim +% /v3/debug/names/update +% /v3/debug/names/transfer +% /v3/debug/names/revoke +% /v3/debug/transactions/spend +% /v3/debug/channels/create +% /v3/debug/channels/deposit +% /v3/debug/channels/withdraw +% /v3/debug/channels/snapshot/solo +% /v3/debug/channels/set-delegates +% /v3/debug/channels/close/mutual +% /v3/debug/channels/close/solo +% /v3/debug/channels/slash +% /v3/debug/channels/settle +% /v3/debug/transactions/pending +% /v3/debug/names/commitment-id +% /v3/debug/accounts/beneficiary +% /v3/debug/accounts/node +% /v3/debug/peers +% /v3/debug/transactions/dry-run +% /v3/debug/transactions/paying-for +% /v3/debug/check-tx/pool/{hash} +% /v3/debug/token-supply/height/{height} +% /v3/debug/crash -spec start() -> ok | {error, Reason :: term()}.