Skip to content

Commit

Permalink
[CT-755] implement logic for FIRST_STRIKE_CLOSE_ONLY mode (#1372) (#1479
Browse files Browse the repository at this point in the history
)

(cherry picked from commit 37d99ef)

Co-authored-by: dydxwill <[email protected]>
  • Loading branch information
mergify[bot] and dydxwill authored May 8, 2024
1 parent f664999 commit 4e47516
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ describe('Compliance status store', () => {
compliantUpsertStatusData,
{
...noncompliantStatusUpsertData,
status: ComplianceStatus.FIRST_STRIKE,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
},
{
...noncompliantStatusUpsertData,
Expand All @@ -217,7 +217,7 @@ describe('Compliance status store', () => {
}),
expect.objectContaining({
...noncompliantStatusUpsertData,
status: ComplianceStatus.FIRST_STRIKE,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
}),
expect.objectContaining({
...noncompliantStatusUpsertData,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as Knex from 'knex';

import { formatAlterTableEnumSql } from '../helpers';

export async function up(knex: Knex): Promise<void> {
return knex.raw(formatAlterTableEnumSql(
'compliance_status',
'status',
['COMPLIANT', 'FIRST_STRIKE_CLOSE_ONLY', 'FIRST_STRIKE', 'CLOSE_ONLY', 'BLOCKED'],
));
}

export async function down(knex: Knex): Promise<void> {
return knex.raw(formatAlterTableEnumSql(
'compliance_status',
'status',
['COMPLIANT', 'FIRST_STRIKE', 'CLOSE_ONLY', 'BLOCKED'],
));
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum ComplianceReason {

export enum ComplianceStatus {
COMPLIANT = 'COMPLIANT',
FIRST_STRIKE_CLOSE_ONLY = 'FIRST_STRIKE_CLOSE_ONLY',
FIRST_STRIKE = 'FIRST_STRIKE',
CLOSE_ONLY = 'CLOSE_ONLY',
BLOCKED = 'BLOCKED',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ describe('ComplianceV2Controller', () => {
}));
});

it('should update exisitng db row for dydx address', async () => {
it('should update existing db row for dydx address', async () => {
await ComplianceStatusTable.create({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE,
Expand Down Expand Up @@ -365,7 +365,7 @@ describe('ComplianceV2Controller', () => {
expect(response.body.updatedAt).toBeDefined();
});

it('should set status to FIRST_STRIKE for CONNECT action from a restricted country with no existing compliance status', async () => {
it('should set status to FIRST_STRIKE_CLOSE_ONLY for CONNECT action from a restricted country with no existing compliance status', async () => {
(Secp256k1.verifySignature as jest.Mock).mockResolvedValueOnce(true);
getGeoComplianceReasonSpy.mockReturnValueOnce(ComplianceReason.US_GEO);
isRestrictedCountryHeadersSpy.mockReturnValue(true);
Expand All @@ -384,11 +384,11 @@ describe('ComplianceV2Controller', () => {
expect(data).toHaveLength(1);
expect(data[0]).toEqual(expect.objectContaining({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
reason: ComplianceReason.US_GEO,
}));

expect(response.body.status).toEqual(ComplianceStatus.FIRST_STRIKE);
expect(response.body.status).toEqual(ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY);
expect(response.body.reason).toEqual(ComplianceReason.US_GEO);
expect(response.body.updatedAt).toBeDefined();
});
Expand All @@ -414,7 +414,7 @@ describe('ComplianceV2Controller', () => {
expect(response.body.status).toEqual(ComplianceStatus.COMPLIANT);
});

it('should update status to FIRST_STRIKE for CONNECT action from a restricted country with existing COMPLIANT status', async () => {
it('should update status to FIRST_STRIKE_CLOSE_ONLY for CONNECT action from a restricted country with existing COMPLIANT status', async () => {
await ComplianceStatusTable.create({
address: testConstants.defaultAddress,
status: ComplianceStatus.COMPLIANT,
Expand All @@ -437,11 +437,11 @@ describe('ComplianceV2Controller', () => {
expect(data).toHaveLength(1);
expect(data[0]).toEqual(expect.objectContaining({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
reason: ComplianceReason.US_GEO,
}));

expect(response.body.status).toEqual(ComplianceStatus.FIRST_STRIKE);
expect(response.body.status).toEqual(ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY);
expect(response.body.reason).toEqual(ComplianceReason.US_GEO);
expect(response.body.updatedAt).toBeDefined();
});
Expand Down Expand Up @@ -476,11 +476,11 @@ describe('ComplianceV2Controller', () => {
expect(response.body.status).toEqual(ComplianceStatus.COMPLIANT);
});

it('should be a no-op for ONBOARD action with existing FIRST_STRIKE status', async () => {
it('should be a no-op for ONBOARD action with existing FIRST_STRIKE_CLOSE_ONLY status', async () => {
const loggerError = jest.spyOn(logger, 'error');
await ComplianceStatusTable.create({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
reason: ComplianceReason.US_GEO,
});
(Secp256k1.verifySignature as jest.Mock).mockResolvedValueOnce(true);
Expand All @@ -497,15 +497,15 @@ describe('ComplianceV2Controller', () => {
expect(data).toHaveLength(1);
expect(data[0]).toEqual(expect.objectContaining({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
reason: ComplianceReason.US_GEO,
}));

expect(loggerError).toHaveBeenCalledWith(expect.objectContaining({
at: 'ComplianceV2Controller POST /geoblock',
message: 'Invalid action for current compliance status',
}));
expect(response.body.status).toEqual(ComplianceStatus.FIRST_STRIKE);
expect(response.body.status).toEqual(ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY);
expect(response.body.reason).toEqual(ComplianceReason.US_GEO);
expect(response.body.updatedAt).toBeDefined();
});
Expand Down Expand Up @@ -542,5 +542,71 @@ describe('ComplianceV2Controller', () => {
expect(response.body.reason).toEqual(ComplianceReason.US_GEO);
expect(response.body.updatedAt).toBeDefined();
});

it('should update status to CLOSE_ONLY for INVALID_SURVEY action with existing FIRST_STRIKE_CLOSE_ONLY status', async () => {
await ComplianceStatusTable.create({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
reason: ComplianceReason.US_GEO,
});
(Secp256k1.verifySignature as jest.Mock).mockResolvedValueOnce(true);
getGeoComplianceReasonSpy.mockReturnValueOnce(ComplianceReason.US_GEO);
isRestrictedCountryHeadersSpy.mockReturnValue(true);

const response: any = await sendRequest({
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
...body,
action: ComplianceAction.INVALID_SURVEY,
},
expectedStatus: 200,
});

const data: ComplianceStatusFromDatabase[] = await ComplianceStatusTable.findAll({}, [], {});
expect(data).toHaveLength(1);
expect(data[0]).toEqual(expect.objectContaining({
address: testConstants.defaultAddress,
status: ComplianceStatus.CLOSE_ONLY,
reason: ComplianceReason.US_GEO,
}));

expect(response.body.status).toEqual(ComplianceStatus.CLOSE_ONLY);
expect(response.body.reason).toEqual(ComplianceReason.US_GEO);
expect(response.body.updatedAt).toBeDefined();
});

it('should update status to FIRST_STRIKE for VALID_SURVEY action with existing FIRST_STRIKE_CLOSE_ONLY status', async () => {
await ComplianceStatusTable.create({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
reason: ComplianceReason.US_GEO,
});
(Secp256k1.verifySignature as jest.Mock).mockResolvedValueOnce(true);
getGeoComplianceReasonSpy.mockReturnValueOnce(ComplianceReason.US_GEO);
isRestrictedCountryHeadersSpy.mockReturnValue(true);

const response: any = await sendRequest({
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
...body,
action: ComplianceAction.VALID_SURVEY,
},
expectedStatus: 200,
});

const data: ComplianceStatusFromDatabase[] = await ComplianceStatusTable.findAll({}, [], {});
expect(data).toHaveLength(1);
expect(data[0]).toEqual(expect.objectContaining({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE,
reason: ComplianceReason.US_GEO,
}));

expect(response.body.status).toEqual(ComplianceStatus.FIRST_STRIKE);
expect(response.body.reason).toEqual(ComplianceReason.US_GEO);
expect(response.body.updatedAt).toBeDefined();
});
});
});
1 change: 1 addition & 0 deletions indexer/services/comlink/public/api-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3240,6 +3240,7 @@ This operation does not require authentication
|Property|Value|
|---|---|
|*anonymous*|COMPLIANT|
|*anonymous*|FIRST_STRIKE_CLOSE_ONLY|
|*anonymous*|FIRST_STRIKE|
|*anonymous*|CLOSE_ONLY|
|*anonymous*|BLOCKED|
Expand Down
1 change: 1 addition & 0 deletions indexer/services/comlink/public/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@
"ComplianceStatus": {
"enum": [
"COMPLIANT",
"FIRST_STRIKE_CLOSE_ONLY",
"FIRST_STRIKE",
"CLOSE_ONLY",
"BLOCKED"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ const controllerName: string = 'compliance-v2-controller';
export enum ComplianceAction {
ONBOARD = 'ONBOARD',
CONNECT = 'CONNECT',
VALID_SURVEY = 'VALID_SURVEY',
INVALID_SURVEY = 'INVALID_SURVEY',
}

const COMPLIANCE_PROGRESSION: Partial<Record<ComplianceStatus, ComplianceStatus>> = {
[ComplianceStatus.COMPLIANT]: ComplianceStatus.FIRST_STRIKE,
[ComplianceStatus.COMPLIANT]: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
[ComplianceStatus.FIRST_STRIKE]: ComplianceStatus.CLOSE_ONLY,
};

Expand Down Expand Up @@ -237,17 +239,25 @@ router.post(
* If the address doesn't exist in the compliance table:
* - if the request is from a restricted country:
* - if the action is ONBOARD, set the status to BLOCKED
* - if the action is CONNECT, set the status to FIRST_STRIKE
* - if the action is CONNECT, set the status to FIRST_STRIKE_CLOSE_ONLY
* - else if the request is from a non-restricted country:
* - set the status to COMPLIANT
*
* if the address is COMPLIANT:
* - the ONLY action should be CONNECT. ONBOARD is a no-op.
* - the ONLY action should be CONNECT. ONBOARD/VALID_SURVEY/INVALID_SURVEY are no-ops.
* - if the request is from a restricted country:
* - set the status to FIRST_STRIKE
* - set the status to FIRST_STRIKE_CLOSE_ONLY
*
* if the address is FIRST_STRIKE_CLOSE_ONLY:
* - the ONLY actions should be VALID_SURVEY/INVALID_SURVEY/CONNECT. ONBOARD/CONNECT
* are no-ops.
* - if the action is VALID_SURVEY:
* - set the status to FIRST_STRIKE
* - if the action is INVALID_SURVEY:
* - set the status to CLOSE_ONLY
*
* if the address is FIRST_STRIKE:
* - the ONLY action should be CONNECT. ONBOARD is a no-op.
* - the ONLY action should be CONNECT. ONBOARD/VALID_SURVEY/INVALID_SURVEY are no-ops.
* - if the request is from a restricted country:
* - set the status to CLOSE_ONLY
*/
Expand All @@ -270,7 +280,7 @@ router.post(
} else if (action === ComplianceAction.CONNECT) {
complianceStatusFromDatabase = await ComplianceStatusTable.upsert({
address,
status: ComplianceStatus.FIRST_STRIKE,
status: ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY,
reason: getGeoComplianceReason(req.headers as CountryHeaders)!,
updatedAt,
});
Expand Down Expand Up @@ -307,6 +317,30 @@ router.post(
updatedAt,
});
}
} else if (
complianceStatus[0].status === ComplianceStatus.FIRST_STRIKE_CLOSE_ONLY
) {
if (action === ComplianceAction.ONBOARD) {
logger.error({
at: 'ComplianceV2Controller POST /geoblock',
message: 'Invalid action for current compliance status',
address,
action,
complianceStatus: complianceStatus[0],
});
} else if (action === ComplianceAction.VALID_SURVEY) {
complianceStatusFromDatabase = await ComplianceStatusTable.update({
address,
status: ComplianceStatus.FIRST_STRIKE,
updatedAt,
});
} else if (action === ComplianceAction.INVALID_SURVEY) {
complianceStatusFromDatabase = await ComplianceStatusTable.update({
address,
status: ComplianceStatus.CLOSE_ONLY,
updatedAt,
});
}
}
}
const response = {
Expand Down

0 comments on commit 4e47516

Please sign in to comment.