Skip to content
This repository has been archived by the owner on Feb 24, 2025. It is now read-only.

Commit

Permalink
Obasilakis/voter rewards api uses voter votes table (#589)
Browse files Browse the repository at this point in the history
* Add celo_voter_votes table

* Add voters_activated_votes_in_last_epoch function

* Add last_non_zero_voter_votes function and format

* Add voter votes to celo_pending_epoch_operations

* Add celo_voter_votes runner and fetcher

* Buffer blocks to fetch epoch rewards and voter votes for

* Add CeloVoterVotes fetcher to supervision tree

* Fix query in voters_activated_votes_in_last_epoch

* Pass array of blocks instead of single blocks

* Add hash and number to account/group pairs in CeloVoterVotes fetcher pipeline

* Lower max batch size

* Remove IO.inspects

* Make sure pending operations are falsified after votes are fetched

* Remove duplicates before fetching votes from archive node

* Reduce max_batch_size to 1 for voter votes fetcher

Also dialyze

* Make sure epoch blocks are ordered when buffered into voter votes fetcher

* Format, credo, dialyze and remove deprecated test

* Fix block fetcher tests

* Set max concurrency to 1

* Change poll_interval to 60 min for voter votes fetcher

* Stop buffering epoch blocks in the block fetcher

The block runner takes care of that

* Add PR to CHANGELOG.md

* Keep large integers consistent

* Set max concurrency and max batch size back to default

The new way of getting account/group pairs doesn't require sequential processing

* Retry unique blocks

* Get all account/group pairs with activated votes instead of just last epoch

* Remove leftover tag from test

* Avoid homonymous multis

* Set default celo_pending_epoch_operations to false

* Format and dialyze

* Add merge_events_with_votes_and_chunk_by_epoch function

* Use celo_voter_votes to calculate voter rewards for one group

* Simplify variable names

* Fix test

* Lower max_batch_size

* Fetch and save vote only for accounts that have activated votes

* Credo

* Use event block_number since block_hash doesn't exist anymore

* Format

* Format

* Update controller test
  • Loading branch information
obasilakis authored Mar 21, 2022
1 parent 3393ebf commit 2796401
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 416 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,63 +94,56 @@ defmodule BlockScoutWeb.API.RPC.RewardControllerTest do
end

test "with valid voter and group address", %{conn: conn} do
{voter_address_1_hash, group_address_hash} = SetupVoterRewardsTest.setup_for_group()
{
voter_hash,
group_hash,
block_2_hash,
block_3_hash,
block_5_hash,
block_7_hash
} = SetupVoterRewardsTest.setup_for_group()

expected_result = %{
"rewards" => [
%{
"amount" => "80",
"blockHash" => "0x0100000000000000000000000000000000000000000000000000000000000001",
"blockHash" => to_string(block_2_hash),
"blockNumber" => "10696320",
"date" => "2022-01-01T17:42:43.162804Z",
"epochNumber" => "619"
},
%{
"amount" => "20",
"blockHash" => "0x0100000000000000000000000000000000000000000000000000000000000002",
"blockHash" => to_string(block_3_hash),
"blockNumber" => "10713600",
"date" => "2022-01-02T17:42:43.162804Z",
"epochNumber" => "620"
},
%{
"amount" => "75",
"blockHash" => "0x0100000000000000000000000000000000000000000000000000000000000003",
"blockHash" => to_string(block_5_hash),
"blockNumber" => "10730880",
"date" => "2022-01-03T17:42:43.162804Z",
"epochNumber" => "621"
},
%{
"amount" => "31",
"blockHash" => "0x0100000000000000000000000000000000000000000000000000000000000004",
"amount" => "0",
"blockHash" => to_string(block_7_hash),
"blockNumber" => "10748160",
"date" => "2022-01-04T17:42:43.162804Z",
"epochNumber" => "622"
},
%{
"amount" => "77",
"blockHash" => "0x0100000000000000000000000000000000000000000000000000000000000005",
"blockNumber" => "10765440",
"date" => "2022-01-05T17:42:43.162804Z",
"epochNumber" => "623"
},
%{
"amount" => "67",
"blockHash" => "0x0100000000000000000000000000000000000000000000000000000000000006",
"blockNumber" => "10782720",
"date" => "2022-01-06T17:42:43.162804Z",
"epochNumber" => "624"
}
],
"total" => "350"
"total" => "175"
}

response =
conn
|> get("/api", %{
"module" => "reward",
"action" => "getvoterrewardsforgroup",
"voterAddress" => to_string(voter_address_1_hash),
"groupAddress" => to_string(group_address_hash)
"voterAddress" => to_string(voter_hash),
"groupAddress" => to_string(group_hash)
})
|> json_response(200)

Expand Down
122 changes: 67 additions & 55 deletions apps/explorer/lib/explorer/celo/voter_rewards_for_group.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,20 @@ defmodule Explorer.Celo.VoterRewardsForGroup do
]

alias Explorer.Celo.{ContractEvents, Util}
alias Explorer.Chain.{Block, CeloContractEvent, CeloValidatorGroupVotes, Wei}
alias Explorer.Chain.{Block, CeloContractEvent, CeloVoterVotes, Wei}
alias Explorer.Repo

alias ContractEvents.Election

alias Election.{
EpochRewardsDistributedToVotersEvent,
ValidatorGroupActiveVoteRevokedEvent,
ValidatorGroupVoteActivatedEvent
}

@validator_group_vote_activated ValidatorGroupVoteActivatedEvent.topic()
@validator_group_active_vote_revoked ValidatorGroupActiveVoteRevokedEvent.topic()

def calculate(voter_address_hash, group_address_hash, to_date \\ DateTime.utc_now()) do
validator_group_active_vote_revoked = ValidatorGroupActiveVoteRevokedEvent.topic()
epoch_rewards_distributed_to_voters = EpochRewardsDistributedToVotersEvent.topic()

query =
from(event in CeloContractEvent,
select: %{
Expand All @@ -35,87 +32,102 @@ defmodule Explorer.Celo.VoterRewardsForGroup do
},
order_by: [asc: event.block_number],
where:
event.topic == ^validator_group_active_vote_revoked or
event.topic == ^@validator_group_active_vote_revoked or
event.topic == ^@validator_group_vote_activated
)

ordered_activated_or_revoked_events_for_voter_for_group =
voter_activated_or_revoked_votes_for_group_events =
query
|> CeloContractEvent.query_by_voter_param(voter_address_hash)
|> CeloContractEvent.query_by_group_param(group_address_hash)
|> Repo.all()

case ordered_activated_or_revoked_events_for_voter_for_group do
case voter_activated_or_revoked_votes_for_group_events do
[] ->
{:error, :not_found}

voter_activated_or_revoked ->
[voter_activated_earliest_block | _] = voter_activated_or_revoked

query =
from(event in CeloContractEvent,
from(votes in CeloVoterVotes,
inner_join: block in Block,
on: event.block_number == block.number,
inner_join: votes in CeloValidatorGroupVotes,
on: votes.block_hash == block.hash,
select: %{
block_hash: block.hash,
block_number: event.block_number,
block_hash: votes.block_hash,
block_number: votes.block_number,
date: block.timestamp,
epoch_reward: json_extract_path(event.params, ["value"]),
event: event.topic,
previous_block_group_votes: votes.previous_block_active_votes
votes: votes.active_votes
},
where: block.number >= ^voter_activated_earliest_block.block_number,
where: event.topic == ^epoch_rewards_distributed_to_voters,
where: votes.account_hash == ^voter_address_hash,
where: votes.group_hash == ^group_address_hash,
where: votes.block_number >= ^voter_activated_earliest_block.block_number,
where: block.timestamp < ^to_date
)

epoch_rewards_distributed_events_after_voter_first_activated_votes =
voter_votes_for_group =
query
|> CeloContractEvent.query_by_group_param(group_address_hash)
|> Repo.all()

{rewards, total} =
Enum.map_reduce(
epoch_rewards_distributed_events_after_voter_first_activated_votes,
0,
fn curr, amount ->
amount_activated_or_revoked =
amount_activated_or_revoked_last_day(voter_activated_or_revoked, curr.block_number)

amount = amount + amount_activated_or_revoked

{:ok, previous_block_group_votes_decimal} = Wei.dump(curr.previous_block_group_votes)

current_amount = div(curr.epoch_reward * amount, Decimal.to_integer(previous_block_group_votes_decimal))

{
%{
amount: current_amount,
block_hash: curr.block_hash,
block_number: curr.block_number,
date: curr.date,
epoch_number: Util.epoch_by_block_number(curr.block_number)
},
amount + current_amount
}
end
events_and_votes_chunked_by_epoch =
merge_events_with_votes_and_chunk_by_epoch(
voter_activated_or_revoked_votes_for_group_events,
voter_votes_for_group
)

{:ok, %{rewards: rewards, total: total, group: group_address_hash}}
{rewards, {rewards_sum, _}} =
Enum.map_reduce(events_and_votes_chunked_by_epoch, {0, 0}, fn epoch, {rewards_sum, previous_epoch_votes} ->
epoch_reward = calculate_single_epoch_reward(epoch, previous_epoch_votes)

current_epoch_votes = epoch |> Enum.reverse() |> hd()
%Wei{value: current_votes} = current_epoch_votes.votes
current_votes_integer = Decimal.to_integer(current_votes)

{
%{
amount: epoch_reward,
block_hash: current_epoch_votes.block_hash,
block_number: current_epoch_votes.block_number,
date: current_epoch_votes.date,
epoch_number: Util.epoch_by_block_number(current_epoch_votes.block_number)
},
{epoch_reward + rewards_sum, current_votes_integer}
}
end)

{:ok, %{rewards: rewards, total: rewards_sum, group: group_address_hash}}
end
end

def amount_activated_or_revoked_last_day(voter_activated_or_revoked, block_number) do
voter_activated_or_revoked
|> Enum.filter(&(&1.block_number < block_number && &1.block_number >= block_number - 17_280))
|> Enum.reduce(0, fn x, acc ->
if x.event == @validator_group_vote_activated do
acc + x.amount_activated_or_revoked
else
acc - x.amount_activated_or_revoked
end
def calculate_single_epoch_reward(epoch, previous_epoch_votes) do
Enum.reduce(epoch, -previous_epoch_votes, fn
%{votes: %Wei{value: votes}}, acc ->
acc + Decimal.to_integer(votes)

%{amount_activated_or_revoked: amount, event: @validator_group_vote_activated}, acc ->
acc - amount

%{amount_activated_or_revoked: amount, event: @validator_group_active_vote_revoked}, acc ->
acc + amount
end)
end

def merge_events_with_votes_and_chunk_by_epoch(events, votes) do
chunk_fun = fn
%{votes: _} = element, acc ->
{:cont, Enum.reverse([element | acc]), []}

element, acc ->
{:cont, [element | acc]}
end

after_fun = fn
[] -> {:cont, []}
acc -> {:cont, Enum.reverse(acc), []}
end

(events ++ votes)
|> Enum.sort_by(& &1.block_number)
|> Enum.chunk_while([], chunk_fun, after_fun)
end
end
Loading

0 comments on commit 2796401

Please sign in to comment.