Compare commits
No commits in common. "master" and "history" have entirely different histories.
@ -3,8 +3,7 @@
|
|||||||
{registered,[]},
|
{registered,[]},
|
||||||
{included_applications,[]},
|
{included_applications,[]},
|
||||||
{applications,[stdlib,kernel,sasl,ssl]},
|
{applications,[stdlib,kernel,sasl,ssl]},
|
||||||
{vsn,"0.6.6"},
|
{vsn,"0.5.2"},
|
||||||
{modules,[gajudesk,gd_con,gd_grids,gd_gui,gd_jt,
|
{modules,[gajudesk,gd_con,gd_grids,gd_gui,gd_jt,gd_key_master,
|
||||||
gd_sophia_editor,gd_sup,gd_v,gd_v_devman,gd_v_netman,
|
gd_sup,gd_v,gd_v_devman,gd_v_netman,gd_v_wallman]},
|
||||||
gd_v_wallman]},
|
|
||||||
{mod,{gajudesk,[]}}]}.
|
{mod,{gajudesk,[]}}]}.
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
%% Node, Chain and Net represent the physical network.
|
%% Node, Chain and Net represent the physical network.
|
||||||
|
|
||||||
-record(node,
|
-record(node,
|
||||||
{ip = "groot.testnet.gajumaru.io" :: string() | inet:ip_address(),
|
{ip = {161,97,102,143} :: string() | inet:ip_address(),
|
||||||
external = 3013 :: none | inet:port_number(), % 3013
|
external = 3013 :: none | inet:port_number(), % 3013
|
||||||
internal = none :: none | inet:port_number(), % 3113
|
internal = none :: none | inet:port_number(), % 3113
|
||||||
rosetta = none :: none | inet:port_number(), % 8080
|
rosetta = none :: none | inet:port_number(), % 8080
|
||||||
channel = none :: none | inet:port_number(), % 3014
|
channel = none :: none | inet:port_number(), % 3014
|
||||||
mdw = none :: none | inet:port_number()}). % 4000
|
mdw = none :: none | inet:port_number()}). % 4000
|
||||||
|
|
||||||
|
|
||||||
-record(chain,
|
-record(chain,
|
||||||
{id = <<"groot.testnet">> :: binary(),
|
{id = <<"groot.devnet">> :: binary(),
|
||||||
coins = ["gaju"] :: [string()],
|
coins = ["gaju"] :: [string()],
|
||||||
nodes = [#node{}] :: [#node{}]}).
|
nodes = [#node{}] :: [#node{}]}).
|
||||||
|
|
||||||
|
|
||||||
-record(net,
|
-record(net,
|
||||||
{id = <<"testnet">> :: binary(),
|
{id = <<"devnet">> :: binary(),
|
||||||
chains = [#chain{}] :: [#chain{}]}).
|
chains = [#chain{}] :: [#chain{}]}).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -29,17 +29,17 @@
|
|||||||
|
|
||||||
|
|
||||||
-record(coin,
|
-record(coin,
|
||||||
{id = "gaju" :: string(),
|
{id = "gaju" :: string(),
|
||||||
mint = <<"groot.testnet">> :: binary(),
|
mint = <<"groot.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 = [{<<"groot.testnet">>, 0}] :: [{Chain :: binary(), non_neg_integer()}]}).
|
dist = [{<<"groot.devnet">>, 0}] :: [{Chain :: binary(), non_neg_integer()}]}).
|
||||||
|
|
||||||
|
|
||||||
-record(poa,
|
-record(poa,
|
||||||
@ -77,14 +77,14 @@
|
|||||||
|
|
||||||
|
|
||||||
-record(wallet,
|
-record(wallet,
|
||||||
{version = 2 :: integer(),
|
{version = 2 :: integer(),
|
||||||
name = "" :: string(),
|
name = "" :: string(),
|
||||||
poas = [] :: [#poa{}],
|
poas = [] :: [#poa{}],
|
||||||
keys = [] :: [#key{}],
|
keys = [] :: [#key{}],
|
||||||
chain_id = <<"groot.testnet">> :: binary(),
|
chain_id = <<"groot.devnet">> :: binary(),
|
||||||
endpoint = #node{} :: #node{},
|
endpoint = #node{} :: #node{},
|
||||||
nets = [#net{}] :: [#net{}],
|
nets = [#net{}] :: [#net{}],
|
||||||
txs = #{} :: gajudesk:key_txs()}).
|
txs = #{} :: gajudesk:key_txs()}).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
4096
priv/words4096.txt
Normal file
4096
priv/words4096.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(gajudesk).
|
-module(gajudesk).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-behavior(application).
|
-behavior(application).
|
||||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
|
219
src/gd_con.erl
219
src/gd_con.erl
@ -3,15 +3,15 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(gd_con).
|
-module(gd_con).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
|
|
||||||
-behavior(gen_server).
|
-behavior(gen_server).
|
||||||
-export([show_ui/1,
|
-export([show_ui/1,
|
||||||
open_wallet/2, close_wallet/0, new_wallet/4, import_wallet/3, drop_wallet/2,
|
open_wallet/2, close_wallet/0, new_wallet/3, import_wallet/3, drop_wallet/2,
|
||||||
selected/1, network/0,
|
selected/1,
|
||||||
password/2,
|
password/2,
|
||||||
refresh/0,
|
refresh/0,
|
||||||
nonce/1, spend/1, chain/1, grids/1, sign_mess/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,
|
||||||
@ -40,7 +40,6 @@
|
|||||||
-record(s,
|
-record(s,
|
||||||
{version = 1 :: integer(),
|
{version = 1 :: integer(),
|
||||||
window = none :: none | wx:wx_object(),
|
window = none :: none | wx:wx_object(),
|
||||||
timer = none :: none | reference(),
|
|
||||||
tasks = [] :: [#ui{}],
|
tasks = [] :: [#ui{}],
|
||||||
selected = 0 :: non_neg_integer(),
|
selected = 0 :: non_neg_integer(),
|
||||||
wallet = none :: none | #wallet{},
|
wallet = none :: none | #wallet{},
|
||||||
@ -80,14 +79,13 @@ close_wallet() ->
|
|||||||
gen_server:cast(?MODULE, close_wallet).
|
gen_server:cast(?MODULE, close_wallet).
|
||||||
|
|
||||||
|
|
||||||
-spec new_wallet(Net, Name, Path, Password) -> ok
|
-spec new_wallet(Name, Path, Password) -> ok
|
||||||
when Net :: mainnet | testnet | devnet,
|
when Name :: string(),
|
||||||
Name :: string(),
|
|
||||||
Path :: string(),
|
Path :: string(),
|
||||||
Password :: string().
|
Password :: string().
|
||||||
|
|
||||||
new_wallet(Net, Name, Path, Password) ->
|
new_wallet(Name, Path, Password) ->
|
||||||
gen_server:cast(?MODULE, {new_wallet, Net, Name, Path, Password}).
|
gen_server:cast(?MODULE, {new_wallet, Name, Path, Password}).
|
||||||
|
|
||||||
|
|
||||||
-spec import_wallet(Name, Path, Password) -> ok
|
-spec import_wallet(Name, Path, Password) -> ok
|
||||||
@ -114,13 +112,6 @@ selected(Index) ->
|
|||||||
gen_server:cast(?MODULE, {selected, Index}).
|
gen_server:cast(?MODULE, {selected, Index}).
|
||||||
|
|
||||||
|
|
||||||
-spec network() -> {ok, NetworkID} | none
|
|
||||||
when NetworkID :: binary().
|
|
||||||
|
|
||||||
network() ->
|
|
||||||
gen_server:call(?MODULE, network).
|
|
||||||
|
|
||||||
|
|
||||||
-spec password(Old, New) -> ok
|
-spec password(Old, New) -> ok
|
||||||
when Old :: none | string(),
|
when Old :: none | string(),
|
||||||
New :: none | string().
|
New :: none | string().
|
||||||
@ -325,8 +316,7 @@ init(none) ->
|
|||||||
GUI_Prefs = maps:get(gd_gui, Prefs, #{}),
|
GUI_Prefs = maps:get(gd_gui, Prefs, #{}),
|
||||||
Window = gd_gui:start_link(GUI_Prefs),
|
Window = gd_gui:start_link(GUI_Prefs),
|
||||||
Wallets = get_prefs(wallets, Prefs, []),
|
Wallets = get_prefs(wallets, Prefs, []),
|
||||||
T = erlang:send_after(tic(), self(), tic),
|
State = #s{window = Window, wallets = Wallets, prefs = Prefs},
|
||||||
State = #s{window = Window, timer = T, wallets = Wallets, prefs = Prefs},
|
|
||||||
NewState = do_show_ui(gd_v_wallman, State),
|
NewState = do_show_ui(gd_v_wallman, State),
|
||||||
{ok, NewState}.
|
{ok, NewState}.
|
||||||
|
|
||||||
@ -338,10 +328,6 @@ read_prefs() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
tic() ->
|
|
||||||
6000.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%% gen_server Message Handling Callbacks
|
%%% gen_server Message Handling Callbacks
|
||||||
|
|
||||||
@ -365,9 +351,6 @@ handle_call(list_keys, _, State) ->
|
|||||||
handle_call({nonce, ID}, _, State) ->
|
handle_call({nonce, ID}, _, State) ->
|
||||||
Response = do_nonce(ID),
|
Response = do_nonce(ID),
|
||||||
{reply, Response, State};
|
{reply, Response, State};
|
||||||
handle_call(network, _, State) ->
|
|
||||||
Response = do_network(State),
|
|
||||||
{reply, Response, State};
|
|
||||||
handle_call({save, Module, Prefs}, _, State) ->
|
handle_call({save, Module, Prefs}, _, State) ->
|
||||||
NewState = do_save(Module, Prefs, State),
|
NewState = do_save(Module, Prefs, State),
|
||||||
{reply, ok, NewState};
|
{reply, ok, NewState};
|
||||||
@ -398,8 +381,8 @@ handle_cast(close_wallet, State) ->
|
|||||||
ok = gd_gui:show([]),
|
ok = gd_gui:show([]),
|
||||||
NewState = do_show_ui(gd_v_wallman, NextState),
|
NewState = do_show_ui(gd_v_wallman, NextState),
|
||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
handle_cast({new_wallet, Net, Name, Path, Password}, State) ->
|
handle_cast({new_wallet, Name, Path, Password}, State) ->
|
||||||
NewState = do_new_wallet(Net, Name, Path, Password, State),
|
NewState = do_new_wallet(Name, Path, Password, State),
|
||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
handle_cast({import_wallet, Name, Path, Password}, State) ->
|
handle_cast({import_wallet, Name, Path, Password}, State) ->
|
||||||
NewState = do_import_wallet(Name, Path, Password, State),
|
NewState = do_import_wallet(Name, Path, Password, State),
|
||||||
@ -460,6 +443,7 @@ handle_cast({set_sole_node, TheOneTrueNode}, State) ->
|
|||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
handle_cast(stop, State) ->
|
handle_cast(stop, State) ->
|
||||||
NewState = do_stop(State),
|
NewState = do_stop(State),
|
||||||
|
ok = zx:stop(),
|
||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
handle_cast(Unexpected, State) ->
|
handle_cast(Unexpected, State) ->
|
||||||
ok = tell(warning, "Unexpected cast: ~tp~n", [Unexpected]),
|
ok = tell(warning, "Unexpected cast: ~tp~n", [Unexpected]),
|
||||||
@ -474,9 +458,6 @@ handle_cast(Unexpected, State) ->
|
|||||||
%% The gen_server:handle_info/2 callback.
|
%% The gen_server:handle_info/2 callback.
|
||||||
%% See: http://erlang.org/doc/man/gen_server.html#Module:handle_info-2
|
%% See: http://erlang.org/doc/man/gen_server.html#Module:handle_info-2
|
||||||
|
|
||||||
handle_info(tic, State) ->
|
|
||||||
NewState = do_tic(State),
|
|
||||||
{noreply, NewState};
|
|
||||||
handle_info({'DOWN', Mon, process, PID, Info}, State) ->
|
handle_info({'DOWN', Mon, process, PID, Info}, State) ->
|
||||||
NewState = handle_down(Mon, PID, Info, State),
|
NewState = handle_down(Mon, PID, Info, State),
|
||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
@ -505,15 +486,11 @@ code_change(_, State, _) ->
|
|||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
|
|
||||||
terminate(Reason, _) ->
|
terminate(normal, _) ->
|
||||||
ok = log(info, "Reason: ~p,", [Reason]),
|
zx:stop();
|
||||||
case whereis(gmc_con) of
|
terminate(Reason, State) ->
|
||||||
undefined ->
|
ok = log(info, "Reason: ~tp, State: ~tp", [Reason, State]),
|
||||||
zx:stop();
|
zx:stop().
|
||||||
PID ->
|
|
||||||
ok = log(info, "gd_con found at: ~p", [PID]),
|
|
||||||
application:stop(gajumine)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -531,7 +508,6 @@ do_show_ui(Name, State = #s{tasks = Tasks, prefs = Prefs}) ->
|
|||||||
Win = Name:start_link({TaskPrefs, TaskData}),
|
Win = Name:start_link({TaskPrefs, TaskData}),
|
||||||
PID = wx_object:get_pid(Win),
|
PID = wx_object:get_pid(Win),
|
||||||
Mon = monitor(process, PID),
|
Mon = monitor(process, PID),
|
||||||
ok = Name:to_front(Win),
|
|
||||||
UI = #ui{name = Name, pid = PID, wx = Win, mon = Mon},
|
UI = #ui{name = Name, pid = PID, wx = Win, mon = Mon},
|
||||||
State#s{tasks = [UI | Tasks]}
|
State#s{tasks = [UI | Tasks]}
|
||||||
end.
|
end.
|
||||||
@ -561,15 +537,12 @@ do_add_node(New, State) ->
|
|||||||
|
|
||||||
do_set_sole_node(TOTN = #node{external = none}, State) ->
|
do_set_sole_node(TOTN = #node{external = none}, State) ->
|
||||||
do_set_sole_node(TOTN#node{external = 3013}, State);
|
do_set_sole_node(TOTN#node{external = 3013}, State);
|
||||||
do_set_sole_node(New = #node{ip = IP, external = Port}, State = #s{wallet = W, wallets = Ws, pass = Pass}) ->
|
do_set_sole_node(New = #node{ip = IP, external = Port}, State = #s{wallet = W}) ->
|
||||||
ok = hz:chain_nodes([{IP, Port}]),
|
ok = hz:chain_nodes([{IP, Port}]),
|
||||||
case ensure_hz_set(New) of
|
case ensure_hz_set(New) of
|
||||||
{ok, ChainID} ->
|
{ok, ChainID} ->
|
||||||
#wallet{name = Name} = W,
|
|
||||||
RW = lists:keyfind(Name, #wr.name, Ws),
|
|
||||||
Net = #net{id = ChainID, chains = [#chain{id = ChainID, nodes = [New]}]},
|
Net = #net{id = ChainID, chains = [#chain{id = ChainID, nodes = [New]}]},
|
||||||
NewWallet = W#wallet{chain_id = ChainID, endpoint = New, nets = [Net]},
|
NewWallet = W#wallet{chain_id = ChainID, endpoint = New, nets = [Net]},
|
||||||
ok = save_wallet(RW, Pass, NewWallet),
|
|
||||||
NewState = State#s{wallet = NewWallet},
|
NewState = State#s{wallet = NewWallet},
|
||||||
do_refresh(NewState);
|
do_refresh(NewState);
|
||||||
Error ->
|
Error ->
|
||||||
@ -593,26 +566,23 @@ do_refresh(State = #s{wallet = #wallet{endpoint = Node}}) ->
|
|||||||
|
|
||||||
|
|
||||||
do_refresh2(ChainID, State = #s{wallet = W = #wallet{poas = POAs}}) ->
|
do_refresh2(ChainID, State = #s{wallet = W = #wallet{poas = POAs}}) ->
|
||||||
CheckBalance = check_balance(ChainID),
|
CheckBalance =
|
||||||
|
fun(This = #poa{id = ID}) ->
|
||||||
|
Pucks =
|
||||||
|
case hz:acc(ID) of
|
||||||
|
{ok, #{"balance" := P}} -> P;
|
||||||
|
{error, "Account not found"} -> 0
|
||||||
|
end,
|
||||||
|
Dist = [{ChainID, Pucks}],
|
||||||
|
Gaju = #balance{coin = "gaju", total = Pucks, dist = Dist},
|
||||||
|
This#poa{balances = [Gaju]}
|
||||||
|
end,
|
||||||
NewPOAs = lists:map(CheckBalance, POAs),
|
NewPOAs = lists:map(CheckBalance, POAs),
|
||||||
ok = gd_gui:show(NewPOAs),
|
ok = gd_gui:show(NewPOAs),
|
||||||
NewW = W#wallet{chain_id = ChainID, poas = NewPOAs},
|
NewW = W#wallet{chain_id = ChainID, poas = NewPOAs},
|
||||||
State#s{wallet = NewW}.
|
State#s{wallet = NewW}.
|
||||||
|
|
||||||
|
|
||||||
check_balance(ChainID) ->
|
|
||||||
fun(This = #poa{id = ID}) ->
|
|
||||||
Pucks =
|
|
||||||
case hz:acc(ID) of
|
|
||||||
{ok, #{"balance" := P}} -> P;
|
|
||||||
{error, "Account not found"} -> 0
|
|
||||||
end,
|
|
||||||
Dist = [{ChainID, Pucks}],
|
|
||||||
Gaju = #balance{coin = "gaju", total = Pucks, dist = Dist},
|
|
||||||
This#poa{balances = [Gaju]}
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
ensure_hz_set(Node = #node{ip = IP, external = Port}) ->
|
ensure_hz_set(Node = #node{ip = IP, external = Port}) ->
|
||||||
case hz:chain_nodes() of
|
case hz:chain_nodes() of
|
||||||
[{IP, Port}] ->
|
[{IP, Port}] ->
|
||||||
@ -629,9 +599,7 @@ ensure_hz_set(Node = #node{ip = IP, external = Port}) ->
|
|||||||
_ ->
|
_ ->
|
||||||
ok = hz:chain_nodes([{IP, Port}]),
|
ok = hz:chain_nodes([{IP, Port}]),
|
||||||
ensure_hz_set(Node)
|
ensure_hz_set(Node)
|
||||||
end;
|
end.
|
||||||
ensure_hz_set(none) ->
|
|
||||||
{error, no_nodes}.
|
|
||||||
|
|
||||||
|
|
||||||
%ensure_connected(ChainID, IP, Port) ->
|
%ensure_connected(ChainID, IP, Port) ->
|
||||||
@ -684,7 +652,7 @@ do_sign_mess(Request = #{"public_id" := ID, "payload" := Message},
|
|||||||
#s{wallet = #wallet{keys = Keys}}) ->
|
#s{wallet = #wallet{keys = Keys}}) ->
|
||||||
case lists:keyfind(ID, #key.id, Keys) of
|
case lists:keyfind(ID, #key.id, Keys) of
|
||||||
#key{pair = #{secret := SecKey}} ->
|
#key{pair = #{secret := SecKey}} ->
|
||||||
Sig = base64:encode(hz:sign_message(list_to_binary(Message), SecKey)),
|
Sig = base64:encode(sign_message(list_to_binary(Message), SecKey)),
|
||||||
do_sign_mess2(Request#{"signature" => Sig});
|
do_sign_mess2(Request#{"signature" => Sig});
|
||||||
false ->
|
false ->
|
||||||
gd_gui:trouble({bad_key, ID})
|
gd_gui:trouble({bad_key, ID})
|
||||||
@ -707,6 +675,37 @@ do_sign_mess2(Request = #{"url" := URL}) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
% TODO: Should probably be part of Hakuzaru
|
||||||
|
sign_message(Message, SecKey) ->
|
||||||
|
Prefix = <<"Gajumaru Signed Message:\n">>,
|
||||||
|
{ok, PSize} = vencode(byte_size(Prefix)),
|
||||||
|
{ok, MSize} = vencode(byte_size(Message)),
|
||||||
|
Smashed = iolist_to_binary([PSize, Prefix, MSize, Message]),
|
||||||
|
{ok, Hashed} = eblake2:blake2b(32, Smashed),
|
||||||
|
ecu_eddsa:sign_detached(Hashed, SecKey).
|
||||||
|
|
||||||
|
|
||||||
|
vencode(N) when N < 0 ->
|
||||||
|
{error, {negative_N, N}};
|
||||||
|
vencode(N) when N < 16#FD ->
|
||||||
|
{ok, <<N>>};
|
||||||
|
vencode(N) when N =< 16#FFFF ->
|
||||||
|
NBytes = eu(N, 2),
|
||||||
|
{ok, <<16#FD, NBytes/binary>>};
|
||||||
|
vencode(N) when N =< 16#FFFF_FFFF ->
|
||||||
|
NBytes = eu(N, 4),
|
||||||
|
{ok, <<16#FE, NBytes/binary>>};
|
||||||
|
vencode(N) when N < (2 bsl 64) ->
|
||||||
|
NBytes = eu(N, 8),
|
||||||
|
{ok, <<16#FF, NBytes/binary>>}.
|
||||||
|
|
||||||
|
eu(N, Size) ->
|
||||||
|
Bytes = binary:encode_unsigned(N, little),
|
||||||
|
NExtraZeros = Size - byte_size(Bytes),
|
||||||
|
ExtraZeros = << <<0>> || _ <- lists:seq(1, NExtraZeros) >>,
|
||||||
|
<<Bytes/binary, ExtraZeros/binary>>.
|
||||||
|
|
||||||
|
|
||||||
do_sign_tx(Request = #{"public_id" := ID, "payload" := CallData, "network_id" := NID},
|
do_sign_tx(Request = #{"public_id" := ID, "payload" := CallData, "network_id" := NID},
|
||||||
#s{wallet = #wallet{keys = Keys}}) ->
|
#s{wallet = #wallet{keys = Keys}}) ->
|
||||||
BinNID = list_to_binary(NID),
|
BinNID = list_to_binary(NID),
|
||||||
@ -811,12 +810,6 @@ do_nonce(ID) ->
|
|||||||
hz:next_nonce(ID).
|
hz:next_nonce(ID).
|
||||||
|
|
||||||
|
|
||||||
do_network(#s{wallet = none}) ->
|
|
||||||
none;
|
|
||||||
do_network(#s{wallet = #wallet{chain_id = ChainID}}) ->
|
|
||||||
{ok, ChainID}.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%% State Operations
|
%%% State Operations
|
||||||
|
|
||||||
@ -869,9 +862,7 @@ do_make_key2(Name, Bin, Transform,
|
|||||||
#wallet{name = WalletName, poas = POAs, keys = Keys} = Current,
|
#wallet{name = WalletName, poas = POAs, keys = Keys} = Current,
|
||||||
T = transform(Transform),
|
T = transform(Transform),
|
||||||
Seed = T(Bin),
|
Seed = T(Bin),
|
||||||
{ID, Pair} = hz_key_master:make_key(Seed),
|
Key = #key{name = KeyName, id = ID} = gd_key_master:make_key(Name, Seed),
|
||||||
KeyName = case Name =:= "" of true -> ID; false -> Name end,
|
|
||||||
Key = #key{name = KeyName, id = ID, pair = Pair},
|
|
||||||
POA = #poa{name = KeyName, id = ID},
|
POA = #poa{name = KeyName, id = ID},
|
||||||
NewKeys = [Key | Keys],
|
NewKeys = [Key | Keys],
|
||||||
NewPOAs = [POA | POAs],
|
NewPOAs = [POA | POAs],
|
||||||
@ -910,7 +901,7 @@ t_xor(B, A) ->
|
|||||||
|
|
||||||
|
|
||||||
do_recover_key(Mnemonic, State) ->
|
do_recover_key(Mnemonic, State) ->
|
||||||
case hz_key_master:decode(Mnemonic) of
|
case gd_key_master:decode(Mnemonic) of
|
||||||
{ok, Seed} ->
|
{ok, Seed} ->
|
||||||
do_recover_key2(Seed, State);
|
do_recover_key2(Seed, State);
|
||||||
Error ->
|
Error ->
|
||||||
@ -920,12 +911,11 @@ do_recover_key(Mnemonic, State) ->
|
|||||||
|
|
||||||
do_recover_key2(Seed, State = #s{wallet = Current, wallets = Wallets, pass = Pass}) ->
|
do_recover_key2(Seed, State = #s{wallet = Current, wallets = Wallets, pass = Pass}) ->
|
||||||
#wallet{name = WalletName, keys = Keys, poas = POAs} = Current,
|
#wallet{name = WalletName, keys = Keys, poas = POAs} = Current,
|
||||||
{ID, Pair} = hz_key_master:make_key(Seed),
|
Recovered = #key{id = ID, name = AccName} = gd_key_master:make_key("", Seed),
|
||||||
Recovered = #key{id = ID, name = ID, pair = Pair},
|
|
||||||
case lists:keymember(ID, #key.id, Keys) of
|
case lists:keymember(ID, #key.id, Keys) of
|
||||||
false ->
|
false ->
|
||||||
NewKeys = [Recovered | Keys],
|
NewKeys = [Recovered | Keys],
|
||||||
POA = #poa{name = ID, id = ID},
|
POA = #poa{name = AccName, id = ID},
|
||||||
NewPOAs = [POA | POAs],
|
NewPOAs = [POA | POAs],
|
||||||
ok = gd_gui:show(NewPOAs),
|
ok = gd_gui:show(NewPOAs),
|
||||||
Updated = Current#wallet{poas = NewPOAs, keys = NewKeys},
|
Updated = Current#wallet{poas = NewPOAs, keys = NewKeys},
|
||||||
@ -940,7 +930,7 @@ do_recover_key2(Seed, State = #s{wallet = Current, wallets = Wallets, pass = Pas
|
|||||||
do_mnemonic(ID, #s{wallet = #wallet{keys = Keys}}) ->
|
do_mnemonic(ID, #s{wallet = #wallet{keys = Keys}}) ->
|
||||||
case lists:keyfind(ID, #key.id, Keys) of
|
case lists:keyfind(ID, #key.id, Keys) of
|
||||||
#key{pair = #{secret := <<K:32/binary, _/binary>>}} ->
|
#key{pair = #{secret := <<K:32/binary, _/binary>>}} ->
|
||||||
Mnemonic = hz_key_master:encode(K),
|
Mnemonic = gd_key_master:encode(K),
|
||||||
{ok, Mnemonic};
|
{ok, Mnemonic};
|
||||||
false ->
|
false ->
|
||||||
{error, bad_key}
|
{error, bad_key}
|
||||||
@ -985,26 +975,22 @@ do_deploy3(#{"tx_hash" := TXHash}) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
do_rename_key(ID, NewName, State = #s{wallet = W, wallets = Wallets, pass = Pass}) ->
|
do_rename_key(ID, NewName, State = #s{wallet = W}) ->
|
||||||
#wallet{name = Name, poas = POAs, keys = Keys} = W,
|
#wallet{poas = POAs, keys = Keys} = W,
|
||||||
RW = lists:keyfind(Name, #wr.name, Wallets),
|
|
||||||
A = lists:keyfind(ID, #poa.id, POAs),
|
A = lists:keyfind(ID, #poa.id, POAs),
|
||||||
K = lists:keyfind(ID, #key.id, Keys),
|
K = lists:keyfind(ID, #key.id, Keys),
|
||||||
NewPOAs = lists:keystore(ID, #poa.id, POAs, A#poa{name = NewName}),
|
NewPOAs = lists:keystore(ID, #poa.id, POAs, A#poa{name = NewName}),
|
||||||
NewKeys = lists:keystore(ID, #key.id, Keys, K#key{name = NewName}),
|
NewKeys = lists:keystore(ID, #key.id, Keys, K#key{name = NewName}),
|
||||||
NewWallet = W#wallet{poas = NewPOAs, keys = NewKeys},
|
NewWallet = W#wallet{poas = NewPOAs, keys = NewKeys},
|
||||||
ok = save_wallet(RW, Pass, NewWallet),
|
|
||||||
ok = gd_gui:show(NewPOAs),
|
ok = gd_gui:show(NewPOAs),
|
||||||
State#s{wallet = NewWallet}.
|
State#s{wallet = NewWallet}.
|
||||||
|
|
||||||
|
|
||||||
do_drop_key(ID, State = #s{wallet = W, wallets = Wallets, pass = Pass}) ->
|
do_drop_key(ID, State = #s{wallet = W}) ->
|
||||||
#wallet{name = Name, poas = POAs, keys = Keys} = W,
|
#wallet{poas = POAs, keys = Keys} = W,
|
||||||
RW = lists:keyfind(Name, #wr.name, Wallets),
|
|
||||||
NewPOAs = lists:keydelete(ID, #poa.id, POAs),
|
NewPOAs = lists:keydelete(ID, #poa.id, POAs),
|
||||||
NewKeys = lists:keydelete(ID, #key.id, Keys),
|
NewKeys = lists:keydelete(ID, #key.id, Keys),
|
||||||
NewWallet = W#wallet{poas = NewPOAs, keys = NewKeys},
|
NewWallet = W#wallet{poas = NewPOAs, keys = NewKeys},
|
||||||
ok = save_wallet(RW, Pass, NewWallet),
|
|
||||||
ok = gd_gui:show(NewPOAs),
|
ok = gd_gui:show(NewPOAs),
|
||||||
State#s{wallet = NewWallet}.
|
State#s{wallet = NewWallet}.
|
||||||
|
|
||||||
@ -1029,25 +1015,12 @@ do_open_wallet(Path, Phrase, State) ->
|
|||||||
|
|
||||||
|
|
||||||
default_wallet() ->
|
default_wallet() ->
|
||||||
default_wallet(testnet).
|
DevNet = #net{id = <<"devnet">>, chains = [#chain{}]},
|
||||||
|
% TestChain1 = #chain{id = <<"groot.testnet">>,
|
||||||
default_wallet(mainnet) ->
|
% nodes = [#node{ip = {1,2,3,4}}, #node{ip = {5,6,7,8}}]},
|
||||||
Node = #node{ip = "groot.mainnet.gajumaru.io"},
|
% TestChain2 = #chain{id = <<"test_ac.testnet">>,
|
||||||
Groot = #chain{id = <<"groot.mainnet">>,
|
% nodes = [#node{ip = {11,12,13,14}}, #node{ip = {15,16,17,18}}]},
|
||||||
nodes = [Node]},
|
% TestNet = #net{id = <<"testnet">>, chains = [TestChain1, TestChain2]},
|
||||||
MainNet = #net{id = <<"mainnet">>, chains = [Groot]},
|
|
||||||
#wallet{nets = [MainNet], endpoint = Node};
|
|
||||||
default_wallet(testnet) ->
|
|
||||||
Node = #node{ip = "groot.testnet.gajumaru.io"},
|
|
||||||
Groot = #chain{id = <<"groot.testnet">>,
|
|
||||||
nodes = [Node]},
|
|
||||||
TestNet = #net{id = <<"testnet">>, chains = [Groot]},
|
|
||||||
#wallet{nets = [TestNet], endpoint = Node};
|
|
||||||
default_wallet(devnet) ->
|
|
||||||
% TODO: This accounts for the nature of devnets by defining *no* chains.
|
|
||||||
% The GUI/CON will need to manage this properly when encountered and prompt.
|
|
||||||
DevNet = #net{id = <<"devnet">>,
|
|
||||||
chains = []},
|
|
||||||
#wallet{nets = [DevNet]}.
|
#wallet{nets = [DevNet]}.
|
||||||
|
|
||||||
|
|
||||||
@ -1082,32 +1055,20 @@ do_password(Old, New, State = #s{pass = Pass}) ->
|
|||||||
|
|
||||||
do_stop(State = #s{prefs = Prefs}) ->
|
do_stop(State = #s{prefs = Prefs}) ->
|
||||||
ok = persist(Prefs),
|
ok = persist(Prefs),
|
||||||
NewState = do_close_wallet(State),
|
do_close_wallet(State).
|
||||||
ok =
|
|
||||||
case is_pid(whereis(gmc_con)) of
|
|
||||||
false -> zx:stop();
|
|
||||||
true -> application:stop(gajudesk)
|
|
||||||
end,
|
|
||||||
NewState.
|
|
||||||
|
|
||||||
|
|
||||||
do_new_wallet(Net, Name, Path, Password, State = #s{wallets = Wallets, prefs = Prefs}) ->
|
do_new_wallet(Name, Path, Password, State = #s{wallets = Wallets, prefs = Prefs}) ->
|
||||||
case lists:keyfind(Name, #wr.name, Wallets) of
|
case lists:keyfind(Name, #wr.name, Wallets) of
|
||||||
false ->
|
false ->
|
||||||
NextState = do_close_wallet(State),
|
NextState = do_close_wallet(State),
|
||||||
Pass = pass(Password),
|
Pass = pass(Password),
|
||||||
HasPass = Pass =/= none,
|
HasPass = Pass =/= none,
|
||||||
Entry = #wr{name = Name, path = Path, pass = HasPass},
|
Entry = #wr{name = Name, path = Path, pass = HasPass},
|
||||||
DW = #wallet{endpoint = Node} = default_wallet(Net),
|
New = #wallet{name = Name},
|
||||||
New = DW#wallet{name = Name},
|
|
||||||
ok = save_wallet(Entry, Pass, New),
|
ok = save_wallet(Entry, Pass, New),
|
||||||
ok = gd_gui:show([]),
|
ok = gd_gui:show([]),
|
||||||
ok = gd_gui:wallet(Name),
|
ok = gd_gui:wallet(Name),
|
||||||
ok =
|
|
||||||
case ensure_hz_set(Node) of
|
|
||||||
{ok, ChainID} -> gd_gui:chain(ChainID, Node);
|
|
||||||
Error -> gd_gui:trouble(Error)
|
|
||||||
end,
|
|
||||||
NewWallets = [Entry | Wallets],
|
NewWallets = [Entry | Wallets],
|
||||||
NewPrefs = put_prefs(wallets, NewWallets, Prefs),
|
NewPrefs = put_prefs(wallets, NewWallets, Prefs),
|
||||||
ok = persist(NewPrefs),
|
ok = persist(NewPrefs),
|
||||||
@ -1265,28 +1226,6 @@ read3(T) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
do_tic(State = #s{wallet = This = #wallet{poas = POAs, endpoint = Node}, selected = Selected}) when Selected > 0 ->
|
|
||||||
NewState =
|
|
||||||
case ensure_hz_set(Node) of
|
|
||||||
{ok, ChainID} ->
|
|
||||||
POA = #poa{id = ID} = lists:nth(Selected, POAs),
|
|
||||||
CheckBalance = check_balance(ChainID),
|
|
||||||
NewPOA = CheckBalance(POA),
|
|
||||||
NewPOAs = lists:keystore(ID, #poa.id, POAs, NewPOA),
|
|
||||||
ok = gd_gui:show(NewPOAs),
|
|
||||||
State#s{wallet = This#wallet{poas = POAs}};
|
|
||||||
Error ->
|
|
||||||
ok = log(info, "Balance update on tic failed with: ~p", [Error]),
|
|
||||||
State
|
|
||||||
end,
|
|
||||||
T = erlang:send_after(tic(), self(), tic),
|
|
||||||
NewState#s{timer = T};
|
|
||||||
do_tic(State) ->
|
|
||||||
T = erlang:send_after(tic(), self(), tic),
|
|
||||||
State#s{timer = T}.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
persist(Prefs) ->
|
persist(Prefs) ->
|
||||||
Path = prefs_path(),
|
Path = prefs_path(),
|
||||||
ok = filelib:ensure_dir(Path),
|
ok = filelib:ensure_dir(Path),
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(gd_grids).
|
-module(gd_grids).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(gd_gui).
|
-module(gd_gui).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
@ -32,7 +32,7 @@
|
|||||||
{wx = none :: none | wx:wx_object(),
|
{wx = none :: none | wx:wx_object(),
|
||||||
frame = none :: none | wx:wx_object(),
|
frame = none :: none | wx:wx_object(),
|
||||||
sizer = none :: none | wx:wx_object(),
|
sizer = none :: none | wx:wx_object(),
|
||||||
lang = en_US :: en_US | ja_JP,
|
lang = en :: en | jp,
|
||||||
j = none :: none | fun(),
|
j = none :: none | fun(),
|
||||||
prefs = #{} :: #{atom() := term()},
|
prefs = #{} :: #{atom() := term()},
|
||||||
accounts = [] :: [gajudesk:poa()],
|
accounts = [] :: [gajudesk:poa()],
|
||||||
@ -84,21 +84,18 @@ start_link(Accounts) ->
|
|||||||
|
|
||||||
init(Prefs) ->
|
init(Prefs) ->
|
||||||
ok = log(info, "GUI starting..."),
|
ok = log(info, "GUI starting..."),
|
||||||
Lang = maps:get(lang, Prefs, en_US),
|
Lang = maps:get(lang, Prefs, en_us),
|
||||||
Trans = gd_jt:read_translations(?MODULE),
|
Trans = gd_jt:read_translations(?MODULE),
|
||||||
J = gd_jt:j(Lang, Trans),
|
J = gd_jt:j(Lang, Trans),
|
||||||
|
|
||||||
AppName = J("GajuDesk"),
|
|
||||||
VSN = proplists:get_value(vsn, ?MODULE:module_info(attributes)),
|
|
||||||
Wx = wx:new(),
|
Wx = wx:new(),
|
||||||
Frame = wxFrame:new(Wx, ?wxID_ANY, AppName ++ " v" ++ VSN),
|
Frame = wxFrame:new(Wx, ?wxID_ANY, J("GajuDesk")),
|
||||||
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}]),
|
||||||
|
|
||||||
WallB = wxButton:new(Frame, ?wxID_ANY, [{label, "[none]"}, {style, ?wxBORDER_NONE}]),
|
WallB = wxButton:new(Frame, ?wxID_ANY, [{label, "[none]"}, {style, ?wxBORDER_NONE}]),
|
||||||
WallW = #w{name = wallet, id = wxButton:getId(WallB), wx = WallB},
|
WallW = #w{name = wallet, id = wxButton:getId(WallB), wx = WallB},
|
||||||
ChainB = wxButton:new(Frame, ?wxID_ANY, [{label, "[ChainID]"}, {style, ?wxBORDER_NONE}]),
|
ChainB = wxButton:new(Frame, ?wxID_ANY, [{label, "[ChainID]"}, {style, ?wxBORDER_NONE}]),
|
||||||
_ = wxButton:disable(ChainB),
|
|
||||||
ChainW = #w{name = chain, id = wxButton:getId(ChainB), wx = ChainB},
|
ChainW = #w{name = chain, id = wxButton:getId(ChainB), wx = ChainB},
|
||||||
NodeB = wxButton:new(Frame, ?wxID_ANY, [{label, "[Node]"}, {style, ?wxBORDER_NONE}]),
|
NodeB = wxButton:new(Frame, ?wxID_ANY, [{label, "[Node]"}, {style, ?wxBORDER_NONE}]),
|
||||||
NodeW = #w{name = node, id = wxButton:getId(NodeB), wx = NodeB},
|
NodeW = #w{name = node, id = wxButton:getId(NodeB), wx = NodeB},
|
||||||
@ -134,7 +131,7 @@ init(Prefs) ->
|
|||||||
{rename, J("Rename")},
|
{rename, J("Rename")},
|
||||||
{drop_key, J("Delete")},
|
{drop_key, J("Delete")},
|
||||||
{send, J("Send Money")},
|
{send, J("Send Money")},
|
||||||
% {recv, J("Receive Money")},
|
{recv, J("Receive Money")},
|
||||||
{grids, J("GRIDS URL")},
|
{grids, J("GRIDS URL")},
|
||||||
{copy, J("Copy")},
|
{copy, J("Copy")},
|
||||||
{www, J("WWW")},
|
{www, J("WWW")},
|
||||||
@ -147,12 +144,6 @@ init(Prefs) ->
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
Buttons = [WallW, ChainW, NodeW, DevW | lists:map(MakeButton, ButtonTemplates)],
|
Buttons = [WallW, ChainW, NodeW, DevW | lists:map(MakeButton, ButtonTemplates)],
|
||||||
Disable =
|
|
||||||
fun(Button) ->
|
|
||||||
#w{wx = W} = lists:keyfind(Button, #w.name, Buttons),
|
|
||||||
wxButton:disable(W)
|
|
||||||
end,
|
|
||||||
ok = lists:foreach(Disable, key_buttons()),
|
|
||||||
|
|
||||||
ChainSz = wxBoxSizer:new(?wxHORIZONTAL),
|
ChainSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||||
AccountSz = wxBoxSizer:new(?wxHORIZONTAL),
|
AccountSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||||
@ -182,18 +173,18 @@ init(Prefs) ->
|
|||||||
_ = wxSizer:add(AccountSz, DropBn, zxw:flags(wide)),
|
_ = wxSizer:add(AccountSz, DropBn, zxw:flags(wide)),
|
||||||
|
|
||||||
#w{wx = SendBn} = lists:keyfind(send, #w.name, Buttons),
|
#w{wx = SendBn} = lists:keyfind(send, #w.name, Buttons),
|
||||||
% #w{wx = RecvBn} = lists:keyfind(recv, #w.name, Buttons),
|
#w{wx = RecvBn} = lists:keyfind(recv, #w.name, Buttons),
|
||||||
#w{wx = GridsBn} = lists:keyfind(grids, #w.name, Buttons),
|
#w{wx = GridsBn} = lists:keyfind(grids, #w.name, Buttons),
|
||||||
_ = wxSizer:add(ActionsSz, SendBn, zxw:flags(wide)),
|
_ = wxSizer:add(ActionsSz, SendBn, zxw:flags(wide)),
|
||||||
% _ = wxSizer:add(ActionsSz, RecvBn, zxw:flags(wide)),
|
_ = wxSizer:add(ActionsSz, RecvBn, zxw:flags(wide)),
|
||||||
_ = wxSizer:add(ActionsSz, GridsBn, zxw:flags(wide)),
|
_ = wxSizer:add(ActionsSz, GridsBn, zxw:flags(wide)),
|
||||||
|
|
||||||
#w{wx = Refresh} = lists:keyfind(refresh, #w.name, Buttons),
|
#w{wx = Refresh} = lists:keyfind(refresh, #w.name, Buttons),
|
||||||
|
|
||||||
% HistoryWin = wxScrolledWindow:new(Frame),
|
HistoryWin = wxScrolledWindow:new(Frame),
|
||||||
% HistorySz = wxBoxSizer:new(?wxVERTICAL),
|
HistorySz = wxBoxSizer:new(?wxVERTICAL),
|
||||||
% ok = wxScrolledWindow:setSizerAndFit(HistoryWin, HistorySz),
|
ok = wxScrolledWindow:setSizerAndFit(HistoryWin, HistorySz),
|
||||||
% ok = wxScrolledWindow:setScrollRate(HistoryWin, 5, 5),
|
ok = wxScrolledWindow:setScrollRate(HistoryWin, 5, 5),
|
||||||
|
|
||||||
_ = wxSizer:add(MainSz, ChainSz, zxw:flags(base)),
|
_ = wxSizer:add(MainSz, ChainSz, zxw:flags(base)),
|
||||||
_ = wxSizer:add(MainSz, AccountSz, zxw:flags(base)),
|
_ = wxSizer:add(MainSz, AccountSz, zxw:flags(base)),
|
||||||
@ -201,7 +192,7 @@ init(Prefs) ->
|
|||||||
_ = wxSizer:add(MainSz, DetailsSz, zxw:flags(base)),
|
_ = wxSizer:add(MainSz, DetailsSz, zxw:flags(base)),
|
||||||
_ = wxSizer:add(MainSz, ActionsSz, zxw:flags(base)),
|
_ = wxSizer:add(MainSz, ActionsSz, zxw:flags(base)),
|
||||||
_ = wxSizer:add(MainSz, Refresh, zxw:flags(base)),
|
_ = wxSizer:add(MainSz, Refresh, zxw:flags(base)),
|
||||||
% _ = wxSizer:add(MainSz, HistoryWin, zxw:flags(wide)),
|
_ = wxSizer:add(MainSz, HistoryWin, zxw:flags(wide)),
|
||||||
ok = wxFrame:setSizer(Frame, MainSz),
|
ok = wxFrame:setSizer(Frame, MainSz),
|
||||||
ok = wxSizer:layout(MainSz),
|
ok = wxSizer:layout(MainSz),
|
||||||
|
|
||||||
@ -216,8 +207,8 @@ init(Prefs) ->
|
|||||||
picker = Picker,
|
picker = Picker,
|
||||||
id = ID_W,
|
id = ID_W,
|
||||||
balance = Balance,
|
balance = Balance,
|
||||||
buttons = Buttons},
|
buttons = Buttons,
|
||||||
% history = #h{win = HistoryWin, sz = HistorySz}},
|
history = #h{win = HistoryWin, sz = HistorySz}},
|
||||||
{Frame, State}.
|
{Frame, State}.
|
||||||
|
|
||||||
|
|
||||||
@ -699,27 +690,15 @@ copy_to_clipboard(String) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
www(State = #s{id = {_, #w{wx = ID_T}}, j = J}) ->
|
www(State = #s{id = {_, #w{wx = ID_T}}}) ->
|
||||||
case wxStaticText:getLabel(ID_T) of
|
String = wxStaticText:getLabel(ID_T),
|
||||||
"" ->
|
URL = unicode:characters_to_list(["https://aescan.io/accounts/", String]),
|
||||||
ok = handle_troubling(State, J("No key selected.")),
|
ok =
|
||||||
State;
|
case wx_misc:launchDefaultBrowser(URL) of
|
||||||
ID ->
|
true -> log(info, "Opened in browser: ~ts", [URL]);
|
||||||
ok = www2(State, ID),
|
false -> log(info, "Failed to open browser: ~ts", [URL])
|
||||||
State
|
end,
|
||||||
end.
|
State.
|
||||||
|
|
||||||
www2(State = #s{j = J}, AccountID) ->
|
|
||||||
case gd_con:network() of
|
|
||||||
{ok, ChainID} ->
|
|
||||||
URL = unicode:characters_to_list(["https://", ChainID, ".gajumaru.io/account/", AccountID]),
|
|
||||||
case wx_misc:launchDefaultBrowser(URL) of
|
|
||||||
true -> log(info, "Opened in browser: ~ts", [URL]);
|
|
||||||
false -> log(info, "Failed to open browser: ~ts", [URL])
|
|
||||||
end;
|
|
||||||
none ->
|
|
||||||
handle_troubling(State, J("No chain assigned."))
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
spend(State = #s{accounts = []}) ->
|
spend(State = #s{accounts = []}) ->
|
||||||
@ -756,11 +735,7 @@ spend2(#poa{id = ID, name = Name}, Nonce, Height, State = #s{frame = Frame, j =
|
|||||||
|
|
||||||
AmtTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
AmtTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
||||||
AmtSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Amount")}]),
|
AmtSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Amount")}]),
|
||||||
AmtInSz = wxBoxSizer:new(?wxHORIZONTAL),
|
_ = wxStaticBoxSizer:add(AmtSz, AmtTx, zxw:flags(wide)),
|
||||||
AmtLabel = wxStaticText:new(Dialog, ?wxID_ANY, "木"),
|
|
||||||
_ = wxStaticBoxSizer:add(AmtInSz, AmtLabel, zxw:flags(base)),
|
|
||||||
_ = wxStaticBoxSizer:add(AmtInSz, AmtTx, zxw:flags(wide)),
|
|
||||||
_ = wxStaticBoxSizer:add(AmtSz, AmtInSz, zxw:flags(wide)),
|
|
||||||
|
|
||||||
DataTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_MULTILINE}]),
|
DataTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_MULTILINE}]),
|
||||||
DataSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Message (optional)")}]),
|
DataSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Message (optional)")}]),
|
||||||
@ -922,28 +897,12 @@ clear_account(State = #s{balance = {_, #w{wx = B}}, id = {_, #w{wx = I}}}) ->
|
|||||||
|
|
||||||
do_wallet(none, #s{buttons = Buttons}) ->
|
do_wallet(none, #s{buttons = Buttons}) ->
|
||||||
#w{wx = WalletB} = lists:keyfind(wallet, #w.name, Buttons),
|
#w{wx = WalletB} = lists:keyfind(wallet, #w.name, Buttons),
|
||||||
Disable =
|
|
||||||
fun(Button) ->
|
|
||||||
#w{wx = W} = lists:keyfind(Button, #w.name, Buttons),
|
|
||||||
wxButton:disable(W)
|
|
||||||
end,
|
|
||||||
ok = lists:foreach(Disable, key_buttons()),
|
|
||||||
ok = wxButton:setLabel(WalletB, "[no wallet]");
|
ok = wxButton:setLabel(WalletB, "[no wallet]");
|
||||||
do_wallet(Name, #s{buttons = Buttons}) ->
|
do_wallet(Name, #s{buttons = Buttons}) ->
|
||||||
#w{wx = WalletB} = lists:keyfind(wallet, #w.name, Buttons),
|
#w{wx = WalletB} = lists:keyfind(wallet, #w.name, Buttons),
|
||||||
Enable =
|
|
||||||
fun(Button) ->
|
|
||||||
#w{wx = W} = lists:keyfind(Button, #w.name, Buttons),
|
|
||||||
wxButton:enable(W)
|
|
||||||
end,
|
|
||||||
ok = lists:foreach(Enable, key_buttons()),
|
|
||||||
ok = wxButton:setLabel(WalletB, Name).
|
ok = wxButton:setLabel(WalletB, Name).
|
||||||
|
|
||||||
|
|
||||||
key_buttons() ->
|
|
||||||
[make_key, recover, mnemonic, rename, drop_key].
|
|
||||||
|
|
||||||
|
|
||||||
do_chain(none, none, #s{buttons = Buttons}) ->
|
do_chain(none, none, #s{buttons = Buttons}) ->
|
||||||
#w{wx = ChainB} = lists:keyfind(chain, #w.name, Buttons),
|
#w{wx = ChainB} = lists:keyfind(chain, #w.name, Buttons),
|
||||||
#w{wx = NodeB} = lists:keyfind(node, #w.name, Buttons),
|
#w{wx = NodeB} = lists:keyfind(node, #w.name, Buttons),
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
%%% translation library is retained).
|
%%% translation library is retained).
|
||||||
|
|
||||||
-module(gd_jt).
|
-module(gd_jt).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-export([read_translations/1, j/2, oneshot_j/2]).
|
-export([read_translations/1, j/2, oneshot_j/2]).
|
||||||
|
|
||||||
|
|
||||||
|
164
src/gd_key_master.erl
Normal file
164
src/gd_key_master.erl
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
%%% @doc
|
||||||
|
%%% Key functions go here.
|
||||||
|
%%%
|
||||||
|
%%% The main reason this is a module of its own is that in the original architecture
|
||||||
|
%%% it was a process rather than just a library of functions. Now that it exists, though,
|
||||||
|
%%% there is little motivation to cram everything here into the controller process's
|
||||||
|
%%% code.
|
||||||
|
%%% @end
|
||||||
|
|
||||||
|
-module(gd_key_master).
|
||||||
|
-vsn("0.5.2").
|
||||||
|
|
||||||
|
|
||||||
|
-export([make_key/2, encode/1, decode/1]).
|
||||||
|
-export([lcg/1]).
|
||||||
|
-include("gd.hrl").
|
||||||
|
|
||||||
|
|
||||||
|
make_key("", <<>>) ->
|
||||||
|
Pair = #{public := Public} = ecu_eddsa:sign_keypair(),
|
||||||
|
ID = gmser_api_encoder:encode(account_pubkey, Public),
|
||||||
|
Name = binary_to_list(ID),
|
||||||
|
#key{name = Name, id = ID, pair = Pair};
|
||||||
|
make_key("", Seed) ->
|
||||||
|
Pair = #{public := Public} = ecu_eddsa:sign_seed_keypair(Seed),
|
||||||
|
ID = gmser_api_encoder:encode(account_pubkey, Public),
|
||||||
|
Name = binary_to_list(ID),
|
||||||
|
#key{name = Name, id = ID, pair = Pair};
|
||||||
|
make_key(Name, <<>>) ->
|
||||||
|
Pair = #{public := Public} = ecu_eddsa:sign_keypair(),
|
||||||
|
ID = gmser_api_encoder:encode(account_pubkey, Public),
|
||||||
|
#key{name = Name, id = ID, pair = Pair};
|
||||||
|
make_key(Name, Seed) ->
|
||||||
|
Pair = #{public := Public} = ecu_eddsa:sign_seed_keypair(Seed),
|
||||||
|
ID = gmser_api_encoder:encode(account_pubkey, Public),
|
||||||
|
#key{name = Name, id = ID, pair = Pair}.
|
||||||
|
|
||||||
|
|
||||||
|
-spec encode(Secret) -> Phrase
|
||||||
|
when Secret :: binary(),
|
||||||
|
Phrase :: string().
|
||||||
|
%% @doc
|
||||||
|
%% The encoding and decoding procesures are written to be able to handle any
|
||||||
|
%% width of bitstring or binary and a variable size dictionary. The magic numbers
|
||||||
|
%% 32, 4096 and 12 have been dropped in because currently these are known, but that
|
||||||
|
%% will change in the future if the key size or type changes.
|
||||||
|
|
||||||
|
encode(Bin) ->
|
||||||
|
<<Number:(32 * 8)>> = Bin,
|
||||||
|
DictSize = 4096,
|
||||||
|
Words = read_words(),
|
||||||
|
% Width = chunksize(DictSize - 1, 2),
|
||||||
|
Width = 12,
|
||||||
|
Chunks = chunksize(Number, DictSize),
|
||||||
|
Binary = <<Number:(Chunks * Width)>>,
|
||||||
|
encode(Width, Binary, Words).
|
||||||
|
|
||||||
|
encode(Width, Bits, Words) ->
|
||||||
|
CheckSum = checksum(Width, Bits),
|
||||||
|
encode(Width, <<CheckSum:Width, Bits/bitstring>>, Words, []).
|
||||||
|
|
||||||
|
encode(_, <<>>, _, Acc) ->
|
||||||
|
unicode:characters_to_list(lists:join(" ", lists:reverse(Acc)));
|
||||||
|
encode(Width, Bits, Words, Acc) ->
|
||||||
|
<<I:Width, Rest/bitstring>> = Bits,
|
||||||
|
Word = lists:nth(I + 1, Words),
|
||||||
|
encode(Width, Rest, Words, [Word | Acc]).
|
||||||
|
|
||||||
|
|
||||||
|
-spec decode(Phrase) -> {ok, Secret} | {error, Reason}
|
||||||
|
when Phrase :: string(),
|
||||||
|
Secret :: binary(),
|
||||||
|
Reason :: bad_phrase | bad_word.
|
||||||
|
%% @doc
|
||||||
|
%% Reverses the encoded secret string back into its binary representation.
|
||||||
|
|
||||||
|
decode(Encoded) ->
|
||||||
|
DictSize = 4096,
|
||||||
|
Words = read_words(),
|
||||||
|
Width = chunksize(DictSize - 1, 2),
|
||||||
|
decode(Width, Words, Encoded).
|
||||||
|
|
||||||
|
decode(Width, Words, Encoded) when is_list(Encoded) ->
|
||||||
|
decode(Width, Words, list_to_binary(Encoded));
|
||||||
|
decode(Width, Words, Encoded) ->
|
||||||
|
Split = string:lexemes(Encoded, " "),
|
||||||
|
decode(Width, Words, Split, <<>>).
|
||||||
|
|
||||||
|
decode(Width, Words, [Word | Rest], Acc) ->
|
||||||
|
case find(Word, Words) of
|
||||||
|
{ok, N} -> decode(Width, Words, Rest, <<Acc/bitstring, N:Width>>);
|
||||||
|
Error -> Error
|
||||||
|
end;
|
||||||
|
decode(Width, _, [], Acc) ->
|
||||||
|
sumcheck(Width, Acc).
|
||||||
|
|
||||||
|
|
||||||
|
chunksize(N, C) ->
|
||||||
|
chunksize(N, C, 0).
|
||||||
|
|
||||||
|
chunksize(0, _, A) -> A;
|
||||||
|
chunksize(N, C, A) -> chunksize(N div C, C, A + 1).
|
||||||
|
|
||||||
|
|
||||||
|
read_words() ->
|
||||||
|
Path = filename:join([zx:get_home(), "priv", "words4096.txt"]),
|
||||||
|
{ok, Bin} = file:read_file(Path),
|
||||||
|
string:lexemes(Bin, "\n").
|
||||||
|
|
||||||
|
|
||||||
|
find(Word, Words) ->
|
||||||
|
find(Word, Words, 0).
|
||||||
|
|
||||||
|
find(Word, [Word | _], N) -> {ok, N};
|
||||||
|
find(Word, [_ | Rest], N) -> find(Word, Rest, N + 1);
|
||||||
|
find(Word, [], _) -> {error, {bad_word, Word}}.
|
||||||
|
|
||||||
|
|
||||||
|
checksum(Width, Bits) ->
|
||||||
|
checksum(Width, Bits, 0).
|
||||||
|
|
||||||
|
checksum(_, <<>>, Sum) ->
|
||||||
|
Sum;
|
||||||
|
checksum(Width, Bits, Sum) ->
|
||||||
|
<<N:Width, Rest/bitstring>> = Bits,
|
||||||
|
checksum(Width, Rest, N bxor Sum).
|
||||||
|
|
||||||
|
|
||||||
|
sumcheck(Width, Bits) ->
|
||||||
|
<<CheckSum:Width, Binary/bitstring>> = Bits,
|
||||||
|
case checksum(Width, Binary) =:= CheckSum of
|
||||||
|
true ->
|
||||||
|
<<N:(bit_size(Binary))>> = Binary,
|
||||||
|
{ok, <<N:(32 * 8)>>};
|
||||||
|
false ->
|
||||||
|
{error, bad_phrase}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec lcg(integer()) -> integer().
|
||||||
|
%% A simple PRNG that fits into 32 bits and is easy to implement anywhere (Kotlin).
|
||||||
|
%% Specifically, it is a "linear congruential generator" of the Lehmer variety.
|
||||||
|
%% The constants used are based on recommendations from Park, Miller and Stockmeyer:
|
||||||
|
%% https://www.firstpr.com.au/dsp/rand31/p105-crawford.pdf#page=4
|
||||||
|
%%
|
||||||
|
%% The input value should be between 1 and 2^31-1.
|
||||||
|
%%
|
||||||
|
%% The purpose of this PRNG is for password-based dictionary shuffling.
|
||||||
|
|
||||||
|
lcg(N) ->
|
||||||
|
M = 16#7FFFFFFF,
|
||||||
|
A = 48271,
|
||||||
|
Q = 44488, % M div A
|
||||||
|
R = 3399, % M rem A
|
||||||
|
Div = N div Q,
|
||||||
|
Rem = N rem Q,
|
||||||
|
S = Rem * A,
|
||||||
|
T = Div * R,
|
||||||
|
Result = S - T,
|
||||||
|
case Result < 0 of
|
||||||
|
false -> Result;
|
||||||
|
true -> Result + M
|
||||||
|
end.
|
@ -1,194 +0,0 @@
|
|||||||
-module(gd_sophia_editor).
|
|
||||||
-vsn("0.6.6").
|
|
||||||
-export([new/1, update/2,
|
|
||||||
get_text/1, set_text/2]).
|
|
||||||
|
|
||||||
-include("$zx_include/zx_logger.hrl").
|
|
||||||
-include_lib("wx/include/wx.hrl").
|
|
||||||
|
|
||||||
|
|
||||||
%%% Formatting Constants
|
|
||||||
|
|
||||||
%% Style labels
|
|
||||||
-define(DEFAULT, 0).
|
|
||||||
-define(KEYWORD, 1).
|
|
||||||
-define(IDENTIFIER, 2).
|
|
||||||
-define(COMMENT, 3).
|
|
||||||
-define(STRING, 4).
|
|
||||||
-define(NUMBER, 5).
|
|
||||||
-define(OPERATOR, 6).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%% Color palette
|
|
||||||
|
|
||||||
% Intensities:
|
|
||||||
-define(H, 255). % High
|
|
||||||
-define(M, 192). % Medium
|
|
||||||
-define(L, 128). % Low
|
|
||||||
-define(X, 32). % X-Low
|
|
||||||
-define(Z, 0). % Zilch
|
|
||||||
|
|
||||||
|
|
||||||
% RGB values
|
|
||||||
% R G B
|
|
||||||
-define(black, {?Z, ?Z, ?Z}).
|
|
||||||
-define(light_red, {?H, ?Z, ?Z}).
|
|
||||||
-define(light_green, {?Z, ?H, ?Z}).
|
|
||||||
-define(light_blue, {?Z, ?Z, ?H}).
|
|
||||||
-define(yellow, {?H, ?H, ?Z}).
|
|
||||||
-define(light_magenta, {?H, ?Z, ?H}).
|
|
||||||
-define(light_cyan, {?Z, ?H, ?H}).
|
|
||||||
-define(high_white, {?H, ?H, ?H}).
|
|
||||||
-define(red, {?L, ?Z, ?Z}).
|
|
||||||
-define(green, {?Z, ?L, ?Z}).
|
|
||||||
-define(blue, {?Z, ?Z, ?L}).
|
|
||||||
-define(brown, {?L, ?L, ?Z}).
|
|
||||||
-define(magenta, {?L, ?Z, ?L}).
|
|
||||||
-define(cyan, {?Z, ?L, ?L}).
|
|
||||||
-define(not_black, {?X, ?X, ?X}).
|
|
||||||
-define(grey, {?L, ?L, ?L}).
|
|
||||||
-define(white, {?M, ?M, ?M}).
|
|
||||||
|
|
||||||
styles() ->
|
|
||||||
[?DEFAULT,
|
|
||||||
?KEYWORD,
|
|
||||||
?IDENTIFIER,
|
|
||||||
?COMMENT,
|
|
||||||
?STRING,
|
|
||||||
?NUMBER,
|
|
||||||
?OPERATOR].
|
|
||||||
|
|
||||||
palette(light) ->
|
|
||||||
#{?DEFAULT => ?black,
|
|
||||||
?KEYWORD => ?blue,
|
|
||||||
?IDENTIFIER => ?cyan,
|
|
||||||
?COMMENT => ?grey,
|
|
||||||
?STRING => ?red,
|
|
||||||
?NUMBER => ?magenta,
|
|
||||||
?OPERATOR => ?brown,
|
|
||||||
bg => ?high_white};
|
|
||||||
palette(dark) ->
|
|
||||||
#{?DEFAULT => ?white,
|
|
||||||
?KEYWORD => ?light_cyan,
|
|
||||||
?IDENTIFIER => ?green,
|
|
||||||
?COMMENT => ?grey,
|
|
||||||
?STRING => ?light_red,
|
|
||||||
?NUMBER => ?light_magenta,
|
|
||||||
?OPERATOR => ?yellow,
|
|
||||||
bg => ?not_black}.
|
|
||||||
|
|
||||||
color_mode() ->
|
|
||||||
{R, G, B, _} = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW),
|
|
||||||
case (lists:sum([R, G, B]) div 3) > 128 of
|
|
||||||
true -> light;
|
|
||||||
false -> dark
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
new(Parent) ->
|
|
||||||
STC = wxStyledTextCtrl:new(Parent),
|
|
||||||
ok = wxStyledTextCtrl:setLexer(STC, ?wxSTC_LEX_CONTAINER),
|
|
||||||
FontSize = 13,
|
|
||||||
Mono = wxFont:new(FontSize,
|
|
||||||
?wxFONTFAMILY_TELETYPE,
|
|
||||||
?wxFONTSTYLE_NORMAL,
|
|
||||||
?wxFONTWEIGHT_NORMAL,
|
|
||||||
[{face, "Monospace"}]),
|
|
||||||
SetMonospace = fun(Style) -> wxStyledTextCtrl:styleSetFont(STC, Style, Mono) end,
|
|
||||||
ok = lists:foreach(SetMonospace, styles()),
|
|
||||||
ok = wxStyledTextCtrl:styleSetFont(STC, ?wxSTC_STYLE_DEFAULT, Mono),
|
|
||||||
ok = set_colors(STC),
|
|
||||||
STC.
|
|
||||||
|
|
||||||
get_text(STC) ->
|
|
||||||
wxStyledTextCtrl:getText(STC).
|
|
||||||
|
|
||||||
set_text(STC, Text) ->
|
|
||||||
ok = wxStyledTextCtrl:setText(STC, Text),
|
|
||||||
%% Force Scintilla to request styling for the entire text
|
|
||||||
wxStyledTextCtrl:colourise(STC, 0, -1).
|
|
||||||
|
|
||||||
|
|
||||||
set_colors(STC) ->
|
|
||||||
ok = wxStyledTextCtrl:styleClearAll(STC),
|
|
||||||
Palette = #{bg := BGC} = palette(color_mode()),
|
|
||||||
Colorize =
|
|
||||||
fun(Style) ->
|
|
||||||
Color = maps:get(Style, Palette),
|
|
||||||
ok = wxStyledTextCtrl:styleSetForeground(STC, Style, Color),
|
|
||||||
ok = wxStyledTextCtrl:styleSetBackground(STC, Style, BGC)
|
|
||||||
end,
|
|
||||||
ok = wxStyledTextCtrl:styleSetBackground(STC, ?wxSTC_STYLE_DEFAULT, BGC),
|
|
||||||
lists:foreach(Colorize, styles()).
|
|
||||||
|
|
||||||
|
|
||||||
update(_Event, STC) ->
|
|
||||||
Text = wxStyledTextCtrl:getText(STC),
|
|
||||||
case so_scan:scan(Text) of
|
|
||||||
{ok, Tokens} ->
|
|
||||||
ok = wxStyledTextCtrl:startStyling(STC, 0),
|
|
||||||
apply_styles(STC, Tokens);
|
|
||||||
{error, _Reason} ->
|
|
||||||
ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
apply_styles(STC, Tokens) ->
|
|
||||||
lists:foreach(fun(Token) -> style_token(STC, Token) end, Tokens).
|
|
||||||
|
|
||||||
|
|
||||||
% FIXME: 'qid' is not properly handled. If there are multi-dot qids, they will break
|
|
||||||
style_token(STC, Token) ->
|
|
||||||
{Type, Value} = type_and_value(Token),
|
|
||||||
{StartOffset, LengthOffset} = offset(Type),
|
|
||||||
{Line, Col} = element(2, Token),
|
|
||||||
Length = byte_size(to_binary(Value)) + LengthOffset,
|
|
||||||
Style = classify_style(Type, Value),
|
|
||||||
Start = wxStyledTextCtrl:positionFromLine(STC, Line - 1) + Col + StartOffset,
|
|
||||||
wxStyledTextCtrl:startStyling(STC, Start),
|
|
||||||
wxStyledTextCtrl:setStyling(STC, Length, Style).
|
|
||||||
|
|
||||||
offset(string) -> { 0, 0};
|
|
||||||
offset(qid) -> {-1, 1};
|
|
||||||
offset(_) -> {-1, 0}.
|
|
||||||
|
|
||||||
to_binary(S) when is_list(S) ->
|
|
||||||
unicode:characters_to_binary(S);
|
|
||||||
to_binary(S) when is_binary(S) ->
|
|
||||||
S;
|
|
||||||
to_binary(I) when is_integer(I) ->
|
|
||||||
integer_to_binary(I).
|
|
||||||
|
|
||||||
|
|
||||||
classify_style(Type, Value) ->
|
|
||||||
case Type of
|
|
||||||
symbol ->
|
|
||||||
case lists:member(Value, keywords()) of
|
|
||||||
true -> ?KEYWORD;
|
|
||||||
false -> ?OPERATOR
|
|
||||||
end;
|
|
||||||
id -> ?IDENTIFIER;
|
|
||||||
qid -> ?IDENTIFIER;
|
|
||||||
con -> ?IDENTIFIER;
|
|
||||||
qcon -> ?IDENTIFIER;
|
|
||||||
tvar -> ?IDENTIFIER;
|
|
||||||
string -> ?STRING;
|
|
||||||
char -> ?STRING;
|
|
||||||
int -> ?NUMBER;
|
|
||||||
hex -> ?NUMBER;
|
|
||||||
bytes -> ?NUMBER;
|
|
||||||
skip -> ?COMMENT;
|
|
||||||
_ -> ?DEFAULT
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
type_and_value({Type, _Line, Value}) -> {Type, Value};
|
|
||||||
type_and_value({Type, _}) -> {Type, atom_to_list(Type)}.
|
|
||||||
|
|
||||||
|
|
||||||
keywords() ->
|
|
||||||
["contract", "include", "let", "switch", "type", "record", "datatype", "if",
|
|
||||||
"elif", "else", "function", "stateful", "payable", "true", "false", "mod",
|
|
||||||
"public", "entrypoint", "private", "indexed", "namespace", "interface",
|
|
||||||
"main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"].
|
|
@ -12,7 +12,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(gd_sup).
|
-module(gd_sup).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-module(gd_v).
|
-module(gd_v).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-module(gd_v_devman).
|
-module(gd_v_devman).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
@ -15,8 +15,6 @@
|
|||||||
-include("$zx_include/zx_logger.hrl").
|
-include("$zx_include/zx_logger.hrl").
|
||||||
-include("gd.hrl").
|
-include("gd.hrl").
|
||||||
|
|
||||||
-define(editorMode, sophia).
|
|
||||||
|
|
||||||
% Widgets
|
% Widgets
|
||||||
-record(w,
|
-record(w,
|
||||||
{name = none :: atom() | {FunName :: binary(), call | dryr},
|
{name = none :: atom() | {FunName :: binary(), call | dryr},
|
||||||
@ -258,9 +256,6 @@ handle_event(E = #wx{event = #wxCommand{type = command_button_clicked},
|
|||||||
State
|
State
|
||||||
end,
|
end,
|
||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
handle_event(#wx{event = Event = #wxStyledText{type = stc_styleneeded}, obj = Win}, State) ->
|
|
||||||
ok = style(State, Win, Event),
|
|
||||||
{noreply, State};
|
|
||||||
handle_event(#wx{event = #wxClose{}}, State = #s{frame = Frame, prefs = Prefs}) ->
|
handle_event(#wx{event = #wxClose{}}, State = #s{frame = Frame, prefs = Prefs}) ->
|
||||||
Geometry =
|
Geometry =
|
||||||
case wxTopLevelWindow:isMaximized(Frame) of
|
case wxTopLevelWindow:isMaximized(Frame) of
|
||||||
@ -296,14 +291,6 @@ terminate(Reason, State) ->
|
|||||||
|
|
||||||
%%% Doers
|
%%% Doers
|
||||||
|
|
||||||
style(#s{code = {_, Pages}}, Win, Event) ->
|
|
||||||
case lists:keyfind(Win, #p.win, Pages) of
|
|
||||||
#p{code = STC} ->
|
|
||||||
gd_sophia_editor:update(Event, STC);
|
|
||||||
false ->
|
|
||||||
tell("Received bogus style event.~nWin: ~p~nEvent: ~p", [Win, Event])
|
|
||||||
end.
|
|
||||||
|
|
||||||
clicked(State = #s{cons = {Consbook, Contracts}}, Name) ->
|
clicked(State = #s{cons = {Consbook, Contracts}}, Name) ->
|
||||||
case wxNotebook:getSelection(Consbook) of
|
case wxNotebook:getSelection(Consbook) of
|
||||||
?wxNOT_FOUND ->
|
?wxNOT_FOUND ->
|
||||||
@ -429,11 +416,12 @@ call_params([{L, C} | T], A) ->
|
|||||||
clicked4(State,
|
clicked4(State,
|
||||||
#c{id = ConID, build = #{aci := ACI}, funs = {_, Funs}},
|
#c{id = ConID, build = #{aci := ACI}, funs = {_, Funs}},
|
||||||
{Name, Type},
|
{Name, Type},
|
||||||
{PK, Nonce, TTL, GasP, Gas, Amt}) ->
|
{PK, Nonce, TTL, GasP, Gas, Amount}) ->
|
||||||
AACI = hz:prepare_aaci(ACI),
|
AACI = hz:prepare_aaci(ACI),
|
||||||
#f{args = ArgFields} = maps:get(Name, Funs),
|
#f{args = ArgFields} = lists:keyfind(Name, #f.name, Funs),
|
||||||
Args = lists:map(fun get_arg/1, ArgFields),
|
Args = lists:map(fun get_arg/1, ArgFields),
|
||||||
case hz:contract_call(PK, Nonce, Gas, GasP, Amt, TTL, AACI, ConID, Name, Args) of
|
FunName = binary_to_list(Name),
|
||||||
|
case hz:contract_call(PK, Nonce, Gas, GasP, Amount, TTL, AACI, ConID, FunName, Args) of
|
||||||
{ok, UnsignedTX} ->
|
{ok, UnsignedTX} ->
|
||||||
case Type of
|
case Type of
|
||||||
call -> do_call(State, ConID, PK, UnsignedTX);
|
call -> do_call(State, ConID, PK, UnsignedTX);
|
||||||
@ -516,13 +504,22 @@ add_code_page2(State, {hash, Address}) ->
|
|||||||
open_hash2(State, Address).
|
open_hash2(State, Address).
|
||||||
|
|
||||||
add_code_page(State = #s{tabs = TopBook, code = {Codebook, Pages}}, Location, Code) ->
|
add_code_page(State = #s{tabs = TopBook, code = {Codebook, Pages}}, Location, Code) ->
|
||||||
Color = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW),
|
% FIXME: One of these days we need to define the text area as a wxStyledTextCtrl and will
|
||||||
tell("Color: ~p", [Color]),
|
% have to contend with system theme issues (light/dark themese, namely)
|
||||||
|
% Leaving this little thing here to remind myself how any of that works later.
|
||||||
|
% The call below returns a wx_color4() type (not that we need alpha...).
|
||||||
|
% Color = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW),
|
||||||
|
% tell("Color: ~p", [Color]),
|
||||||
Window = wxWindow:new(Codebook, ?wxID_ANY),
|
Window = wxWindow:new(Codebook, ?wxID_ANY),
|
||||||
PageSz = wxBoxSizer:new(?wxHORIZONTAL),
|
PageSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||||
|
|
||||||
CodeTx = gd_sophia_editor:new(Window),
|
CodeTxStyle = {style, ?wxTE_MULTILINE bor ?wxTE_PROCESS_TAB bor ?wxTE_DONTWRAP},
|
||||||
ok = gd_sophia_editor:set_text(CodeTx, Code),
|
CodeTx = wxTextCtrl:new(Window, ?wxID_ANY, [CodeTxStyle]),
|
||||||
|
TextAt = wxTextAttr:new(),
|
||||||
|
Mono = wxFont:new(10, ?wxMODERN, ?wxNORMAL, ?wxNORMAL, [{face, "Monospace"}]),
|
||||||
|
ok = wxTextAttr:setFont(TextAt, Mono),
|
||||||
|
true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt),
|
||||||
|
ok = wxTextCtrl:setValue(CodeTx, Code),
|
||||||
|
|
||||||
_ = wxSizer:add(PageSz, CodeTx, zxw:flags(wide)),
|
_ = wxSizer:add(PageSz, CodeTx, zxw:flags(wide)),
|
||||||
|
|
||||||
@ -534,7 +531,6 @@ add_code_page(State = #s{tabs = TopBook, code = {Codebook, Pages}}, Location, Co
|
|||||||
{file, Path} -> filename:basename(Path);
|
{file, Path} -> filename:basename(Path);
|
||||||
{hash, Addr} -> Addr
|
{hash, Addr} -> Addr
|
||||||
end,
|
end,
|
||||||
ok = wxStyledTextCtrl:connect(Window, stc_styleneeded),
|
|
||||||
true = wxNotebook:addPage(Codebook, Window, FileName, [{bSelect, true}]),
|
true = wxNotebook:addPage(Codebook, Window, FileName, [{bSelect, true}]),
|
||||||
Page = #p{path = Location, win = Window, code = CodeTx},
|
Page = #p{path = Location, win = Window, code = CodeTx},
|
||||||
NewPages = Pages ++ [Page],
|
NewPages = Pages ++ [Page],
|
||||||
@ -598,39 +594,33 @@ deploy2(State, Source) ->
|
|||||||
case compile(Source) of
|
case compile(Source) of
|
||||||
% Options = sophia_options(),
|
% Options = sophia_options(),
|
||||||
% case so_compiler:from_string(Source, Options) of
|
% case so_compiler:from_string(Source, Options) of
|
||||||
% TODO: Make hz accept either the aaci or the aci, preferring the aaci if present
|
|
||||||
{ok, Build} ->
|
{ok, Build} ->
|
||||||
ACI = maps:get(aci, Build),
|
deploy3(State, Build);
|
||||||
RawAACI = {aaci, ContractName, Funs, NS} = hz:prepare_aaci(ACI),
|
|
||||||
{InitSpec, Callable} = maps:take("init", Funs),
|
|
||||||
AACI = setelement(3, RawAACI, Callable),
|
|
||||||
Complete = maps:put(aaci, AACI, Build),
|
|
||||||
ok = tell(info, "Deploying Contract: ~p", [ContractName]),
|
|
||||||
deploy3(State, InitSpec, Complete, NS);
|
|
||||||
Other ->
|
Other ->
|
||||||
ok = tell(info, "Compilation Failed!~n~tp", [Other]),
|
ok = tell(info, "Compilation Failed!~n~tp", [Other]),
|
||||||
State
|
State
|
||||||
end.
|
end.
|
||||||
|
|
||||||
deploy3(State, InitSpec, Build, NS) ->
|
deploy3(State, Build) ->
|
||||||
case gd_con:list_keys() of
|
case gd_con:list_keys() of
|
||||||
{ok, 0, []} ->
|
{ok, 0, []} ->
|
||||||
handle_troubling(State, "No keys exist in the current wallet.");
|
handle_troubling(State, "No keys exist in the current wallet.");
|
||||||
{ok, Selected, Keys} ->
|
{ok, Selected, Keys} ->
|
||||||
deploy4(State, InitSpec, Build, NS, Selected, Keys);
|
deploy4(State, Build, Selected, Keys);
|
||||||
error ->
|
error ->
|
||||||
handle_troubling(State, "No wallet is selected!")
|
handle_troubling(State, "No wallet is selected!")
|
||||||
end.
|
end.
|
||||||
|
|
||||||
deploy4(State = #s{frame = Frame, j = J}, InitSpec, Build, NS, Selected, Keys) ->
|
deploy4(State = #s{frame = Frame, j = J}, Build = #{aci := ACI}, Selected, Keys) ->
|
||||||
InitArgs = element(1, InitSpec),
|
{#{functions := Funs}, _} = find_main(ACI),
|
||||||
|
#{arguments := As} = lom:find(name, <<"init">>, Funs),
|
||||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Deploy Contract")),
|
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Deploy Contract")),
|
||||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||||
ScrollWin = wxScrolledWindow:new(Dialog),
|
ScrollWin = wxScrolledWindow:new(Dialog),
|
||||||
ScrollSz = wxBoxSizer:new(?wxVERTICAL),
|
ScrollSz = wxBoxSizer:new(?wxVERTICAL),
|
||||||
ok = wxScrolledWindow:setSizerAndFit(ScrollWin, ScrollSz),
|
ok = wxScrolledWindow:setSizerAndFit(ScrollWin, ScrollSz),
|
||||||
ok = wxScrolledWindow:setScrollRate(ScrollWin, 5, 5),
|
ok = wxScrolledWindow:setScrollRate(ScrollWin, 5, 5),
|
||||||
FunName = unicode:characters_to_list(["init/", integer_to_list(length(InitArgs))]),
|
FunName = unicode:characters_to_list(["init/", integer_to_list(length(As))]),
|
||||||
FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]),
|
FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]),
|
||||||
KeySz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Signature Key")}]),
|
KeySz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Signature Key")}]),
|
||||||
KeyPicker = wxChoice:new(ScrollWin, ?wxID_ANY, [{choices, Keys}]),
|
KeyPicker = wxChoice:new(ScrollWin, ?wxID_ANY, [{choices, Keys}]),
|
||||||
@ -645,7 +635,24 @@ deploy4(State = #s{frame = Frame, j = J}, InitSpec, Build, NS, Selected, Keys) -
|
|||||||
GridSz = wxFlexGridSizer:new(2, 4, 4),
|
GridSz = wxFlexGridSizer:new(2, 4, 4),
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
|
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
|
||||||
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
|
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
|
||||||
ArgFields = make_arg_fields(ScrollWin, GridSz, InitArgs, NS, J),
|
MakeArgField =
|
||||||
|
fun(#{name := AN, type := T}) ->
|
||||||
|
Type =
|
||||||
|
case T of
|
||||||
|
<<"address">> -> address;
|
||||||
|
<<"int">> -> integer;
|
||||||
|
<<"bool">> -> boolean;
|
||||||
|
L when is_list(L) -> list; % FIXME
|
||||||
|
% I when is_binary(I) -> iface % FIXME
|
||||||
|
I when is_binary(I) -> address % FIXME
|
||||||
|
end,
|
||||||
|
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
||||||
|
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
|
||||||
|
_ = wxFlexGridSizer:add(GridSz, ANT, zxw:flags(base)),
|
||||||
|
_ = wxFlexGridSizer:add(GridSz, TCT, zxw:flags(wide)),
|
||||||
|
{ANT, TCT, Type}
|
||||||
|
end,
|
||||||
|
ArgFields = lists:map(MakeArgField, As),
|
||||||
_ = wxStaticBoxSizer:add(FunSz, GridSz, zxw:flags(wide)),
|
_ = wxStaticBoxSizer:add(FunSz, GridSz, zxw:flags(wide)),
|
||||||
_ = wxStaticBoxSizer:add(ScrollSz, FunSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
_ = wxStaticBoxSizer:add(ScrollSz, FunSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
||||||
_ = wxStaticBoxSizer:add(ScrollSz, KeySz, [{proportion, 0}, {flag, ?wxEXPAND}]),
|
_ = wxStaticBoxSizer:add(ScrollSz, KeySz, [{proportion, 0}, {flag, ?wxEXPAND}]),
|
||||||
@ -661,7 +668,7 @@ deploy4(State = #s{frame = Frame, j = J}, InitSpec, Build, NS, Selected, Keys) -
|
|||||||
?wxID_OK ->
|
?wxID_OK ->
|
||||||
ID = wxChoice:getString(KeyPicker, wxChoice:getSelection(KeyPicker)),
|
ID = wxChoice:getString(KeyPicker, wxChoice:getSelection(KeyPicker)),
|
||||||
PK = unicode:characters_to_binary(ID),
|
PK = unicode:characters_to_binary(ID),
|
||||||
IArgs = lists:map(fun get_arg/1, ArgFields),
|
InitArgs = lists:map(fun get_arg/1, ArgFields),
|
||||||
Controls =
|
Controls =
|
||||||
[{"TTL", TTL_Tx},
|
[{"TTL", TTL_Tx},
|
||||||
{"Gas Price", GasP_Tx},
|
{"Gas Price", GasP_Tx},
|
||||||
@ -671,7 +678,7 @@ deploy4(State = #s{frame = Frame, j = J}, InitSpec, Build, NS, Selected, Keys) -
|
|||||||
{ok, [TTL, GasP, Gas, Amount]} ->
|
{ok, [TTL, GasP, Gas, Amount]} ->
|
||||||
{ok, Nonce} = hz:next_nonce(PK),
|
{ok, Nonce} = hz:next_nonce(PK),
|
||||||
DeployParams = {PK, Nonce, TTL, GasP, Gas, Amount},
|
DeployParams = {PK, Nonce, TTL, GasP, Gas, Amount},
|
||||||
{ok, DeployParams, IArgs};
|
{ok, DeployParams, InitArgs};
|
||||||
E ->
|
E ->
|
||||||
E
|
E
|
||||||
end;
|
end;
|
||||||
@ -837,9 +844,9 @@ open_hash3(State, Address, Source) ->
|
|||||||
% TODO: Compile on load and verify the deployed hash for validity.
|
% TODO: Compile on load and verify the deployed hash for validity.
|
||||||
Options = sophia_options(),
|
Options = sophia_options(),
|
||||||
case so_compiler:from_string(Source, Options) of
|
case so_compiler:from_string(Source, Options) of
|
||||||
{ok, Build = #{aaci := AACI}} ->
|
{ok, Build = #{aci := ACI}} ->
|
||||||
{Defs = #{functions := Funs}, ConIfaces} = find_main(AACI),
|
{Defs = #{functions := Funs}, ConIfaces} = find_main(ACI),
|
||||||
Callable = maps:remove("init", Funs),
|
Callable = lom:delete(name, <<"init">>, Funs),
|
||||||
FunDefs = {maps:put(functions, Callable, Defs), ConIfaces},
|
FunDefs = {maps:put(functions, Callable, Defs), ConIfaces},
|
||||||
ok = tell(info, "Compilation Succeeded!~n~tp~n~n~tp", [Build, FunDefs]),
|
ok = tell(info, "Compilation Succeeded!~n~tp~n~n~tp", [Build, FunDefs]),
|
||||||
add_code_page(State, {hash, Address}, Source);
|
add_code_page(State, {hash, Address}, Source);
|
||||||
@ -903,7 +910,7 @@ save(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}})
|
|||||||
_ -> Name ++ ".aes"
|
_ -> Name ++ ".aes"
|
||||||
end,
|
end,
|
||||||
Path = filename:join(Dir, File),
|
Path = filename:join(Dir, File),
|
||||||
Source = get_source(Widget),
|
Source = wxTextCtrl:getValue(Widget),
|
||||||
case filelib:ensure_dir(Path) of
|
case filelib:ensure_dir(Path) of
|
||||||
ok ->
|
ok ->
|
||||||
case file:write_file(Path, Source) of
|
case file:write_file(Path, Source) of
|
||||||
@ -931,19 +938,6 @@ save(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}})
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_source(Widget) ->
|
|
||||||
case ?editorMode of
|
|
||||||
plain -> wxTextCtrl:getValue(Widget);
|
|
||||||
sophia -> gd_sophia_editor:get_text(Widget)
|
|
||||||
end.
|
|
||||||
|
|
||||||
set_source(Widget, Src) ->
|
|
||||||
case ?editorMode of
|
|
||||||
plain -> wxTextCtrl:setValue(Widget, Src);
|
|
||||||
sophia -> gd_sophia_editor:set_text(Widget, Src)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
% TODO: Break this down -- tons of things in here recur.
|
% TODO: Break this down -- tons of things in here recur.
|
||||||
rename(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}}) ->
|
rename(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}}) ->
|
||||||
case wxNotebook:getSelection(Codebook) of
|
case wxNotebook:getSelection(Codebook) of
|
||||||
@ -974,7 +968,7 @@ rename(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}}
|
|||||||
_ -> Name ++ ".aes"
|
_ -> Name ++ ".aes"
|
||||||
end,
|
end,
|
||||||
NewPath = filename:join(Dir, File),
|
NewPath = filename:join(Dir, File),
|
||||||
Source = get_source(Widget),
|
Source = wxTextCtrl:getValue(Widget),
|
||||||
case filelib:ensure_dir(NewPath) of
|
case filelib:ensure_dir(NewPath) of
|
||||||
ok ->
|
ok ->
|
||||||
case file:write_file(NewPath, Source) of
|
case file:write_file(NewPath, Source) of
|
||||||
@ -1090,7 +1084,7 @@ load3(State = #s{tabs = TopBook, cons = {Consbook, Pages}, buttons = Buttons, j
|
|||||||
TextAt = wxTextAttr:new(),
|
TextAt = wxTextAttr:new(),
|
||||||
ok = wxTextAttr:setFont(TextAt, Mono),
|
ok = wxTextAttr:setFont(TextAt, Mono),
|
||||||
true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt),
|
true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt),
|
||||||
ok = set_source(CodeTx, Source),
|
ok = wxTextCtrl:setValue(CodeTx, Source),
|
||||||
_ = wxSizer:add(CodeSz, CodeTx, zxw:flags(wide)),
|
_ = wxSizer:add(CodeSz, CodeTx, zxw:flags(wide)),
|
||||||
ScrollWin = wxScrolledWindow:new(Window),
|
ScrollWin = wxScrolledWindow:new(Window),
|
||||||
FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Function Interfaces")}]),
|
FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Function Interfaces")}]),
|
||||||
@ -1106,18 +1100,14 @@ load3(State = #s{tabs = TopBook, cons = {Consbook, Pages}, buttons = Buttons, j
|
|||||||
_ = wxSizer:add(PageSz, ConsSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
_ = wxSizer:add(PageSz, ConsSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
||||||
{Out, IFaces, Build, NewButtons} =
|
{Out, IFaces, Build, NewButtons} =
|
||||||
case compile(Source) of
|
case compile(Source) of
|
||||||
{ok, Output} ->
|
{ok, B = #{aci := ACI}} ->
|
||||||
ACI = maps:get(aci, Output),
|
{#{functions := Fs}, _} = find_main(ACI),
|
||||||
AACI = {aaci, ContractName, Funs, NS} = hz:prepare_aaci(ACI),
|
Callable = lom:delete(name, <<"init">>, Fs),
|
||||||
Complete = maps:put(aaci, AACI, Output),
|
{NB, IFs} = fun_interfaces(ScrollWin, FunSz, Buttons, Callable, J),
|
||||||
ok = tell(info, "Loading Contract: ~p", [ContractName]),
|
O = io_lib:format("Compilation Succeeded!~n~tp~n~nDone!~n", [B]),
|
||||||
Callable = maps:remove("init", Funs),
|
{O, IFs, B, NB};
|
||||||
tell(info, "Callable: ~p", [Callable]),
|
|
||||||
{NB, IFs} = fun_interfaces(ScrollWin, FunSz, Buttons, Callable, NS, J),
|
|
||||||
O = io_lib:format("Compilation Succeeded!~n~tp~n~nDone!~n", [Complete]),
|
|
||||||
{O, IFs, Complete, NB};
|
|
||||||
Other ->
|
Other ->
|
||||||
O = io_lib:format("Compilation Failed!~n~tp~n", [Other]),
|
O = io_llib:format("Compilation Failed!~n~tp~n", [Other]),
|
||||||
{O, [], none, Buttons}
|
{O, [], none, Buttons}
|
||||||
end,
|
end,
|
||||||
ok = wxWindow:setSizer(Window, PageSz),
|
ok = wxWindow:setSizer(Window, PageSz),
|
||||||
@ -1133,18 +1123,9 @@ load3(State = #s{tabs = TopBook, cons = {Consbook, Pages}, buttons = Buttons, j
|
|||||||
State#s{cons = {Consbook, NewPages}, buttons = NewButtons}.
|
State#s{cons = {Consbook, NewPages}, buttons = NewButtons}.
|
||||||
|
|
||||||
|
|
||||||
get_arg({list, AFs, _}) ->
|
get_arg({_, TextCtrl, _}) ->
|
||||||
lists:map(fun get_arg/1, AFs);
|
|
||||||
get_arg({record, AFs}) ->
|
|
||||||
get_record(AFs);
|
|
||||||
get_arg({tuple, AFs}) ->
|
|
||||||
list_to_tuple(lists:map(fun get_arg/1, AFs));
|
|
||||||
get_arg({_, TextCtrl}) ->
|
|
||||||
wxTextCtrl:getValue(TextCtrl).
|
wxTextCtrl:getValue(TextCtrl).
|
||||||
|
|
||||||
get_record([{L, A} | T]) ->
|
|
||||||
[{L, get_arg(A)} | get_record(T)].
|
|
||||||
|
|
||||||
find_main(ACI) ->
|
find_main(ACI) ->
|
||||||
find_main(ACI, none, []).
|
find_main(ACI, none, []).
|
||||||
|
|
||||||
@ -1160,166 +1141,60 @@ find_main([C | T], M, Is) ->
|
|||||||
find_main([], M, Is) ->
|
find_main([], M, Is) ->
|
||||||
{M, Is}.
|
{M, Is}.
|
||||||
|
|
||||||
fun_interfaces(ScrollWin, FunSz, Buttons, Funs, NS, J) ->
|
fun_interfaces(ScrollWin, FunSz, Buttons, Funs, J) ->
|
||||||
MakeIface =
|
MakeIface =
|
||||||
fun(Name, {Args, _}) ->
|
fun(#{name := N, arguments := As}) ->
|
||||||
tell(info, "Fun: ~p, Args: ~p", [Name, Args]),
|
FunName = unicode:characters_to_list([N, "/", integer_to_list(length(As))]),
|
||||||
FunName = unicode:characters_to_list([Name, "/", integer_to_list(length(Args))]),
|
|
||||||
FN = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]),
|
FN = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]),
|
||||||
GridSz = wxFlexGridSizer:new(2, 4, 4),
|
GridSz = wxFlexGridSizer:new(2, 4, 4),
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
|
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
|
||||||
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
|
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
|
||||||
ArgFields = make_arg_fields(ScrollWin, GridSz, Args, NS, J),
|
MakeArgField =
|
||||||
|
fun(#{name := AN, type := T}) ->
|
||||||
|
Type =
|
||||||
|
case T of
|
||||||
|
<<"address">> -> address;
|
||||||
|
<<"int">> -> integer;
|
||||||
|
<<"bool">> -> boolean;
|
||||||
|
L when is_list(L) -> list; % FIXME
|
||||||
|
% I when is_binary(I) -> iface % FIXME
|
||||||
|
I when is_binary(I) -> address % FIXME
|
||||||
|
end,
|
||||||
|
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
||||||
|
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
|
||||||
|
_ = wxFlexGridSizer:add(GridSz, ANT, zxw:flags(base)),
|
||||||
|
_ = wxFlexGridSizer:add(GridSz, TCT, zxw:flags(wide)),
|
||||||
|
{ANT, TCT, Type}
|
||||||
|
end,
|
||||||
|
ArgFields = lists:map(MakeArgField, As),
|
||||||
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||||
CallBn = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Call")}]),
|
{CallButton, DryRunButton} =
|
||||||
DryRBn = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Dry Run")}]),
|
case N =:= <<"init">> of
|
||||||
_ = wxBoxSizer:add(ButtSz, CallBn, zxw:flags(wide)),
|
false ->
|
||||||
_ = wxBoxSizer:add(ButtSz, DryRBn, zxw:flags(wide)),
|
CallBn = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Call")}]),
|
||||||
CallButton = #w{name = {Name, call}, id = wxButton:getId(CallBn), wx = CallBn},
|
DryRBn = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Dry Run")}]),
|
||||||
DryRButton = #w{name = {Name, dryr}, id = wxButton:getId(DryRBn), wx = DryRBn},
|
_ = wxBoxSizer:add(ButtSz, CallBn, zxw:flags(wide)),
|
||||||
|
_ = wxBoxSizer:add(ButtSz, DryRBn, zxw:flags(wide)),
|
||||||
|
{#w{name = {N, call}, id = wxButton:getId(CallBn), wx = CallBn},
|
||||||
|
#w{name = {N, dryr}, id = wxButton:getId(DryRBn), wx = DryRBn}};
|
||||||
|
true ->
|
||||||
|
Deploy = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Deploy")}]),
|
||||||
|
_ = wxBoxSizer:add(ButtSz, Deploy, zxw:flags(wide)),
|
||||||
|
{#w{name = {N, call}, id = wxButton:getId(Deploy), wx = Deploy},
|
||||||
|
none}
|
||||||
|
end,
|
||||||
_ = wxStaticBoxSizer:add(FN, GridSz, zxw:flags(wide)),
|
_ = wxStaticBoxSizer:add(FN, GridSz, zxw:flags(wide)),
|
||||||
_ = wxStaticBoxSizer:add(FN, ButtSz, zxw:flags(base)),
|
_ = wxStaticBoxSizer:add(FN, ButtSz, zxw:flags(base)),
|
||||||
_ = wxSizer:add(FunSz, FN, zxw:flags(base)),
|
_ = wxSizer:add(FunSz, FN, zxw:flags(base)),
|
||||||
#f{name = Name, call = CallButton, dryrun = DryRButton, args = ArgFields}
|
#f{name = N, call = CallButton, dryrun = DryRunButton, args = ArgFields}
|
||||||
end,
|
end,
|
||||||
IFaces = maps:map(MakeIface, Funs),
|
IFaces = lists:map(MakeIface, Funs),
|
||||||
tell(info, "IFaces: ~p", [IFaces]),
|
NewButtons = lists:foldl(fun map_iface_buttons/2, Buttons, IFaces),
|
||||||
NewButtons = maps:fold(fun map_iface_buttons/3, Buttons, IFaces),
|
|
||||||
{NewButtons, IFaces}.
|
{NewButtons, IFaces}.
|
||||||
|
|
||||||
% FIXME: This can be simplified and needs to provide better widgets for types.
|
map_iface_buttons(#f{call = C = #w{id = CID}, dryrun = D = #w{id = DID}}, A) ->
|
||||||
% "variant" types should be wxChoice
|
maps:put(DID, D, maps:put(CID, C, A));
|
||||||
% Booleans should either be wxChoice or check boxes
|
map_iface_buttons(#f{call = C = #w{id = CID}, dryrun = none}, A) ->
|
||||||
% The sizer expansion direction for vertical elements is stupid
|
|
||||||
make_arg_fields(ScrollWin, GridSz, Args, NS, J) ->
|
|
||||||
MakeArgField =
|
|
||||||
fun
|
|
||||||
({AN, {T, already_normalized, T}}) ->
|
|
||||||
% tell(info, "~p Arg: ~p, Type: ~p", [?LINE, AN, T]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
||||||
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, TCT, fill()),
|
|
||||||
{T, TCT};
|
|
||||||
({T, already_normalized, T}) ->
|
|
||||||
% tell(info, "~p Type: ~p", [?LINE, T]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, atom_to_list(T)),
|
|
||||||
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, TCT, fill()),
|
|
||||||
{T, TCT};
|
|
||||||
({AN, {_TypeName, T, T}}) ->
|
|
||||||
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, T]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
||||||
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, TCT, fill()),
|
|
||||||
{T, TCT};
|
|
||||||
({AN, {_TypeName, already_normalized, {record, InnerArgs}}}) ->
|
|
||||||
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, InnerArgs]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
||||||
InnerSz = wxFlexGridSizer:new(2, 4, 4),
|
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
|
|
||||||
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
|
|
||||||
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
|
|
||||||
{record, AFs};
|
|
||||||
({AN, {_TypeName, already_normalized, {tuple, InnerArgs}}}) ->
|
|
||||||
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, InnerArgs]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
||||||
InnerSz = wxFlexGridSizer:new(2, 4, 4),
|
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
|
|
||||||
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
|
|
||||||
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
|
|
||||||
{tuple, AFs};
|
|
||||||
({AN, {_TypeName, already_normalized, {list, InnerArgs}}}) ->
|
|
||||||
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, InnerArgs]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
||||||
ArgSz = wxBoxSizer:new(?wxVERTICAL),
|
|
||||||
InnerSz = wxFlexGridSizer:new(2, 4, 4),
|
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
|
|
||||||
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
|
|
||||||
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
|
|
||||||
B = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Add")}]),
|
|
||||||
AB = #w{name = {AN, add}, id = wxButton:getId(B), wx = B},
|
|
||||||
_ = wxBoxSizer:add(ArgSz, InnerSz, fill()),
|
|
||||||
_ = wxBoxSizer:add(ArgSz, B, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ArgSz, fill()),
|
|
||||||
{list, AFs, AB};
|
|
||||||
({AN, {_TypeName, already_normalized, T}}) ->
|
|
||||||
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, T]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
||||||
InnerSz = wxFlexGridSizer:new(2, 4, 4),
|
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
|
|
||||||
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
|
|
||||||
AFs = make_arg_fields(ScrollWin, InnerSz, T, NS, J),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
|
|
||||||
{tuple, AFs};
|
|
||||||
({AN, {{tuple, _}, already_normalized, {tuple, InnerArgs}}}) ->
|
|
||||||
% tell(info, "~p Arg: ~p, Tuple: ~p", [?LINE, AN, InnerArgs]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
||||||
InnerSz = wxFlexGridSizer:new(2, 4, 4),
|
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
|
|
||||||
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
|
|
||||||
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
|
|
||||||
{tuple, AFs};
|
|
||||||
({AN, {{list, _}, already_normalized, {list, InnerArgs}}}) ->
|
|
||||||
% tell(info, "~p Arg: ~p, List: ~p", [?LINE, AN, InnerArgs]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
||||||
ArgSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
||||||
InnerSz = wxFlexGridSizer:new(2, 4, 4),
|
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
|
|
||||||
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
|
|
||||||
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
|
|
||||||
B = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Add")}]),
|
|
||||||
AB = #w{name = {AN, add}, id = wxButton:getId(B), wx = B},
|
|
||||||
_ = wxBoxSizer:add(ArgSz, InnerSz, fill()),
|
|
||||||
_ = wxBoxSizer:add(ArgSz, B, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ArgSz, fill()),
|
|
||||||
{list, AFs, AB};
|
|
||||||
({{tuple, _}, already_normalized, {tuple, InnerArgs}}) ->
|
|
||||||
% tell(info, "~p Tuple: ~p", [?LINE, InnerArgs]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, "tuple"),
|
|
||||||
InnerSz = wxFlexGridSizer:new(2, 4, 4),
|
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
|
|
||||||
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
|
|
||||||
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
|
|
||||||
{tuple, AFs};
|
|
||||||
({{list, _}, already_normalized, {list, InnerArgs}}) ->
|
|
||||||
% tell(info, "~p List: ~p", [?LINE, InnerArgs]),
|
|
||||||
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, "list"),
|
|
||||||
ArgSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
||||||
InnerSz = wxFlexGridSizer:new(2, 4, 4),
|
|
||||||
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
|
|
||||||
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
|
|
||||||
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
|
|
||||||
B = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Add")}]),
|
|
||||||
AB = #w{name = {list, add}, id = wxButton:getId(B), wx = B},
|
|
||||||
_ = wxBoxSizer:add(ArgSz, InnerSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
|
||||||
_ = wxBoxSizer:add(ArgSz, B, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
|
|
||||||
_ = wxFlexGridSizer:add(GridSz, ArgSz, fill()),
|
|
||||||
{list, AFs, AB}
|
|
||||||
end,
|
|
||||||
lists:map(MakeArgField, Args).
|
|
||||||
|
|
||||||
fill() ->
|
|
||||||
[{proportion, 0}, {flag, ?wxEXPAND}].
|
|
||||||
|
|
||||||
|
|
||||||
map_iface_buttons(_, #f{call = C = #w{id = CID}, dryrun = D = #w{id = DID}}, A) ->
|
|
||||||
maps:merge(#{CID => C, DID => D}, A);
|
|
||||||
map_iface_buttons(_, #f{call = C = #w{id = CID}, dryrun = none}, A) ->
|
|
||||||
maps:put(CID, C, A).
|
maps:put(CID, C, A).
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-module(gd_v_netman).
|
-module(gd_v_netman).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-author("Craig Everett <zxq9@zxq9.com>").
|
-author("Craig Everett <zxq9@zxq9.com>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-module(gd_v_wallman).
|
-module(gd_v_wallman).
|
||||||
-vsn("0.6.6").
|
-vsn("0.5.2").
|
||||||
-author("Craig Everett <zxq9@zxq9.com>").
|
-author("Craig Everett <zxq9@zxq9.com>").
|
||||||
-copyright("QPQ AG <info@qpq.swiss>").
|
-copyright("QPQ AG <info@qpq.swiss>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
@ -89,34 +89,12 @@ init({Prefs, Manifest}) ->
|
|||||||
ok = wxFrame:setSizer(Frame, MainSz),
|
ok = wxFrame:setSizer(Frame, MainSz),
|
||||||
ok = wxSizer:layout(MainSz),
|
ok = wxSizer:layout(MainSz),
|
||||||
|
|
||||||
NewPrefs =
|
ok = gd_v:safe_size(Frame, Prefs),
|
||||||
case maps:is_key(geometry, Prefs) of
|
|
||||||
true ->
|
|
||||||
Prefs;
|
|
||||||
false ->
|
|
||||||
Display = wxDisplay:new(),
|
|
||||||
{_, _, W, H} = wxDisplay:getGeometry(Display),
|
|
||||||
ok = wxDisplay:destroy(Display),
|
|
||||||
WW = 500,
|
|
||||||
WH = 350,
|
|
||||||
X = (W div 2) - (WW div 2),
|
|
||||||
Y = (H div 2) - (WH div 2),
|
|
||||||
Prefs#{geometry => {X, Y, WW, WH}}
|
|
||||||
end,
|
|
||||||
ok = gd_v:safe_size(Frame, NewPrefs),
|
|
||||||
|
|
||||||
ok = wxFrame:connect(Frame, command_button_clicked),
|
ok = wxFrame:connect(Frame, command_button_clicked),
|
||||||
ok = wxFrame:connect(Frame, close_window),
|
ok = wxFrame:connect(Frame, close_window),
|
||||||
true = wxFrame:show(Frame),
|
true = wxFrame:show(Frame),
|
||||||
ok = wxListBox:connect(Picker, command_listbox_doubleclicked),
|
ok = wxListBox:connect(Picker, command_listbox_doubleclicked),
|
||||||
ok =
|
|
||||||
case length(Manifest) =:= 0 of
|
|
||||||
false ->
|
|
||||||
ok;
|
|
||||||
true ->
|
|
||||||
self() ! new,
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
State = #s{wx = Wx, frame = Frame, lang = Lang, j = J, prefs = Prefs,
|
State = #s{wx = Wx, frame = Frame, lang = Lang, j = J, prefs = Prefs,
|
||||||
wallets = Manifest,
|
wallets = Manifest,
|
||||||
picker = Picker,
|
picker = Picker,
|
||||||
@ -143,9 +121,6 @@ handle_cast(Unexpected, State) ->
|
|||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
|
|
||||||
handle_info(new, State) ->
|
|
||||||
NewState = do_new(State),
|
|
||||||
{noreply, NewState};
|
|
||||||
handle_info(Unexpected, State) ->
|
handle_info(Unexpected, State) ->
|
||||||
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
|
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
@ -277,7 +252,6 @@ do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
|||||||
{defaultDir, DefaultDir},
|
{defaultDir, DefaultDir},
|
||||||
{defaultFile, "default.gaju"},
|
{defaultFile, "default.gaju"},
|
||||||
{wildCard, "*.gaju"},
|
{wildCard, "*.gaju"},
|
||||||
{sz, {300, 270}},
|
|
||||||
{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}],
|
{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}],
|
||||||
Dialog = wxFileDialog:new(Frame, Options),
|
Dialog = wxFileDialog:new(Frame, Options),
|
||||||
case wxFileDialog:showModal(Dialog) of
|
case wxFileDialog:showModal(Dialog) of
|
||||||
@ -302,7 +276,6 @@ do_new2(Path, J, Frame) ->
|
|||||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Wallet"), [{size, {400, 250}}]),
|
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Wallet"), [{size, {400, 250}}]),
|
||||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||||
|
|
||||||
Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]),
|
|
||||||
NameSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Name")}]),
|
NameSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Name")}]),
|
||||||
NameTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
NameTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
||||||
_ = wxSizer:add(NameSz, NameTx, zxw:flags(wide)),
|
_ = wxSizer:add(NameSz, NameTx, zxw:flags(wide)),
|
||||||
@ -321,14 +294,12 @@ do_new2(Path, J, Frame) ->
|
|||||||
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
||||||
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
||||||
|
|
||||||
_ = wxBoxSizer:add(Sizer, Network, zxw:flags(base)),
|
|
||||||
_ = wxSizer:add(Sizer, NameSz, zxw:flags(base)),
|
_ = wxSizer:add(Sizer, NameSz, zxw:flags(base)),
|
||||||
_ = wxSizer:add(Sizer, PassSz, zxw:flags(base)),
|
_ = wxSizer:add(Sizer, PassSz, zxw:flags(base)),
|
||||||
_ = wxSizer:add(Sizer, PassConSz, zxw:flags(base)),
|
_ = wxSizer:add(Sizer, PassConSz, zxw:flags(base)),
|
||||||
_ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)),
|
_ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)),
|
||||||
|
|
||||||
ok = wxDialog:setSizer(Dialog, Sizer),
|
ok = wxDialog:setSizer(Dialog, Sizer),
|
||||||
ok = wxDialog:setSize(Dialog, {300, 270}),
|
|
||||||
ok = wxBoxSizer:layout(Sizer),
|
ok = wxBoxSizer:layout(Sizer),
|
||||||
ok = wxDialog:center(Dialog),
|
ok = wxDialog:center(Dialog),
|
||||||
ok = wxStyledTextCtrl:setFocus(NameTx),
|
ok = wxStyledTextCtrl:setFocus(NameTx),
|
||||||
@ -336,12 +307,6 @@ do_new2(Path, J, Frame) ->
|
|||||||
Result =
|
Result =
|
||||||
case wxDialog:showModal(Dialog) of
|
case wxDialog:showModal(Dialog) of
|
||||||
?wxID_OK ->
|
?wxID_OK ->
|
||||||
Net =
|
|
||||||
case wxRadioBox:getSelection(Network) of
|
|
||||||
0 -> mainnet;
|
|
||||||
1 -> testnet;
|
|
||||||
_ -> mainnet
|
|
||||||
end,
|
|
||||||
Name =
|
Name =
|
||||||
case wxTextCtrl:getValue(NameTx) of
|
case wxTextCtrl:getValue(NameTx) of
|
||||||
"" -> Path;
|
"" -> Path;
|
||||||
@ -353,17 +318,17 @@ do_new2(Path, J, Frame) ->
|
|||||||
{P, P} -> P;
|
{P, P} -> P;
|
||||||
{_, _} -> bad
|
{_, _} -> bad
|
||||||
end,
|
end,
|
||||||
do_new3(Net, Name, Path, Pass);
|
do_new3(Name, Path, Pass);
|
||||||
?wxID_CANCEL ->
|
?wxID_CANCEL ->
|
||||||
abort
|
abort
|
||||||
end,
|
end,
|
||||||
ok = wxDialog:destroy(Dialog),
|
ok = wxDialog:destroy(Dialog),
|
||||||
Result.
|
Result.
|
||||||
|
|
||||||
do_new3(_, _, _, bad) ->
|
do_new3(_, _, bad) ->
|
||||||
abort;
|
abort;
|
||||||
do_new3(Net, Name, Path, Pass) ->
|
do_new3(Name, Path, Pass) ->
|
||||||
gd_con:new_wallet(Net, Name, Path, Pass).
|
gd_con:new_wallet(Name, Path, Pass).
|
||||||
|
|
||||||
|
|
||||||
do_import(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
do_import(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
||||||
|
10
zomp.meta
10
zomp.meta
@ -1,18 +1,18 @@
|
|||||||
{name,"GajuDesk"}.
|
{name,"Clutch"}.
|
||||||
{type,gui}.
|
{type,gui}.
|
||||||
{modules,[]}.
|
{modules,[]}.
|
||||||
{prefix,"gd"}.
|
{prefix,"gd"}.
|
||||||
{author,"Craig Everett"}.
|
{author,"Craig Everett"}.
|
||||||
{desc,"A desktop client for the Gajumaru network of blockchain networks"}.
|
{desc,"A desktop client for the Gajumaru network of blockchain networks"}.
|
||||||
{package_id,{"otpr","gajudesk",{0,6,6}}}.
|
{package_id,{"otpr","gajudesk",{0,5,2}}}.
|
||||||
{deps,[{"otpr","hakuzaru",{0,6,1}},
|
{deps,[{"otpr","hakuzaru",{0,5,0}},
|
||||||
{"otpr","eblake2",{1,0,1}},
|
|
||||||
{"otpr","base58",{0,1,1}},
|
|
||||||
{"otpr","gmserialization",{0,1,3}},
|
{"otpr","gmserialization",{0,1,3}},
|
||||||
{"otpr","sophia",{9,0,0}},
|
{"otpr","sophia",{9,0,0}},
|
||||||
{"otpr","gmbytecode",{3,4,1}},
|
{"otpr","gmbytecode",{3,4,1}},
|
||||||
{"otpr","lom",{1,0,0}},
|
{"otpr","lom",{1,0,0}},
|
||||||
{"otpr","zj",{1,1,0}},
|
{"otpr","zj",{1,1,0}},
|
||||||
|
{"otpr","erl_base58",{0,1,0}},
|
||||||
|
{"otpr","eblake2",{1,0,0}},
|
||||||
{"otpr","ec_utils",{1,0,0}},
|
{"otpr","ec_utils",{1,0,0}},
|
||||||
{"otpr","zxwidgets",{1,0,1}}]}.
|
{"otpr","zxwidgets",{1,0,1}}]}.
|
||||||
{key_name,none}.
|
{key_name,none}.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user