WIP
This commit is contained in:
parent
d77ee6d8f5
commit
a13bec7fcc
@ -1,3 +1,48 @@
|
|||||||
|
%%% @doc
|
||||||
|
%%% GRIDS URL parsing
|
||||||
|
%%%
|
||||||
|
%%% GRID(S): Gajumaru Remote Instruction Dispatch (Serialization)
|
||||||
|
%%% GRIDS is a Gajumaru protocol for encoding wallet instructions as URLs.
|
||||||
|
%%% Version 1 of the protocol consists of two verbs with two contexts each, collapsed to
|
||||||
|
%%% four symbols for brevity.
|
||||||
|
%%%
|
||||||
|
%%% The GRIDS schema begins with "grids://" or "grids://"
|
||||||
|
%%% Which way this is interpreted can vary depending on the verb.
|
||||||
|
%%%
|
||||||
|
%%% The typical "host" component is either an actual hostname or address and an optional
|
||||||
|
%%% port number (the defaut port being 3013), or a Gajumaru chain network IDi (in which
|
||||||
|
%%% case the port number is ignored if provided). Which way this field is interpreted
|
||||||
|
%%% depends on the verb.
|
||||||
|
%%%
|
||||||
|
%%% The first element of the path after the host component indicates the protocol version.
|
||||||
|
%%% Only version 1 exists at the time of this release.
|
||||||
|
%%%
|
||||||
|
%%% The next element of the path after the version is a single letter that indicates which
|
||||||
|
%%% action to take. The following actions are available:
|
||||||
|
%%% "s": Spend on Chain
|
||||||
|
%%% Constructs a spend transaction to the address indicated in the path component
|
||||||
|
%%% indicated in the final path element. Two qargs are valid in the trailing arguments
|
||||||
|
%%% section: "a" for amount (in Pucks, not Gajus!), and "p" for data payload.
|
||||||
|
%%% In this context the "host" field in the URL is interpreted as a chain network ID.
|
||||||
|
%%% "t": Transfer (spend) on Host
|
||||||
|
%%% The same as "spend" above, but in this context the host field of the URL is
|
||||||
|
%%% interpreted as host[:port] information and the network chain ID that will be used
|
||||||
|
%%% will be derived from whatever chain the given host reports.
|
||||||
|
%%% "d": Dead-drop signature request
|
||||||
|
%%% This instructs the wallet to retrieve a signature data blob from an HTTP or HTTPS
|
||||||
|
%%% URL that can be reconstructed by replacing "grids" with "https" or "grid" with
|
||||||
|
%%% "http", omitting the "/1/d" path component and then recnstructing the URL.
|
||||||
|
%%% This provides a lightweight method for services to enable contract calls from
|
||||||
|
%%% wallets that are not capable of compiling contract source.
|
||||||
|
%%% "v": Verify
|
||||||
|
%%% Similar to the contract call signature request above, this requests a message
|
||||||
|
%%% signature over arbitrary data. The normal use case for this action is public
|
||||||
|
%%% key based identity verification by signing time-stamped, random data. This verb
|
||||||
|
%%% is required because message signature requests are specially salted to prevent
|
||||||
|
%%% a situation where sneaky types might request signature of contract call data,
|
||||||
|
%%% but present it as a random message signature.
|
||||||
|
%%% @end
|
||||||
|
|
||||||
-module(gmc_grids).
|
-module(gmc_grids).
|
||||||
-vsn("0.1.0").
|
-vsn("0.1.0").
|
||||||
-author("Craig Everett <craigeverett@qpq.swiss>").
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
||||||
@ -11,7 +56,7 @@
|
|||||||
-spec parse(URL) -> {ok, Instruction} | {error, Reason}
|
-spec parse(URL) -> {ok, Instruction} | {error, Reason}
|
||||||
when URL :: string(),
|
when URL :: string(),
|
||||||
Instruction :: {{spend, chain | node}, {Location, Recipient, Amount, Payload}}
|
Instruction :: {{spend, chain | node}, {Location, Recipient, Amount, Payload}}
|
||||||
| {{sign, http | https}, URL},
|
| {{sign | mess, http | https}, URL},
|
||||||
Location :: Node :: {inet:ip_address() | inet:hostname(), inet:port_number()}
|
Location :: Node :: {inet:ip_address() | inet:hostname(), inet:port_number()}
|
||||||
| Chain :: binary(),
|
| Chain :: binary(),
|
||||||
Recipient :: clutch:id(),
|
Recipient :: clutch:id(),
|
||||||
@ -40,6 +85,12 @@ parse(URL) ->
|
|||||||
U = #{path := "/1/d/" ++ L, scheme := "grid"} ->
|
U = #{path := "/1/d/" ++ L, scheme := "grid"} ->
|
||||||
NewURL = uri_string:recompose(U#{scheme := "http", path := L}),
|
NewURL = uri_string:recompose(U#{scheme := "http", path := L}),
|
||||||
{ok, {{sign, http}, NewURL}};
|
{ok, {{sign, http}, NewURL}};
|
||||||
|
U = #{path := "/1/v/" ++ L, scheme := "grids"} ->
|
||||||
|
NewURL = uri_string:recompose(U#{scheme := "https", path := L}),
|
||||||
|
{ok ,{{mess, https}, NewURL}};
|
||||||
|
U = #{path := "/1/v/" ++ L, scheme := "grid"} ->
|
||||||
|
NewURL = uri_string:recompose(U#{scheme := "http", path := L}),
|
||||||
|
{ok, {{mess, http}, NewURL}};
|
||||||
{error, Reason, Info} ->
|
{error, Reason, Info} ->
|
||||||
ok = tell("URL parsing failed with ~w: ~p", [Reason, Info]),
|
ok = tell("URL parsing failed with ~w: ~p", [Reason, Info]),
|
||||||
{error, bad_url};
|
{error, bad_url};
|
||||||
@ -51,7 +102,6 @@ parse(URL) ->
|
|||||||
spend(Recipient, Context, Location, Qwargs) ->
|
spend(Recipient, Context, Location, Qwargs) ->
|
||||||
case dissect_query(Qwargs) of
|
case dissect_query(Qwargs) of
|
||||||
{ok, Amount, Payload} ->
|
{ok, Amount, Payload} ->
|
||||||
|
|
||||||
{ok, {{spend, Context}, {Location, Recipient, Amount, Payload}}};
|
{ok, {{spend, Context}, {Location, Recipient, Amount, Payload}}};
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
@ -64,8 +114,18 @@ dissect_query(Qwargs) ->
|
|||||||
ok = tell("URL parsing failed with ~w: ~p", [Reason, Info]),
|
ok = tell("URL parsing failed with ~w: ~p", [Reason, Info]),
|
||||||
{error, bad_url};
|
{error, bad_url};
|
||||||
ArgList ->
|
ArgList ->
|
||||||
Amount = proplists:get_value("a", ArgList, 0),
|
case l_to_i(proplists:get_value("a", ArgList, "0")) of
|
||||||
Payload = proplists:get_value("p", ArgList, <<>>),
|
{ok, Amount} ->
|
||||||
{ok, Amount, Payload}
|
Payload = list_to_binary(proplists:get_value("p", ArgList, "")),
|
||||||
|
{ok, Amount, Payload};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
l_to_i(S) ->
|
||||||
|
try
|
||||||
|
{ok, list_to_integer(S)}
|
||||||
|
catch
|
||||||
|
error:badarg -> {error, bad_url}
|
||||||
|
end.
|
||||||
|
112
src/gmc_gui.erl
112
src/gmc_gui.erl
@ -291,7 +291,7 @@ handle_event(#wx{event = #wxCommand{type = command_button_clicked},
|
|||||||
NewState =
|
NewState =
|
||||||
case lists:keyfind(ID, #w.id, Buttons) of
|
case lists:keyfind(ID, #w.id, Buttons) of
|
||||||
#w{name = chain} -> netman(State);
|
#w{name = chain} -> netman(State);
|
||||||
% #w{name = node} -> set_node(State);
|
#w{name = node} -> set_node(State);
|
||||||
#w{name = make_key} -> make_key(State);
|
#w{name = make_key} -> make_key(State);
|
||||||
#w{name = recover} -> recover_key(State);
|
#w{name = recover} -> recover_key(State);
|
||||||
#w{name = mnemonic} -> show_mnemonic(State);
|
#w{name = mnemonic} -> show_mnemonic(State);
|
||||||
@ -344,39 +344,91 @@ terminate(Reason, State) ->
|
|||||||
|
|
||||||
%%% Doers
|
%%% Doers
|
||||||
|
|
||||||
% TODO: Make a network/chain management activity
|
|
||||||
netman(State) ->
|
netman(State) ->
|
||||||
ok = gmc_con:show_ui(gmc_v_netman),
|
ok = gmc_con:show_ui(gmc_v_netman),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
%set_chain(State = #s{frame = Frame, j = J, buttons = Buttons}) ->
|
set_node(State = #s{frame = Frame, j = J}) ->
|
||||||
% Label = "Node",
|
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Set Node")),
|
||||||
% Dialog = wxDialog:new(Frame, ?wxID_ANY, J(Label)),
|
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
||||||
% Sizer = wxBoxSizer:new(?wxVERTICAL),
|
|
||||||
% NodeSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J(Label)}]),
|
AddressSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Address")}]),
|
||||||
% NodeTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
AddressTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
||||||
% _ = wxStaticBoxSizer:add(NodeSz, NodeTx, zxw:flags(wide)),
|
_ = wxSizer:add(AddressSz, AddressTx, zxw:flags(wide)),
|
||||||
% ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
||||||
% Affirm = wxButton:new(Dialog, ?wxID_OK),
|
PortSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||||
% Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
|
Labels =
|
||||||
% _ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
[J("External"),
|
||||||
% _ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
J("Internal"),
|
||||||
% _ = wxBoxSizer:add(Sizer, NodeSz, zxw:flags(base)),
|
J("Rosetta"),
|
||||||
% _ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(base)),
|
J("Channel"),
|
||||||
% ok =
|
J("Middleware")],
|
||||||
% case wxDialog:showModal(Dialog) of
|
MakePortCtrl =
|
||||||
% ?wxID_OK ->
|
fun(L) ->
|
||||||
% String = wxTextCtrl:getValue(NodeTx),
|
Sz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, L}]),
|
||||||
% set_node2(String);
|
Tx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
||||||
% ?wxID_CANCEL ->
|
_ = wxSizer:add(Sz, Tx, zxw:flags(wide)),
|
||||||
% ok
|
_ = wxSizer:add(PortSz, Sz, zxw:flags(wide)),
|
||||||
% end,
|
Tx
|
||||||
% State.
|
end,
|
||||||
%
|
PortCtrls = lists:map(MakePortCtrl, Labels),
|
||||||
%set_node2("") ->
|
|
||||||
% ok;
|
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
||||||
%set_node2(String) ->
|
Affirm = wxButton:new(Dialog, ?wxID_OK),
|
||||||
% case lists:
|
Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
|
||||||
|
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
||||||
|
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
||||||
|
|
||||||
|
_ = wxSizer:add(Sizer, AddressSz, zxw:flags(base)),
|
||||||
|
_ = wxSizer:add(Sizer, PortSz, zxw:flags(base)),
|
||||||
|
_ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)),
|
||||||
|
|
||||||
|
ok = wxDialog:setSizer(Dialog, Sizer),
|
||||||
|
ok = wxBoxSizer:layout(Sizer),
|
||||||
|
ok = wxFrame:setSize(Dialog, {500, 200}),
|
||||||
|
ok = wxFrame:center(Dialog),
|
||||||
|
ok = wxStyledTextCtrl:setFocus(AddressTx),
|
||||||
|
|
||||||
|
ok =
|
||||||
|
case wxDialog:showModal(Dialog) of
|
||||||
|
?wxID_OK ->
|
||||||
|
case wxTextCtrl:getValue(AddressTx) of
|
||||||
|
"" -> ok;
|
||||||
|
Address -> add_node2(Address, PortCtrls)
|
||||||
|
end;
|
||||||
|
?wxID_CANCEL ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
ok = wxDialog:destroy(Dialog),
|
||||||
|
State.
|
||||||
|
|
||||||
|
add_node2(Address, PortCtrls) ->
|
||||||
|
IP =
|
||||||
|
case inet:parse_address(Address) of
|
||||||
|
{ok, N} -> N;
|
||||||
|
{error, einval} -> Address
|
||||||
|
end,
|
||||||
|
[E, I, R, C, M] = lists:map(fun numerify_port/1, PortCtrls),
|
||||||
|
New = #node{ip = IP, external = E, internal = I, rosetta = R, channel = C, mdw = M},
|
||||||
|
gmc_con:set_sole_node(New).
|
||||||
|
|
||||||
|
numerify_port(Ctrl) ->
|
||||||
|
case wxTextCtrl:getValue(Ctrl) of
|
||||||
|
"" -> none;
|
||||||
|
String ->
|
||||||
|
case s_to_i(String) of
|
||||||
|
{ok, PortNum} -> PortNum;
|
||||||
|
error -> none
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
s_to_i(S) ->
|
||||||
|
try
|
||||||
|
I = list_to_integer(S),
|
||||||
|
{ok, I}
|
||||||
|
catch error:badarg ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
make_key(State = #s{frame = Frame, j = J}) ->
|
make_key(State = #s{frame = Frame, j = J}) ->
|
||||||
|
@ -296,7 +296,6 @@ add_node(State = #s{frame = Frame, j = J}) ->
|
|||||||
_ = wxSizer:add(Sizer, PortSz, zxw:flags(base)),
|
_ = wxSizer:add(Sizer, PortSz, 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 = wxBoxSizer:layout(Sizer),
|
ok = wxBoxSizer:layout(Sizer),
|
||||||
ok = wxFrame:setSize(Dialog, {500, 200}),
|
ok = wxFrame:setSize(Dialog, {500, 200}),
|
||||||
|
@ -2,10 +2,14 @@
|
|||||||
{type,gui}.
|
{type,gui}.
|
||||||
{modules,[]}.
|
{modules,[]}.
|
||||||
{prefix,"gmc"}.
|
{prefix,"gmc"}.
|
||||||
{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"}.
|
||||||
|
{author,"Craig Everett"}.
|
||||||
{package_id,{"otpr","clutch",{0,1,0}}}.
|
{package_id,{"otpr","clutch",{0,1,0}}}.
|
||||||
{deps,[{"otpr","erl_base58",{0,1,0}},
|
{deps,[{"otpr","zj",{1,1,0}},
|
||||||
|
{"otpr","aesophia",{7,1,2}},
|
||||||
|
{"otpr","aebytecode",{3,2,1}},
|
||||||
|
{"otpr","hakuzaru",{0,1,0}},
|
||||||
|
{"otpr","erl_base58",{0,1,0}},
|
||||||
{"otpr","aeserialization",{0,1,0}},
|
{"otpr","aeserialization",{0,1,0}},
|
||||||
{"otpr","eblake2",{1,0,0}},
|
{"otpr","eblake2",{1,0,0}},
|
||||||
{"otpr","ec_utils",{1,0,0}},
|
{"otpr","ec_utils",{1,0,0}},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user