contract #5
@ -2,8 +2,8 @@
|
||||
[{description,"A desktop client for the Gajumaru network of blockchain networks"},
|
||||
{registered,[]},
|
||||
{included_applications,[]},
|
||||
{applications,[stdlib,kernel]},
|
||||
{vsn,"0.1.4"},
|
||||
{applications,[stdlib,kernel,sasl,ssl]},
|
||||
{vsn,"0.3.0"},
|
||||
{modules,[clutch,gmc_con,gmc_grids,gmc_gui,gmc_jt,
|
||||
gmc_key_master,gmc_sup,gmc_v,gmc_v_netman,
|
||||
gmc_v_wallman]},
|
||||
|
@ -60,7 +60,9 @@
|
||||
-record(tx,
|
||||
{id = none :: none | clutch:id(),
|
||||
amount = 0 :: non_neg_integer(),
|
||||
type = spend :: spend | atom()}).
|
||||
type = spend :: atom(),
|
||||
status = submitted :: submitted | mined | rejected | failed,
|
||||
meta = "" :: string()}).
|
||||
|
||||
|
||||
-record(spend_tx,
|
||||
@ -75,13 +77,14 @@
|
||||
|
||||
|
||||
-record(wallet,
|
||||
{version = 1 :: integer(),
|
||||
{version = 2 :: integer(),
|
||||
name = "" :: string(),
|
||||
poas = [] :: [#poa{}],
|
||||
keys = [] :: [#key{}],
|
||||
chain_id = <<"groot.devnet">> :: binary(),
|
||||
endpoint = #node{} :: #node{},
|
||||
nets = [#net{}] :: [#net{}]}).
|
||||
nets = [#net{}] :: [#net{}],
|
||||
txs = #{} :: clutch:key_txs()}).
|
||||
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
%%% @end
|
||||
|
||||
-module(clutch).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-behavior(application).
|
||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-copyright("QPQ AG <info@qpq.swiss>").
|
||||
@ -12,7 +12,7 @@
|
||||
-export([ts/0]).
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
-export_type([id/0, key/0, poa/0, tx/0, ts/0]).
|
||||
-export_type([id/0, key/0, poa/0, tx/0, ts/0, key_txs/0]).
|
||||
|
||||
-include("$zx_include/zx_logger.hrl").
|
||||
-include("gmc.hrl").
|
||||
@ -23,6 +23,8 @@
|
||||
-type poa() :: #poa{}.
|
||||
-type tx() :: #tx{}.
|
||||
-type ts() :: integer().
|
||||
-type chain_txs() :: #{ChainID :: binary() := [tx()]}.
|
||||
-type key_txs() :: #{id() := {{LastCheck :: ts(), mdw | node}, chain_txs()}}.
|
||||
|
||||
|
||||
ts() ->
|
||||
@ -55,9 +57,9 @@ start(normal, _Args) ->
|
||||
ok = tell(error, "DANGER! This node is in distributed mode!"),
|
||||
init:stop(1)
|
||||
end,
|
||||
ok = application:ensure_started(sasl),
|
||||
ok = application:ensure_started(hakuzaru),
|
||||
ok = application:ensure_started(zxwidgets),
|
||||
ok = application:ensure_started(sophia),
|
||||
gmc_sup:start_link().
|
||||
|
||||
|
||||
|
201
src/gmc_con.erl
201
src/gmc_con.erl
@ -1,11 +1,9 @@
|
||||
%%% @doc
|
||||
%%% Clutch Controller
|
||||
%%%
|
||||
%%% This process is a in charge of maintaining the program's state.
|
||||
%%% GajuDesk Controller
|
||||
%%% @end
|
||||
|
||||
-module(gmc_con).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-copyright("QPQ AG <info@qpq.swiss>").
|
||||
-license("GPL-3.0-or-later").
|
||||
@ -13,10 +11,12 @@
|
||||
-behavior(gen_server).
|
||||
-export([show_ui/1,
|
||||
open_wallet/2, close_wallet/0, new_wallet/3, import_wallet/3, drop_wallet/2,
|
||||
selected/1,
|
||||
password/2,
|
||||
refresh/0,
|
||||
nonce/1, spend/2, chain/1, grids/1, sign_mess/1, sign_tx/1,
|
||||
make_key/6, recover_key/1, mnemonic/1, rename_key/2, drop_key/1,
|
||||
nonce/1, spend/2, chain/1, grids/1, sign_mess/1, sign_tx/1, sign_call/3, dry_run/2,
|
||||
deploy/3,
|
||||
make_key/6, recover_key/1, mnemonic/1, rename_key/2, drop_key/1, list_keys/0,
|
||||
add_node/1, set_sole_node/1]).
|
||||
-export([encrypt/2, decrypt/2]).
|
||||
-export([save/2]).
|
||||
@ -41,6 +41,7 @@
|
||||
{version = 1 :: integer(),
|
||||
window = none :: none | wx:wx_object(),
|
||||
tasks = [] :: [#ui{}],
|
||||
selected = 0 :: non_neg_integer(),
|
||||
wallet = none :: none | #wallet{},
|
||||
pass = none :: none | binary(),
|
||||
prefs = #{} :: #{module() := term()},
|
||||
@ -104,6 +105,13 @@ drop_wallet(Path, Delete) ->
|
||||
gen_server:cast(?MODULE, {drop_wallet, Path, Delete}).
|
||||
|
||||
|
||||
-spec selected(Index) -> ok
|
||||
when Index :: pos_integer() | none.
|
||||
|
||||
selected(Index) ->
|
||||
gen_server:cast(?MODULE, {selected, Index}).
|
||||
|
||||
|
||||
-spec password(Old, New) -> ok
|
||||
when Old :: none | string(),
|
||||
New :: none | string().
|
||||
@ -162,6 +170,35 @@ sign_tx(Request) ->
|
||||
gen_server:cast(?MODULE, {sign_tx, Request}).
|
||||
|
||||
|
||||
-spec sign_call(ConID, PubKey, TX) -> ok
|
||||
when ConID :: clutch:id(),
|
||||
PubKey :: clutch:id(),
|
||||
TX :: binary().
|
||||
|
||||
sign_call(ConID, PubKey, TX) ->
|
||||
gen_server:cast(?MODULE, {sign_call, ConID, PubKey, TX}).
|
||||
|
||||
|
||||
-spec dry_run(ConID, TX) -> ok
|
||||
when ConID :: clutch:id(),
|
||||
TX :: binary().
|
||||
|
||||
dry_run(ConID, TX) ->
|
||||
gen_server:cast(?MODULE, {dry_run, ConID, TX}).
|
||||
|
||||
|
||||
-spec deploy(CreatorID, Build, InitArgs) -> Result
|
||||
when CreatorID :: clutch:id(),
|
||||
Build :: map(),
|
||||
InitArgs :: [Arg :: string()],
|
||||
Result :: {ok, TX_Hash :: clutch:id()}
|
||||
| {error, Reason},
|
||||
Reason :: term(). % FIXME
|
||||
|
||||
deploy(CreatorID, Build, InitArgs) ->
|
||||
gen_server:cast(?MODULE, {deploy, CreatorID, Build, InitArgs}).
|
||||
|
||||
|
||||
-spec make_key(Type, Size, Name, Seed, Encoding, Transform) -> ok
|
||||
when Type :: {eddsa, ed25519},
|
||||
Size :: 256,
|
||||
@ -211,6 +248,14 @@ drop_key(ID) ->
|
||||
gen_server:cast(?MODULE, {drop_key, ID}).
|
||||
|
||||
|
||||
-spec list_keys() -> Result
|
||||
when Result :: {ok, Selected :: non_neg_integer(), Keys :: [clutch:id()]}
|
||||
| error.
|
||||
|
||||
list_keys() ->
|
||||
gen_server:call(?MODULE, list_keys).
|
||||
|
||||
|
||||
-spec add_node(New) -> ok
|
||||
when New :: #node{}.
|
||||
|
||||
@ -262,6 +307,7 @@ start_link() ->
|
||||
|
||||
init(none) ->
|
||||
ok = log(info, "Starting"),
|
||||
process_flag(sensitive, true),
|
||||
Prefs = read_prefs(),
|
||||
GUI_Prefs = maps:get(gmc_gui, Prefs, #{}),
|
||||
Window = gmc_gui:start_link(GUI_Prefs),
|
||||
@ -295,6 +341,9 @@ read_prefs() ->
|
||||
%% The gen_server:handle_call/3 callback.
|
||||
%% See: http://erlang.org/doc/man/gen_server.html#Module:handle_call-3
|
||||
|
||||
handle_call(list_keys, _, State) ->
|
||||
Response = do_list_keys(State),
|
||||
{reply, Response, State};
|
||||
handle_call({nonce, ID}, _, State) ->
|
||||
Response = do_nonce(ID),
|
||||
{reply, Response, State};
|
||||
@ -324,9 +373,9 @@ handle_cast({open_wallet, Path, Phrase}, State) ->
|
||||
NewState = do_open_wallet(Path, Phrase, State),
|
||||
{noreply, NewState};
|
||||
handle_cast(close_wallet, State) ->
|
||||
NewState = do_close_wallet(State),
|
||||
NextState = do_close_wallet(State),
|
||||
ok = gmc_gui:show([]),
|
||||
ok = do_show_ui(gmc_v_wallman, NewState),
|
||||
NewState = do_show_ui(gmc_v_wallman, NextState),
|
||||
{noreply, NewState};
|
||||
handle_cast({new_wallet, Name, Path, Password}, State) ->
|
||||
NewState = do_new_wallet(Name, Path, Password, State),
|
||||
@ -337,6 +386,9 @@ handle_cast({import_wallet, Name, Path, Password}, State) ->
|
||||
handle_cast({drop_wallet, Path, Delete}, State) ->
|
||||
NewState = do_drop_wallet(Path, Delete, State),
|
||||
{noreply, NewState};
|
||||
handle_cast({selected, Index}, State) ->
|
||||
NewState = State#s{selected = Index},
|
||||
{noreply, NewState};
|
||||
handle_cast({password, Old, New}, State) ->
|
||||
NewState = do_password(Old, New, State),
|
||||
{noreply, NewState};
|
||||
@ -358,6 +410,15 @@ handle_cast({sign_mess, Request}, State) ->
|
||||
handle_cast({sign_tx, Request}, State) ->
|
||||
ok = do_sign_tx(Request, State),
|
||||
{noreply, State};
|
||||
handle_cast({sign_call, ConID, PubKey, TX}, State) ->
|
||||
ok = do_sign_call(State, ConID, PubKey, TX),
|
||||
{noreply, State};
|
||||
handle_cast({dry_run, ConID, TX}, State) ->
|
||||
ok = do_dry_run(ConID, TX),
|
||||
{noreply, State};
|
||||
handle_cast({deploy, CreatorID, Build, InitArgs}, State) ->
|
||||
ok = do_deploy(CreatorID, Build, InitArgs, State),
|
||||
{noreply, State};
|
||||
handle_cast({make_key, Name, Seed, Encoding, Transform}, State) ->
|
||||
NewState = do_make_key(Name, Seed, Encoding, Transform, State),
|
||||
{noreply, NewState};
|
||||
@ -587,8 +648,8 @@ do_grids_sig2(WTF) ->
|
||||
do_sign_mess(Request = #{"public_id" := ID, "payload" := Message},
|
||||
#s{wallet = #wallet{keys = Keys}}) ->
|
||||
case lists:keyfind(ID, #key.id, Keys) of
|
||||
#key{pair = #{secret := PrivKey}} ->
|
||||
Sig = base64:encode(sign_message(list_to_binary(Message), PrivKey)),
|
||||
#key{pair = #{secret := SecKey}} ->
|
||||
Sig = base64:encode(sign_message(list_to_binary(Message), SecKey)),
|
||||
do_sign_mess2(Request#{"signature" => Sig});
|
||||
false ->
|
||||
gmc_gui:trouble({bad_key, ID})
|
||||
@ -612,13 +673,13 @@ do_sign_mess2(Request = #{"url" := URL}) ->
|
||||
|
||||
|
||||
% TODO: Should probably be part of Hakuzaru
|
||||
sign_message(Message, PrivKey) ->
|
||||
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, PrivKey).
|
||||
ecu_eddsa:sign_detached(Hashed, SecKey).
|
||||
|
||||
|
||||
vencode(N) when N < 0 ->
|
||||
@ -646,9 +707,9 @@ do_sign_tx(Request = #{"public_id" := ID, "payload" := CallData, "network_id" :=
|
||||
#s{wallet = #wallet{keys = Keys}}) ->
|
||||
BinNID = list_to_binary(NID),
|
||||
case lists:keyfind(ID, #key.id, Keys) of
|
||||
#key{pair = #{secret := PrivKey}} ->
|
||||
#key{pair = #{secret := SecKey}} ->
|
||||
BinaryTX = list_to_binary(CallData),
|
||||
SignedTX = sign_tx_hash(BinaryTX, PrivKey, BinNID),
|
||||
SignedTX = sign_tx_hash(BinaryTX, SecKey, BinNID),
|
||||
do_sign_tx2(Request#{"signed" => true, "payload" := SignedTX});
|
||||
false ->
|
||||
gmc_gui:trouble({bad_key, ID})
|
||||
@ -670,11 +731,11 @@ do_sign_tx2(Request = #{"url" := URL}) ->
|
||||
Error -> gmc_gui:trouble(Error)
|
||||
end.
|
||||
|
||||
sign_tx_hash(Unsigned, PrivKey, NetworkID) ->
|
||||
{ok, TX_Data} = aeser_api_encoder:safe_decode(transaction, Unsigned),
|
||||
sign_tx_hash(Unsigned, SecKey, NetworkID) ->
|
||||
{ok, TX_Data} = gmser_api_encoder:safe_decode(transaction, Unsigned),
|
||||
{ok, Hash} = eblake2:blake2b(32, TX_Data),
|
||||
NetworkHash = <<NetworkID/binary, Hash/binary>>,
|
||||
Signature = ecu_eddsa:sign_detached(NetworkHash, PrivKey),
|
||||
Signature = ecu_eddsa:sign_detached(NetworkHash, SecKey),
|
||||
SigTxType = signed_tx,
|
||||
SigTxVsn = 1,
|
||||
SigTemplate =
|
||||
@ -683,19 +744,57 @@ sign_tx_hash(Unsigned, PrivKey, NetworkID) ->
|
||||
TX =
|
||||
[{signatures, [Signature]},
|
||||
{transaction, TX_Data}],
|
||||
SignedTX = aeser_chain_objects:serialize(SigTxType, SigTxVsn, SigTemplate, TX),
|
||||
aeser_api_encoder:encode(transaction, SignedTX).
|
||||
SignedTX = gmser_chain_objects:serialize(SigTxType, SigTxVsn, SigTemplate, TX),
|
||||
gmser_api_encoder:encode(transaction, SignedTX).
|
||||
|
||||
|
||||
do_sign_call(#s{wallet = #wallet{keys = Keys, chain_id = ChainID}},
|
||||
ConID,
|
||||
PubKey,
|
||||
TX) ->
|
||||
#key{pair = #{secret := SecKey}} = lists:keyfind(PubKey, #key.id, Keys),
|
||||
SignedTX = sign_tx_hash(TX, SecKey, ChainID),
|
||||
case hz:post_tx(SignedTX) of
|
||||
{ok, Data = #{"tx_hash" := TXHash}} ->
|
||||
ok = tell("Contract deploy TX succeded with: ~p", [TXHash]),
|
||||
do_sign_call2(ConID, Data);
|
||||
{ok, WTF} ->
|
||||
gmc_v_devman:trouble({error, WTF});
|
||||
Error ->
|
||||
gmc_v_devman:trouble(Error)
|
||||
end;
|
||||
do_sign_call(_, _, _, _) ->
|
||||
gmc_v_devman:trouble({error, no_chain}).
|
||||
|
||||
do_sign_call2(ConID, #{"tx_hash" := TXHash}) ->
|
||||
case hz:tx_info(TXHash) of
|
||||
{ok, CallInfo = #{"call_info" := #{"return_type" := "ok"}}} ->
|
||||
gmc_v_devman:call_result(ConID, CallInfo);
|
||||
{error, "Tx not mined"} ->
|
||||
gmc_v_devman:trouble({tx_hash, TXHash});
|
||||
{ok, Reason = #{"call_info" := #{"return_type" := "revert"}}} ->
|
||||
gmc_v_devman:trouble({error, Reason});
|
||||
Error ->
|
||||
gmc_v_devman:trouble(Error)
|
||||
end.
|
||||
|
||||
|
||||
do_dry_run(ConID, TX) ->
|
||||
case hz:dry_run(TX) of
|
||||
{ok, Result} -> gmc_v_devman:dryrun_result(ConID, Result);
|
||||
Other -> gmc_v_devmam:trouble({error, ConID, Other})
|
||||
end.
|
||||
|
||||
|
||||
do_spend(KeyID, TX, State = #s{wallet = #wallet{keys = Keys}}) ->
|
||||
case lists:keyfind(KeyID, #key.id, Keys) of
|
||||
#key{pair = #{secret := PrivKey}} ->
|
||||
do_spend2(PrivKey, TX, State);
|
||||
#key{pair = #{secret := SecKey}} ->
|
||||
do_spend2(SecKey, TX, State);
|
||||
false ->
|
||||
log(warning, "Tried do_spend with a bad key: ~p", [KeyID])
|
||||
end.
|
||||
|
||||
do_spend2(PrivKey,
|
||||
do_spend2(SecKey,
|
||||
#spend_tx{sender_id = SenderID,
|
||||
recipient_id = RecipientID,
|
||||
amount = Amount,
|
||||
@ -725,9 +824,9 @@ do_spend2(PrivKey,
|
||||
{ttl, int},
|
||||
{nonce, int},
|
||||
{payload, binary}],
|
||||
BinaryTX = aeser_chain_objects:serialize(Type, Vsn, Template, Fields),
|
||||
BinaryTX = gmser_chain_objects:serialize(Type, Vsn, Template, Fields),
|
||||
NetworkTX = <<ChainID/binary, BinaryTX/binary>>,
|
||||
Signature = ecu_eddsa:sign_detached(NetworkTX, PrivKey),
|
||||
Signature = ecu_eddsa:sign_detached(NetworkTX, SecKey),
|
||||
SigTxType = signed_tx,
|
||||
SigTxVsn = 1,
|
||||
SigTemplate =
|
||||
@ -736,12 +835,18 @@ do_spend2(PrivKey,
|
||||
TX_Data =
|
||||
[{signatures, [Signature]},
|
||||
{transaction, BinaryTX}],
|
||||
SignedTX = aeser_chain_objects:serialize(SigTxType, SigTxVsn, SigTemplate, TX_Data),
|
||||
Encoded = aeser_api_encoder:encode(transaction, SignedTX),
|
||||
SignedTX = gmser_chain_objects:serialize(SigTxType, SigTxVsn, SigTemplate, TX_Data),
|
||||
Encoded = gmser_api_encoder:encode(transaction, SignedTX),
|
||||
Outcome = hz:post_tx(Encoded),
|
||||
tell("Outcome: ~p", [Outcome]).
|
||||
|
||||
|
||||
do_list_keys(#s{selected = Selected, wallet = #wallet{poas = POAs}}) ->
|
||||
{ok, Selected, [ID || #poa{id = ID} <- POAs]};
|
||||
do_list_keys(#s{wallet = none}) ->
|
||||
error.
|
||||
|
||||
|
||||
do_nonce(ID) ->
|
||||
hz:next_nonce(ID).
|
||||
|
||||
@ -790,6 +895,9 @@ do_make_key(Name, Seed, base58, Transform, State) ->
|
||||
end.
|
||||
|
||||
|
||||
do_make_key2(_, _, _, State = #s{wallet = none}) ->
|
||||
ok = gmc_gui:trouble("No wallet selected!"),
|
||||
do_show_ui(gmc_v_wallman, State);
|
||||
do_make_key2(Name, Bin, Transform,
|
||||
State = #s{wallet = Current, wallets = Wallets, pass = Pass}) ->
|
||||
#wallet{name = WalletName, poas = POAs, keys = Keys} = Current,
|
||||
@ -870,6 +978,42 @@ do_mnemonic(ID, #s{wallet = #wallet{keys = Keys}}) ->
|
||||
end.
|
||||
|
||||
|
||||
do_deploy(CreatorID,
|
||||
Build,
|
||||
InitArgs,
|
||||
#s{wallet = #wallet{keys = Keys, chain_id = ChainID}}) ->
|
||||
#key{pair = #{secret := SecKey}} = lists:keyfind(CreatorID, #key.id, Keys),
|
||||
case hz:contract_create_built(CreatorID, Build, InitArgs) of
|
||||
{ok, CreateTX} -> do_deploy2(SecKey, CreateTX, ChainID);
|
||||
Error -> gmc_v_devman:trouble(Error)
|
||||
end.
|
||||
|
||||
do_deploy2(SecKey, CreateTX, ChainID) ->
|
||||
SignedTX = sign_tx_hash(CreateTX, SecKey, ChainID),
|
||||
tell(info, "SignedTX: ~p", [SignedTX]),
|
||||
case hz:post_tx(SignedTX) of
|
||||
{ok, Data = #{"tx_hash" := TXHash}} ->
|
||||
ok = tell("Contract deploy TX succeded with: ~p", [TXHash]),
|
||||
do_deploy3(Data);
|
||||
{ok, WTF} ->
|
||||
gmc_v_devman:trouble({error, WTF});
|
||||
Error ->
|
||||
gmc_v_devman:trouble(Error)
|
||||
end.
|
||||
|
||||
do_deploy3(#{"tx_hash" := TXHash}) ->
|
||||
case hz:tx_info(TXHash) of
|
||||
{ok, #{"call_info" := #{"return_type" := "ok", "contract_id" := ConID}}} ->
|
||||
gmc_v_devman:open_contract(ConID);
|
||||
{error, "Tx not mined"} ->
|
||||
gmc_v_devman:trouble({tx_hash, TXHash});
|
||||
{ok, Reason = #{"call_info" := #{"return_type" := "revert"}}} ->
|
||||
gmc_v_devman:trouble({error, Reason});
|
||||
Error ->
|
||||
gmc_v_devman:trouble(Error)
|
||||
end.
|
||||
|
||||
|
||||
do_rename_key(ID, NewName, State = #s{wallet = W}) ->
|
||||
#wallet{poas = POAs, keys = Keys} = W,
|
||||
A = lists:keyfind(ID, #poa.id, POAs),
|
||||
@ -1073,7 +1217,7 @@ do_close_wallet(State = #s{wallet = Current, wallets = Wallets, pass = Pass}) ->
|
||||
#wallet{name = Name} = Current,
|
||||
RW = lists:keyfind(Name, #wr.name, Wallets),
|
||||
ok = save_wallet(RW, Pass, Current),
|
||||
State#s{pass = none, wallet = none}.
|
||||
State#s{selected = 0, pass = none, wallet = none}.
|
||||
|
||||
|
||||
save_wallet(#wr{path = Path, pass = false}, none, Wallet) ->
|
||||
@ -1115,7 +1259,8 @@ read2(Bin) ->
|
||||
|
||||
read3(T) ->
|
||||
case element(2, T) of
|
||||
1 -> {ok, T};
|
||||
1 -> read3(setelement(2, erlang:append_element(T, #{}), 2));
|
||||
2 -> {ok, T};
|
||||
_ -> {error, bad_wallet}
|
||||
end.
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
%%% @end
|
||||
|
||||
-module(gmc_grids).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-copyright("QPQ AG <info@qpq.swiss>").
|
||||
-license("GPL-3.0-or-later").
|
||||
|
@ -1,13 +1,9 @@
|
||||
%%% @doc
|
||||
%%% Clutch GUI
|
||||
%%%
|
||||
%%% This process is responsible for creating the main GUI frame displayed to the user.
|
||||
%%%
|
||||
%%% Reference: http://erlang.org/doc/man/wx_object.html
|
||||
%%% GajuDesk GUI
|
||||
%%% @end
|
||||
|
||||
-module(gmc_gui).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-copyright("QPQ AG <info@qpq.swiss>").
|
||||
-license("GPL-3.0-or-later").
|
||||
@ -62,8 +58,8 @@ chain(ChainID, Node) ->
|
||||
wx_object:cast(?MODULE, {chain, ChainID, Node}).
|
||||
|
||||
|
||||
trouble(Message) ->
|
||||
wx_object:cast(?MODULE, {trouble, Message}).
|
||||
trouble(Info) ->
|
||||
wx_object:cast(?MODULE, {trouble, Info}).
|
||||
|
||||
|
||||
ask_password() ->
|
||||
@ -239,7 +235,7 @@ handle_cast({chain, ChainID, Node}, State) ->
|
||||
ok = do_chain(ChainID, Node, State),
|
||||
{noreply, State};
|
||||
handle_cast({trouble, Info}, State) ->
|
||||
ok = handle_trouble(Info, State),
|
||||
ok = handle_troubling(State, Info),
|
||||
{noreply, State};
|
||||
handle_cast(password, State) ->
|
||||
ok = do_ask_password(State),
|
||||
@ -321,6 +317,10 @@ handle_event(Event, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
handle_troubling(#s{frame = Frame}, Info) ->
|
||||
zxw:show_message(Frame, Info).
|
||||
|
||||
|
||||
|
||||
code_change(_, State, _) ->
|
||||
{ok, State}.
|
||||
@ -765,7 +765,7 @@ spend2(#poa{id = ID, name = Name}, Nonce, Height, State = #s{frame = Frame, j =
|
||||
?wxID_OK ->
|
||||
{ok, PK} = decode_account_id(ID),
|
||||
TX =
|
||||
#spend_tx{sender_id = aeser_id:create(account, PK),
|
||||
#spend_tx{sender_id = gmser_id:create(account, PK),
|
||||
recipient_id = wxTextCtrl:getValue(ToTx),
|
||||
amount = wxTextCtrl:getValue(AmtTx),
|
||||
gas_price = wxSlider:getValue(GasSl),
|
||||
@ -784,7 +784,7 @@ clean_spend(_, #spend_tx{recipient_id = ""}) ->
|
||||
ok;
|
||||
clean_spend(ID, TX = #spend_tx{recipient_id = S}) when is_list(S) ->
|
||||
case decode_account_id(S) of
|
||||
{ok, PK} -> clean_spend(ID, TX#spend_tx{recipient_id = aeser_id:create(account, PK)});
|
||||
{ok, PK} -> clean_spend(ID, TX#spend_tx{recipient_id = gmser_id:create(account, PK)});
|
||||
Error -> tell("Decode recipient_id failed with: ~tp", [Error])
|
||||
end;
|
||||
clean_spend(ID, TX = #spend_tx{amount = S}) when is_list(S) ->
|
||||
@ -811,7 +811,7 @@ decode_account_id(S) when is_list(S) ->
|
||||
decode_account_id(list_to_binary(S));
|
||||
decode_account_id(B) ->
|
||||
try
|
||||
{account_pubkey, PK} = aeser_api_encoder:decode(B),
|
||||
{account_pubkey, PK} = gmser_api_encoder:decode(B),
|
||||
{ok, PK}
|
||||
catch
|
||||
E:R -> {E, R}
|
||||
@ -863,10 +863,12 @@ do_selection(Selected,
|
||||
State = #s{prefs = Prefs, accounts = Accounts,
|
||||
balance = {_, #w{wx = B}}, id = {_, #w{wx = I}}})
|
||||
when Selected < length(Accounts) ->
|
||||
#poa{id = ID, balances = Balances} = lists:nth(Selected + 1, Accounts),
|
||||
OneBasedIndex = Selected + 1,
|
||||
#poa{id = ID, balances = Balances} = lists:nth(OneBasedIndex, Accounts),
|
||||
[#balance{total = Pucks}] = Balances,
|
||||
ok = wxStaticText:setLabel(I, ID),
|
||||
ok = wxStaticText:setLabel(B, price_to_string(Pucks)),
|
||||
ok = gmc_con:selected(OneBasedIndex),
|
||||
NewPrefs = maps:put(selected, Selected, Prefs),
|
||||
State#s{prefs = NewPrefs};
|
||||
do_selection(_, State) ->
|
||||
@ -924,10 +926,6 @@ do_chain(ChainID, #node{ip = IP}, #s{buttons = Buttons}) ->
|
||||
ok = wxButton:setLabel(NodeB, Address).
|
||||
|
||||
|
||||
handle_trouble(Info, #s{frame = Frame}) ->
|
||||
zxw:show_message(Frame, Info).
|
||||
|
||||
|
||||
do_ask_password(#s{frame = Frame, prefs = Prefs, j = J}) ->
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Password")),
|
||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||
|
@ -15,7 +15,7 @@
|
||||
%%% translation library is retained).
|
||||
|
||||
-module(gmc_jt).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-export([read_translations/1, j/2, oneshot_j/2]).
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
%%% @end
|
||||
|
||||
-module(gmc_key_master).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
|
||||
|
||||
-export([make_key/2, encode/1, decode/1]).
|
||||
@ -18,21 +18,21 @@
|
||||
|
||||
make_key("", <<>>) ->
|
||||
Pair = #{public := Public} = ecu_eddsa:sign_keypair(),
|
||||
ID = aeser_api_encoder:encode(account_pubkey, Public),
|
||||
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 = aeser_api_encoder:encode(account_pubkey, Public),
|
||||
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 = aeser_api_encoder:encode(account_pubkey, Public),
|
||||
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 = aeser_api_encoder:encode(account_pubkey, Public),
|
||||
ID = gmser_api_encoder:encode(account_pubkey, Public),
|
||||
#key{name = Name, id = ID, pair = Pair}.
|
||||
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
%%% @end
|
||||
|
||||
-module(gmc_sup).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-behaviour(supervisor).
|
||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-copyright("QPQ AG <info@qpq.swiss>").
|
||||
@ -36,11 +36,11 @@ start_link() ->
|
||||
|
||||
init([]) ->
|
||||
RestartStrategy = {one_for_one, 0, 60},
|
||||
Clients = {gmc_con,
|
||||
Controller = {gmc_con,
|
||||
{gmc_con, start_link, []},
|
||||
permanent,
|
||||
5000,
|
||||
worker,
|
||||
[gmc_con]},
|
||||
Children = [Clients],
|
||||
Children = [Controller],
|
||||
{ok, {RestartStrategy, Children}}.
|
||||
|
@ -1,5 +1,5 @@
|
||||
-module(gmc_v).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||
-copyright("QPQ AG <info@qpq.swiss>").
|
||||
-license("GPL-3.0-or-later").
|
||||
|
1108
src/gmc_v_devman.erl
1108
src/gmc_v_devman.erl
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
-module(gmc_v_netman).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-author("Craig Everett <zxq9@zxq9.com>").
|
||||
-copyright("QPQ AG <info@qpq.swiss>").
|
||||
-license("GPL-3.0-or-later").
|
||||
|
@ -1,5 +1,5 @@
|
||||
-module(gmc_v_wallman).
|
||||
-vsn("0.1.4").
|
||||
-vsn("0.3.0").
|
||||
-author("Craig Everett <zxq9@zxq9.com>").
|
||||
-copyright("QPQ AG <info@qpq.swiss>").
|
||||
-license("GPL-3.0-or-later").
|
||||
@ -273,7 +273,7 @@ do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
||||
end.
|
||||
|
||||
do_new2(Path, J, Frame) ->
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Set Node"), [{size, {400, 250}}]),
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Wallet"), [{size, {400, 250}}]),
|
||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||
|
||||
NameSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Name")}]),
|
||||
@ -360,7 +360,7 @@ do_import2(_, "", _, _) ->
|
||||
abort;
|
||||
do_import2(Dir, File, J, Frame) ->
|
||||
Path = filename:join(Dir, File),
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Set Node")),
|
||||
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Import Wallet")),
|
||||
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||
|
||||
NameSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Name")}]),
|
||||
|
13
zomp.meta
13
zomp.meta
@ -2,14 +2,15 @@
|
||||
{type,gui}.
|
||||
{modules,[]}.
|
||||
{prefix,"gmc"}.
|
||||
{desc,"A desktop client for the Gajumaru network of blockchain networks"}.
|
||||
{author,"Craig Everett"}.
|
||||
{package_id,{"otpr","clutch",{0,1,4}}}.
|
||||
{deps,[{"otpr","hakuzaru",{0,2,0}},
|
||||
{"otpr","aesophia",{8,0,1}},
|
||||
{"otpr","aeserialization",{0,1,2}},
|
||||
{desc,"A desktop client for the Gajumaru network of blockchain networks"}.
|
||||
{package_id,{"otpr","clutch",{0,3,0}}}.
|
||||
{deps,[{"otpr","sophia",{9,0,0}},
|
||||
{"otpr","hakuzaru",{0,3,0}},
|
||||
{"otpr","gmbytecode",{3,4,1}},
|
||||
{"otpr","lom",{1,0,0}},
|
||||
{"otpr","gmserialization",{0,1,2}},
|
||||
{"otpr","zj",{1,1,0}},
|
||||
{"otpr","aebytecode",{3,2,1}},
|
||||
{"otpr","erl_base58",{0,1,0}},
|
||||
{"otpr","eblake2",{1,0,0}},
|
||||
{"otpr","ec_utils",{1,0,0}},
|
||||
|
Loading…
x
Reference in New Issue
Block a user