Skip to content

Commit

Permalink
Merge pull request #500 from ArweaveTeam/jp/20231120-fix-331
Browse files Browse the repository at this point in the history
VDF Client now selects the correct upper bound from the cached vdf_session
  • Loading branch information
JamesPiechota authored Nov 21, 2023
2 parents 9ea611c + 496f683 commit 511f1d2
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 52 deletions.
63 changes: 36 additions & 27 deletions apps/arweave/src/ar_nonce_limiter.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
get_or_init_nonce_limiter_info/1, get_or_init_nonce_limiter_info/2,
apply_external_update/2, get_session/1,
compute/3, resolve_remote_server_raw_peers/0,
get_entropy_reset_point/2, maybe_add_entropy/4, mix_seed/2]).
maybe_add_entropy/4, mix_seed/2]).

-export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2]).

Expand Down Expand Up @@ -605,7 +605,7 @@ handle_info({'DOWN', Ref, process, _, Reason}, #state{ worker_monitor_ref = Ref

handle_info({computed, Args}, State) ->
#state{ current_session_key = CurrentSessionKey } = State,
{StepNumber, PrevOutput, Output, UpperBound, Checkpoints} = Args,
{StepNumber, PrevOutput, Output, Checkpoints} = Args,
Session = get_session(CurrentSessionKey, State),
#vdf_session{ next_vdf_difficulty = NextVDFDifficulty, steps = [SessionOutput | _] } = Session,
{NextSeed, IntervalNumber, NextVDFDifficulty} = CurrentSessionKey,
Expand All @@ -621,8 +621,7 @@ handle_info({computed, Args}, State) ->
true ->
Session2 = update_session(Session, StepNumber, Checkpoints, [Output]),
State2 = cache_session(State, CurrentSessionKey, CurrentSessionKey, Session2),
ar_events:send(nonce_limiter, {computed_output,
{CurrentSessionKey, StepNumber, Output, UpperBound}}),
send_output(CurrentSessionKey, Session2),
{noreply, State2}
end;

Expand Down Expand Up @@ -656,6 +655,19 @@ update_session(Session, StepNumber, Steps) ->
#vdf_session{ steps = CurrentSteps } = Session,
Session#vdf_session{ step_number = StepNumber, steps = Steps ++ CurrentSteps }.

send_output(SessionKey, Session) ->
{_, IntervalNumber, _} = SessionKey,
#vdf_session{ step_number = StepNumber, steps = [Output | _] } = Session,
IntervalStart = IntervalNumber * ?NONCE_LIMITER_RESET_FREQUENCY,
UpperBound =
case get_entropy_reset_point(IntervalStart, StepNumber) of
none ->
Session#vdf_session.upper_bound;
_ ->
Session#vdf_session.next_upper_bound
end,
ar_events:send(nonce_limiter, {computed_output, {SessionKey, StepNumber, Output, UpperBound}}).

dump_error(Data) ->
{ok, Config} = application:get_env(arweave, config),
ErrorID = binary_to_list(ar_util:encode(crypto:strong_rand_bytes(8))),
Expand Down Expand Up @@ -876,11 +888,11 @@ verify_no_reset(StartStepNumber, PrevOutput, NumCheckpointsBetweenHashes, Hashes

worker() ->
receive
{compute, {StepNumber, PrevOutput, UpperBound, VDFDifficulty}, From} ->
{compute, {StepNumber, PrevOutput, VDFDifficulty}, From} ->
{ok, Output, Checkpoints} = prometheus_histogram:observe_duration(
vdf_step_time_milliseconds, [], fun() -> compute(StepNumber, PrevOutput,
VDFDifficulty) end),
From ! {computed, {StepNumber, PrevOutput, Output, UpperBound, Checkpoints}},
From ! {computed, {StepNumber, PrevOutput, Output, Checkpoints}},
worker();
stop ->
ok
Expand Down Expand Up @@ -918,25 +930,24 @@ schedule_step(State) ->
#state{ current_session_key = {NextSeed, IntervalNumber, NextVDFDifficulty} = Key,
worker = Worker } = State,
#vdf_session{ step_number = PrevStepNumber,
upper_bound = UpperBound, next_upper_bound = NextUpperBound,
vdf_difficulty = VDFDifficulty, next_vdf_difficulty = NextVDFDifficulty,
steps = Steps } = get_session(Key, State),
PrevOutput = hd(Steps),
StepNumber = PrevStepNumber + 1,
IntervalStart = IntervalNumber * ?NONCE_LIMITER_RESET_FREQUENCY,
PrevOutput2 = ar_nonce_limiter:maybe_add_entropy(
PrevOutput, IntervalStart, StepNumber, NextSeed),
{UpperBound2, VDFDifficulty2} =
VDFDifficulty2 =
case get_entropy_reset_point(IntervalStart, StepNumber) of
none ->
{UpperBound, VDFDifficulty};
VDFDifficulty;
_ ->
?LOG_DEBUG([{event, entropy_reset_point_found}, {step_number, StepNumber},
{interval_start, IntervalStart}, {vdf_difficulty, VDFDifficulty},
{next_vdf_difficulty, NextVDFDifficulty}]),
{NextUpperBound, NextVDFDifficulty}
NextVDFDifficulty
end,
Worker ! {compute, {StepNumber, PrevOutput2, UpperBound2, VDFDifficulty2}, self()},
Worker ! {compute, {StepNumber, PrevOutput2, VDFDifficulty2}, self()},
State.

get_or_init_nonce_limiter_info(#block{ height = Height } = B, Seed, PartitionUpperBound) ->
Expand All @@ -955,8 +966,8 @@ get_or_init_nonce_limiter_info(#block{ height = Height } = B, Seed, PartitionUpp
apply_external_update2(Update, State) ->
#state{ current_session_key = CurrentSessionKey, last_external_update = {Peer, _} } = State,
#nonce_limiter_update{ session_key = SessionKey,
session = #vdf_session{ upper_bound = UpperBound, prev_session_key = PrevSessionKey,
step_number = StepNumber } = Session,
session = #vdf_session{
prev_session_key = PrevSessionKey, step_number = StepNumber } = Session,
checkpoints = Checkpoints, is_partial = IsPartial } = Update,
{SessionSeed, SessionInterval, SessionVDFDifficulty} = SessionKey,
case get_session(SessionKey, State) of
Expand Down Expand Up @@ -991,8 +1002,7 @@ apply_external_update2(Update, State) ->
{_, Steps} = get_step_range(
Session, min(RangeStart, NextSessionStart), StepNumber),
State2 = apply_external_update3(State,
SessionKey, CurrentSessionKey,
Session, Steps, UpperBound),
SessionKey, CurrentSessionKey, Session, Steps),
{reply, ok, State2}
end;
CurrentSession ->
Expand All @@ -1002,8 +1012,7 @@ apply_external_update2(Update, State) ->
[Output | _] = Session#vdf_session.steps,
CurrentSession2 = update_session(CurrentSession, StepNumber, Checkpoints, [Output]),
State2 = apply_external_update3(State,
SessionKey, CurrentSessionKey,
CurrentSession2, [Output], UpperBound),
SessionKey, CurrentSessionKey, CurrentSession2, [Output]),
{reply, ok, State2};
false ->
case CurrentStepNumber >= StepNumber of
Expand Down Expand Up @@ -1048,8 +1057,7 @@ apply_external_update2(Update, State) ->
{_, Steps} = get_step_range(
Session, CurrentStepNumber + 1, StepNumber),
State2 = apply_external_update3(State,
SessionKey, CurrentSessionKey,
Session, Steps, UpperBound),
SessionKey, CurrentSessionKey, Session, Steps),
{reply, ok, State2}
end
end
Expand All @@ -1068,9 +1076,8 @@ apply_external_update2(Update, State) ->
%%
%% Note: an important job of this function is to ensure that VDF steps are only processed once.
%% We truncate Session.steps such the previously processed steps are not sent to
%% trigger_computed_outputs.
apply_external_update3(
State, SessionKey, CurrentSessionKey, Session, Steps, UpperBound) ->
%% send_events_for_external_update.
apply_external_update3(State, SessionKey, CurrentSessionKey, Session, Steps) ->
#state{ last_external_update = {Peer, _} } = State,
?LOG_DEBUG([{event, apply_external_vdf},
{result, ok},
Expand All @@ -1080,7 +1087,7 @@ apply_external_update3(
{session_difficulty, element(3, SessionKey)},
{length, length(Steps)}]),
State2 = cache_session(State, SessionKey, CurrentSessionKey, Session),
send_events_for_external_update(SessionKey, Session#vdf_session.step_number, UpperBound, Steps),
send_events_for_external_update(SessionKey, Session#vdf_session{ steps = Steps }),
State2.

%% @doc Returns a sub-range of steps out of a larger list of steps. This is
Expand Down Expand Up @@ -1187,11 +1194,13 @@ maybe_set_vdf_metrics(SessionKey, CurrentSessionKey, Session) ->
ok
end.

send_events_for_external_update(_SessionKey, _StepNumber, _UpperBound, []) ->
send_events_for_external_update(_SessionKey, #vdf_session{ steps = [] }) ->
ok;
send_events_for_external_update(SessionKey, StepNumber, UpperBound, [Step | Steps]) ->
ar_events:send(nonce_limiter, {computed_output, {SessionKey, StepNumber, Step, UpperBound}}),
send_events_for_external_update(SessionKey, StepNumber-1, UpperBound, Steps).
send_events_for_external_update(SessionKey, Session) ->
send_output(SessionKey, Session),
#vdf_session{ step_number = StepNumber, steps = [_ | RemainingSteps] } = Session,
send_events_for_external_update(SessionKey,
Session#vdf_session{ step_number = StepNumber-1, steps = RemainingSteps }).

debug_double_check(Label, Result, Func, Args) ->
{ok, Config} = application:get_env(arweave, config),
Expand Down
11 changes: 5 additions & 6 deletions apps/arweave/src/ar_nonce_limiter_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

-behaviour(gen_server).

-export([start_link/0, make_full_nonce_limiter_update/2, make_partial_nonce_limiter_update/5]).
-export([start_link/0, make_full_nonce_limiter_update/2, make_partial_nonce_limiter_update/4]).

-export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2]).

Expand All @@ -22,11 +22,11 @@
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

make_partial_nonce_limiter_update(SessionKey, Session, StepNumber, Output, PartitionUpperBound) ->
make_partial_nonce_limiter_update(SessionKey, Session, StepNumber, Output) ->
make_nonce_limiter_update(
SessionKey,
Session#vdf_session{
upper_bound = PartitionUpperBound, step_number = StepNumber, steps = [Output]
step_number = StepNumber, steps = [Output]
},
true).

Expand Down Expand Up @@ -69,13 +69,12 @@ handle_cast(Cast, State) ->
{noreply, State}.

handle_info({event, nonce_limiter, {computed_output, Args}}, State) ->
{SessionKey, StepNumber, Output, PartitionUpperBound} = Args,
{SessionKey, StepNumber, Output, _PartitionUpperBound} = Args,
Session = ar_nonce_limiter:get_session(SessionKey),
PrevSessionKey = Session#vdf_session.prev_session_key,
PrevSession = ar_nonce_limiter:get_session(PrevSessionKey),

PartialUpdate = make_partial_nonce_limiter_update(
SessionKey, Session, StepNumber, Output, PartitionUpperBound),
PartialUpdate = make_partial_nonce_limiter_update(SessionKey, Session, StepNumber, Output),
FullUpdate = make_full_nonce_limiter_update(SessionKey, Session),
FullPrevUpdate = make_full_nonce_limiter_update(PrevSessionKey, PrevSession),

Expand Down
13 changes: 5 additions & 8 deletions apps/arweave/src/ar_nonce_limiter_server_worker.erl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ handle_info({event, nonce_limiter, _Event}, #state{ peer = undefined } = State)
{noreply, State};
handle_info({event, nonce_limiter, {computed_output, Args}}, State) ->
#state{ peer = Peer, pause_until = Timestamp, format = Format } = State,
{SessionKey, StepNumber, Output, PartitionUpperBound} = Args,
{SessionKey, StepNumber, Output, _PartitionUpperBound} = Args,
CurrentStepNumber = ar_nonce_limiter:get_current_step_number(),
case os:system_time(second) < Timestamp of
true ->
Expand All @@ -73,8 +73,7 @@ handle_info({event, nonce_limiter, {computed_output, Args}}, State) ->
true ->
{noreply, State};
false ->
{noreply, push_update(SessionKey, StepNumber, Output,
PartitionUpperBound, Peer, Format, State)}
{noreply, push_update(SessionKey, StepNumber, Output, Peer, Format, State)}
end
end;

Expand All @@ -92,11 +91,10 @@ terminate(_Reason, _State) ->
%%% Private functions.
%%%===================================================================

push_update(SessionKey, StepNumber, Output,
PartitionUpperBound, Peer, Format, State) ->
push_update(SessionKey, StepNumber, Output, Peer, Format, State) ->
Session = ar_nonce_limiter:get_session(SessionKey),
Update = ar_nonce_limiter_server:make_partial_nonce_limiter_update(
SessionKey, Session, StepNumber, Output, PartitionUpperBound),
SessionKey, Session, StepNumber, Output),
case Update of
not_found -> State;
_ ->
Expand All @@ -120,8 +118,7 @@ push_update(SessionKey, StepNumber, Output,
?LOG_DEBUG([{event, vdf_client_requested_different_format},
{peer, ar_util:format_peer(Peer)},
{format, Format}, {requested_format, RequestedFormat}]),
push_update(SessionKey, StepNumber,
Output, PartitionUpperBound, Peer, RequestedFormat,
push_update(SessionKey, StepNumber, Output, Peer, RequestedFormat,
State#state{ format = RequestedFormat });
{true, false, _, _} ->
%% Client requested we pause updates
Expand Down
45 changes: 34 additions & 11 deletions apps/arweave/test/ar_vdf_server_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,11 @@ test_session_overlap() ->
timer:sleep(2000),
?assertEqual(
[<<"8">>, <<"7">>, <<"6">>, <<"5">>, <<"9">>, <<"10">>, <<"11">>, <<"12">>],
computed_steps()).
computed_steps()),
?assertEqual(
[10, 10, 10, 10, 10, 20, 20, 20],
computed_upper_bounds()).


%% @doc This test asserts that the client responds correctly when it is ahead of the VDF server.
test_client_ahead() ->
Expand All @@ -481,7 +485,10 @@ test_client_ahead() ->
timer:sleep(2000),
?assertEqual(
[<<"8">>, <<"7">>, <<"6">>, <<"5">>],
computed_steps()).
computed_steps()),
?assertEqual(
[10, 10, 10, 10],
computed_upper_bounds()).

%% @doc
%% Test case:
Expand Down Expand Up @@ -522,7 +529,10 @@ test_skip_ahead() ->
timer:sleep(2000),
?assertEqual(
[<<"6">>, <<"5">>, <<"8">>, <<"7">>, <<"9">>, <<"12">>, <<"11">>, <<"10">>],
computed_steps()).
computed_steps()),
?assertEqual(
[10, 10, 10, 10, 10, 20, 20, 20],
computed_upper_bounds()).

test_2_servers_switching() ->
SessionKey0 = {<<"session0">>, 0, 1},
Expand Down Expand Up @@ -569,7 +579,10 @@ test_2_servers_switching() ->
?assertEqual([
<<"7">>, <<"6">>, <<"5">>, <<"8">>, <<"9">>,
<<"11">>, <<"10">>, <<"12">>, <<"13">>, <<"14">>
], computed_steps()).
], computed_steps()),
?assertEqual(
[10, 10, 10, 10, 10, 20, 20, 20, 20, 20],
computed_upper_bounds()).

test_backtrack() ->
SessionKey0 = {<<"session0">>, 0, 1},
Expand Down Expand Up @@ -606,7 +619,10 @@ test_backtrack() ->
<<"17">>,<<"16">>,<<"15">>,<<"14">>,<<"13">>,<<"12">>,
<<"11">>,<<"10">>,<<"9">>,<<"8">>,<<"7">>,<<"6">>,
<<"5">>,<<"18">>,<<"15">>
], computed_steps()).
], computed_steps()),
?assertEqual(
[20, 20, 20, 20, 20, 20, 20, 20, 10, 10, 10, 10, 10, 20, 30],
computed_upper_bounds()).

test_2_servers_backtrack() ->
SessionKey0 = {<<"session0">>, 0, 1},
Expand Down Expand Up @@ -638,7 +654,10 @@ test_2_servers_backtrack() ->
<<"17">>,<<"16">>,<<"15">>,<<"14">>,<<"13">>,<<"12">>,
<<"11">>,<<"10">>,<<"9">>,<<"8">>,<<"7">>,<<"6">>,
<<"5">>,<<"18">>,<<"15">>
], computed_steps()).
], computed_steps()),
?assertEqual(
[20, 20, 20, 20, 20, 20, 20, 20, 10, 10, 10, 10, 10, 20, 30],
computed_upper_bounds()).

%%
%% serialize_test_
Expand Down Expand Up @@ -819,25 +838,29 @@ vdf_server_2() ->
{127,0,0,1,2002}.

computed_steps() ->
lists:reverse(ets:foldl(fun({_, Int}, Acc) -> [Int | Acc] end, [], ?MODULE)).
lists:reverse(ets:foldl(fun({_, Step, _}, Acc) -> [Step | Acc] end, [], ?MODULE)).

computed_upper_bounds() ->
lists:reverse(ets:foldl(fun({_, _, UpperBound}, Acc) -> [UpperBound | Acc] end, [], ?MODULE)).

computed_output() ->
receive
{event, nonce_limiter, {computed_output, Args}} ->
{_SessionKey, _StepNumber, Output, _UpperBound} = Args,
{_SessionKey, _StepNumber, Output, UpperBound} = Args,
Key = ets:info(?MODULE, size) + 1, % Unique key based on current size, ensures ordering
ets:insert(?MODULE, {Key, Output}),
ets:insert(?MODULE, {Key, Output, UpperBound}),
computed_output()
end.

apply_external_update(SessionKey, ExistingSteps, StepNumber, IsPartial, PrevSessionKey) ->
apply_external_update(SessionKey, ExistingSteps, StepNumber, IsPartial, PrevSessionKey,
vdf_server_1()).
apply_external_update(SessionKey, ExistingSteps, StepNumber, IsPartial, PrevSessionKey, Peer) ->
{Seed, _Interval, _Difficulty} = SessionKey,
{Seed, Interval, _Difficulty} = SessionKey,
Steps = [list_to_binary(integer_to_list(Step)) || Step <- [StepNumber | ExistingSteps]],
Session = #vdf_session{
upper_bound = 0,
upper_bound = Interval * 10,
next_upper_bound = (Interval+1) * 10,
prev_session_key = PrevSessionKey,
step_number = StepNumber,
seed = Seed,
Expand Down

0 comments on commit 511f1d2

Please sign in to comment.