Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/core/resolver/hb_opts.erl
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,23 @@ raw_default_message() ->
<<"opts">> => ?DEFAULT_HTTP_OPTS
}
},
% Wait for 2 node responses when fetching current block or height information
#{
<<"template">> => <<"^/arweave/block/current">>,
<<"nodes">> => add_opts(?ARWEAVE_BOOTSTRAP_CHAIN_NODES),
<<"parallel">> => true,
<<"responses">> => 2,
<<"stop-after">> => true,
<<"admissible-status">> => 200
},
#{
<<"template">> => <<"^/arweave/height">>,
<<"nodes">> => add_opts(?ARWEAVE_BOOTSTRAP_CHAIN_NODES),
<<"parallel">> => true,
<<"responses">> => 2,
<<"stop-after">> => true,
<<"admissible-status">> => 200
},
% General Arweave requests: race all chain nodes, take
% the first 200.
#{
Expand Down
62 changes: 61 additions & 1 deletion src/preloaded/arweave/dev_arweave.erl
Original file line number Diff line number Diff line change
Expand Up @@ -885,10 +885,19 @@ request(Method, Path, Extra, LogExtra, Opts) ->
<<"cache-control">> => [<<"no-cache">>, <<"no-store">>]
}
),
to_message(Path, Method, best_response(Res), LogExtra, Opts).
to_message(Path, Method, best_response(Path, Res), LogExtra, Opts).

%% @doc Select the best response from a list of responses by sorting them
%% ascending by HTTP status code. Returns the first (best) response tuple.
best_response(Path, {error, {no_viable_responses, Responses}}) ->
best_response(Path, Responses);
best_response(<<"/height">>, Responses) when is_list(Responses) ->

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/height Also needs to be added to the templates I think?

current_height_best_response(current_height_responses(Responses));
best_response(<<"/block/current">>, Responses) when is_list(Responses) ->
Comment thread
JamesPiechota marked this conversation as resolved.
current_height_best_response(current_height_responses(Responses));
best_response(_Path, Response) ->
best_response(Response).

best_response({error, {no_viable_responses, Responses}}) ->
best_response(Responses);
best_response([]) ->
Expand All @@ -911,6 +920,47 @@ response_status(Response) when is_map(Response) ->
response_status(_Response) ->
999.

%% @doc Parse responses that contains block height to return the one with the
%% highest value. This helps when one node lags behind.
current_height_responses(Responses) ->
lists:filtermap(
fun
({ok, #{ <<"body">> := Body }} = Response) ->
try hb_json:decode(Body) of
#{ <<"height">> := Height } ->
{true, {hb_util:int(Height), Response}};
Height when is_integer(Height) ->
{true, {Height, Response}};
_ ->
false
catch _:_ ->
case hb_util:safe_int(Body) of
{ok, Height} -> {true, {Height, Response}};
{error, invalid} -> false
end
end;
(_) ->
false
end,
Responses
).

current_height_best_response([]) ->
{error, no_viable_responses};
current_height_best_response([{Height, Response} | Rest]) ->
{_, BestResponse} =
lists:foldl(
fun({NextHeight, NextResponse}, {BestHeight, Best}) ->
case NextHeight > BestHeight of
true -> {NextHeight, NextResponse};
false -> {BestHeight, Best}
end
end,
{Height, Response},
Rest
),
BestResponse.

%% @doc Transform a response from the Arweave node into an AO-Core message.
to_message(Path, Method, {error, #{ <<"status">> := 404 }}, LogExtra, _Opts) ->
event_request(Path, Method, 404, LogExtra),
Expand Down Expand Up @@ -1209,6 +1259,16 @@ best_response_handles_failed_connect_entries_test_parallel() ->
best_response(Responses)
).

best_response_height_integer_body_test_parallel() ->
Responses = [
{ok, #{ <<"status">> => 200, <<"body">> => <<"5">> }},
{ok, #{ <<"status">> => 200, <<"body">> => <<"8">> }}
],
?assertEqual(
{ok, #{ <<"status">> => 200, <<"body">> => <<"8">> }},
best_response(<<"/height">>, Responses)
).

best_response_non_map_error_round_trips_test_parallel() ->
FailedConnect =
{failed_connect,
Expand Down
42 changes: 42 additions & 0 deletions src/preloaded/query/dev_copycat_arweave.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,48 @@ latest_height_failure_test_parallel() ->
hb_mock_server:stop(MockHandle)
end.

latest_height_uses_highest_mockserver_height_test_parallel() ->
{ok, MockURL1, MockHandle1} = hb_mock_server:start([
{"/block/current", block_current, {200, <<"{\"height\": 5}">>}}
]),
{ok, MockURL2, MockHandle2} = hb_mock_server:start([
{"/block/current", block_current, {200, <<"{\"height\": 8}">>}}
]),
TestStore = hb_test_utils:test_store(),
Opts = #{
<<"store">> => [TestStore],
<<"arweave-index-blocks">> => false,
<<"routes">> => [
#{
<<"template">> => <<"^/arweave">>,
<<"nodes">> => [
#{
<<"match">> => <<"^/arweave">>,
<<"with">> => MockURL1,
<<"opts">> => #{ <<"http-client">> => httpc }
},
#{
<<"match">> => <<"^/arweave">>,
<<"with">> => MockURL2,
<<"opts">> => #{ <<"http-client">> => httpc }
}
],
<<"parallel">> => false,
<<"responses">> => 2,
<<"stop-after">> => false,
<<"admissible-status">> => 200
}
]
},
try
?assertEqual({ok, 8}, latest_height(Opts)),
[_] = hb_mock_server:get_requests(block_current, 1, MockHandle1),
[_] = hb_mock_server:get_requests(block_current, 1, MockHandle2)
after
hb_mock_server:stop(MockHandle1),
hb_mock_server:stop(MockHandle2)
end.

negative_resolved_height_test_parallel() ->
{ok, MockURL, MockHandle} = hb_mock_server:start([
{"/block/current", block_current,
Expand Down