Add message signing
This commit is contained in:
parent
aa2d1adefd
commit
4dea4b766c
@ -3,6 +3,6 @@
|
|||||||
{included_applications,[]},
|
{included_applications,[]},
|
||||||
{applications,[stdlib,kernel]},
|
{applications,[stdlib,kernel]},
|
||||||
{description,"Gajumaru interoperation library"},
|
{description,"Gajumaru interoperation library"},
|
||||||
{vsn,"0.5.2"},
|
{vsn,"0.6.0"},
|
||||||
{modules,[hakuzaru,hz,hz_fetcher,hz_man,hz_sup]},
|
{modules,[hakuzaru,hz,hz_fetcher,hz_man,hz_sup]},
|
||||||
{mod,{hakuzaru,[]}}]}.
|
{mod,{hakuzaru,[]}}]}.
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hakuzaru).
|
-module(hakuzaru).
|
||||||
-vsn("0.5.2").
|
-vsn("0.6.0").
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
-author("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
|
17
src/hz.erl
17
src/hz.erl
@ -23,7 +23,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz).
|
-module(hz).
|
||||||
-vsn("0.5.2").
|
-vsn("0.6.0").
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
-author("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
@ -73,6 +73,7 @@
|
|||||||
decode_bytearray_fate/1, decode_bytearray/2,
|
decode_bytearray_fate/1, decode_bytearray/2,
|
||||||
spend/5, spend/10,
|
spend/5, spend/10,
|
||||||
sign_tx/2, sign_tx/3,
|
sign_tx/2, sign_tx/3,
|
||||||
|
sign_message/2,
|
||||||
verify_signature/3]).
|
verify_signature/3]).
|
||||||
|
|
||||||
|
|
||||||
@ -2272,6 +2273,20 @@ spend3(DSenderID,
|
|||||||
hz:post_tx(Encoded).
|
hz:post_tx(Encoded).
|
||||||
|
|
||||||
|
|
||||||
|
-spec sign_message(Message, SecKey) -> Sig
|
||||||
|
when Message :: binary(),
|
||||||
|
SecKey :: binary(),
|
||||||
|
Sig :: binary().
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
|
||||||
-spec verify_signature(Sig, Message, PubKey) -> Result
|
-spec verify_signature(Sig, Message, PubKey) -> Result
|
||||||
when Sig :: binary(),
|
when Sig :: binary(),
|
||||||
Message :: iodata(),
|
Message :: iodata(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-module(hz_fetcher).
|
-module(hz_fetcher).
|
||||||
-vsn("0.5.2").
|
-vsn("0.6.0").
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
-author("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-license("MIT").
|
-license("MIT").
|
||||||
|
159
src/hz_grids.erl
Normal file
159
src/hz_grids.erl
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
%%% @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 "grid://"
|
||||||
|
%%% 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.
|
||||||
|
%%% @end
|
||||||
|
|
||||||
|
-module(hz_grids).
|
||||||
|
-vsn("0.6.0").
|
||||||
|
-export([url/2, parse/1, req/2, req/3]).
|
||||||
|
|
||||||
|
|
||||||
|
-spec url(Instruction, HTTP) -> Result
|
||||||
|
when Instruction :: spend | transfer | sign,
|
||||||
|
HTTP :: uri_string:uri_string(),
|
||||||
|
GRIDS :: uri_string:uri_string(),
|
||||||
|
Result :: {ok, GRIDS} | uri_string:uri_error().
|
||||||
|
%% @doc
|
||||||
|
%% Takes
|
||||||
|
|
||||||
|
url(Instruction, HTTP) ->
|
||||||
|
case uri_string:parse(HTTP) of
|
||||||
|
U = #{scheme := "https"} -> url2(Instruction, U#{scheme := "grids"});
|
||||||
|
U = #{scheme := "http"} -> url2(Instruction, U#{scheme := "grid"});
|
||||||
|
Error -> Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
url2(Instruction, URL = #{path := Path}) ->
|
||||||
|
GRIDS =
|
||||||
|
case Instruction of
|
||||||
|
spend -> URL#{path := "/1/s" ++ Path};
|
||||||
|
transfer -> URL#{path := "/1/t" ++ Path};
|
||||||
|
sign -> URL#{path := "/1/d" ++ Path}
|
||||||
|
end,
|
||||||
|
{ok, uri_string:recompose(GRIDS)}.
|
||||||
|
|
||||||
|
|
||||||
|
-spec parse(GRIDS) -> Result
|
||||||
|
when GRIDS :: string(),
|
||||||
|
Result :: {ok, Instruction} | uri_string:error(),
|
||||||
|
Instruction :: {{spend, chain | node}, {Location, Recipient, Amount, Payload}}
|
||||||
|
| {{sign, http | https}, URL},
|
||||||
|
Location :: Node :: {inet:ip_address() | inet:hostname(), inet:port_number()}
|
||||||
|
| Chain :: binary(),
|
||||||
|
Recipient :: gajudesk:id(),
|
||||||
|
Amount :: non_neg_integer(),
|
||||||
|
Payload :: binary(),
|
||||||
|
URL :: string().
|
||||||
|
|
||||||
|
parse(GRIDS) ->
|
||||||
|
case uri_string:parse(GRIDS) of
|
||||||
|
#{path := "/1/s/" ++ R, host := H, query := Q, scheme := "grids"} ->
|
||||||
|
spend(R, chain, list_to_binary(H), Q);
|
||||||
|
#{path := "/1/s/" ++ R, host := H, query := Q, scheme := "grid"} ->
|
||||||
|
spend(R, chain, list_to_binary(H), Q);
|
||||||
|
#{path := "/1/t/" ++ R, host := H, port := P, query := Q, scheme := "grids"} ->
|
||||||
|
spend(R, node, {H, P}, Q);
|
||||||
|
#{path := "/1/t/" ++ R, host := H, port := P, query := Q, scheme := "grid"} ->
|
||||||
|
spend(R, node, {H, P}, Q);
|
||||||
|
#{path := "/1/t/" ++ R, host := H, query := Q, scheme := "grids"} ->
|
||||||
|
spend(R, node, {H, 3013}, Q);
|
||||||
|
#{path := "/1/t/" ++ R, host := H, query := Q, scheme := "grid"} ->
|
||||||
|
spend(R, node, {H, 3013}, Q);
|
||||||
|
U = #{path := "/1/d/" ++ L, scheme := "grids"} ->
|
||||||
|
HTTP = uri_string:recompose(U#{scheme := "https", path := L}),
|
||||||
|
{ok ,{{sign, https}, HTTP}};
|
||||||
|
U = #{path := "/1/d/" ++ L, scheme := "grid"} ->
|
||||||
|
HTTP = uri_string:recompose(U#{scheme := "http", path := L}),
|
||||||
|
{ok, {{sign, http}, HTTP}};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
spend(Recipient, Context, Location, Qwargs) ->
|
||||||
|
case dissect_query(Qwargs) of
|
||||||
|
{ok, Amount, Payload} ->
|
||||||
|
{ok, {{spend, Context}, {Location, Recipient, Amount, Payload}}};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
dissect_query(Qwargs) ->
|
||||||
|
case uri_string:dissect_query(Qwargs) of
|
||||||
|
{error, Reason, Info} ->
|
||||||
|
{error, Reason, Info};
|
||||||
|
ArgList ->
|
||||||
|
case l_to_i(proplists:get_value("a", ArgList, "0")) of
|
||||||
|
{ok, Amount} ->
|
||||||
|
Payload = list_to_binary(proplists:get_value("p", ArgList, "")),
|
||||||
|
{ok, Amount, Payload};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
l_to_i(S) ->
|
||||||
|
try
|
||||||
|
{ok, list_to_integer(S)}
|
||||||
|
catch
|
||||||
|
error:badarg -> {error, bad_url}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
req(Type, Message) ->
|
||||||
|
req(Type, Message, false).
|
||||||
|
|
||||||
|
req(sign, Message, ID) ->
|
||||||
|
#{"grids" => 1,
|
||||||
|
"chain" => "gajumaru",
|
||||||
|
"network_id" => hz:network_id(),
|
||||||
|
"type" => "message",
|
||||||
|
"public_id" => ID,
|
||||||
|
"payload" => Message};
|
||||||
|
req(tx, Data, ID) ->
|
||||||
|
#{"grids" => 1,
|
||||||
|
"chain" => "gajumaru",
|
||||||
|
"network_id" => hz:network_id(),
|
||||||
|
"type" => "tx",
|
||||||
|
"public_id" => ID,
|
||||||
|
"payload" => Data};
|
||||||
|
req(ack, Message, ID) ->
|
||||||
|
#{"grids" => 1,
|
||||||
|
"chain" => "gajumaru",
|
||||||
|
"network_id" => hz:network_id(),
|
||||||
|
"type" => "ack",
|
||||||
|
"public_id" => ID,
|
||||||
|
"payload" => Message}.
|
@ -9,7 +9,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_man).
|
-module(hz_man).
|
||||||
-vsn("0.5.2").
|
-vsn("0.6.0").
|
||||||
-behavior(gen_server).
|
-behavior(gen_server).
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
-author("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_sup).
|
-module(hz_sup).
|
||||||
-vsn("0.5.2").
|
-vsn("0.6.0").
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
-author("Craig Everett <zxq9@zxq9.com>").
|
-author("Craig Everett <zxq9@zxq9.com>").
|
||||||
-copyright("Craig Everett <zxq9@zxq9.com>").
|
-copyright("Craig Everett <zxq9@zxq9.com>").
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{prefix,"hz"}.
|
{prefix,"hz"}.
|
||||||
{author,"Craig Everett"}.
|
{author,"Craig Everett"}.
|
||||||
{desc,"Gajumaru interoperation library"}.
|
{desc,"Gajumaru interoperation library"}.
|
||||||
{package_id,{"otpr","hakuzaru",{0,5,2}}}.
|
{package_id,{"otpr","hakuzaru",{0,6,0}}}.
|
||||||
{deps,[{"otpr","gmserialization",{0,1,3}},
|
{deps,[{"otpr","gmserialization",{0,1,3}},
|
||||||
{"otpr","sophia",{8,0,1}},
|
{"otpr","sophia",{8,0,1}},
|
||||||
{"otpr","gmbytecode",{3,4,1}},
|
{"otpr","gmbytecode",{3,4,1}},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user