diff --git a/src/fd_spy.erl b/src/fd_spy.erl new file mode 100644 index 0000000..24b2e9d --- /dev/null +++ b/src/fd_spy.erl @@ -0,0 +1,177 @@ +% @doc spy: knows constraints {recipient, amount, payload} +% +% spies the chain for transactions that satisfy that constraint +% +-module(fd_spy). +-vsn("0.2.0"). + +% MVP: register search patterns +% simply print to console when one of them is found + +-behavior(gen_server). + +-export_type([ +]). + +-export([ + %% caller context + reg/3, + + %% api + start_link/0, + + %% process context + init/1, handle_call/3, handle_cast/2, handle_info/2, + code_change/3, terminate/2 +]). + +% recipient is a string +-type pubkey32() :: <<_:256>>. + +-record(sp, + {recipient :: pubkey32(), + amount :: pos_integer(), + payload :: binary()}). +-type search_pattern() :: #sp{}. + +-record(s, + {searching_for = [] :: search_pattern()}). + +-type state() :: #s{}. + +-include("$zx_include/zx_logger.hrl"). + +%%----------------------------------------------------------------------------- +%% caller context +%%----------------------------------------------------------------------------- + +-spec reg(Recipient, Amount, Payload) -> ok | {error, Reason} + when Recipient :: pubkey32(), + Amount :: pos_integer(), + Payload :: binary(), + Reason :: any(). + +reg(R, A, P) when is_binary(R), byte_size(R) =:= 32, + is_integer(A), A >= 0, + is_binary(P) -> + gen_server:call(?MODULE, {reg, R, A, P}). + + + +%% gen_server callbacks +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, none, []). + + +%%----------------------------------------------------------------------------- +%% process context below this line +%%----------------------------------------------------------------------------- + +%% gen_server callbacks + +-spec init(none) -> {ok, state()}. + +init(none) -> + tell("starting fd_spy"), + InitState = #s{}, + {ok, InitState}. + + + +-spec handle_call(Msg, From, State) -> Result + when Msg :: any(), + From :: {pid(), reference()}, + State :: state(), + Result :: {reply, Reply, NewState} + | {noreply, NewState}, + Reply :: any(), + NewState :: state(). + +handle_call({reg, Recipient, Amount, Payload}, _From, State) -> + {Reply, NewState} = do_reg(Recipient, Amount, Payload, State), + {reply, Reply, NewState}; +handle_call(Unexpected, From, State) -> + tell("~tp: unexpected call from ~tp: ~tp", [?MODULE, Unexpected, From]), + {noreply, State}. + + + +handle_cast(Unexpected, State) -> + tell("~tp: unexpected cast: ~tp", [?MODULE, Unexpected]), + {noreply, State}. + + + +handle_info(Unexpected, State) -> + tell("~tp: unexpected info: ~tp", [?MODULE, Unexpected]), + {noreply, State}. + + + +code_change(_, State, _) -> + {ok, State}. + + + +terminate(_, _) -> + ok. + + + +%%----------------------------------------------------------------------------- +%% internals +%%----------------------------------------------------------------------------- + +-spec do_reg(Recipient, Amount, Payload, State) -> {Reply, NewState} + when Recipient :: pubkey32(), + Amount :: pos_integer(), + Payload :: binary(), + State :: state(), + Reply :: ok + | {error, Reason}, + Reason :: any(), + NewState :: state(). + +do_reg(Recipient, Amount, Payload, State) -> + case already_registered(Recipient, Payload, State) of + true -> {error, already_registered}; + false -> really_register(Recipient, Amount, Payload, State) + end. + + + +-spec already_registered(Recipient, Payload, State) -> boolean() + when Recipient :: pubkey32(), + Payload :: binary(), + State :: state(). + +already_registered(Recipient, Payload, _State = #s{searching_for = SPs}) -> + ar(Recipient, Payload, SPs). + +ar(Recipient, Payload, [#sp{recipient=Recipient, payload=Payload} | _]) -> + true; +ar(Recipient, Payload, [_ | Rest]) -> + ar(Recipient, Payload, Rest); +ar(_, _, []) -> + false. + + + +-spec really_register(Recipient, Amount, Payload, State) -> {Reply, NewState} + when Recipient :: pubkey32(), + Amount :: pos_integer(), + Payload :: binary(), + State :: state(), + Reply :: ok + | {error, Reason}, + Reason :: any(), + NewState :: state(). + +really_register(R, A, P, State = #s{searching_for = SPs}) -> + NewSearchPattern = #sp{recipient = R, + amount = A, + payload = P}, + tell("~tp: really_register(~tp,~tp,~tp)", [?MODULE, R, A, P]), + NewSearchPatterns = [NewSearchPattern | SPs], + NewState = State#s{searching_for = NewSearchPatterns}, + {reply, ok, NewState}. diff --git a/src/fd_sup.erl b/src/fd_sup.erl index 3a31653..e8804c8 100644 --- a/src/fd_sup.erl +++ b/src/fd_sup.erl @@ -36,6 +36,12 @@ start_link() -> init([]) -> RestartStrategy = {one_for_one, 1, 60}, + Spy = {fd_spy, + {fd_spy, start_link, []}, + permanent, + 5000, + worker, + [fd_spy]}, GridsD = {fd_gridsd, {fd_gridsd, start_link, []}, permanent,