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