From 17f13803ced96f5afe627488d29d831f85b12ab4 Mon Sep 17 00:00:00 2001 From: Marc Nijdam Date: Thu, 6 Jan 2022 07:20:08 -0700 Subject: [PATCH] Add /v1/accounts|hotspots/rewards/:block --- priv/rewards.sql | 12 +++++-- src/bh_route_accounts.erl | 12 +++++++ src/bh_route_hotspots.erl | 12 +++++++ src/bh_route_rewards.erl | 59 +++++++++++++++++++++++++++++--- test/bh_route_accounts_SUITE.erl | 24 +++++++++++++ test/bh_route_hotspots_SUITE.erl | 13 +++++++ 6 files changed, 125 insertions(+), 7 deletions(-) diff --git a/priv/rewards.sql b/priv/rewards.sql index a24e775e..101d7882 100644 --- a/priv/rewards.sql +++ b/priv/rewards.sql @@ -1,10 +1,10 @@ -- :reward_fields -r.block, r.transaction_hash, to_timestamp(r.time) as timestamp, r.account, r.gateway, r.amount +r.block, r.transaction_hash, to_timestamp(r.time) as timestamp, r.account, r.gateway, r.amount, r.type -- Make sure that marker fields and fields are equivalent except for the marker -- placeholder! -- :reward_marker_fields -r.block, r.transaction_hash, to_timestamp(r.time) as timestamp, r.account, r.gateway, r.amount, :marker +r.block, r.transaction_hash, to_timestamp(r.time) as timestamp, r.account, r.gateway, r.amount, r.type, :marker -- :reward_list_base select :fields @@ -20,6 +20,14 @@ from rewards r and r.block = $2 and :marker> $3 order by :marker +-- :reward_block_list_base +select :fields +from rewards r +:scope +and r.block = $2 +order by r.gateway, r.type +offset $3 fetch next $4 rows only + -- :reward_sum_hotspot_source (select sum(r.amount) as amount diff --git a/src/bh_route_accounts.erl b/src/bh_route_accounts.erl index a528bcb8..2f101ad8 100644 --- a/src/bh_route_accounts.erl +++ b/src/bh_route_accounts.erl @@ -147,6 +147,18 @@ handle('GET', [Account, <<"rewards">>], Req) -> handle('GET', [Account, <<"rewards">>, <<"sum">>], Req) -> Args = ?GET_ARGS([max_time, min_time, bucket], Req), ?MK_RESPONSE(bh_route_rewards:get_reward_sum({account, Account}, Args), block_time); +handle('GET', [Account, <<"rewards">>, Block], Req) -> + Args = ?GET_ARGS([cursor], Req), + bh_route_handler:try_or_else( + fun() -> binary_to_integer(Block) end, + fun(Height) -> + ?MK_RESPONSE( + bh_route_rewards:get_reward_list({account, Account}, Args ++ [{block, Height}]), + infinity + ) + end, + ?RESPONSE_400 + ); handle('GET', [Account, <<"pending_transactions">>], Req) -> Args = ?GET_ARGS([cursor], Req), ?MK_RESPONSE(bh_route_pending_txns:get_pending_txn_list({actor, Account}, Args), never); diff --git a/src/bh_route_hotspots.erl b/src/bh_route_hotspots.erl index 2496adab..ab74488b 100644 --- a/src/bh_route_hotspots.erl +++ b/src/bh_route_hotspots.erl @@ -324,6 +324,18 @@ handle('GET', [Address, <<"rewards">>], Req) -> handle('GET', [Address, <<"rewards">>, <<"sum">>], Req) -> Args = ?GET_ARGS([max_time, min_time, bucket], Req), ?MK_RESPONSE(bh_route_rewards:get_reward_sum({hotspot, Address}, Args), block_time); +handle('GET', [Address, <<"rewards">>, Block], Req) -> + Args = ?GET_ARGS([cursor], Req), + bh_route_handler:try_or_else( + fun() -> binary_to_integer(Block) end, + fun(Height) -> + ?MK_RESPONSE( + bh_route_rewards:get_reward_list({hotspot, Address}, Args ++ [{block, Height}]), + infinity + ) + end, + ?RESPONSE_400 + ); handle('GET', [<<"rewards">>, <<"sum">>], Req) -> %% We do not allow bucketing across all hotspots as that takes way too long Args = ?GET_ARGS([max_time, min_time], Req), diff --git a/src/bh_route_rewards.erl b/src/bh_route_rewards.erl index 15b7f6b7..e33245a8 100644 --- a/src/bh_route_rewards.erl +++ b/src/bh_route_rewards.erl @@ -17,8 +17,10 @@ -define(REWARD_LIST_LIMIT, 100). -define(S_REWARD_LIST_HOTSPOT, "reward_list_hotspot"). -define(S_REWARD_LIST_HOTSPOT_REM, "reward_list_hotspot_rem"). +-define(S_REWARD_LIST_HOTSPOT_BLOCK, "reward_list_hotspot_block"). -define(S_REWARD_LIST_ACCOUNT, "reward_list_account"). -define(S_REWARD_LIST_ACCOUNT_REM, "reward_list_account_rem"). +-define(S_REWARD_LIST_ACCOUNT_BLOCK, "reward_list_account_block"). -define(S_REWARD_SUM_HOTSPOT, "reward_sum_hotstpot"). -define(S_REWARD_SUM_HOTSPOTS, "reward_sum_hotspots"). -define(S_REWARD_SUM_VALIDATOR, "reward_sum_validator"). @@ -52,6 +54,13 @@ prepare_conn(_Conn) -> {marker, "r.transaction_hash"} ], [text, int8, text]}}, + {?S_REWARD_LIST_HOTSPOT_BLOCK, + {reward_block_list_base, + [ + {fields, reward_fields}, + {scope, "where r.gateway = $1"} + ], + [text, int8, int8, int8]}}, {?S_REWARD_LIST_ACCOUNT, {reward_list_base, [ @@ -71,6 +80,13 @@ prepare_conn(_Conn) -> {marker, "r.gateway"} ], [text, int8, text]}}, + {?S_REWARD_LIST_ACCOUNT_BLOCK, + {reward_block_list_base, + [ + {fields, reward_fields}, + {scope, "where r.account = $1"} + ], + [text, int8, int8, int8]}}, {?S_REWARD_SUM_HOTSPOT, {reward_sum_base, @@ -199,10 +215,14 @@ get_full_reward_list(Args, Query, [{max_time, MaxTime}, {min_time, MinTime}]) -> get_reward_list({hotspot, Address}, Args = [{cursor, _}, {max_time, _}, {min_time, _}]) -> get_reward_list([Address], {?S_REWARD_LIST_HOTSPOT, ?S_REWARD_LIST_HOTSPOT_REM}, Args); +get_reward_list({hotspot, Address}, Args = [{cursor, _}, {block, _}]) -> + get_reward_list([Address], ?S_REWARD_LIST_HOTSPOT_BLOCK, Args); get_reward_list({validator, Address}, Args = [{cursor, _}, {max_time, _}, {min_time, _}]) -> get_reward_list([Address], {?S_REWARD_LIST_HOTSPOT, ?S_REWARD_LIST_HOTSPOT_REM}, Args); get_reward_list({account, Address}, Args = [{cursor, _}, {max_time, _}, {min_time, _}]) -> - get_reward_list([Address], {?S_REWARD_LIST_ACCOUNT, ?S_REWARD_LIST_ACCOUNT_REM}, Args). + get_reward_list([Address], {?S_REWARD_LIST_ACCOUNT, ?S_REWARD_LIST_ACCOUNT_REM}, Args); +get_reward_list({account, Address}, Args = [{cursor, _}, {block, _}]) -> + get_reward_list([Address], ?S_REWARD_LIST_ACCOUNT_BLOCK, Args). %% network get_reward_sum( @@ -318,6 +338,23 @@ calc_low_block(HighBlock, EndBlock) -> max(EndBlock, Other) end. +get_reward_list( + Args, + Query, + [{cursor, Cursor}, {block, Height}] +) -> + Offset = + case Cursor of + undefined -> + 0; + C -> + case ?CURSOR_DECODE(C) of + {ok, #{<<"offset">> := V}} -> V; + _ -> throw(?RESPONSE_400) + end + end, + Result = ?PREPARED_QUERY(Query, Args ++ [Height, Offset, ?REWARD_LIST_LIMIT + 1]), + mk_reward_block_list_result(Offset, Result); get_reward_list( Args, {Query, _RemQuery}, @@ -401,11 +438,20 @@ get_reward_sum_cache_time([{max_time, _MaxTime}, {min_time, _MinTime}, {bucket, % But if min/max are specified, cache as long as possible infinity. +mk_reward_block_list_result(Offset, {ok, _, Results}) when length(Results) > ?REWARD_LIST_LIMIT -> + {Trimmed, _Remainder} = lists:split(?REWARD_LIST_LIMIT, Results), + {ok, reward_list_to_json(Trimmed), mk_reward_block_list_cursor(Offset + length(Trimmed))}; +mk_reward_block_list_result(_Offset, {ok, _, Results}) -> + {ok, reward_list_to_json(Results)}. + +mk_reward_block_list_cursor(Offset) -> + #{offset => Offset}. + mk_reward_list_result(State = #state{results = Results}) when length(Results) > ?REWARD_LIST_LIMIT -> {Trimmed, _Remainder} = lists:split(?REWARD_LIST_LIMIT, Results), - {Block, _Hash, _Timestamp, _Account, _Gateway, _Amount, Marker} = lists:last(Trimmed), + {Block, _Hash, _Timestamp, _Account, _Gateway, _Amount, _Type, Marker} = lists:last(Trimmed), {ok, reward_list_to_json(Trimmed), mk_reward_list_cursor(Block, Marker, State)}; mk_reward_list_result(State = #state{results = Results}) -> {ok, reward_list_to_json(Results), @@ -460,15 +506,18 @@ mk_reward_list_cursor(BeforeBlock, Marker, State = #state{}) -> reward_list_to_json(Results) -> lists:map(fun reward_to_json/1, Results). -reward_to_json({Block, Hash, Timestamp, Account, Gateway, Amount, _Marker}) -> +reward_to_json({Block, Hash, Timestamp, Account, Gateway, Amount, Type}) -> #{ <<"block">> => Block, <<"hash">> => Hash, <<"timestamp">> => iso8601:format(Timestamp), <<"account">> => Account, <<"gateway">> => Gateway, - <<"amount">> => Amount - }. + <<"amount">> => Amount, + <<"type">> => Type + }; +reward_to_json({Block, Hash, Timestamp, Account, Gateway, Amount, Type, _Marker}) -> + reward_to_json({Block, Hash, Timestamp, Account, Gateway, Amount, Type}). reward_buckets_to_json(Results) -> lists:map(fun reward_stat_to_json/1, Results). diff --git a/test/bh_route_accounts_SUITE.erl b/test/bh_route_accounts_SUITE.erl index 56e60b0f..d68e4e8b 100644 --- a/test/bh_route_accounts_SUITE.erl +++ b/test/bh_route_accounts_SUITE.erl @@ -25,6 +25,7 @@ all() -> rewards_dupe_test, rewards_sum_test, rewards_buckets_test, + rewards_block_test, rich_list_test ]. @@ -261,6 +262,29 @@ rewards_test(_Config) -> ok. +rewards_block_test(_Config) -> + Account = "13ESLoXiie3eXoyitxryNQNamGAnJjKt2WkiB4gNq95knxAiGEp", + {ok, {_, _, Json}} = + ?json_request([ + "/v1/accounts/", + Account, + "/rewards/1167207" + ]), + #{<<"data">> := Data, <<"cursor">> := Cursor} = Json, + ?assert(length(Data) >= 0), + + {ok, {_, _, CursorJson}} = + ?json_request([ + "/v1/accounts/", + Account, + "/rewards/1167207?cursor=", + Cursor + ]), + #{<<"data">> := CursorData} = CursorJson, + ?assert(length(CursorData) >= 0), + + ok. + rewards_dupe_test(_Config) -> % This account and time range was reported to have a duplicate between the % two pages that build it up. This test ensures that the fetched diff --git a/test/bh_route_hotspots_SUITE.erl b/test/bh_route_hotspots_SUITE.erl index 0c43ca80..b31af6d1 100644 --- a/test/bh_route_hotspots_SUITE.erl +++ b/test/bh_route_hotspots_SUITE.erl @@ -28,6 +28,7 @@ all() -> rewards_all_sum_test, rewards_sum_test, rewards_buckets_test, + rewards_block_test, witnesses_test, witnessed_test, witnesses_buckets_test, @@ -300,6 +301,18 @@ rewards_test(_Config) -> end, ok. +rewards_block_test(_Config) -> + Hotspot = "112WaLcxSnEQTSTiyB46ey4WVr9SUiYcCjrFRXYcv1mp9bdobDxR", + {ok, {_, _, Json}} = + ?json_request([ + "/v1/hotspots/", + Hotspot, + "/rewards/1167207" + ]), + #{<<"data">> := Data} = Json, + ?assert(length(Data) >= 0), + ok. + rewards_all_sum_test(_Config) -> {ok, {_, _, Json}} = ?json_request([