Skip to content

Commit

Permalink
address cmts
Browse files Browse the repository at this point in the history
  • Loading branch information
dydxwill committed Mar 5, 2024
1 parent 2ae9e5c commit 9879d1f
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { getIpAddr } from '../../../../src/lib/utils';
import { sendRequest } from '../../../helpers/helpers';
import { RequestMethod } from '../../../../src/types';
import { stats } from '@dydxprotocol-indexer/base';
import { logger, stats } from '@dydxprotocol-indexer/base';
import { redis } from '@dydxprotocol-indexer/redis';
import { ratelimitRedis } from '../../../../src/caches/rate-limiters';
import { ComplianceControllerHelper } from '../../../../src/controllers/api/v4/compliance-controller';
Expand Down Expand Up @@ -239,6 +239,15 @@ describe('ComplianceV2Controller', () => {
let getGeoComplianceReasonSpy: jest.SpyInstance;
let isRestrictedCountryHeadersSpy: jest.SpyInstance;

const body: any = {
address: testConstants.defaultAddress,
message: 'Test message',
action: ComplianceAction.ONBOARD,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000,
};

beforeEach(async () => {
getGeoComplianceReasonSpy = getGeoComplianceReason as unknown as jest.Mock;
isRestrictedCountryHeadersSpy = isRestrictedCountryHeaders as unknown as jest.Mock;
Expand Down Expand Up @@ -267,12 +276,8 @@ describe('ComplianceV2Controller', () => {
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
...body,
address: '0x123', // Non-dYdX address
message: 'Test message',
action: ComplianceAction.ONBOARD,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000,
},
expectedStatus: 400,
});
Expand All @@ -283,11 +288,7 @@ describe('ComplianceV2Controller', () => {
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
address: testConstants.defaultAddress,
message: 'Test message',
action: ComplianceAction.ONBOARD,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
...body,
timestamp: 1619996600, // More than 30 seconds difference
},
expectedStatus: 400,
Expand All @@ -301,14 +302,7 @@ describe('ComplianceV2Controller', () => {
await sendRequest({
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
address: testConstants.defaultAddress,
message: 'Test message',
action: ComplianceAction.ONBOARD,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000,
},
body,
expectedStatus: 400,
});
});
Expand All @@ -319,14 +313,7 @@ describe('ComplianceV2Controller', () => {
const response: any = await sendRequest({
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
address: testConstants.defaultAddress,
message: 'Test message',
action: ComplianceAction.ONBOARD,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000, // Valid timestamp
},
body,
});

expect(response.status).toEqual(200);
Expand All @@ -341,14 +328,7 @@ describe('ComplianceV2Controller', () => {
const response: any = await sendRequest({
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
address: testConstants.defaultAddress,
message: 'Test message',
action: ComplianceAction.ONBOARD,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000, // Valid timestamp
},
body,
expectedStatus: 200,
});

Expand All @@ -373,12 +353,8 @@ describe('ComplianceV2Controller', () => {
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
address: testConstants.defaultAddress,
message: 'Test message',
...body,
action: ComplianceAction.CONNECT,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000, // Valid timestamp
},
expectedStatus: 200,
});
Expand All @@ -402,14 +378,7 @@ describe('ComplianceV2Controller', () => {
const response: any = await sendRequest({
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
address: testConstants.defaultAddress,
message: 'Test message',
action: ComplianceAction.ONBOARD, // Or CONNECT, should work the same
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000, // Valid timestamp
},
body,
expectedStatus: 200,
});

Expand All @@ -436,12 +405,8 @@ describe('ComplianceV2Controller', () => {
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
address: testConstants.defaultAddress,
message: 'Test message',
...body,
action: ComplianceAction.CONNECT,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000, // Valid timestamp
},
expectedStatus: 200,
});
Expand All @@ -458,6 +423,69 @@ describe('ComplianceV2Controller', () => {
expect(response.body.reason).toEqual(ComplianceReason.US_GEO);
});

it('should be a no-op for ONBOARD action with existing COMPLIANT status', async () => {
const loggerError = jest.spyOn(logger, 'error');
await ComplianceStatusTable.create({
address: testConstants.defaultAddress,
status: ComplianceStatus.COMPLIANT,
});
(Secp256k1.verifySignature as jest.Mock).mockResolvedValueOnce(true);
isRestrictedCountryHeadersSpy.mockReturnValue(true);

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

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

expect(loggerError).toHaveBeenCalledWith(expect.objectContaining({
at: 'ComplianceV2Controller POST /geoblock',
message: 'Invalid action for current compliance status',
}));
expect(response.body.status).toEqual(ComplianceStatus.COMPLIANT);
});

it('should be a no-op for ONBOARD action with existing FIRST_STRIKE status', async () => {
const loggerError = jest.spyOn(logger, 'error');
await ComplianceStatusTable.create({
address: testConstants.defaultAddress,
status: ComplianceStatus.FIRST_STRIKE,
reason: ComplianceReason.US_GEO,
});
(Secp256k1.verifySignature as jest.Mock).mockResolvedValueOnce(true);
isRestrictedCountryHeadersSpy.mockReturnValue(true);

const response: any = await sendRequest({
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body,
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(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.reason).toEqual(ComplianceReason.US_GEO);
});

it('should update status to CLOSE_ONLY for CONNECT action from a restricted country with existing FIRST_STRIKE status', async () => {
await ComplianceStatusTable.create({
address: testConstants.defaultAddress,
Expand All @@ -472,12 +500,8 @@ describe('ComplianceV2Controller', () => {
type: RequestMethod.POST,
path: '/v4/compliance/geoblock',
body: {
address: testConstants.defaultAddress,
message: 'Test message',
...body,
action: ComplianceAction.CONNECT,
signedMessage: sha256(Buffer.from('msg')),
pubkey: new Uint8Array([/* public key bytes */]),
timestamp: 1620000000, // Valid timestamp
},
expectedStatus: 200,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export enum ComplianceAction {
CONNECT = 'CONNECT',
}

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

@Route('compliance')
class ComplianceV2Controller extends Controller {
private ipAddress: string;
Expand Down Expand Up @@ -216,16 +221,16 @@ router.post(
* - 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
* - else if the request is not from a non-restricted country:
* - 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
* - the ONLY action should be CONNECT. ONBOARD is a no-op.
* - if the request is from a restricted country:
* - set the status to FIRST_STRIKE
*
* if the address is FIRST_STRIKE:
* - the ONLY action should be CONNECT
* - the ONLY action should be CONNECT. ONBOARD is a no-op.
* - if the request is from a restricted country:
* - set the status to CLOSE_ONLY
*/
Expand Down Expand Up @@ -277,11 +282,6 @@ router.post(
isRestrictedCountryHeaders(req.headers as CountryHeaders) &&
action === ComplianceAction.CONNECT
) {
const complianceProgression: Partial<Record<ComplianceStatus, ComplianceStatus>> = {
[ComplianceStatus.COMPLIANT]: ComplianceStatus.FIRST_STRIKE,
[ComplianceStatus.FIRST_STRIKE]: ComplianceStatus.CLOSE_ONLY,
};

complianceStatusFromDatabase = await ComplianceStatusTable.update({
address,
status: complianceProgression[complianceStatus[0].status],
Expand Down

0 comments on commit 9879d1f

Please sign in to comment.