diff --git a/src/gajumine.erl b/src/gajumine.erl index c6fadce..a554125 100644 --- a/src/gajumine.erl +++ b/src/gajumine.erl @@ -9,7 +9,7 @@ -copyright("Craig Everett "). -license("GPL-3.0-or-later"). --export([start/2, stop/1]). +-export([start/2, stop/1, exec_bin_dir/0]). -include("$zx_include/zx_logger.hrl"). @@ -28,7 +28,6 @@ start(normal, _Args) -> end, ok = application:ensure_started(hakuzaru), ok = application:ensure_started(zxwidgets), - ok = application:ensure_started(sophia), gmc_sup:start_link(). @@ -36,3 +35,9 @@ start(normal, _Args) -> stop(_State) -> ok. + + +-spec exec_bin_dir() -> file:filename(). + +exec_bin_dir() -> + gmc_con:bin_dir(). diff --git a/src/gmc_con.erl b/src/gmc_con.erl index 1a2ec3c..211687c 100644 --- a/src/gmc_con.erl +++ b/src/gmc_con.erl @@ -9,7 +9,8 @@ -license("GPL-3.0-or-later"). -behavior(gen_server). --export([unlock/1, make_key/1, load_key/2, end_setup/0]). +-export([start_stop/0]). +-export([unlock/1, make_key/2, load_key/3, end_setup/0, exec_bin_dir/0]). -export([network/0]). -export([start_link/0, stop/0]). -export([init/1, terminate/2, code_change/3, @@ -22,11 +23,12 @@ -record(s, - {version = 1 :: integer(), - window = none :: none | wx:wx_object(), - network = none :: none | mainnet | testnet, - key = none :: none | {blob, binary()} | #key{}, - pass = none :: none | binary()}). + {version = 1 :: integer(), + window = none :: none | wx:wx_object(), + network = none :: none | mainnet | testnet, + exec_dir = platform_dir() :: file:filename(), + key = none :: none | {blob, binary()} | #key{}, + pass = none :: none | binary()}). -type state() :: #s{}. @@ -34,6 +36,12 @@ %% Interface +-spec start_stop() -> ok. + +start_stop() -> + gen_server:cast(?MODULE, start_stop). + + -spec unlock(Phrase) -> ok when Phrase :: string(). @@ -41,19 +49,21 @@ unlock(Phrase) -> gen_server:cast(?MODULE, {unlock, Phrase}). --spec make_key(Phrase) -> ok - when Phrase :: string(). +-spec make_key(Phrase, Network) -> ok + when Phrase :: string(), + Network :: mainnet | testnet. -make_key(Phrase) -> - gen_server:cast(?MODULE, {make_key, Phrase}). +make_key(Phrase, Network) -> + gen_server:cast(?MODULE, {make_key, Phrase, Network}). --spec load_key(Phrase, Mnemonic) -> ok +-spec load_key(Phrase, Mnemonic, Network) -> ok when Phrase :: string(), - Mnemonic :: string(). + Mnemonic :: string(), + Network :: mainnet | testnet. -load_key(Phrase, Mnemonic) -> - gen_server:cast(?MODULE, {load_key, Phrase, Mnemonic}). +load_key(Phrase, Mnemonic, Network) -> + gen_server:cast(?MODULE, {load_key, Phrase, Mnemonic, Network}). -spec end_setup() -> ok. @@ -62,6 +72,12 @@ end_setup() -> gen_server:call(?MODULE, end_setup). +-spec bin_dir() -> file:filename(). + +bin_dir() -> + gen_server:call(?MODULE, bin_dir). + + -spec network() -> mainnet | testnet | none. network() -> @@ -134,6 +150,22 @@ open_wallet() -> error end. +platform_dir() -> + #{deps := Deps} = zx_daemon:meta(), + AppID = lists:keyfind("cuckoo_cpu", 2, Deps), + Priv = filename:join(zx_lib:ppath(lib, AppID), "priv"), + Dir = + case os:type() of + {unix, linux} -> + "linux_x86_64"; + {unix, darwin} -> + % TODO: Check M2 vs x86 + "mac_m2"; + {win32, nt} -> + "win_x86_64" + end, + filename:join(Priv, Dir). + start_gui(State = #s{key = #key{id = ID}}) -> Window = gmc_gui:start_link(#{}), @@ -152,6 +184,8 @@ handle_call(end_setup, _, State) -> {reply, ok, NewState}; handle_call(network, _, State = #s{network = Network}) -> {reply, Network, State}; +handle_call(bin_dir, _, State = #s{exec = {BinDir, _}}) -> + {reply, BinDir, State}; handle_call(Unexpected, From, State) -> ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]), {noreply, State}. @@ -160,15 +194,19 @@ handle_call(Unexpected, From, State) -> handle_cast({unlock, Phrase}, State) -> NewState = unlock(Phrase, State), {noreply, NewState}; -handle_cast({make_key, Phrase}, State) -> - NewState = make_key(Phrase, State), +handle_cast({make_key, Phrase, Network}, State) -> + NewState = make_key(Phrase, Network, State), {noreply, NewState}; -handle_cast({load_key, Phrase, Mnemonic}, State) -> - NewState = load_key(Phrase, Mnemonic, State), +handle_cast({load_key, Phrase, Mnemonic, Network}, State) -> + NewState = load_key(Phrase, Mnemonic, Network, State), {noreply, NewState}; +handle_cast(start_stop, State) -> + ok = do_start_stop(State), + {noreply, State}; handle_cast(stop, State) -> + ok = do_stop(), ok = log(info, "Received a 'stop' message."), - {stop, normal, State}; + {noreply, State}; handle_cast(Unexpected, State) -> ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]), {noreply, State}. @@ -194,6 +232,13 @@ terminate(Reason, _) -> end. +do_stop() -> + case is_pid(whereis(gd_con)) of + false -> zx:stop(); + true -> application:stop(gajumine) + end. + + %%% Doers unlock(Phrase, State = #s{key = {blob, Cipher}}) -> @@ -213,32 +258,36 @@ unlock(Phrase, State = #s{key = {blob, Cipher}}) -> end. -make_key(Phrase, State) -> +make_key(Phrase, Network, State) -> {ID, Pair = #{secret := <>}} = hz_key_master:make_key(<<>>), Mnemonic = hz_key_master:encode(K), ok = gmc_setup:done(Mnemonic), - finalize_key(Phrase, ID, Pair, State). + finalize_key(Phrase, ID, Pair, Network, State). -load_key(Phrase, Mnemonic, State) -> +load_key(Phrase, Mnemonic, Network, State) -> case hz_key_master:decode(Mnemonic) of {ok, Secret} -> {ID, Pair} = hz_key_master:make_key(Secret), - finalize_key(Phrase, ID, Pair, State); + ok = gmc_setup:done(), + finalize_key(Phrase, ID, Pair, Network, State); Error -> - ok = gmc_gui:trouble(Error), - ok = gmc_gui:bad_mnemonic(), + ok = gmc_setup:trouble(Error), State end. -finalize_key(Phrase, ID, Pair, State) -> - Pass = pass(unicode:characters_to_binary(Phrase)), - Key = #key{id = ID} = add_wallet(Pass, ID, Pair), - State#s{key = Key, pass = Pass}. +finalize_key(Phrase, ID, Pair, Network, State) -> + Pass = + case Phrase =:= "" of + false -> pass(unicode:characters_to_binary(Phrase)); + true -> none + end, + Key = #key{id = ID} = add_wallet(Pass, ID, Pair, Network), + State#s{key = Key, pass = Pass, network = Network}. -add_wallet(Pass, ID, Pair = #{secret := Secret}) -> +add_wallet(Pass, ID, Pair = #{secret := Secret}, Network) -> GDPrefsPath = gd_prefs_path(), GDPrefs = case file:consult(GDPrefsPath) of @@ -247,21 +296,29 @@ add_wallet(Pass, ID, Pair = #{secret := Secret}) -> end, Wallets = gd_get_prefs(wallets, GDPrefs, []), WalletPath = gajumining_wallet(), - Entry = #wr{name = gm_name(), path = WalletPath, pass = true}, - NewWallets = lists:keystore(gm_name(), #wr.name, Wallets, Entry), - NewGDPrefs = gd_put_prefs(wallets, NewWallets, GDPrefs), Created = #key{name = gm_name(), id = ID, pair = Pair}, + {Net, ChainID, Node} = + case Network of + mainnet -> {#net{id = <<"mainnet">>}, <<"groot.mainnet">>, #node{ip = "groot.mainnet.gajumaru.io"}}; + testnet -> {#net{id = <<"testnet">>}, <<"groot.testnet">>, #node{ip = "groot.testnet.gajumaru.io"}} + end, New = #wallet{name = gm_name(), poas = [#poa{name = gm_name(), id = ID}], keys = [Created], - chain_id = <<"groot.devnet">>, - endpoint = #node{}, - nets = [#net{}]}, - Cipher = encrypt(Pass, term_to_binary(New)), - ok = file:write_file(WalletPath, Cipher), + chain_id = ChainID, + endpoint = Node, + nets = [Net]}, + {WalletBin, KeyPair, HasPass} = + case Pass =:= none of + false -> {encrypt(Pass, term_to_binary(New)), Pair#{secret => {cipher, encrypt(Pass, Secret)}}, true}; + true -> {term_to_binary(New), Pair, false} + end, + Entry = #wr{name = gm_name(), path = WalletPath, pass = HasPass}, + NewWallets = lists:keystore(gm_name(), #wr.name, Wallets, Entry), + NewGDPrefs = gd_put_prefs(wallets, NewWallets, GDPrefs), + ok = file:write_file(WalletPath, WalletBin), ok = zx_lib:write_terms(GDPrefsPath, proplists:from_map(NewGDPrefs)), - Encrypted = Pair#{secret => encrypt(Pass, Secret)}, - Created#key{pair = Encrypted}. + Created#key{pair = KeyPair}. end_setup(State = #s{window = Window}) -> @@ -270,6 +327,32 @@ end_setup(State = #s{window = Window}) -> start_gui(State#s{window = none}). +do_start_stop(#s{key = #key{id = PubKey}, network = Network}) -> + % smrt guy stuff happens here + {Bits, Eureka} = + case Network of + mainnet -> {"29", <<"https://gajumining.com/api/workers/", PubKey/binary>>}; + testnet -> {"15", <<"https://test.gajumining.com/api/workers/", PubKey/binary>>} + end, + {Fatness, Type} = + case {os:type(), Network} of + {unix, linux} -> + % Check memory. >7gb gets mean, <7gb gets lean + % Check avx2. + {unix, darwin} -> + % Check memory. >7gb gets mean, <7gb gets lean + {win32, nt} -> + % Check memory. >7gb gets mean, <7gb gets lean + % Check avx2. + % Both should be provided by the F# start program + end, + Miner = string:join([Fatness, Bits, "-", Type]), + Profile = + [{pubkey, PubKey}, + {mining, #{executable => Miner, executable_group => <<"gajumine">>}}, + {pool_admin_url, Eureka}], + gmmpc_app:start(Profile). + %%% Utils diff --git a/src/gmc_gui.erl b/src/gmc_gui.erl index be77306..ab4c335 100644 --- a/src/gmc_gui.erl +++ b/src/gmc_gui.erl @@ -10,7 +10,7 @@ -behavior(wx_object). -include_lib("wx/include/wx.hrl"). --export([ask_passphrase/0, set_account/1, show/1]). +-export([ask_passphrase/0, set_account/1, message/1]). -export([start_link/1]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, handle_event/2]). @@ -47,8 +47,8 @@ ask_passphrase() -> wx_object:cast(?MODULE, ask_passphrase). -show(Terms) -> - wx_object:cast(?MODULE, {show, Terms}). +message(Terms) -> + wx_object:cast(?MODULE, {message, Terms}). @@ -106,7 +106,7 @@ init(Prefs) -> ok = wxFrame:connect(Frame, command_button_clicked), ok = wxFrame:connect(Frame, close_window), - ok = wxFrame:setSize(Frame, {650, 160}), + ok = wxFrame:setSize(Frame, {650, 200}), ok = wxFrame:center(Frame), true = wxFrame:show(Frame), State = @@ -145,8 +145,8 @@ handle_cast(ask_passphrase, State) -> handle_cast({set_account, ID}, State) -> ok = set_account(ID, State), {noreply, State}; -handle_cast({show, Terms}, State) -> - ok = do_show(Terms, State), +handle_cast({message, Terms}, State) -> + ok = do_message(Terms, State), {noreply, State}; handle_cast(Unexpected, State) -> ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]), @@ -218,7 +218,7 @@ set_account(ID, #s{id = Widget}) -> wxStaticText:setLabel(Widget, ID). -do_show(Terms, #s{mess = Mess}) -> +do_message(Terms, #s{mess = Mess}) -> String = io_lib:format("Received args: ~tp", [Terms]), wxTextCtrl:changeValue(Mess, String). @@ -257,22 +257,27 @@ eureka(State = #s{frame = Frame, j = J}) -> eureka_url() -> case gmc_con:network() of - mainnet -> "https://eureka.gajumining.com"; - testnet -> "https://eureka.test.gajumarumining.com"; - none -> "https://gajumarumining.com" + mainnet -> "https://gajumining.com"; + testnet -> "https://test.gajumining.com"; + none -> "https://gajumining.com" end. -explorer(State = #s{frame = Frame, j = J}) -> +explorer(State = #s{frame = Frame, j = J, id = ID}) -> ok = tell(info, "Opening Explorer"), - ok = open_browser(Frame, J, explorer_url()), + URL = + case wxStaticText:getLabel(ID) of + "" -> explorer_url(); + AccountID -> unicode:characters_to_list([explorer_url(), "/account/", AccountID]) + end, + ok = open_browser(Frame, J, URL), State. explorer_url() -> case gmc_con:network() of mainnet -> "https://groot.mainnet.gajumaru.io"; testnet -> "https://groot.testnet.gajumaru.io"; - none -> "https://gajumaru.io" + none -> "https://groot.mainnet.gajumaru.io" end. diff --git a/src/gmc_setup.erl b/src/gmc_setup.erl index 47f480a..4b70d5c 100644 --- a/src/gmc_setup.erl +++ b/src/gmc_setup.erl @@ -19,7 +19,7 @@ -behavior(wx_object). -include_lib("wx/include/wx.hrl"). --export([trouble/1, done/1]). +-export([trouble/1, done/0, done/1]). -export([start_link/1]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, handle_event/2]). @@ -46,6 +46,10 @@ trouble(Info) -> wx_object:cast(?MODULE, {trouble, Info}). +done() -> + wx_object:cast(?MODULE, done). + + done(Mnemonic) -> wx_object:cast(?MODULE, {done, Mnemonic}). @@ -105,6 +109,10 @@ handle_call(Unexpected, From, State) -> handle_cast({trouble, Info}, State) -> ok = handle_troubling(State, Info), {noreply, State}; +handle_cast(done, State = #s{frame = Frame}) -> + ok = gmc_con:end_setup(), + ok = wxWindow:destroy(Frame), + {noreply, State}; handle_cast({done, Mnemonic}, State) -> ok = done(Mnemonic, State), {noreply, State}; @@ -150,9 +158,9 @@ terminate(Reason, State) -> new_key(State = #s{frame = Frame, j = J}) -> - case prompt_passphrase(Frame, J) of - {ok, Phrase} -> - gmc_con:make_key(Phrase); + case new_key(Frame, J) of + {ok, Phrase, Network} -> + gmc_con:make_key(Phrase, Network); mismatch -> ok = zxw:show_message(Frame, J("Password entered incorrectly. Try again.")), new_key(State); @@ -161,10 +169,11 @@ new_key(State = #s{frame = Frame, j = J}) -> end. -prompt_passphrase(Frame, J) -> +new_key(Frame, J) -> Label = J("Generate New Account Key"), Dialog = wxDialog:new(Frame, ?wxID_ANY, Label, [{size, {500, 180}}]), Sizer = wxBoxSizer:new(?wxVERTICAL), + Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]), PassSz1 = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Create Password")}]), PassTx1 = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]), _ = wxStaticBoxSizer:add(PassSz1, PassTx1, zxw:flags(wide)), @@ -176,19 +185,27 @@ prompt_passphrase(Frame, J) -> Cancel = wxButton:new(Dialog, ?wxID_CANCEL), _ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)), _ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)), + _ = wxBoxSizer:add(Sizer, Network, zxw:flags(base)), _ = wxBoxSizer:add(Sizer, PassSz1, zxw:flags(base)), _ = wxBoxSizer:add(Sizer, PassSz2, zxw:flags(base)), _ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(wide)), ok = wxDialog:setSizer(Dialog, Sizer), + ok = wxDialog:setSize(Dialog, {400, 250}), ok = wxBoxSizer:layout(Sizer), ok = wxFrame:center(Dialog), Outcome = case wxDialog:showModal(Dialog) of ?wxID_OK -> + Net = + case wxRadioBox:getSelection(Network) of + 0 -> mainnet; + 1 -> testnet; + _ -> mainnet + end, Pass1 = wxTextCtrl:getValue(PassTx1), Pass2 = wxTextCtrl:getValue(PassTx2), case Pass1 =:= Pass2 of - true -> {ok, Pass1}; + true -> {ok, Pass1, Net}; false -> mismatch end; ?wxID_CANCEL -> @@ -199,8 +216,20 @@ prompt_passphrase(Frame, J) -> recover(State = #s{frame = Frame, j = J}) -> + case recover(Frame, J) of + {ok, Phrase, Mnemonic, Network} -> + gmc_con:load_key(Phrase, Mnemonic, Network); + mismatch -> + ok = zxw:show_message(Frame, J("Password entered incorrectly. Try again.")), + recover(State); + cancel -> + ok + end. + +recover(Frame, J) -> Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Import Account Key")), Sizer = wxBoxSizer:new(?wxVERTICAL), + Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]), PassSz1 = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Create Password")}]), PassTx1 = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]), _ = wxStaticBoxSizer:add(PassSz1, PassTx1, zxw:flags(wide)), @@ -215,11 +244,13 @@ recover(State = #s{frame = Frame, j = J}) -> Cancel = wxButton:new(Dialog, ?wxID_CANCEL), _ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)), _ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)), + _ = wxBoxSizer:add(Sizer, Network, zxw:flags(base)), _ = wxBoxSizer:add(Sizer, PassSz1, zxw:flags(base)), _ = wxBoxSizer:add(Sizer, PassSz2, zxw:flags(base)), _ = wxBoxSizer:add(Sizer, MnemSz, zxw:flags(wide)), _ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(base)), ok = wxDialog:setSizer(Dialog, Sizer), + ok = wxDialog:setSize(Dialog, {400, 400}), ok = wxBoxSizer:layout(Sizer), ok = wxFrame:center(Dialog), Outcome = @@ -229,8 +260,14 @@ recover(State = #s{frame = Frame, j = J}) -> Pass2 = wxTextCtrl:getValue(PassTx2), case Pass1 =:= Pass2 of true -> + Net = + case wxRadioBox:getSelection(Network) of + 0 -> mainnet; + 1 -> testnet; + _ -> mainnet + end, Mnemonic = wxTextCtrl:getValue(MnemTx), - gmc_con:load_key(Pass1, Mnemonic); + {ok, Pass1, Mnemonic, Net}; false -> mismatch end; @@ -238,15 +275,7 @@ recover(State = #s{frame = Frame, j = J}) -> cancel end, ok = wxDialog:destroy(Dialog), - case Outcome of - ok -> - ok; - mismatch -> - ok = zxw:show_message(Frame, J("Password entered incorrectly. Try again.")), - recover(State); - cancel -> - ok - end. + Outcome. diff --git a/zomp.meta b/zomp.meta index 500506b..227fbe5 100644 --- a/zomp.meta +++ b/zomp.meta @@ -1,11 +1,19 @@ {name,"GajuMine"}. {type,gui}. {modules,[]}. -{author,"Craig Everett"}. {prefix,"gmc"}. +{author,"Craig Everett"}. {desc,"Mining client for the Gajumaru Root"}. {package_id,{"qpq","gajumine",{0,1,0}}}. -{deps,[{"otpr","hakuzaru",{0,6,1}}, +{deps,[{"uwiger","gm_mining_pool_protocol",{0,2,0}}, + {"uwiger","gmmp_client",{0,3,0}}, + {"uwiger","gproc",{1,0,0}}, + {"uwiger","enoise",{1,3,0}}, + {"uwiger","gmminer",{1,0,1}}, + {"uwiger","gmcuckoo",{1,1,0}}, + {"uwiger","setup",{2,2,4}}, + {"uwiger","gmconfig",{0,1,1}}, + {"otpr","hakuzaru",{0,6,1}}, {"otpr","gajudesk",{0,5,3}}, {"otpr","zxwidgets",{1,0,1}}, {"otpr","ec_utils",{1,0,0}},