diff --git a/api/account/deduplication/__init__.py b/api/account/deduplication/__init__.py index 8cd025dcb..aed593d86 100644 --- a/api/account/deduplication/__init__.py +++ b/api/account/deduplication/__init__.py @@ -3,7 +3,7 @@ class Rules(Enum): LIFO = "LIFO" - FIFO = "FIFO" + FIFO = "FIFO" # DEPRECATED - this shall not be used any more @classmethod def choices(cls): diff --git a/api/account/deduplication/fifo.py b/api/account/deduplication/fifo.py deleted file mode 100644 index 2be778c42..000000000 --- a/api/account/deduplication/fifo.py +++ /dev/null @@ -1,59 +0,0 @@ -import copy -from typing import Tuple - -import api_logging as logging -from account.models import Community -from registry.models import Event, Stamp - -log = logging.getLogger(__name__) - - -# --> FIFO deduplication -async def afifo( - community: Community, fifo_passport: dict, address: str -) -> Tuple[dict, list]: - deduped_passport = copy.deepcopy(fifo_passport) - affected_passports = [] - if "stamps" in fifo_passport: - dedup_event_data = [] - new_stamp_hashes = [ - stamp["credential"]["credentialSubject"]["hash"] - for stamp in fifo_passport["stamps"] - ] - - existing_stamps = ( - Stamp.objects.filter( - hash__in=new_stamp_hashes, passport__community=community - ) - .exclude(passport__address=address) - .select_related("passport") - ) - - async for existing_stamp in existing_stamps: - existing_stamp_passport = existing_stamp.passport - affected_passports.append(existing_stamp_passport) - dedup_event_data.append( - { - "hash": existing_stamp.hash, - "provider": existing_stamp.provider, - "prev_owner": existing_stamp_passport.address, - "address": address, - "community_id": community.pk, - } - ) - - await existing_stamps.adelete() - - if dedup_event_data: - await Event.objects.abulk_create( - [ - Event( - action=Event.Action.FIFO_DEDUPLICATION, - address=data["prev_owner"], - data=data, - ) - for data in dedup_event_data - ] - ) - - return (deduped_passport, affected_passports) diff --git a/api/account/test/test_deduplication_fifo.py b/api/account/test/test_deduplication_fifo.py deleted file mode 100644 index dcc1177f3..000000000 --- a/api/account/test/test_deduplication_fifo.py +++ /dev/null @@ -1,178 +0,0 @@ -from datetime import datetime, timedelta - -from account.deduplication import Rules -from account.deduplication.fifo import afifo -from account.models import Account, Community -from asgiref.sync import async_to_sync -from django.contrib.auth import get_user_model -from django.test import TestCase -from ninja_jwt.schema import RefreshToken -from registry.models import Passport, Stamp -from scorer_weighted.models import Scorer, WeightedScorer - - -class ExistingStamp: - def __init__(self, hash): - self.hash = hash - - -User = get_user_model() - -mock_community_body = {"name": "test", "description": "test"} -google_credential = { - "type": ["VerifiableCredential"], - "proof": { - "jws": "eyJhbGciOiJFZERTQSIsImNyaXQiOlsiYjY0Il0sImI2NCI6ZmFsc2V9..UvANt5nz16WNjkGTyUFIxbMBmYdEFZcVrD97L3EzOkvxz8eN-6UKeFZul_uPBfa88h50jKQgVgJlJqxR8kpSAQ", - "type": "Ed25519Signature2018", - "created": "2022-06-03T15:33:04.698Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:key:z6MkghvGHLobLEdj1bgRLhS4LPGJAvbMA1tn2zcRyqmYU5LC#z6MkghvGHLobLEdj1bgRLhS4LPGJAvbMA1tn2zcRyqmYU5LC", - }, - "issuer": "did:key:z6MkghvGHLobLEdj1bgRLhS4LPGJAvbMA1tn2zcRyqmYU5LC", - "@context": ["https://www.w3.org/2018/credentials/v1"], - "issuanceDate": (datetime.utcnow() - timedelta(days=3)).strftime( - "%Y-%m-%dT%H:%M:%SZ" - ), - "expirationDate": (datetime.utcnow() + timedelta(days=30)).strftime( - "%Y-%m-%dT%H:%M:%SZ" - ), - "credentialSubject": { - "id": "did:pkh:eip155:1:0x0636F974D29d947d4946b2091d769ec6D2d415DE", - "hash": "v0.0.0:edgFWHsCSaqGxtHSqdiPpEXR06Ejw+YLO9K0BSjz0d8=", - "@context": [ - { - "hash": "https://schema.org/Text", - "provider": "https://schema.org/Text", - } - ], - "provider": "Google", - }, -} - -mock_passport = { - "issuanceDate": "2022-06-03T15:31:56.944Z", - "expirationDate": "2022-06-03T15:31:56.944Z", - "stamps": [ - {"provider": "Google", "credential": google_credential}, - ], -} - - -class FifoDeduplication(TestCase): - def setUp(self): - self.create_test_users() - self.create_test_communities() - self.create_sample_passport() - - def create_test_users(self): - User.objects.create_user(username="admin", password="12345") - self.user = User.objects.create_user(username="testuser-1", password="12345") - self.user2 = User.objects.create_user(username="testuser-2", password="12345") - - refresh = RefreshToken.for_user(self.user) - refresh["ip_address"] = "127.0.0.1" - self.access_token = refresh.access_token - - def create_test_communities(self): - (self.account1, _) = Account.objects.get_or_create( - user=self.user, defaults={"address": "0x0"} - ) - scorer1 = WeightedScorer.objects.create( - type=Scorer.Type.WEIGHTED, weights={"test_provider": 10} - ) - self.community1 = Community.objects.create( - name="Community1", scorer=scorer1, rule=Rules.FIFO, account=self.account1 - ) - - (self.account2, _) = Account.objects.get_or_create( - user=self.user2, defaults={"address": "0x0"} - ) - scorer2 = WeightedScorer.objects.create( - type=Scorer.Type.WEIGHTED, weights={"test_provider": 10} - ) - self.community2 = Community.objects.create( - name="Community2", scorer=scorer2, rule=Rules.FIFO, account=self.account2 - ) - - def create_sample_passport(self): - self.sample_passport = { - "stamps": [ - {"credential": {"credentialSubject": {"hash": "123"}}}, - {"credential": {"credentialSubject": {"hash": "456"}}}, - {"credential": {"credentialSubject": {"hash": "123"}}}, - ] - } - - @async_to_sync - async def test_fifo_removes_deduplicate_stamp_from_passport_in_same_community(self): - """ - Test that the deduplicate stamps are found, deleted, and passport score is updated - """ - passport1 = await Passport.objects.acreate( - address="0xaddress_1", community=self.community1 - ) - - passport2 = await Passport.objects.acreate( - address="0xaddress_2", community=self.community1 - ) - - await Stamp.objects.acreate( - passport=passport1, - hash=google_credential["credentialSubject"]["hash"], - provider="test_provider", - credential=google_credential, - ) - - # We call the `fifo` deduplication method - await afifo( - community=self.community1, - fifo_passport=mock_passport, - address=passport2.address, - ) - - updated_passport = await Passport.objects.aget(address="0xaddress_1") - # We check that the `deduplicated_stamps` queryset contains the stamp we added - - self.assertEqual( - await updated_passport.stamps.acount(), - 0, - "The stamp should not have been deleted from the passpo", - ) - - @async_to_sync - async def test_fifo_does_not_remove_deduplicate_stamp_from_passport_in_different_community( - self, - ): - """ - Test that the deduplicate stamps are found, deleted, and passport score is updated - """ - passport1 = await Passport.objects.acreate( - address="0xaddress_1", community=self.community2 - ) - - passport2 = await Passport.objects.acreate( - address="0xaddress_2", community=self.community1 - ) - - await Stamp.objects.acreate( - passport=passport1, - hash=google_credential["credentialSubject"]["hash"], - provider="test_provider", - credential=google_credential, - ) - - # We call the `fifo` deduplication method - await afifo( - community=self.community1, - fifo_passport=mock_passport, - address=passport2.address, - ) - - updated_passport = await Passport.objects.aget(address="0xaddress_1") - # We check that the `deduplicated_stamps` queryset contains the stamp we added - - self.assertEqual( - await updated_passport.stamps.acount(), - 1, - "The stamp should not have been deleted from the passpo", - ) diff --git a/api/registry/api/schema.py b/api/registry/api/schema.py index a51c01477..11ad13552 100644 --- a/api/registry/api/schema.py +++ b/api/registry/api/schema.py @@ -67,7 +67,9 @@ class GenericCommunityResponse(Schema): class ActionEnum(str, Enum): - fifo_deduplication = Event.Action.FIFO_DEDUPLICATION + fifo_deduplication = ( + Event.Action.FIFO_DEDUPLICATION + ) # DEPRECATED: this deduplication method was deprecated lifo_deduplication = Event.Action.LIFO_DEDUPLICATION trustalab_score = Event.Action.TRUSTALAB_SCORE score_update = Event.Action.SCORE_UPDATE diff --git a/api/registry/atasks.py b/api/registry/atasks.py index e14fe374d..67ca8c18e 100644 --- a/api/registry/atasks.py +++ b/api/registry/atasks.py @@ -3,7 +3,6 @@ from typing import Dict, List import api_logging as logging -from account.deduplication.fifo import afifo from account.deduplication.lifo import alifo # --- Deduplication Modules @@ -75,7 +74,6 @@ async def aprocess_deduplication(passport, community, passport_data, score: Scor """ rule_map = { Rules.LIFO.value: alifo, - Rules.FIFO.value: afifo, } method = rule_map.get(community.rule) @@ -100,19 +98,19 @@ async def aprocess_deduplication(passport, community, passport_data, score: Scor ) # If the rule is FIFO, we need to re-score all affected passports - if community.rule == Rules.FIFO.value: - for passport in affected_passports: - log.debug( - "FIFO scoring selected, rescoring passport='%s'", - passport, - ) - - affected_score, _ = await Score.objects.aupdate_or_create( - passport=passport, - defaults=dict(score=None, status=score.status), - ) - await acalculate_score(passport, passport.community_id, affected_score) - await affected_score.asave() + # if community.rule == Rules.FIFO.value: + # for passport in affected_passports: + # log.debug( + # "FIFO scoring selected, rescoring passport='%s'", + # passport, + # ) + + # affected_score, _ = await Score.objects.aupdate_or_create( + # passport=passport, + # defaults=dict(score=None, status=score.status), + # ) + # await acalculate_score(passport, passport.community_id, affected_score) + # await affected_score.asave() return deduplicated_passport diff --git a/api/registry/test/test_passport_submission.py b/api/registry/test/test_passport_submission.py index 4cb32c962..ed64b7521 100644 --- a/api/registry/test/test_passport_submission.py +++ b/api/registry/test/test_passport_submission.py @@ -945,77 +945,6 @@ def test_lifo_deduplication_duplicate_stamps( self.assertEqual(updated_passport.address, submission_address) self.assertEqual(updated_passport.stamps.all()[0].provider, "Google") - @patch("registry.atasks.validate_credential", side_effect=[[], []]) - @patch( - "registry.atasks.aget_passport", - return_value=mock_passport_google, - ) - def test_fifo_deduplication_duplicate_stamps( - self, aget_passport, validate_credential - ): - """ - Test the successful deduplication of stamps by first in first out (FIFO) - """ - address_1 = self.account.address - address_2 = self.mock_account.address - - fifo_community = Community.objects.create( - name="My FIFO Community", - description="My FIFO Community description", - account=self.user_account, - rule=Rules.FIFO.value, - ) - - # Create first passport - first_passport = Passport.objects.create( - address=address_1.lower(), - community=fifo_community, - requires_calculation=True, - ) - - Stamp.objects.create( - passport=first_passport, - hash=ens_credential["credentialSubject"]["hash"], - provider="Ens", - credential=ens_credential, - ) - - # Create existing stamp that is a duplicate of the one we are going to submit - Stamp.objects.create( - passport=first_passport, - hash=google_credential_2["credentialSubject"]["hash"], - provider="Google", - credential=google_credential_2, - ) - - # Now we submit a duplicate hash, and expect deduplication to happen - submission_test_payload = { - "community": fifo_community.pk, - "address": address_2, - "nonce": self.nonce, - } - - submission_response = self.client.post( - f"{self.base_url}/submit-passport", - json.dumps(submission_test_payload), - content_type="application/json", - HTTP_AUTHORIZATION=f"Token {self.secret}", - ) - - self.assertEqual(submission_response.status_code, 200) - - # first_passport should have just one stamp and the google stamps should be deleted - deduped_first_passport = Passport.objects.get(address=address_1) - - self.assertEqual(deduped_first_passport.stamps.count(), 1) - self.assertEqual(deduped_first_passport.stamps.all()[0].provider, "Ens") - - # assert submitted passport contains the google stamp - submitted_passport = Passport.objects.get(address=address_2) - - self.assertEqual(submitted_passport.stamps.count(), 1) - self.assertEqual(submitted_passport.stamps.all()[0].provider, "Google") - @patch("registry.atasks.validate_credential", side_effect=[[], []]) @patch( "registry.atasks.aget_passport", diff --git a/api/registry/test/test_score_passport.py b/api/registry/test/test_score_passport.py index 36d348409..5320150e7 100644 --- a/api/registry/test/test_score_passport.py +++ b/api/registry/test/test_score_passport.py @@ -3,10 +3,9 @@ from decimal import Decimal from unittest.mock import call, patch -from asgiref.sync import async_to_sync - from account.deduplication import Rules from account.models import Account, AccountAPIKey, Community +from asgiref.sync import async_to_sync from django.conf import settings from django.contrib.auth import get_user_model from django.test import Client, TransactionTestCase @@ -370,147 +369,6 @@ def test_lifo_duplicate_stamp_scoring(self): == 2 ) - def test_fifo_duplicate_stamp_scoring(self): - with patch( - "scorer_weighted.models.settings.GITCOIN_PASSPORT_WEIGHTS", - { - "Google": 1, - "Ens": 2, - "POAP": 4, - }, - ): - fifo_community = Community.objects.create( - name="My Community", - description="My Community description", - account=self.user_account, - rule=Rules.FIFO.value, - ) - - passport, _ = Passport.objects.update_or_create( - address=self.account.address, - community_id=fifo_community.pk, - requires_calculation=True, - ) - - passport_for_already_existing_stamp, _ = Passport.objects.update_or_create( - address=self.account_2.address, - community_id=fifo_community.pk, - requires_calculation=True, - ) - - passport_with_duplicates, _ = Passport.objects.update_or_create( - address=self.account_3.address, - community_id=fifo_community.pk, - requires_calculation=True, - ) - - already_existing_stamp = { - "provider": "POAP", - "credential": { - "type": ["VerifiableCredential"], - "credentialSubject": { - "id": settings.TRUSTED_IAM_ISSUER, - "hash": "0x1111", - "provider": "Gitcoin", - }, - "issuer": settings.TRUSTED_IAM_ISSUER, - "issuanceDate": "2023-02-06T23:22:58.848Z", - "expirationDate": "2099-02-06T23:22:58.848Z", - }, - } - - Stamp.objects.update_or_create( - hash=already_existing_stamp["credential"]["credentialSubject"]["hash"], - passport=passport_for_already_existing_stamp, - defaults={ - "provider": already_existing_stamp["provider"], - "credential": json.dumps(already_existing_stamp["credential"]), - }, - ) - - mock_passport_data_with_duplicates = { - "stamps": [ - mock_passport_data["stamps"][0], - already_existing_stamp, - { - "provider": "Google", - "credential": { - "type": ["VerifiableCredential"], - "credentialSubject": { - "id": settings.TRUSTED_IAM_ISSUER, - "hash": "0x12121", - "provider": "Google", - }, - "issuer": settings.TRUSTED_IAM_ISSUER, - "issuanceDate": "2023-02-06T23:22:58.848Z", - "expirationDate": "2099-02-06T23:22:58.848Z", - }, - }, - ] - } - - with patch("registry.atasks.validate_credential", side_effect=mock_validate): - # Score original passport - with patch( - "registry.atasks.aget_passport", return_value=mock_passport_data - ): - score_registry_passport(fifo_community.pk, passport.address) - - assert ( - Event.objects.filter(action=Event.Action.FIFO_DEDUPLICATION).count() - == 0 - ) - - assert Stamp.objects.filter(passport=passport).count() == 3 - assert (Score.objects.get(passport=passport).score) == Decimal("3") - - # Score passport with duplicates (one duplicate from original passport, - # one duplicate from already existing stamp) - with patch( - "registry.atasks.aget_passport", - return_value=mock_passport_data_with_duplicates, - ): - score_registry_passport( - fifo_community.pk, passport_with_duplicates.address - ) - - # One stamp should be removed from original passport - original_stamps = Stamp.objects.filter(passport=passport) - assert len(original_stamps) == 2 - - assert (Score.objects.get(passport=passport).score) == Decimal("1") - - assert ( - Event.objects.filter(action=Event.Action.FIFO_DEDUPLICATION).count() - == 2 - ) - - new_stamps = Stamp.objects.filter(passport=passport_with_duplicates) - assert len(new_stamps) == 3 - - assert ( - Score.objects.get(passport=passport_with_duplicates).score - ) == Decimal("7") - - passport.requires_calculation = True - passport.save() - # Re-score original passport, it should get the full score again - with patch( - "registry.atasks.aget_passport", return_value=mock_passport_data - ): - score_registry_passport(fifo_community.pk, passport.address) - - assert ( - Event.objects.filter(action=Event.Action.FIFO_DEDUPLICATION).count() - == 3 - ) - - assert (Score.objects.get(passport=passport).score) == Decimal("3") - - assert ( - Score.objects.get(passport=passport_with_duplicates).score - ) == Decimal("5") - def test_score_events(self): count = Event.objects.filter(action=Event.Action.SCORE_UPDATE).count() diff --git a/interface/components/NewScorer.tsx b/interface/components/NewScorer.tsx index 1b00c86a5..db5cf170c 100644 --- a/interface/components/NewScorer.tsx +++ b/interface/components/NewScorer.tsx @@ -27,7 +27,7 @@ import { createCommunity } from "../utils/account-requests"; import PopoverInfo from "./PopoverInfo"; import { warningToast } from "./Toasts"; -type DeduplicationType = "FIFO" | "LIFO"; +type DeduplicationType = "LIFO"; interface GitcoinScoringMechanismInterface { icon: (classes?: string) => JSX.Element; @@ -273,7 +273,6 @@ const NewScorer = () => { onChange={(e: any) => setDeduplication(e.target.value)} > -