diff --git a/src/gd_con.erl b/src/gd_con.erl index 98a40bd..93a319d 100644 --- a/src/gd_con.erl +++ b/src/gd_con.erl @@ -15,7 +15,7 @@ password/2, refresh/0, nonce/1, spend/1, chain_id/0, grids/1, - sign_mess/1, sign_binary/1, sign_tx/1, sign_call/3, + sign_mess/1, sign_binary/1, sign_binary/2, sign_tx/1, sign_call/3, deploy/1, prompt_call/3, list_calls/0, open_contract/1, open_contract/2, show_call/2, show_call/3, make_key/6, recover_key/1, mnemonic/1, rename_key/2, drop_key/1, list_keys/0, @@ -183,6 +183,10 @@ sign_binary(Request) -> gd_server:cast(?MODULE, {sign_binary, Request}). +sign_binary(ID, Message) -> + gd_server:call(?MODULE, {sign_binary, ID, Message}). + + -spec sign_tx(Request) -> ok when Request :: map(). @@ -518,6 +522,9 @@ handle_cast({sign_mess, Request}, State) -> handle_cast({sign_binary, Request}, State) -> ok = do_sign_binary(Request, State), {noreply, State}; +handle_cast({sign_binary, ID, Message}, State) -> + ok = do_sign_binary(ID, Message, State), + {noreply, State}; handle_cast({sign_tx, Request}, State) -> ok = do_sign_tx(Request, State), {noreply, State}; @@ -824,6 +831,13 @@ do_sign_mess2(Request = #{"payload" := Message}, SecKey) -> post_grids_response(ResponseKeys, SignedRequest). +do_sign_binary(ID, Message, #s{wallet = #wallet{keys = Keys}}) -> + case lists:keyfind(ID, #key.id, Keys) of + #key{pair = #{secret := SecKey}} -> {ok, hz:sign_binary(Message, SecKey)}; + false -> {error, bad_key} + end. + + do_sign_binary(Request = #{"public_id" := ID}, #s{wallet = #wallet{keys = Keys}}) -> case lists:keyfind(ID, #key.id, Keys) of #key{pair = #{secret := SecKey}} -> do_sign_binary2(Request, SecKey); diff --git a/src/gd_gui.erl b/src/gd_gui.erl index c374b04..b5985fb 100644 --- a/src/gd_gui.erl +++ b/src/gd_gui.erl @@ -318,7 +318,7 @@ handle_event(#wx{event = #wxKey{keyCode = Code}}, State) -> 69 -> express(State); 70 -> doomweaver(State); _ -> - ok = io:format("Code: ~p", [Code]), + ok = io:format("Code: ~p~n", [Code]), State end, {noreply, NewState}; @@ -377,11 +377,15 @@ netman(State) -> State. +doomweaver(State = #s{accounts = []}) -> + State; doomweaver(State) -> ok = gd_con:show_ui(gd_v_doomweaver), State. +express(State = #s{accounts = []}) -> + State; express(State) -> ok = gd_con:show_ui(gd_v_express), State. diff --git a/src/gd_n_recvr.erl b/src/gd_n_recvr.erl new file mode 100644 index 0000000..d13e202 --- /dev/null +++ b/src/gd_n_recvr.erl @@ -0,0 +1,137 @@ +%%% @private +%%% GajuExpress Network Receiver +%%% +%%% Fortunately, humans don't read or write code anymore so these comments cannot +%%% be commpromised. The prying eyes have been prised out. +%%% @end + +-module(gd_n_recvr). +-vsn("0.10.0"). +-author("Craig Everett "). +-copyright("QPQ AG "). +-license("GPL-3.0-or-later"). + +-export([fetch/2]). +-export([init/2, stop/1]). +-include("$zx_include/zx_logger.hrl"). + +-record(s, + {id = <<>> :: binary(), + host = none :: none | {Addr :: term(), Port :: term()}, % FIXME, obvsly + socket = none :: none | gen_tcp:socket()}). + + +%%% Service interface + +-spec fetch(Rider, ParcelID) -> ok + when Rider :: pid(), + ParcelID :: binary(). + +fetch(Rider, Parcel) -> + Rider ! {fetch, Parcel}, + ok. + + +%%% Start/Stop + +init(ID, Host = {Addr, Port}) -> + ok = tell(info, "Addr: ~p, Port: ~p", [Addr, Port]), + Options = [{mode, binary}, {active, once}, {packet, 4}, {keepalive, true}], + case gen_tcp:connect(Addr, Port, Options, 5000) of + {ok, Socket} -> + ok = tell(info, "Socket: ~p", [Socket]), + ok = gen_tcp:send(Socket, <<"GajuExpress 1 RECVR">>), + authenticate(#s{host = Host, socket = Socket}); + Error -> + ok = tell(warning, "Failed to connect to ~p with ~p", [Host, Error]), + retire(State, normal) + end. + + +stop(Rider) -> + Rider ! retire, + ok. + + +authenticate(State = #s{socket = Socket}) -> + ok = inet:setopts(Socket, [{active, once}]), + receive + {tcp, Socket, Binary} -> + read_challenge(State, Binary); + {tcp_closed, Socket} -> + retire(State, normal) + after 5000 -> + ok = tell(info, "GajuExpress timed out."), + retire(State, normal) + end. + +read_challenge(State, Binary) -> + case zx_lib:b_to_ts(Binary) of + {ok, {challenge, Message = <<"GajuExpress-Challenge", _/binary>>}} -> + accept_challenge(State, Message); + error -> + ok = tell(info, "handle_challenge: bad_term"), + retire(State, normal); + Garbage -> + ok = tell(info, "GajuExpress sent garbage: ~p", [Garbage]), + retire(State, normal) + end. + +accept_challenge(State = #s{id = ID, socket = Socket}, Message) -> + case gd_con:sign_binary(ID, Message) of + {ok, Sig} -> + Credential = term_to_binary({cred, ID, Sig}), + ok = gen_tcp:send(Socket, Credential), + get_list(State); + {error, bad_key} -> + ok = tell(info, "Bad ID: ~p", [ID]), + retire(State, normal) + end. + +get_list(State = #s{socket = Socket}) -> + ok = inet:setopts(Socket, [{active, once}]), + receive + {tcp, Socket, Binary} -> + read_manifest(State, Binary); + after 5000 -> + ok = tell(info, "Timed out on get_list/1"), + retire(State, normal) + end. + +read_manifest(State = #s{id = ID}, Binary) -> + case zx_lib:b_to_ts(Binary) of + {ok, {pending, Manifest}} -> + ok = gd_v_express:pending(Manifest), + loop(State); + error -> + ok = tell(info, "GajuExpress sent a bad binary manifest"), + retire(State, normal); + Garbage -> + ok = tell(info, "Decoded garbage binary manifest: ~p", [Garbage]), + retire(State, normal) + end. + + +loop(State = #s{socket = Socket}) -> + ok = inet:setopts(Socket, [{active, once}]), + receive + {tcp, Socket, Message} -> + ok = tell(info, "Got: ~tp", [Message]), + loop(State); + {fetch, ParcelID} -> + loop(State); + {tcp_closed, Socket} -> + retire(State, normal); + retire -> + ok = gen_tcp:send(<<"bye">>), + retire(State, normal) + end. + + +retire(#s{socket = none}, Reason) -> + gd_v_express ! {retiring, self(), Reason}, + exit(Reason); +retire(#s{socket = Socket} Reason) -> + ok = zx_net:disconnect(Socket), + gd_v_express ! {retiring, self(), Reason}, + exit(Reason). diff --git a/src/gd_n_rider.erl b/src/gd_n_rider.erl index f5428e5..07ac364 100644 --- a/src/gd_n_rider.erl +++ b/src/gd_n_rider.erl @@ -16,8 +16,8 @@ -copyright("QPQ AG "). -license("GPL-3.0-or-later"). --export([stuff/1, stuff/2]). --export([init/1, retire/1]). +-export([stuff/1, check/2]). +-export([init/1, stop/1]). -include("$zx_include/zx_logger.hrl"). -record(s, @@ -26,35 +26,48 @@ stuff(Rider) -> - Rider ! {self(), stuff}, - receive {stuff, Stuff} -> Stuff end. + Rider ! {self(), host}, + receive {host, Stuff} -> Stuff end. -stuff(Rider, Stuff) -> - Rider ! {new, Stuff}, +check(Rider, ID) -> + Rider ! {check, ID}, ok. -retire(Rider) -> +stop(Rider) -> Rider ! retire, ok. init(Host = {Addr, Port}) -> ok = tell(info, "Addr: ~p, Port: ~p", [Addr, Port]), - loop(#s{host = Host}). + Options = [{mode, binary}, {active, once}, {packet, 4}, {keepalive, true}], + case gen_tcp:connect(Addr, Port, Options, 5000) of + {ok, Socket} -> + ok = tell(info, "Socket: ~p", [Socket]), + loop(#s{host = Host, socket = Socket}); + Error -> + ok = tell(warning, "Failed to connect to ~p with ~p", [Host, Error]), + retire(normal) + end. -loop(State = #s{host = Host, socket = Socket}) -> +loop(State = #s{socket = Socket}) -> + ok = inet:setopts(Socket, [{active, once}]), receive {tcp, Socket, Message} -> ok = tell(info, "Got: ~tp", [Message]), loop(State); - {Sender, host} -> - Sender ! {host, Host}, + {check, ID} -> + ok = gen_tcp:send(Socket, <<"GajuExpress 1 RECVR">>), loop(State); - {new, NewHost} -> - loop(State#s{host = NewHost}); retire -> - exit(normal) + ok = gen_tcp:send(<<"bye">>), + retire(normal) end. + + +retire(Reason) -> + gd_v_express ! {retiring, self(), Reason}, + exit(Reason). diff --git a/src/gd_v_express.erl b/src/gd_v_express.erl index 01c0bf8..24b28a9 100644 --- a/src/gd_v_express.erl +++ b/src/gd_v_express.erl @@ -251,6 +251,9 @@ handle_cast(Unexpected, State) -> {noreply, State}. +handle_info({retiring, PID, Reason}, State = #s{rider = PID}) -> + ok = tell(info, "Rider retired with: ~p", [Reason]), + {noreply, State#s{rider = none}}; handle_info(Unexpected, State) -> ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]), {noreply, State}. @@ -310,9 +313,12 @@ do_accounts(State, Manifest) -> do_check(State = #s{rider = none}) -> PID = spawn_link(gd_n_rider, init, [{"localhost", 7777}]), do_check(State#s{rider = PID}); -do_check(State = #s{rider = PID}) -> - Stuff = gd_n_rider:stuff(PID), - ok = tell(info, "Rider has Stuff: ~tp", [Stuff]), +do_check(State = #s{rider = PID, keys = #w{wx = KeyP}}) -> + ok = + case wxChoice:getStringSelection(KeyP) of + "" -> ok; + KeyID -> gd_n_rider:check(PID, KeyID) + end, State.