gmhive_client/src/gmhc_eureka.erl
2025-08-21 22:46:54 +02:00

159 lines
5.4 KiB
Erlang

-module(gmhc_eureka).
-vsn("0.4.8").
-export([get_pool_address/0]).
-include_lib("kernel/include/logger.hrl").
-include("gmhc_events.hrl").
get_pool_address() ->
case gmconfig:find_config([<<"pool_admin">>, <<"url">>], [user_config]) of
{ok, URL0} ->
case expand_url(URL0) of
<<"local">> ->
#{<<"address">> => <<"127.0.0.1">>,
<<"port">> => gmconfig:get_config(
[<<"pool">>, <<"port">>], [schema_default]),
<<"pool_id">> => gmhc_config:get_config([<<"pool">>, <<"id">>]) };
URL ->
?LOG_INFO("Trying to connect to ~p", [URL]),
connect1(URL)
end;
undefined ->
Network = gmconfig:get_config([<<"network">>]),
URL0 = gmconfig:get_config([ <<"pool_admin">>
, <<"default_per_network">>
, Network ],
[schema_default]),
URL = expand_url(URL0),
?LOG_INFO("Using default for ~p: ~p", [Network, URL]),
connect1(URL)
end.
connect1(URL0) ->
URL = binary_to_list(URL0),
Res = request(get, URL),
?LOG_DEBUG("Res = ~p", [Res]),
case Res of
{ok, Body} ->
try get_host_port(json:decode(iolist_to_binary(Body)))
catch
error:_ ->
gmhc_events:publish(error, ?ERR_EVT(#{error => invalid_json,
data => Body})),
{error, invalid_json}
end;
{error, _} = Error ->
gmhc_events:publish(error, ?ERR_EVT(#{error => connect_failure,
data => Error})),
Error
end.
get_host_port(Data) ->
?LOG_DEBUG("Data = ~p", [Data]),
maps:with([<<"address">>, <<"port">>, <<"pool_id">>], Data).
request(get, URL) ->
case request(get, URL, []) of
{ok, #{body := Body}} ->
{ok, Body};
Other ->
%% TODO: perhaps return a more informative reason?
gmhc_events:publish(error, ?ERR_EVT(#{error => get_failed,
url => URL,
data => Other })),
{error, failed}
end.
request(get, URL, []) ->
Headers = [],
HttpOpts = [{timeout, 15000}],
Opts = [],
Profile = default,
request_result(httpc:request(get, {URL, Headers}, HttpOpts, Opts, Profile)).
%% request(post, URL, Body) ->
%% post_request(URL, Body).
expand_url(URL) ->
case re:run(URL, <<"{[^}]+}">>, []) of
{match, _} ->
expand_vars(URL);
nomatch ->
URL
end.
expand_vars(S) ->
expand_vars(S, <<>>).
expand_vars(<<"{", Rest/binary>>, Acc) ->
{Var, Rest1} = get_var_name(Rest),
expand_vars(Rest1, <<Acc/binary, (expand_var(Var))/binary>>);
expand_vars(<<H, T/binary>>, Acc) ->
expand_vars(T, <<Acc/binary, H>>);
expand_vars(<<>>, Acc) ->
Acc.
expand_var(<<"CLIENT_ID">>) ->
gmhc_config:get_config([<<"pubkey">>]).
get_var_name(S) ->
get_var_name(S, <<>>).
get_var_name(<<"}", Rest/binary>>, Acc) ->
{Acc, Rest};
get_var_name(<<H, T/binary>>, Acc) ->
get_var_name(T, <<Acc/binary, H>>).
%% From hz.erl ==========================================================
% This is Bitcoin's variable-length unsigned integer encoding
% See: https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
%% vencode(N) when N =< 0 ->
%% {error, {non_pos_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 = encode unsigned (little endian with a given byte width)
% means add zero bytes to the end as needed
%% eu(N, Size) ->
%% Bytes = binary:encode_unsigned(N, little),
%% NExtraZeros = Size - byte_size(Bytes),
%% ExtraZeros = << <<0>> || _ <- lists:seq(1, NExtraZeros) >>,
%% <<Bytes/binary, ExtraZeros/binary>>.
%% ======================================================================
%% From gmplugin_web_demo_handler.erl ===================================
%% post_request(URL, Map) ->
%% ?LOG_DEBUG("Map = ~p", [Map]),
%% Body = json:encode(Map),
%% ?LOG_DEBUG("Body = ~s", [Body]),
%% PostRes = httpc:request(post, {URL, [], "application/json", Body}, [], []),
%% request_result(PostRes).
%% ======================================================================
request_result(Result) ->
?LOG_DEBUG("Request result: ~p", [Result]),
request_result_(Result).
request_result_({ok, {{_, C200, Ok}, _Hdrs, Body}}) when C200 >= 200, C200 < 300 ->
{ok, #{code => C200, msg => Ok, body => Body}};
request_result_({ok, {{_, C200, Ok}, Body}}) when C200 >= 200, C200 < 300 ->
{ok, #{code => C200, msg => Ok, body => Body}};
request_result_({ok, {{_, Code, Error}, _Hdrs, Body}}) ->
{error, #{code => Code, msg => Error, body => Body}};
request_result_(_) ->
{error, #{code => 500, msg => <<"Internal error">>, body => <<>>}}.