Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
8258d56b34 | |||
5a8e0b602d | |||
ae81b7eb0a | |||
b487f98d9e | |||
1dc686215e | |||
c8670ae1b9 |
@ -3,7 +3,6 @@
|
|||||||
{included_applications,[]},
|
{included_applications,[]},
|
||||||
{applications,[stdlib,kernel]},
|
{applications,[stdlib,kernel]},
|
||||||
{description,"Gajumaru interoperation library"},
|
{description,"Gajumaru interoperation library"},
|
||||||
{vsn,"0.6.1"},
|
{vsn,"0.5.1"},
|
||||||
{modules,[hakuzaru,hz,hz_fetcher,hz_grids,hz_key_master,hz_man,
|
{modules,[hakuzaru,hz,hz_fetcher,hz_man,hz_sup]},
|
||||||
hz_sup]},
|
|
||||||
{mod,{hakuzaru,[]}}]}.
|
{mod,{hakuzaru,[]}}]}.
|
||||||
|
4096
priv/words4096.txt
4096
priv/words4096.txt
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hakuzaru).
|
-module(hakuzaru).
|
||||||
-vsn("0.6.1").
|
-vsn("0.5.1").
|
||||||
-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").
|
||||||
|
24
src/hz.erl
24
src/hz.erl
@ -23,7 +23,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz).
|
-module(hz).
|
||||||
-vsn("0.6.1").
|
-vsn("0.5.1").
|
||||||
-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,7 +73,6 @@
|
|||||||
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]).
|
||||||
|
|
||||||
|
|
||||||
@ -1143,9 +1142,10 @@ assemble_calldata2(OwnerID, Nonce, Amount, TTL, Gas, GasPrice, Compiled, CallDat
|
|||||||
read_aci(Path) ->
|
read_aci(Path) ->
|
||||||
case file:read_file(Path) of
|
case file:read_file(Path) of
|
||||||
{ok, Bin} ->
|
{ok, Bin} ->
|
||||||
case zx_lib:b_to_ts(Bin) of
|
try
|
||||||
error -> {error, bad_aci};
|
{ok, binary_to_term(Bin, [safe])}
|
||||||
OK -> OK
|
catch
|
||||||
|
error:badarg -> error
|
||||||
end;
|
end;
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
@ -2273,20 +2273,6 @@ 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,13 +1,11 @@
|
|||||||
-module(hz_fetcher).
|
-module(hz_fetcher).
|
||||||
-vsn("0.6.1").
|
-vsn("0.5.1").
|
||||||
-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").
|
||||||
|
|
||||||
-export([connect/4, slowly_connect/4]).
|
-export([connect/4, slowly_connect/4]).
|
||||||
|
|
||||||
-include("$zx_include/zx_logger.hrl").
|
|
||||||
|
|
||||||
|
|
||||||
connect(Node = {Host, Port}, Request, From, Timeout) ->
|
connect(Node = {Host, Port}, Request, From, Timeout) ->
|
||||||
Timer = erlang:send_after(Timeout, self(), timeout),
|
Timer = erlang:send_after(Timeout, self(), timeout),
|
||||||
@ -78,7 +76,7 @@ parse(Received, Sock, From, Timer) ->
|
|||||||
<<"HTTP/1.1 500 Internal Server Error\r\n", Tail/binary>> ->
|
<<"HTTP/1.1 500 Internal Server Error\r\n", Tail/binary>> ->
|
||||||
parse2(500, Tail, Sock, From, Timer);
|
parse2(500, Tail, Sock, From, Timer);
|
||||||
_ ->
|
_ ->
|
||||||
ok = zx_net:disconnect(Sock),
|
ok = disconnect(Sock),
|
||||||
ok = erlang:cancel_timer(Timer, [{async, true}]),
|
ok = erlang:cancel_timer(Timer, [{async, true}]),
|
||||||
gen_server:reply(From, {error, {received, Received}})
|
gen_server:reply(From, {error, {received, Received}})
|
||||||
end.
|
end.
|
||||||
@ -115,7 +113,7 @@ consume2(Length, Received, Sock, From, Timer) ->
|
|||||||
if
|
if
|
||||||
Size == Length ->
|
Size == Length ->
|
||||||
ok = erlang:cancel_timer(Timer, [{async, true}]),
|
ok = erlang:cancel_timer(Timer, [{async, true}]),
|
||||||
ok = zx_net:disconnect(Sock),
|
ok = disconnect(Sock),
|
||||||
Result = zj:decode(Received),
|
Result = zj:decode(Received),
|
||||||
gen_server:reply(From, Result);
|
gen_server:reply(From, Result);
|
||||||
Size < Length ->
|
Size < Length ->
|
||||||
@ -236,3 +234,47 @@ url({Node, Port}, Path) when is_list(Node) ->
|
|||||||
["https://", Node, ":", integer_to_list(Port), Path];
|
["https://", Node, ":", integer_to_list(Port), Path];
|
||||||
url({Node, Port}, Path) when is_tuple(Node) ->
|
url({Node, Port}, Path) when is_tuple(Node) ->
|
||||||
["https://", inet:ntoa(Node), ":", integer_to_list(Port), Path].
|
["https://", inet:ntoa(Node), ":", integer_to_list(Port), Path].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log(Level, Format, Args) ->
|
||||||
|
Raw = io_lib:format("~w ~w: " ++ Format, [?MODULE, self() | Args]),
|
||||||
|
Entry = unicode:characters_to_list(Raw),
|
||||||
|
logger:log(Level, Entry).
|
||||||
|
|
||||||
|
|
||||||
|
disconnect(Socket) ->
|
||||||
|
case peername(Socket) of
|
||||||
|
{ok, {Addr, Port}} ->
|
||||||
|
Host = inet:ntoa(Addr),
|
||||||
|
disconnect(Socket, Host, Port);
|
||||||
|
{error, Reason} ->
|
||||||
|
log(warning, "Disconnect failed with: ~w", [Reason])
|
||||||
|
end.
|
||||||
|
|
||||||
|
disconnect(Socket, Host, Port) ->
|
||||||
|
case gen_tcp:shutdown(Socket, read_write) of
|
||||||
|
ok ->
|
||||||
|
ok;
|
||||||
|
{error, enotconn} ->
|
||||||
|
receive
|
||||||
|
{tcp_closed, Socket} -> ok
|
||||||
|
after 0 -> ok
|
||||||
|
end;
|
||||||
|
{error, E} ->
|
||||||
|
ok = log(warning, "~ts:~w disconnect failed with: ~w", [Host, Port, E]),
|
||||||
|
receive
|
||||||
|
{tcp_closed, Socket} -> ok
|
||||||
|
after 0 -> ok
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
peername(Socket) ->
|
||||||
|
case inet:peername(Socket) of
|
||||||
|
{ok, {{0, 0, 0, 0, 0, 65535, X, Y}, Port}} ->
|
||||||
|
<<A:8, B:8, C:8, D:8>> = <<X:16, Y:16>>,
|
||||||
|
{ok, {{A, B, C, D}, Port}};
|
||||||
|
Other ->
|
||||||
|
Other
|
||||||
|
end.
|
||||||
|
159
src/hz_grids.erl
159
src/hz_grids.erl
@ -1,159 +0,0 @@
|
|||||||
%%% @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.1").
|
|
||||||
-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}.
|
|
@ -1,154 +0,0 @@
|
|||||||
%%% @doc
|
|
||||||
%%% Key functions
|
|
||||||
%%%
|
|
||||||
%%% 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(hz_key_master).
|
|
||||||
-vsn("0.6.1").
|
|
||||||
|
|
||||||
|
|
||||||
-export([make_key/1, encode/1, decode/1]).
|
|
||||||
-export([lcg/1]).
|
|
||||||
|
|
||||||
make_key(<<>>) ->
|
|
||||||
Pair = #{public := Public} = ecu_eddsa:sign_keypair(),
|
|
||||||
ID = gmser_api_encoder:encode(account_pubkey, Public),
|
|
||||||
{ID, Pair};
|
|
||||||
make_key(Seed) ->
|
|
||||||
Pair = #{public := Public} = ecu_eddsa:sign_seed_keypair(Seed),
|
|
||||||
ID = gmser_api_encoder:encode(account_pubkey, Public),
|
|
||||||
{ID, 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() ->
|
|
||||||
{ok, V} = zx_lib:string_to_version(proplists:get_value(vsn, module_info(attributes))),
|
|
||||||
HZ_Lib = zx_lib:ppath(lib, {"otpr", "hakuzaru", V}),
|
|
||||||
Path = filename:join([HZ_Lib, "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.
|
|
@ -9,7 +9,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_man).
|
-module(hz_man).
|
||||||
-vsn("0.6.1").
|
-vsn("0.5.1").
|
||||||
-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>").
|
||||||
@ -28,8 +28,6 @@
|
|||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
code_change/3, terminate/2]).
|
code_change/3, terminate/2]).
|
||||||
|
|
||||||
%% TODO: Make logging more flexible
|
|
||||||
-include("$zx_include/zx_logger.hrl").
|
|
||||||
|
|
||||||
|
|
||||||
%%% Type and Record Definitions
|
%%% Type and Record Definitions
|
||||||
@ -268,3 +266,11 @@ do_request(Request,
|
|||||||
do_request(Request, From, State = #s{chain_nodes = {[], Used}}) ->
|
do_request(Request, From, State = #s{chain_nodes = {[], Used}}) ->
|
||||||
Fresh = lists:reverse(Used),
|
Fresh = lists:reverse(Used),
|
||||||
do_request(Request, From, State#s{chain_nodes = {Fresh, []}}).
|
do_request(Request, From, State#s{chain_nodes = {Fresh, []}}).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log(Level, Format, Args) ->
|
||||||
|
Raw = io_lib:format("~w ~w: " ++ Format, [?MODULE, self() | Args]),
|
||||||
|
Entry = unicode:characters_to_list(Raw),
|
||||||
|
logger:log(Level, Entry).
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_sup).
|
-module(hz_sup).
|
||||||
-vsn("0.6.1").
|
-vsn("0.5.1").
|
||||||
-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,10 +4,10 @@
|
|||||||
{prefix,"hz"}.
|
{prefix,"hz"}.
|
||||||
{desc,"Gajumaru interoperation library"}.
|
{desc,"Gajumaru interoperation library"}.
|
||||||
{author,"Craig Everett"}.
|
{author,"Craig Everett"}.
|
||||||
{package_id,{"otpr","hakuzaru",{0,6,1}}}.
|
{package_id,{"otpr","hakuzaru",{0,5,1}}}.
|
||||||
{deps,[{"otpr","sophia",{9,0,0}},
|
{deps,[{"otpr","sophia",{8,0,1}},
|
||||||
{"otpr","gmserialization",{0,1,3}},
|
|
||||||
{"otpr","gmbytecode",{3,4,1}},
|
{"otpr","gmbytecode",{3,4,1}},
|
||||||
|
{"otpr","gmserialization",{0,1,2}},
|
||||||
{"otpr","base58",{0,1,1}},
|
{"otpr","base58",{0,1,1}},
|
||||||
{"otpr","eblake2",{1,0,1}},
|
{"otpr","eblake2",{1,0,1}},
|
||||||
{"otpr","ec_utils",{1,0,0}},
|
{"otpr","ec_utils",{1,0,0}},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user