diff --git a/include/gmc.hrl b/include/gmc.hrl index 5bd8985..5858f46 100644 --- a/include/gmc.hrl +++ b/include/gmc.hrl @@ -10,14 +10,14 @@ -record(chain, - {id = "mint.devnet" :: string(), - coins = ["gaju"] :: [string()], - nodes = [#node{}] :: [#node{}]}). + {id = <<"mint.devnet">> :: binary(), + coins = ["gaju"] :: [string()], + nodes = [#node{}] :: [#node{}]}). -record(net, - {id = "devnet" :: string(), - chains = [#chain{}] :: [#chain{}]}). + {id = <<"devnet">> :: binary(), + chains = [#chain{}] :: [#chain{}]}). @@ -29,17 +29,17 @@ -record(coin, - {id = "gaju" :: string(), - mint = "mint.devnet" :: string(), - acs = [#ac{}] :: [#ac{}]}). + {id = "gaju" :: string(), + mint = <<"mint.devnet">> :: binary(), + acs = [#ac{}] :: [#ac{}]}). % Balance, POA, Key, TXs, all culminate in capturing a complete Wallet view. -record(balance, - {coin = "gaju" :: string(), - total = 0 :: non_neg_integer(), - dist = [{"mint.devnet", 0}] :: [{Chain :: string(), non_neg_integer()}]}). + {coin = "gaju" :: string(), + total = 0 :: non_neg_integer(), + dist = [{<<"mint.devnet">>, 0}] :: [{Chain :: binary(), non_neg_integer()}]}). -record(poa, @@ -75,9 +75,10 @@ -record(wallet, - {version = 1 :: integer(), - poas = [] :: [#poa{}], - keys = [] :: [#key{}], - pass = none :: none | binary(), - network_id = <<"mint.devnet">> :: binary(), - chains = [#chain{}] :: [#chain{}]}). + {version = 1 :: integer(), + poas = [] :: [#poa{}], + keys = [] :: [#key{}], + pass = none :: none | binary(), + chain_id = <<"mint.devnet">> :: binary(), + endpoint = #node{} :: #node{}, + chains = [#chain{}] :: [#chain{}]}). diff --git a/src/gmc_con.erl b/src/gmc_con.erl index ab681f0..fb82aa5 100644 --- a/src/gmc_con.erl +++ b/src/gmc_con.erl @@ -39,14 +39,12 @@ %% Interface --spec open_wallet(Path, Password) -> {ok, Wallet} | {error, Reason} +-spec open_wallet(Path, Password) -> ok when Path :: file:filename(), - Password :: string(), - Wallet :: {Accounts :: [clutch:ak()], Selected :: integer()}, - Reason :: bad_password | file:posix(). + Password :: string(). open_wallet(Path, Password) -> - gen_server:call(?MODULE, {open_wallet, Path, Password}). + gen_server:cast(?MODULE, {open_wallet, Path, Password}). -spec close_wallet() -> ok. @@ -160,6 +158,7 @@ read_prefs() -> proplists:to_map(Prefs); _ -> #{selected => 0, + last => save_path(), lang => en_us, geometry => none} end. @@ -182,9 +181,6 @@ read_prefs() -> %% The gen_server:handle_call/3 callback. %% See: http://erlang.org/doc/man/gen_server.html#Module:handle_call-3 -handle_call({open_wallet, Path, Phrase}, _, State) -> - {Response, NewState} = do_open_wallet(Path, Phrase, State), - {reply, Response, NewState}; handle_call({save, Prefs}, _, State) -> Response = do_save(State#s{prefs = Prefs}), {reply, Response, State}; @@ -216,6 +212,9 @@ handle_cast({rename_key, ID, NewName}, State) -> handle_cast({drop_key, ID}, State) -> NewState = do_drop_key(ID, State), {noreply, NewState}; +handle_cast({open_wallet, Path, Phrase}, State) -> + NewState = do_open_wallet(Path, Phrase, State), + {noreply, NewState}; handle_cast({password, Old, New}, State) -> NewState = do_password(Old, New, State), {noreply, NewState}; @@ -270,7 +269,7 @@ do_make_key(Name, Seed, base64, Transform, State) -> {ok, Bin} -> do_make_key2(Name, Bin, Transform, State); {error, Reason} -> - ok = gmc_gui:notify({error, {base64, Reason}}), + ok = gmc_gui:trouble({error, {base64, Reason}}), State end; do_make_key(Name, Seed, base58, Transform, State) -> @@ -279,7 +278,7 @@ do_make_key(Name, Seed, base58, Transform, State) -> Bin = base58:base58_to_binary(Seed), do_make_key2(Name, Bin, Transform, State); false -> - ok = gmc_gui:notify({error, {base58, badarg}}), + ok = gmc_gui:trouble({error, {base58, badarg}}), State end. @@ -330,7 +329,7 @@ do_recover_key(Mnemonic, State) -> {ok, Seed} -> do_recover_key2(Seed, State); Error -> - ok = gmc_gui:notify(Error), + ok = gmc_gui:trouble(Error), State end. @@ -395,15 +394,25 @@ pass(Phrase) -> do_open_wallet(Path, none, State) -> - case read(none) of - {ok, Recovered = #wallet{poas = POAs}} -> {POAs, State#s{wallet = Recovered}}; - Error -> {Error, State} + case read(Path, none) of + {ok, Recovered = #wallet{poas = POAs, chain_id = ChainID, endpoint = Node}} -> + ok = gmc_gui:show(POAs), + ok = gmc_gui:chain(ChainID, Node), + State#s{wallet = Recovered}; + Error -> + ok = gmc_gui:trouble(Error), + State end; do_open_wallet(Path, Phrase, State) -> Pass = pass(Phrase), - case read(Pass) of - {ok, Recovered = #wallet{poas = POAs}} -> {POAs, State#s{wallet = Recovered}}; - Error -> {Error, State} + case read(Path, Pass) of + {ok, Recovered = #wallet{poas = POAs, chain_id = ChainID, endpoint = Node}} -> + ok = gmc_gui:show(POAs), + ok = gmc_gui:chain(ChainID, Node), + State#s{wallet = Recovered}; + Error -> + ok = gmc_gui:trouble(Error), + State end. @@ -441,7 +450,7 @@ do_spend2(PrivKey, ttl = TTL, nonce = Nonce, payload = Payload}, - #s{wallet = #wallet{network_id = NetworkID}}) -> + #s{wallet = #wallet{chain_id = ChainID}}) -> Type = spend_tx, Vsn = 1, Fields = @@ -463,7 +472,7 @@ do_spend2(PrivKey, {nonce, int}, {payload, binary}], BinaryTX = aeser_chain_objects:serialize(Type, Vsn, Template, Fields), - NetworkTX = <>, + NetworkTX = <>, Signature = ecu_eddsa:sign_detached(NetworkTX, PrivKey), SigTxType = signed_tx, SigTxVsn = 1, @@ -482,30 +491,35 @@ do_save(State = #s{prefs = Prefs}) -> do_save2(State). -do_save2(State = #s{wallet = W = #wallet{pass = none}}) -> +do_save2(#s{wallet = W = #wallet{pass = none}}) -> Path = save_path(), ok = filelib:ensure_dir(Path), file:write_file(Path, term_to_binary(W)); -do_save2(State = #s{wallet = W = #wallet{pass = Pass}}) -> +do_save2(#s{wallet = W = #wallet{pass = Pass}}) -> Path = save_path(), ok = filelib:ensure_dir(Path), Cipher = encrypt(Pass, term_to_binary(W)), file:write_file(Path, Cipher). -read(none) -> - case file:read_file(save_path()) of +read(Path, none) -> + case file:read_file(Path) of {ok, Bin} -> read2(Bin); Error -> Error end; -read(Pass) -> - case file:read_file(save_path()) of +read(Path, Pass) -> + case file:read_file(Path) of {ok, Cipher} -> try Bin = decrypt(Pass, Cipher), read2(Bin) catch - E:R -> {E, R} + error:{error, L, "Can't finalize"} -> + ok = log(info, "Decrypt failed at ~p", [L]), + {error, bad_password}; + E:R -> + tell("Here: ~p", [{E, R}]), + {E, R} end; Error -> Error @@ -519,7 +533,7 @@ read2(Bin) -> save_path() -> - filename:join(zx_lib:path(var, "otpr", "clutch"), "opaque.data"). + filename:join(zx_lib:path(var, "otpr", "clutch"), "default.gaju"). persist(Prefs) -> diff --git a/src/gmc_gui.erl b/src/gmc_gui.erl index 0860c2a..82acf01 100644 --- a/src/gmc_gui.erl +++ b/src/gmc_gui.erl @@ -14,7 +14,7 @@ -behavior(wx_object). -include_lib("wx/include/wx.hrl"). --export([show/1, notify/1, ask_password/0]). +-export([show/1, chain/2, trouble/1, ask_password/0]). -export([start_link/1]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, handle_event/2]). @@ -52,8 +52,12 @@ show(Accounts) -> wx_object:cast(?MODULE, {show, Accounts}). -notify(Message) -> - wx_object:cast(?MODULE, {notify, Message}). +chain(ChainID, Node) -> + wx_object:cast(?MODULE, {chain, ChainID, Node}). + + +trouble(Message) -> + wx_object:cast(?MODULE, {trouble, Message}). ask_password() -> @@ -78,6 +82,11 @@ init(Prefs) -> MainSz = wxBoxSizer:new(?wxVERTICAL), Picker = wxListBox:new(Frame, ?wxID_ANY, [{style, ?wxLC_SINGLE_SEL}]), + ChainB = wxButton:new(Frame, ?wxID_ANY, [{label, "[ChainID]"}, {style, ?wxBORDER_NONE}]), + ChainW = #w{name = chain, id = wxButton:getId(ChainB), wx = ChainB}, + NodeB = wxButton:new(Frame, ?wxID_ANY, [{label, "[Node]"}, {style, ?wxBORDER_NONE}]), + NodeW = #w{name = node, id = wxButton:getId(NodeB), wx = NodeB}, + ID_L = wxStaticText:new(Frame, ?wxID_ANY, J("Account ID: ")), ID_T = wxStaticText:new(Frame, ?wxID_ANY, ""), ID_W = @@ -119,13 +128,17 @@ init(Prefs) -> #w{name = Name, id = wxButton:getId(B), wx = B} end, - Buttons = lists:map(MakeButton, ButtonTemplates), + Buttons = [ChainW, NodeW | lists:map(MakeButton, ButtonTemplates)], + ChainSz = wxBoxSizer:new(?wxHORIZONTAL), AccountSz = wxBoxSizer:new(?wxHORIZONTAL), DetailsSz = wxBoxSizer:new(?wxHORIZONTAL), ActionsSz = wxBoxSizer:new(?wxHORIZONTAL), HistorySz = wxBoxSizer:new(?wxVERTICAL), + _ = wxSizer:add(ChainSz, ChainB, zxw:flags(wide)), + _ = wxSizer:add(ChainSz, NodeB, zxw:flags(wide)), + #w{wx = CopyBn} = lists:keyfind(copy, #w.name, Buttons), #w{wx = WWW_Bn} = lists:keyfind(www, #w.name, Buttons), _ = wxSizer:add(DetailsSz, NumbersSz, zxw:flags(wide)), @@ -153,6 +166,7 @@ init(Prefs) -> #w{wx = Refresh} = lists:keyfind(refresh, #w.name, Buttons), _ = wxSizer:add(HistorySz, Refresh, zxw:flags(base)), + _ = wxSizer:add(MainSz, ChainSz, zxw:flags(base)), _ = wxSizer:add(MainSz, AccountSz, zxw:flags(base)), _ = wxSizer:add(MainSz, Picker, zxw:flags(wide)), _ = wxSizer:add(MainSz, DetailsSz, zxw:flags(base)), @@ -239,6 +253,9 @@ handle_call(Unexpected, From, State) -> handle_cast({show, Accounts}, State) -> NewState = do_show(Accounts, State), {noreply, NewState}; +handle_cast({chain, ChainID, Node}, State) -> + ok = do_chain(ChainID, Node, State), + {noreply, State}; handle_cast(password, State) -> ok = do_ask_password(State), {noreply, State}; @@ -601,7 +618,8 @@ spend(State) -> % State State. -grids_dialogue(State = #s{frame = Frame, j = J}) -> +grids_dialogue(State) -> +%grids_dialogue(State = #s{frame = Frame, j = J}) -> tell("Handle GRIDS URL"), % ok = % case zxw:modal_text_input(Frame, J("GRIDS"), J("ZA GRIDS"), [J("URL")]) of @@ -645,7 +663,20 @@ do_show(Accounts, State = #s{prefs = Prefs, picker = Picker}) -> end. -do_ask_password(#s{frame = Frame, j = J}) -> +do_chain(ChainID, #node{ip = IP}, #s{buttons = Buttons}) -> + #w{wx = ChainB} = lists:keyfind(chain, #w.name, Buttons), + #w{wx = NodeB} = lists:keyfind(node, #w.name, Buttons), + Address = + case inet:is_ip_address(IP) of + true -> inet:ntoa(IP); + false -> IP + end, + Label = unicode:characters_to_list(ChainID), + ok = wxButton:setLabel(ChainB, Label), + ok = wxButton:setLabel(NodeB, Address). + + +do_ask_password(#s{frame = Frame, prefs = Prefs, j = J}) -> Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Password")), Sizer = wxBoxSizer:new(?wxVERTICAL), Label = "Password (leave blank for no password)", @@ -661,7 +692,10 @@ do_ask_password(#s{frame = Frame, j = J}) -> ok = wxBoxSizer:layout(Sizer), ok = wxFrame:center(Dialog), ok = wxStyledTextCtrl:setFocus(PassTx), - Path = filename:join(zx_lib:path(var, "otpr", "clutch"), "opaque.data"), + Path = + maps:get(last, + Prefs, + filename:join(zx_lib:path(var, "otpr", "clutch"), "default.gaju")), ok = case wxDialog:showModal(Dialog) of ?wxID_OK ->