diff --git a/src/hz.erl b/src/hz.erl index b081f5d..59509db 100644 --- a/src/hz.erl +++ b/src/hz.erl @@ -66,6 +66,7 @@ contract_create/8, prepare_contract/1, prepare_aaci/1, + cache_aaci/2, aaci_lookup_spec/2, contract_call/5, contract_call/6, @@ -1073,7 +1074,7 @@ contract_create2(CreatorID, Nonce, Amount, TTL, Gas, GasPrice, Source, Options, InitArgs :: [string()], Result :: {ok, CreateTX} | {error, Reason}, CreateTX :: binary(), - Reason :: file:posix() | term(). + Reason :: file:posix() | bad_fun_name | aaci_not_found | term(). %% @doc %% This function takes the compiler output (instead of starting from source), %% and returns the unsigned create contract call data with default values. @@ -2207,17 +2208,35 @@ zip_record_field({Name, Type}, {Remaining, Missing}) -> {missing, {Remaining, [Name | Missing]}} end. + +-spec cache_aaci(Label, AACI) -> ok + when Label :: term(), + AACI :: aaci(). +%% @doc +%% Caches an AACI for future reference in calls that would otherwise require +%% the AACI as an argument. Once cached, a pre-built AACI can be referenced in +%% later calls by substituting the AACI argument with `{aaci, Label}'. + +cache_aaci(Label, AACI) -> + hz_man:cache_aaci(Label, AACI). + + -spec aaci_lookup_spec(AACI, Fun) -> {ok, Type} | {error, Reason} when AACI :: aaci(), Fun :: binary() | string(), Type :: {term(), term()}, % FIXME - Reason :: bad_fun_name. + Reason :: bad_fun_name | aaci_not_found. %% @doc %% Look up the type information of a given function, in the AACI provided by %% prepare_contract/1. This type information, particularly the return type, is %% useful for calling decode_bytearray/2. +aaci_lookup_spec({aaci, Label}, Fun) -> + case hz_man:lookup_aaci(Label) of + {ok, AACI} -> aaci_lookup_spec(AACI, Fun); + error -> {error, aaci_not_found} + end; aaci_lookup_spec({aaci, _, FunDefs, _}, Fun) -> case maps:find(Fun, FunDefs) of A = {ok, _} -> A; @@ -2248,10 +2267,15 @@ min_gas() -> 200000. +encode_call_data({aaci, Label}, Fun, Args) -> + case hz_man:lookup_aaci(Label) of + {ok, AACI} -> encode_call_data(AACI, Fun, Args); + error -> {error, aaci_not_found} + end; encode_call_data({aaci, _ContractName, FunDefs, _TypeDefs}, Fun, Args) -> case maps:find(Fun, FunDefs) of {ok, {ArgDef, _ResultDef}} -> encode_call_data2(ArgDef, Fun, Args); - error -> {error, bad_fun_name} + error -> {error, bad_fun_name} end. encode_call_data2(ArgDef, Fun, Args) -> diff --git a/src/hz_man.erl b/src/hz_man.erl index 18248ff..a2b353a 100644 --- a/src/hz_man.erl +++ b/src/hz_man.erl @@ -20,6 +20,9 @@ chain_nodes/0, chain_nodes/1, timeout/0, timeout/1]). +%% Contract caching +-export([cache_aaci/2, lookup_aaci/1]). + %% The whole point of this module: -export([request_sticky/1, request_sticky/2, request/1, request/2]). @@ -44,7 +47,8 @@ chain_nodes = {[], []} :: {[hz:chain_node()], [hz:chain_node()]}, sticky = none :: none | hz:chain_node(), fetchers = [] :: [#fetcher{}], - timeout = 5000 :: pos_integer()}). + timeout = 5000 :: pos_integer(), + cache = #{} :: #{Label :: term() := AACI :: hz:aaci()}}). -type state() :: #s{}. @@ -94,6 +98,22 @@ timeout(Value) when 0 < Value, Value =< 120000 -> gen_server:cast(?MODULE, {timeout, Value}). +-spec cache_aaci(Label, AACI) -> ok + when Label :: term(), + AACI :: hz:aaci(). + +cache_aaci(Label, AACI) -> + gen_server:call(?MODULE, {cache, Label, AACI}). + + +-spec lookup_aaci(Label) -> Result + when Label :: term(), + Result :: {ok, hz:aaci()} | error. + +lookup_aaci(Label) -> + gen_server:call(?MODULE, {lookup, Label}). + + -spec request_sticky(Path) -> {ok, Value} | {error, Reason} when Path :: unicode:charlist(), Value :: map(), @@ -167,6 +187,12 @@ handle_call({request, Request}, From, State) -> handle_call({request_sticky, Request}, From, State) -> NewState = do_request_sticky(Request, From, State), {noreply, NewState}; +handle_call({lookup, Label}, _, State) -> + Result = do_lookup(Label, State), + {reply, Result, State}; +handle_call({cache, Label, AACI}, _, State) -> + NewState = do_cache_aaci(Label, AACI, State), + {reply, ok, NewState}; handle_call(tls, _, State = #s{tls = TLS}) -> {reply, TLS, State}; handle_call(chain_nodes, _, State) -> @@ -265,6 +291,15 @@ do_tls(_, State) -> State. +do_cache_aaci(Label, AACI, State = #s{cache = Cache}) -> + NewCache = maps:put(Label, AACI, Cache), + State#s{cache = NewCache}. + + +do_lookup(Label, #s{cache = Cache}) -> + maps:find(Label, Cache). + + do_request_sticky(_, From, State = #s{sticky = none}) -> ok = gen_server:reply(From, {error, no_nodes}), State;