Compare commits

..

3 Commits

Author SHA1 Message Date
Jarvis Carroll
4220c71417 differently ugly 2025-08-29 12:02:50 +10:00
Jarvis Carroll
e2c0634cb3 Ugly import wizard 2025-08-28 17:16:22 +10:00
Jarvis Carroll
493290a998 Don't create 'Account 1' 2025-08-28 10:36:04 +10:00
13 changed files with 131 additions and 124 deletions

View File

@ -3,7 +3,7 @@
{registered,[]},
{included_applications,[]},
{applications,[stdlib,kernel,sasl,ssl]},
{vsn,"0.8.0"},
{vsn,"0.7.0"},
{modules,[gajudesk,gd_con,gd_grids,gd_gui,gd_jt,
gd_sophia_editor,gd_sup,gd_v,gd_v_devman,gd_v_netman,
gd_v_wallman]},

View File

@ -3,7 +3,7 @@
%%% @end
-module(gajudesk).
-vsn("0.8.0").
-vsn("0.7.0").
-behavior(application).
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>").

View File

@ -3,7 +3,7 @@
%%% @end
-module(gd_con).
-vsn("0.8.0").
-vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later").
@ -14,8 +14,7 @@
selected/1, network/0,
password/2,
refresh/0,
nonce/1, spend/1, chain/1, grids/1,
sign_mess/1, sign_binary/1, sign_tx/1, sign_call/3, dry_run/2,
nonce/1, spend/1, chain/1, grids/1, sign_mess/1, sign_tx/1, sign_call/3, dry_run/2,
deploy/3,
make_key/6, recover_key/1, mnemonic/1, rename_key/2, drop_key/1, list_keys/0,
add_node/1, set_sole_node/1]).
@ -173,13 +172,6 @@ sign_mess(Request) ->
gen_server:cast(?MODULE, {sign_mess, Request}).
-spec sign_binary(Request) -> ok
when Request :: map().
sign_binary(Request) ->
gen_server:cast(?MODULE, {sign_binary, Request}).
-spec sign_tx(Request) -> ok
when Request :: map().
@ -439,9 +431,6 @@ handle_cast({grids, String}, State) ->
handle_cast({sign_mess, Request}, State) ->
ok = do_sign_mess(Request, State),
{noreply, State};
handle_cast({sign_binary, Request}, State) ->
ok = do_sign_binary(Request, State),
{noreply, State};
handle_cast({sign_tx, Request}, State) ->
ok = do_sign_tx(Request, State),
{noreply, State};
@ -690,23 +679,23 @@ do_grids_sig(JSON, URL) ->
do_grids_sig2(Request = #{"grids" := 1, "type" := "message"}) ->
gd_gui:grids_mess_sig(Request);
do_grids_sig2(Request = #{"grids" := 1, "type" := "binary"}) ->
gd_gui:grids_mess_sig(Request);
do_grids_sig2(Request = #{"grids" := 1, "type" := "tx"}) ->
gd_gui:grids_mess_sig(Request);
do_grids_sig2(WTF) ->
gd_gui:trouble({trash, WTF}).
do_sign_mess(Request = #{"public_id" := ID}, #s{wallet = #wallet{keys = Keys}}) ->
do_sign_mess(Request = #{"public_id" := ID, "payload" := Message},
#s{wallet = #wallet{keys = Keys}}) ->
case lists:keyfind(ID, #key.id, Keys) of
#key{pair = #{secret := SecKey}} -> do_sign_mess2(Request, SecKey);
false -> gd_gui:trouble({bad_key, ID})
#key{pair = #{secret := SecKey}} ->
Sig = base64:encode(hz:sign_message(list_to_binary(Message), SecKey)),
do_sign_mess2(Request#{"signature" => Sig});
false ->
gd_gui:trouble({bad_key, ID})
end.
do_sign_mess2(Request = #{"payload" := Message}, SecKey) ->
Sig = base64:encode(hz:sign_message(list_to_binary(Message), SecKey)),
SignedRequest = maps:put("signature", Sig, Request),
do_sign_mess2(Request = #{"url" := URL}) ->
ResponseKeys =
["grids",
"chain",
@ -715,31 +704,11 @@ do_sign_mess2(Request = #{"payload" := Message}, SecKey) ->
"public_id",
"payload",
"signature"],
post_grids_response(ResponseKeys, SignedRequest).
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);
false -> gd_gui:trouble({bad_key, ID})
end.
do_sign_binary2(Request = #{"payload" := Payload}, SecKey) ->
case base64_decode(Payload) of
{ok, Binary} ->
Sig = base64:encode(hz:sign_binary(Binary, SecKey)),
SignedRequest = maps:put("signature", Sig, Request),
ResponseKeys =
["grids",
"chain",
"network_id",
"type",
"public_id",
"payload",
"signature"],
post_grids_response(ResponseKeys, SignedRequest);
Error ->
gd_gui:trouble(Error)
Response = zj:encode(maps:with(ResponseKeys, Request)),
case httpc:request(post, {URL, [], "application/json", Response}, [], []) of
{ok, {{_, 200, _}, _, JSON}} -> log(info, "Signature posted: ~p", [JSON]);
{error, socket_closed_remotely} -> tell("Yep, closed remotely.");
Error -> gd_gui:trouble(Error)
end.
@ -750,22 +719,20 @@ do_sign_tx(Request = #{"public_id" := ID, "payload" := CallData, "network_id" :=
#key{pair = #{secret := SecKey}} ->
BinaryTX = list_to_binary(CallData),
SignedTX = hz:sign_tx(BinaryTX, SecKey, BinNID),
SignedRequest = Request#{"signed" => true, "payload" := SignedTX},
ResponseKeys =
["grids",
"chain",
"network_id",
"type",
"public_id",
"payload",
"signed"],
post_grids_response(ResponseKeys, SignedRequest);
do_sign_tx2(Request#{"signed" => true, "payload" := SignedTX});
false ->
gd_gui:trouble({bad_key, ID})
end.
post_grids_response(ResponseKeys, Request = #{"url" := URL}) ->
do_sign_tx2(Request = #{"url" := URL}) ->
ResponseKeys =
["grids",
"chain",
"network_id",
"type",
"public_id",
"payload",
"signed"],
Response = zj:encode(maps:with(ResponseKeys, Request)),
case httpc:request(post, {URL, [], "application/json", Response}, [], []) of
{ok, {{_, 200, _}, _, JSON}} -> log(info, "Signed TX posted: ~p", [JSON]);

View File

@ -37,7 +37,7 @@
%%% @end
-module(gd_grids).
-vsn("0.8.0").
-vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later").

View File

@ -3,7 +3,7 @@
%%% @end
-module(gd_gui).
-vsn("0.8.0").
-vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later").
@ -1060,48 +1060,6 @@ do_grids_mess_sig2(Request = #{"grids" := 1,
?wxID_CANCEL -> ok
end,
wxDialog:destroy(Dialog);
do_grids_mess_sig2(Request = #{"grids" := 1,
"type" := "binary",
"url" := URL,
"public_id" := ID,
"payload" := Base64},
#s{frame = Frame, j = J}) ->
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Binary Data Signature Request")),
Sizer = wxBoxSizer:new(?wxVERTICAL),
Instruction =
J("The server at the URL below is requesting you sign the following binary data."),
InstTx = wxStaticText:new(Dialog, ?wxID_ANY, Instruction),
AcctSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Signature Account")}]),
AcctTx = wxStaticText:new(Dialog, ?wxID_ANY, ID),
_ = wxStaticBoxSizer:add(AcctSz, AcctTx, zxw:flags(wide)),
URL_Label = J("Originating URL"),
URL_Sz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, URL_Label}]),
URL_Tx = wxStaticText:new(Dialog, ?wxID_ANY, URL),
_ = wxStaticBoxSizer:add(URL_Sz, URL_Tx, zxw:flags(wide)),
MessSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Base-64 Data")}]),
MessStyle = ?wxTE_MULTILINE bor ?wxTE_READONLY,
MessTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{value, Base64}, {style, MessStyle}]),
_ = wxStaticBoxSizer:add(MessSz, MessTx, zxw:flags(wide)),
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
Affirm = wxButton:new(Dialog, ?wxID_OK),
Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
_ = wxBoxSizer:add(Sizer, InstTx, zxw:flags(wide)),
_ = wxBoxSizer:add(Sizer, AcctSz, zxw:flags(wide)),
_ = wxBoxSizer:add(Sizer, URL_Sz, zxw:flags(wide)),
_ = wxBoxSizer:add(Sizer, MessSz, zxw:flags(wide)),
_ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(base)),
ok = wxDialog:setSizer(Dialog, Sizer),
ok = wxDialog:setSize(Dialog, {500, 500}),
ok = wxBoxSizer:layout(Sizer),
ok = wxFrame:center(Dialog),
ok =
case wxDialog:showModal(Dialog) of
?wxID_OK -> gd_con:sign_binary(Request);
?wxID_CANCEL -> ok
end,
wxDialog:destroy(Dialog);
do_grids_mess_sig2(Request = #{"grids" := 1,
"type" := "tx",
"url" := URL,

View File

@ -15,7 +15,7 @@
%%% translation library is retained).
-module(gd_jt).
-vsn("0.8.0").
-vsn("0.7.0").
-export([read_translations/1, j/2, oneshot_j/2]).

View File

@ -1,5 +1,5 @@
-module(gd_sophia_editor).
-vsn("0.8.0").
-vsn("0.7.0").
-export([new/1, update/2,
get_text/1, set_text/2]).

View File

@ -12,7 +12,7 @@
%%% @end
-module(gd_sup).
-vsn("0.8.0").
-vsn("0.7.0").
-behaviour(supervisor).
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>").

View File

@ -1,5 +1,5 @@
-module(gd_v).
-vsn("0.8.0").
-vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later").

View File

@ -1,5 +1,5 @@
-module(gd_v_devman).
-vsn("0.8.0").
-vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later").
@ -590,7 +590,7 @@ deploy(State = #s{code = {Codebook, Pages}}) ->
State;
Index ->
#p{code = CodeTx} = lists:nth(Index + 1, Pages),
Source = wxStyledTextCtrl:getText(CodeTx),
Source = wxTextCtrl:getValue(CodeTx),
deploy2(State, Source)
end.

View File

@ -1,5 +1,5 @@
-module(gd_v_netman).
-vsn("0.8.0").
-vsn("0.7.0").
-author("Craig Everett <zxq9@zxq9.com>").
-copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later").

View File

@ -13,7 +13,7 @@
-module(gd_v_wallman).
-vsn("0.8.0").
-vsn("0.7.0").
-author("Craig Everett <zxq9@zxq9.com>").
-copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later").
@ -203,7 +203,7 @@ handle_event(#wx{event = #wxClose{}}, State = #s{wiz = none}) ->
{noreply, State};
handle_event(#wx{event = #wxCommand{type = command_button_clicked},
id = ID},
State = #s{wiz = {_, WizButtons}}) ->
State = #s{wiz = {_, WizButtons, _}}) ->
NewState =
case lists:keyfind(ID, #w.id, WizButtons) of
#w{name = noob} -> wiz_noob_assist(State);
@ -211,7 +211,7 @@ handle_event(#wx{event = #wxCommand{type = command_button_clicked},
false -> State
end,
{noreply, NewState};
handle_event(#wx{event = #wxClose{}}, State = #s{wiz = {_, _}}) ->
handle_event(#wx{event = #wxClose{}}, State = #s{wiz = WizTuple}) when is_tuple(WizTuple) ->
NewState = close_wiz(State),
{noreply, NewState};
handle_event(Event, State) ->
@ -257,7 +257,7 @@ do_first_run(State = #s{frame = Frame, wallets = Manifest}) ->
end.
close_wiz(State = #s{frame = Frame, wiz = {Wiz, _}}) ->
close_wiz(State = #s{frame = Frame, wiz = {Wiz, _, _}}) ->
ok = wxWindow:destroy(Wiz),
true = wxFrame:show(Frame),
State#s{wiz = none}.
@ -355,7 +355,15 @@ do_open3(Path, State = #s{frame = Frame, j = J}) ->
end.
do_wiz(State = #s{wx = WX, lang = Lang, wiz = none}) ->
do_wiz(State) ->
case find_wallet_files(State) of
false ->
do_wiz_create(State);
{true, WalletDir} ->
do_wiz_import(State, WalletDir)
end.
do_wiz_create(State = #s{wx = WX, lang = Lang, wiz = none}) ->
Trans = gd_jt:read_translations(?MODULE),
J = gd_jt:j(Lang, Trans),
@ -385,22 +393,96 @@ do_wiz(State = #s{wx = WX, lang = Lang, wiz = none}) ->
ok = wxFrame:setSize(Wiz, {300, 300}),
ok = wxFrame:center(Wiz),
true = wxFrame:show(Wiz),
State#s{wiz = {Wiz, Buttons}}.
State#s{wiz = {Wiz, Buttons, create}}.
do_wiz_import(State = #s{wx = WX, lang = Lang, wiz = none}, WalletDir) ->
Trans = gd_jt:read_translations(?MODULE),
J = gd_jt:j(Lang, Trans),
Wiz = wxFrame:new(WX, ?wxID_ANY, J("Initializing Wallet")),
MainSz = wxBoxSizer:new(?wxVERTICAL),
Explanation = wxStaticText:new(Wiz, ?wxID_ANY,
"Reinstall detected.\nDo you want to import an old wallet?",
[{style, ?wxALIGN_CENTRE_HORIZONTAL}]),
wxBoxSizer:add(MainSz, Explanation, zxw:flags(base)),
ButtonTemplates =
[{noob, J("Import an old wallet.")},
{l33t, J("Open the wallet manager.")}],
MakeButton =
fun({Name, Label}) ->
B = wxButton:new(Wiz, ?wxID_ANY, [{label, Label}]),
#w{name = Name, id = wxButton:getId(B), wx = B}
end,
Buttons = lists:map(MakeButton, ButtonTemplates),
Add = fun(#w{wx = Button}) -> wxBoxSizer:add(MainSz, Button, zxw:flags(wide)) end,
ok = lists:foreach(Add, Buttons),
ok = wxFrame:setSizer(Wiz, MainSz),
ok = wxSizer:layout(MainSz),
ok = wxFrame:connect(Wiz, command_button_clicked),
ok = wxFrame:connect(Wiz, close_window),
ok = wxFrame:setSize(Wiz, {300, 300}),
ok = wxFrame:center(Wiz),
true = wxFrame:show(Wiz),
State#s{wiz = {Wiz, Buttons, {import, WalletDir}}}.
wiz_noob_assist(State = #s{j = J, wiz = {Wiz, _}}) ->
find_wallet_files(#s{prefs = Prefs}) ->
DefaultDir = zx_lib:path(var, "otpr", "gajudesk"),
case maps:find(dir, Prefs) of
{ok, DefaultDir} -> find_wallet_files3(DefaultDir);
error -> find_wallet_files3(DefaultDir);
{ok, PrefDir} -> find_wallet_files2(PrefDir, DefaultDir)
end.
find_wallet_files2(PrefDir, DefaultDir) ->
case has_wallet_file(PrefDir) of
true -> {true, PrefDir};
false -> find_wallet_files3(DefaultDir)
end.
find_wallet_files3(DefaultDir) ->
case has_wallet_file(DefaultDir) of
true -> {true, DefaultDir};
false -> false
end.
has_wallet_file(Dir) ->
case file:list_dir_all(Dir) of
{ok, Filenames} ->
lists:any(fun path_is_wallet_file/1, Filenames);
{error, _} ->
false
end.
path_is_wallet_file(Path) ->
case filename:extension(Path) of
".gaju" -> true;
<<".gaju">> -> true;
_ -> false
end.
wiz_noob_assist(State = #s{j = J, wiz = {Wiz, _, create}}) ->
DefaultDir = zx_lib:path(var, "otpr", "gajudesk"),
Name = default_name(),
Path = filename:join(DefaultDir, Name),
case do_new2(Path, J, Wiz) of
ok ->
Label = J("Account 1"),
ok = gd_con:make_key({eddsa, ed25519}, 256, Label, <<>>, none, {sha3, 256}),
ok = do_close(State),
State;
abort ->
State
end.
end;
wiz_noob_assist(State = #s{prefs = Prefs, wiz = {_, _, {import, WalletDir}}}) ->
NewPrefs = maps:put(dir, WalletDir, Prefs),
NewState = State#s{prefs = NewPrefs},
do_import(NewState).
default_name() ->
{{YY, MM, DD}, {Hr, Mn, Sc}} = calendar:local_time(),

View File

@ -2,10 +2,10 @@
{type,gui}.
{modules,[]}.
{prefix,"gd"}.
{desc,"A desktop client for the Gajumaru network of blockchain networks"}.
{author,"Craig Everett"}.
{package_id,{"otpr","gajudesk",{0,8,0}}}.
{deps,[{"otpr","hakuzaru",{0,7,0}},
{desc,"A desktop client for the Gajumaru network of blockchain networks"}.
{package_id,{"otpr","gajudesk",{0,7,0}}}.
{deps,[{"otpr","hakuzaru",{0,6,1}},
{"otpr","eblake2",{1,0,1}},
{"otpr","base58",{0,1,1}},
{"otpr","gmserialization",{0,1,3}},