Skip to content

Commit

Permalink
Merge branch 'fix/OCT-1128-fix-ws-connection-missing-snapshot-excepti…
Browse files Browse the repository at this point in the history
…on' into 'develop'

OCT-1128: fix ws connection missing snapshot exception

See merge request golemfoundation/governance/octant!927
  • Loading branch information
Piotr Żelazko committed Nov 10, 2023
2 parents 6f9af46 + 0799fa2 commit af91d90
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 27 deletions.
6 changes: 3 additions & 3 deletions backend/app/contracts/epochs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict
from typing import Dict, Optional

from flask import current_app as app
from web3 import exceptions
Expand All @@ -24,14 +24,14 @@ def get_current_epoch(self) -> int:
# HN:Epochs/not-started-yet
return 0

def get_pending_epoch(self) -> int:
def get_pending_epoch(self) -> Optional[int]:
try:
app.logger.debug("[Epochs contract] Getting pending epoch")
return self.contract.functions.getPendingEpoch().call()
except exceptions.ContractLogicError:
app.logger.warning("[Epochs contract] No pending epoch")
# HN:Epochs/not-pending
return 0
return None

def get_finalized_epoch(self) -> int:
try:
Expand Down
21 changes: 14 additions & 7 deletions backend/app/controllers/rewards.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from dataclasses import dataclass
from decimal import Decimal
from functools import reduce
from typing import List, Tuple
from typing import List, Tuple, Optional

from dataclass_wizard import JSONWizard

Expand Down Expand Up @@ -43,18 +43,25 @@ class RewardsMerkleTree(JSONWizard):

def get_allocation_threshold(epoch: int = None) -> int:
epoch = epochs.get_pending_epoch() if epoch is None else epoch

if epoch is None:
raise exceptions.NotInDecisionWindow

return proposals.get_proposal_allocation_threshold(epoch)


def get_estimated_proposals_rewards() -> List[ProposalReward]:
epoch = epochs.get_pending_epoch()
def get_estimated_proposals_rewards() -> Optional[List[ProposalReward]]:
pending_epoch = epochs.get_pending_epoch()

if pending_epoch is None:
raise exceptions.NotInDecisionWindow

if not has_pending_epoch_snapshot(epoch):
if not has_pending_epoch_snapshot(pending_epoch):
raise exceptions.MissingSnapshot

matched_rewards = get_estimated_matched_rewards()
proposals_with_allocations = get_proposals_with_allocations(epoch)
threshold = get_allocation_threshold(epoch)
proposals_with_allocations = get_proposals_with_allocations(pending_epoch)
threshold = get_allocation_threshold(pending_epoch)

total_allocated_above_threshold = sum(
[
Expand All @@ -66,7 +73,7 @@ def get_estimated_proposals_rewards() -> List[ProposalReward]:

rewards = {
address: ProposalReward(address, 0, 0)
for address in proposals.get_proposals_addresses(epoch)
for address in proposals.get_proposals_addresses(pending_epoch)
}

for address, allocated in proposals_with_allocations:
Expand Down
2 changes: 1 addition & 1 deletion backend/app/controllers/withdrawals.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def get_withdrawable_eth(address: str) -> List[WithdrawableEth]:

withdrawable_eth = []

if pending_epoch > 0:
if pending_epoch is not None:
pending_rewards = get_user_claimed_rewards(address, pending_epoch)
withdrawable_eth.append(
WithdrawableEth(
Expand Down
8 changes: 5 additions & 3 deletions backend/app/core/allocations.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import List, Dict, Tuple
from typing import List, Dict, Tuple, Optional

from dataclass_wizard import JSONWizard
from eth_utils import to_checksum_address
Expand Down Expand Up @@ -65,8 +65,10 @@ def deserialize_payload(payload) -> Tuple[int, List[Allocation]]:
return payload["nonce"], allocations


def verify_allocations(epoch: int, user_address: str, allocations: List[Allocation]):
if epoch == 0:
def verify_allocations(
epoch: Optional[int], user_address: str, allocations: List[Allocation]
):
if epoch is None:
raise exceptions.NotInDecisionWindow

if not has_pending_epoch_snapshot(epoch):
Expand Down
13 changes: 8 additions & 5 deletions backend/app/infrastructure/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@
from app.core.allocations import AllocationRequest
from app.core.common import AccountFunds
from app.exceptions import OctantException
from app.extensions import socketio
from app.extensions import socketio, epochs
from app.infrastructure.exception_handler import UNEXPECTED_EXCEPTION, ExceptionHandler


@socketio.on("connect")
def handle_connect():
app.logger.debug("Client connected")
threshold = get_allocation_threshold()
emit("threshold", {"threshold": str(threshold)})
proposal_rewards = get_estimated_proposals_rewards()
emit("proposal_rewards", _serialize_proposal_rewards(proposal_rewards))

if epochs.get_pending_epoch() is not None:
threshold = get_allocation_threshold()
emit("threshold", {"threshold": str(threshold)})

proposal_rewards = get_estimated_proposals_rewards()
emit("proposal_rewards", _serialize_proposal_rewards(proposal_rewards))


@socketio.on("disconnect")
Expand Down
Empty file.
2 changes: 1 addition & 1 deletion backend/tests/test_allocations.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ def test_allocation_validation_errors(proposal_accounts, user_accounts, tos_user
]

# Set invalid epoch on purpose (mimicking no pending epoch)
MOCK_EPOCHS.get_pending_epoch.return_value = 0
MOCK_EPOCHS.get_pending_epoch.return_value = None

# Call allocate method, expect exception
with pytest.raises(exceptions.NotInDecisionWindow):
Expand Down
17 changes: 16 additions & 1 deletion backend/tests/test_rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
calculate_all_individual_rewards,
calculate_matched_rewards_threshold,
)
from app import database
from app import database, exceptions
from app.database.user import toggle_patron_mode
from app.extensions import db
from .conftest import (
Expand All @@ -29,6 +29,7 @@
MOCK_PROPOSALS,
USER2_BUDGET,
USER3_BUDGET,
MOCK_EPOCHS,
)
from .test_allocations import (
sign,
Expand Down Expand Up @@ -122,6 +123,13 @@ def test_get_allocation_threshold(app, tos_users, proposal_accounts):
)


def test_get_allocation_threshold_raises_when_not_in_allocation_period(app):
MOCK_EPOCHS.get_pending_epoch.return_value = None

with pytest.raises(exceptions.NotInDecisionWindow):
get_allocation_threshold(None)


@pytest.mark.parametrize(
# The structure of these parameters is as follows
#
Expand Down Expand Up @@ -218,6 +226,13 @@ def test_estimated_proposal_rewards_when_allocation_has_0_value(
assert proposal.matched == 0


def test_estimated_proposal_rewards_raises_when_not_in_allocation_period(app):
MOCK_EPOCHS.get_pending_epoch.return_value = None

with pytest.raises(exceptions.NotInDecisionWindow):
get_estimated_proposals_rewards()


def test_proposals_rewards_with_patron(
app, mock_pending_epoch_snapshot_db, tos_users, proposal_accounts
):
Expand Down
12 changes: 6 additions & 6 deletions backend/tests/test_withdrawals.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def before(mocker, app, graphql_client, patch_vault, patch_epochs):
],
)
def test_get_withdrawable_eth(user_accounts, expected_rewards):
MOCK_EPOCHS.get_pending_epoch.return_value = 0
MOCK_EPOCHS.get_pending_epoch.return_value = None

# Populate db
for user_index, rewards in expected_rewards.items():
Expand All @@ -198,7 +198,7 @@ def test_get_withdrawable_eth(user_accounts, expected_rewards):


def test_get_withdrawable_eth_returns_only_user_not_claimed_rewards(user_accounts):
MOCK_EPOCHS.get_pending_epoch.return_value = 0
MOCK_EPOCHS.get_pending_epoch.return_value = None
MOCK_VAULT.get_last_claimed_epoch.return_value = 2

database.rewards.add_user_reward(1, user_accounts[0].address, 100_000000000)
Expand All @@ -219,7 +219,7 @@ def test_get_withdrawable_eth_returns_only_user_not_claimed_rewards(user_account
def test_get_withdrawable_eth_returns_only_proposal_not_claimed_rewards(
proposal_accounts,
):
MOCK_EPOCHS.get_pending_epoch.return_value = 0
MOCK_EPOCHS.get_pending_epoch.return_value = None
MOCK_VAULT.get_last_claimed_epoch.return_value = 2

database.rewards.add_proposal_reward(
Expand All @@ -246,7 +246,7 @@ def test_get_withdrawable_eth_returns_only_proposal_not_claimed_rewards(


def test_get_withdrawable_eth_result_sorted_by_epochs(user_accounts):
MOCK_EPOCHS.get_pending_epoch.return_value = 0
MOCK_EPOCHS.get_pending_epoch.return_value = None

database.rewards.add_user_reward(2, user_accounts[0].address, 200_000000000)
database.rewards.add_user_reward(4, user_accounts[0].address, 400_000000000)
Expand Down Expand Up @@ -339,7 +339,7 @@ def test_get_withdrawable_eth_in_pending_epoch_in_epoch_3(
def test_get_withdrawable_eth_in_finalized_epoch_when_merkle_root_not_set_yet_epoch_1(
mocker, user_accounts, proposal_accounts, patch_user_budget
):
MOCK_EPOCHS.get_pending_epoch.return_value = 0
MOCK_EPOCHS.get_pending_epoch.return_value = None
mock_graphql(mocker, merkle_roots_events=[])

database.rewards.add_user_reward(1, user_accounts[0].address, 100_000000000)
Expand All @@ -360,7 +360,7 @@ def test_get_withdrawable_eth_in_finalized_epoch_when_merkle_root_not_set_yet_ep
def test_get_withdrawable_eth_in_finalized_epoch_when_merkle_root_not_set_yet_epoch_2(
mocker, user_accounts, proposal_accounts, patch_user_budget
):
MOCK_EPOCHS.get_pending_epoch.return_value = 0
MOCK_EPOCHS.get_pending_epoch.return_value = None
mock_graphql(mocker, merkle_roots_events=[{"epoch": 1}])

database.rewards.add_user_reward(1, user_accounts[0].address, 100_000000000)
Expand Down

0 comments on commit af91d90

Please sign in to comment.