Skip to content
This repository was archived by the owner on Mar 18, 2025. It is now read-only.

Commit 26dd29d

Browse files
authored
Merge pull request #2847 from w3f/michalis-sanctions-validity-check-3
New sanctions validity check
2 parents 62982e2 + 99fef04 commit 26dd29d

20 files changed

+208
-41
lines changed

apps/1kv-backend-staging/templates/kusama-otv-backend.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ spec:
6161
"minSelfStake": 10000000000000,
6262
"commission": 150000000,
6363
"unclaimedEraThreshold": 4,
64+
"sanctionedGeoArea": {
65+
"skip": true,
66+
"sanctionedCountries": ["XXX"],
67+
"sanctionedRegions": ["XXX" ]
68+
},
6469
"forceClientVersion": "v1.10.0"
6570
},
6671
"cron": {

apps/1kv-backend-staging/templates/polkadot-otv-backend.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ spec:
6060
"minSelfStake": 50000000000000,
6161
"commission": 50000000,
6262
"unclaimedEraThreshold": 1,
63+
"sanctionedGeoArea": {
64+
"skip": true,
65+
"sanctionedCountries": ["XXX"],
66+
"sanctionedRegions": ["XXX" ]
67+
},
6368
"forceClientVersion": "v1.10.0"
6469
},
6570
"cron": {

apps/1kv-backend/templates/kusama-otv-backend.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ spec:
5959
"minSelfStake": 10000000000000,
6060
"commission": 150000000,
6161
"unclaimedEraThreshold": 4,
62+
"sanctionedGeoArea": {
63+
"skip": true,
64+
"sanctionedCountries": ["XXX"],
65+
"sanctionedRegions": ["XXX" ]
66+
},
6267
"forceClientVersion": "v1.10.0"
6368
},
6469
"cron": {

apps/1kv-backend/templates/polkadot-otv-backend.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ spec:
5858
"minSelfStake": 50000000000000,
5959
"commission": 50000000,
6060
"unclaimedEraThreshold": 1,
61+
"sanctionedGeoArea": {
62+
"skip": true,
63+
"sanctionedCountries": ["XXX"],
64+
"sanctionedRegions": ["XXX" ]
65+
},
6166
"forceClientVersion": "v1.10.0"
6267
},
6368
"cron": {

charts/otv-backend/values.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ config: |
4848
},
4949
"constraints": {
5050
"skipConnectionTime": false,
51-
"skipIdentity": false
51+
"skipIdentity": false,
52+
"sanctionedGeoArea": {
53+
"skip": false,
54+
"sanctionedCountries": ["XXX"],
55+
"sanctionedRegions": ["XXX" ]
56+
}
5257
},
5358
"db": {
5459
"mongo": {

packages/common/src/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ export type ConfigSchema = {
3131
minSelfStake: number;
3232
commission: number;
3333
unclaimedEraThreshold: number;
34+
sanctionedGeoArea?: {
35+
skip: boolean;
36+
sanctionedCountries: string[];
37+
sanctionedRegions: string[];
38+
};
3439
};
3540
cron: {
3641
monitor: string;

packages/common/src/constraints/CheckCandidates.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
checkSelfStake,
1616
checkUnclaimed,
1717
checkValidateIntention,
18+
checkSanctionedGeoArea,
1819
} from "./ValidityChecks";
1920
import { percentage, timeRemaining } from "../utils/util";
2021

@@ -129,6 +130,12 @@ export const checkCandidate = async (
129130
logger.info(`${candidate.name} beefy keys not valid`, constraintsLabel);
130131
}
131132

133+
const sanctionedGeoAreaValid =
134+
constraints.config?.constraints?.sanctionedGeoArea?.skip == true
135+
? true
136+
: (await checkSanctionedGeoArea(constraints.config, candidate)) ||
137+
false;
138+
132139
valid =
133140
onlineValid &&
134141
validateValid &&
@@ -142,7 +149,8 @@ export const checkCandidate = async (
142149
blockedValid &&
143150
kusamaValid &&
144151
providerValid &&
145-
beefyValid;
152+
beefyValid &&
153+
sanctionedGeoAreaValid;
146154

147155
await setValid(candidate, valid);
148156

packages/common/src/constraints/ValidityChecks.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
setSelfStakeInvalidity,
1616
setUnclaimedInvalidity,
1717
setValidateIntentionValidity,
18+
setSanctionedGeoAreaValidity,
1819
} from "../db";
1920
import { ChainData, Config, Constants, queries, Util } from "../index";
2021
import axios from "axios";
@@ -434,3 +435,61 @@ export const checkBeefyKeys = async (
434435
throw new Error("could not make validity check");
435436
}
436437
};
438+
439+
// Checks if the candidate is in a sanctioned location
440+
export const checkSanctionedGeoArea = async (
441+
config: Config.ConfigSchema,
442+
candidate: Candidate,
443+
): Promise<boolean> => {
444+
try {
445+
if (
446+
!config.constraints?.sanctionedGeoArea?.sanctionedCountries?.length &&
447+
!config.constraints?.sanctionedGeoArea?.sanctionedRegions?.length
448+
) {
449+
await setSanctionedGeoAreaValidity(candidate, true);
450+
return true;
451+
}
452+
453+
const location = await queries.getCandidateLocation(candidate.slotId);
454+
if (!location?.region || !location?.country) {
455+
await setSanctionedGeoAreaValidity(candidate, true);
456+
return true;
457+
}
458+
459+
const sanctionedCountries = config.constraints?.sanctionedGeoArea
460+
?.sanctionedCountries
461+
? config.constraints.sanctionedGeoArea.sanctionedCountries.map((x) =>
462+
x.trim().toLowerCase(),
463+
)
464+
: [];
465+
const sanctionedRegions = config.constraints?.sanctionedGeoArea
466+
?.sanctionedRegions
467+
? config.constraints.sanctionedGeoArea.sanctionedRegions.map((x) =>
468+
x.trim().toLowerCase(),
469+
)
470+
: [];
471+
472+
if (
473+
sanctionedCountries.includes(location.country.trim().toLowerCase()) ||
474+
sanctionedRegions.includes(location.region.trim().toLowerCase())
475+
) {
476+
logger.info(
477+
`${candidate.name} is in a sanctioned location: Country: ${location.country}, Region: ${location.region}`,
478+
{
479+
label: "Constraints",
480+
},
481+
);
482+
await setSanctionedGeoAreaValidity(candidate, false);
483+
return false;
484+
}
485+
486+
await setSanctionedGeoAreaValidity(candidate, true);
487+
return true;
488+
} catch (e) {
489+
logger.error(
490+
`Error checking location for sanctions: ${e}`,
491+
constraintsLabel,
492+
);
493+
throw new Error("could not make validity check");
494+
}
495+
};

packages/common/src/db/models.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export enum InvalidityReasonType {
8888
KUSAMA_RANK = "KUSAMA_RANK",
8989
PROVIDER = "PROVIDER",
9090
BEEFY = "BEEFY",
91+
SANCTIONED_GEO_AREA = "SANCTIONED_GEO_AREA",
9192
}
9293

9394
export interface InvalidityReason {
@@ -117,6 +118,7 @@ export const InvalidityReason = new Schema({
117118
"KUSAMA_RANK",
118119
"PROVIDER",
119120
"BEEFY",
121+
"SANCTIONED_GEO_AREA",
120122
],
121123
default: "NEW",
122124
},

packages/common/src/db/queries/Candidate.ts

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ export const getIdentityAddresses = async (
339339
export const setCandidateOnlineValid = async (
340340
candidate: Candidate,
341341
): Promise<void> => {
342-
setCandidateInvalidity(candidate, InvalidityReasonType.ONLINE, true);
342+
await setCandidateInvalidity(candidate, InvalidityReasonType.ONLINE, true);
343343

344344
await CandidateModel.findOneAndUpdate(
345345
{
@@ -355,7 +355,7 @@ export const setCandidateOnlineNotValid = async (
355355
candidate: Candidate,
356356
): Promise<void> => {
357357
const invalidityMessage = `Candidate ${candidate.name} offline. Offline since ${Date.now()}.`;
358-
setCandidateInvalidity(
358+
await setCandidateInvalidity(
359359
candidate,
360360
InvalidityReasonType.ONLINE,
361361
false,
@@ -827,7 +827,7 @@ export const setOnlineValidity = async (
827827
): Promise<void> => {
828828
const candidate = await getCandidateBySlotId(slotId);
829829
const invalidityMessage = `${candidate.name} offline. Last seen online ${candidate.onlineSince}.`;
830-
setCandidateInvalidity(
830+
await setCandidateInvalidity(
831831
candidate,
832832
InvalidityReasonType.ONLINE,
833833
isValid,
@@ -841,7 +841,7 @@ export const setValidateIntentionValidity = async (
841841
isValid: boolean,
842842
): Promise<void> => {
843843
const invalidityMessage = `${candidate.name} does not have a validate intention.`;
844-
setCandidateInvalidity(
844+
await setCandidateInvalidity(
845845
candidate,
846846
InvalidityReasonType.VALIDATE_INTENTION,
847847
isValid,
@@ -855,7 +855,7 @@ export const setLatestClientReleaseValidity = async (
855855
isValid: boolean,
856856
): Promise<void> => {
857857
const invalidityMessage = `${candidate.name} is not on the latest client version`;
858-
setCandidateInvalidity(
858+
await setCandidateInvalidity(
859859
candidate,
860860
InvalidityReasonType.CLIENT_UPGRADE,
861861
isValid,
@@ -870,7 +870,7 @@ export const setConnectionTimeInvalidity = async (
870870
isValid: boolean,
871871
): Promise<void> => {
872872
const invalidityMessage = `${candidate.name} has not been connected for minimum length`;
873-
setCandidateInvalidity(
873+
await setCandidateInvalidity(
874874
candidate,
875875
InvalidityReasonType.CONNECTION_TIME,
876876
isValid,
@@ -888,7 +888,7 @@ export const setIdentityInvalidity = async (
888888
const invalidityMessage = message
889889
? message
890890
: `${candidate.name} has not properly set their identity`;
891-
setCandidateInvalidity(
891+
await setCandidateInvalidity(
892892
candidate,
893893
InvalidityReasonType.IDENTITY,
894894
isValid,
@@ -903,7 +903,7 @@ export const setOfflineAccumulatedInvalidity = async (
903903
isValid: boolean,
904904
): Promise<void> => {
905905
const invalidityMessage = `${candidate.name} has been offline ${candidate.offlineAccumulated / 1000 / 60} minutes this week.`;
906-
setCandidateInvalidity(
906+
await setCandidateInvalidity(
907907
candidate,
908908
InvalidityReasonType.ACCUMULATED_OFFLINE_TIME,
909909
isValid,
@@ -919,7 +919,7 @@ export const setRewardDestinationInvalidity = async (
919919
isValid: boolean,
920920
): Promise<void> => {
921921
const invalidityMessage = `${candidate.name} does not have reward destination as Staked`;
922-
setCandidateInvalidity(
922+
await setCandidateInvalidity(
923923
candidate,
924924
InvalidityReasonType.REWARD_DESTINATION,
925925
isValid,
@@ -937,7 +937,7 @@ export const setCommissionInvalidity = async (
937937
const invalidityMessage = message
938938
? message
939939
: `${candidate.name} has not properly set their commission`;
940-
setCandidateInvalidity(
940+
await setCandidateInvalidity(
941941
candidate,
942942
InvalidityReasonType.COMMISION,
943943
isValid,
@@ -955,7 +955,7 @@ export const setSelfStakeInvalidity = async (
955955
const invalidityMessage = message
956956
? message
957957
: `${candidate.name} has not properly bonded enough self stake`;
958-
setCandidateInvalidity(
958+
await setCandidateInvalidity(
959959
candidate,
960960
InvalidityReasonType.SELF_STAKE,
961961
isValid,
@@ -973,7 +973,7 @@ export const setUnclaimedInvalidity = async (
973973
const invalidityMessage = message
974974
? message
975975
: `${candidate.name} has not properly claimed era rewards`;
976-
setCandidateInvalidity(
976+
await setCandidateInvalidity(
977977
candidate,
978978
InvalidityReasonType.UNCLAIMED_REWARDS,
979979
isValid,
@@ -991,7 +991,7 @@ export const setBlockedInvalidity = async (
991991
const invalidityMessage = message
992992
? message
993993
: `${candidate.name} blocks external nominations`;
994-
setCandidateInvalidity(
994+
await setCandidateInvalidity(
995995
candidate,
996996
InvalidityReasonType.BLOCKED,
997997
isValid,
@@ -1009,7 +1009,7 @@ export const setProviderInvalidity = async (
10091009
const invalidityMessage = message
10101010
? message
10111011
: `${candidate.name} has banned infrastructure provider`;
1012-
setCandidateInvalidity(
1012+
await setCandidateInvalidity(
10131013
candidate,
10141014
InvalidityReasonType.PROVIDER,
10151015
isValid,
@@ -1027,7 +1027,7 @@ export const setKusamaRankInvalidity = async (
10271027
const invalidityMessage = message
10281028
? message
10291029
: `${candidate.name} has not properly claimed era rewards`;
1030-
setCandidateInvalidity(
1030+
await setCandidateInvalidity(
10311031
candidate,
10321032
InvalidityReasonType.KUSAMA_RANK,
10331033
isValid,
@@ -1044,7 +1044,7 @@ export const setBeefyKeysInvalidity = async (
10441044
const invalidityMessage = message
10451045
? message
10461046
: `${candidate.name} does not have beefy keys`;
1047-
setCandidateInvalidity(
1047+
await setCandidateInvalidity(
10481048
candidate,
10491049
InvalidityReasonType.BEEFY,
10501050
isValid,
@@ -1053,6 +1053,20 @@ export const setBeefyKeysInvalidity = async (
10531053
);
10541054
};
10551055

1056+
// Set Sanctions Validity Status
1057+
export const setSanctionedGeoAreaValidity = async (
1058+
candidate: Candidate,
1059+
isValid: boolean,
1060+
): Promise<void> => {
1061+
const invalidityMessage = `${candidate.name} in sanctioned area.`;
1062+
await setCandidateInvalidity(
1063+
candidate,
1064+
InvalidityReasonType.SANCTIONED_GEO_AREA,
1065+
isValid,
1066+
invalidityMessage,
1067+
);
1068+
};
1069+
10561070
// Sets valid boolean for node
10571071
export const setValid = async (
10581072
candidate: Candidate,

0 commit comments

Comments
 (0)