Skip to content

Commit

Permalink
Merge branch 'master' into more-strict-rate-limit-for-spammer
Browse files Browse the repository at this point in the history
  • Loading branch information
wa0x6e committed Nov 14, 2023
2 parents e6ca0bb + 7222efe commit 504817e
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 27 deletions.
1 change: 0 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ updates:
schedule:
interval: "daily"
allow:
# Allow updates for snapshot.js only
- dependency-name: "@snapshot-labs/*"
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
"@ethersproject/wallet": "^5.6.2",
"@shutter-network/shutter-crypto": "^1.0.0",
"@snapshot-labs/pineapple": "^1.1.0",
"@snapshot-labs/snapshot-metrics": "^1.3.1",
"@snapshot-labs/snapshot-metrics": "^1.4.0",
"@snapshot-labs/snapshot-sentry": "^1.5.2",
"@snapshot-labs/snapshot.js": "^0.7.9",
"@snapshot-labs/snapshot.js": "^0.8.3",
"bluebird": "^3.7.2",
"connection-string": "^1.0.1",
"cors": "^2.8.5",
Expand Down
5 changes: 3 additions & 2 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ router.post('/flag', verifyAuth, async (req, res) => {
const { type, value, action } = req.body;

try {
await flagEntity({ type, value, action });
return res.json({ success: true });
const resp = await flagEntity({ type, value, action });
return res.json({ success: !!resp.affectedRows });
} catch (e: any) {
console.log(e);
return sendError(res, e.message || 'failed');
}
});
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ export async function addOrUpdateSpace(space: string, settings: any) {
if (!settings?.name) return false;
const ts = (Date.now() / 1e3).toFixed();
const query =
'INSERT IGNORE INTO spaces SET ? ON DUPLICATE KEY UPDATE updated_at = ?, settings = ?, name = ?';
'INSERT IGNORE INTO spaces SET ? ON DUPLICATE KEY UPDATE updated = ?, settings = ?, name = ?';
await db.queryAsync(query, [
{
id: space,
name: settings.name,
created_at: ts,
updated_at: ts,
created: ts,
updated: ts,
settings: JSON.stringify(settings)
},
ts,
Expand Down
12 changes: 10 additions & 2 deletions src/helpers/moderation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,28 @@ export function flagEntity({ type, action, value }) {
if (!type || !action || !value)
throw new Error(`missing params. 'type', 'action' and 'value' required`);
if (!['proposal', 'space'].includes(type)) throw new Error('invalid type');
if (type === 'proposal' && action !== 'flag') throw new Error('invalid action');
if (type === 'space' && !['flag', 'verify'].includes(action)) throw new Error('invalid action');
if (type === 'proposal' && !['flag', 'unflag'].includes(action))
throw new Error('invalid action');
if (type === 'space' && !['flag', 'unflag', 'verify'].includes(action))
throw new Error('invalid action');

let query;
switch (`${type}-${action}`) {
case 'space-flag':
query = `UPDATE spaces SET flagged = 1, verified = 0 WHERE id = ? LIMIT 1`;
break;
case 'space-unflag':
query = `UPDATE spaces SET flagged = 0 WHERE id = ? LIMIT 1`;
break;
case 'space-verify':
query = `UPDATE spaces SET verified = 1, flagged = 0 WHERE id = ? LIMIT 1`;
break;
case 'proposal-flag':
query = `UPDATE proposals SET flagged = 1 WHERE id = ? LIMIT 1`;
break;
case 'proposal-flag':
query = `UPDATE proposals SET flagged = 0 WHERE id = ? LIMIT 1`;
break;
}

if (!query) throw new Error('invalid query');
Expand Down
2 changes: 1 addition & 1 deletion src/ingestor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import snapshot from '@snapshot-labs/snapshot.js';
import hashTypes from '@snapshot-labs/snapshot.js/src/sign/types.json';
import hashTypes from '@snapshot-labs/snapshot.js/src/sign/hashedTypes.json';
import { pin } from '@snapshot-labs/pineapple';
import kebabCase from 'lodash/kebabCase';
import relayer, { issueReceipt } from './helpers/relayer';
Expand Down
1 change: 1 addition & 0 deletions test/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ RELAYER_PK=01686849e86499c1860ea0afc97f29c11018cbac049abf843df875c60054076e
NODE_ENV=test
RATE_LIMIT_DATABASE_URL=redis://localhost:6379
RATE_LIMIT_KEYS_PREFIX=snapshot-sequencer-test:
AUTH_SECRET=5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9
137 changes: 137 additions & 0 deletions test/e2e/api.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import fetch from 'node-fetch';
import proposalInput from '../fixtures/ingestor-payload/proposal.json';
import redis from '../../src/helpers/redis';
import spacesFixtures from '../fixtures/space';
import proposalsFixtures from '../fixtures/proposal';
import db from '../../src/helpers/mysql';

const HOST = `http://localhost:${process.env.PORT || 3003}`;
const SPACE_PREFIX = 'e2e-';

async function getFlaggedSpacesCount() {
return (await db.queryAsync('SELECT COUNT(id) as count FROM spaces WHERE flagged = 1'))[0].count;
}

async function flagSpace(space: string, action = 'flag') {
return await fetch(`${HOST}/flag`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', secret: '0' },
body: JSON.stringify({ type: 'space', value: `${SPACE_PREFIX}${space}`, action })
});
}

function successRequest() {
return fetch(`${HOST}`);
Expand Down Expand Up @@ -69,3 +85,124 @@ describe('POST /', () => {
});
});
});

describe('POST /flag', () => {
afterAll(() => {
db.endAsync();
});

beforeEach(async () => {
await db.queryAsync(
`
DELETE FROM snapshot_sequencer_test.spaces WHERE id LIKE ?;
TRUNCATE TABLE snapshot_sequencer_test.proposals
`,
[`${SPACE_PREFIX}%`]
);

await Promise.all(
spacesFixtures
.map(space => ({
...space,
id: `${SPACE_PREFIX}${space.id}`,
settings: JSON.stringify(space.settings)
}))
.map(async space => {
db.queryAsync('INSERT INTO snapshot_sequencer_test.spaces SET ?', space);
})
);

await Promise.all(
proposalsFixtures
.map(proposal => ({
...proposal,
strategies: JSON.stringify(proposal.strategies),
validation: JSON.stringify(proposal.validation),
plugins: JSON.stringify(proposal.plugins),
choices: JSON.stringify(proposal.choices),
scores: JSON.stringify(proposal.scores),
scores_by_strategy: JSON.stringify(proposal.scores_by_strategy)
}))
.map(async proposal => {
db.queryAsync('INSERT INTO snapshot_sequencer_test.proposals SET ?', proposal);
})
);
});

it('returns a 401 error when not authorized', async () => {
const response = await fetch(`${HOST}/flag`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
});

expect(response.status).toBe(401);
});

describe('when flagging a space', () => {
it('does nothing when the space does not exist', async () => {
const beforeFlaggedSpacesCount = await getFlaggedSpacesCount();
const response = await flagSpace('test-not-exist.eth');
const body = await response.json();

expect(response.status).toBe(200);
expect(body.success).toBe(false);
expect(await getFlaggedSpacesCount()).toBe(beforeFlaggedSpacesCount);
});

it('return true when the space is already flagged', async () => {
await db.queryAsync('UPDATE spaces SET flagged = 1 WHERE id = ?', `${SPACE_PREFIX}test.eth`);

const response = await flagSpace('test.eth');
const body = await response.json();

expect(response.status).toBe(200);
expect(body.success).toBe(true);
expect(await getFlaggedSpacesCount()).toBe(1);
});

it('flags the space when it exists', async () => {
const response = await flagSpace('test.eth');
const body = await response.json();

expect(body).toEqual({ success: true });
expect(
(await db.queryAsync('SELECT id FROM spaces WHERE flagged = 1')).map(r => r.id)
).toEqual([`${SPACE_PREFIX}test.eth`]);
});
});

describe('when un-flagging a space', () => {
it('does nothing when the space does not exist', async () => {
const beforeFlaggedSpacesCount = await getFlaggedSpacesCount();

const response = await flagSpace('test-not-exist.eth', 'unflag');
const body = await response.json();

expect(response.status).toBe(200);
expect(body.success).toBe(false);
expect(await getFlaggedSpacesCount()).toBe(beforeFlaggedSpacesCount);
});

it('returns true when the space is not flagged', async () => {
const beforeFlaggedSpacesCount = await getFlaggedSpacesCount();
const response = await flagSpace('test.eth', 'unflag');
const body = await response.json();

expect(response.status).toBe(200);
expect(body.success).toBe(true);
expect(await getFlaggedSpacesCount()).toBe(beforeFlaggedSpacesCount);
});

it('un-flags the space when it is flagged', async () => {
await db.queryAsync('UPDATE spaces SET flagged = 1 WHERE id = ?', `${SPACE_PREFIX}test.eth`);

const response = await flagSpace('test.eth', 'unflag');
const body = await response.json();

expect(response.status).toBe(200);
expect(body.success).toBe(true);
expect(await getFlaggedSpacesCount()).toBe(0);
});
});
});
35 changes: 35 additions & 0 deletions test/fixtures/proposal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Mock return results from SQL
const ProposalsSqlFixtures: Record<string, any>[] = [
{
id: '0x01',
ipfs: '1234',
author: '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3',
created: 1688788535,
space: 'test.eth',
network: '1',
symbol: 'VOTE',
type: 'single-choice',
strategies: {},
validation: {},
plugins: {},
title: 'My first proposal',
body: 'Hello world',
discussion: '',
choices: {},
start: 1688788535,
end: 1689047735,
quorum: 0,
privacy: '',
snapshot: 9310476,
app: 'snapshot',
scores: {},
scores_by_strategy: {},
scores_state: '',
scores_total: 0,
scores_updated: 0,
votes: 0,
flagged: 0
}
];

export default ProposalsSqlFixtures;
8 changes: 4 additions & 4 deletions test/fixtures/space.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const SpacesSqlFixtures: Record<string, any>[] = [
verified: 1,
flagged: 0,
deleted: 0,
created_at: 1649844547,
updated_at: 1649844547,
created: 1649844547,
updated: 1649844547,
settings: {
name: 'Test Space',
admins: ['0xFC01614d28595d9ea5963daD9f44C0E0F0fE10f0'],
Expand All @@ -22,8 +22,8 @@ const SpacesSqlFixtures: Record<string, any>[] = [
verified: 0,
flagged: 0,
deleted: 0,
created_at: 1649844547,
updated_at: 1649844547,
created: 1649844547,
updated: 1649844547,
settings: {
name: 'Test Space 2',
admins: ['0x87D68ecFBcF53c857ABf494728Cf3DE1016b27B0'],
Expand Down
8 changes: 4 additions & 4 deletions test/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ CREATE TABLE spaces (
verified INT NOT NULL DEFAULT '0',
deleted INT NOT NULL DEFAULT '0',
flagged INT NOT NULL DEFAULT '0',
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
created BIGINT NOT NULL,
updated BIGINT NOT NULL,
PRIMARY KEY (id),
INDEX name (name),
INDEX verified (verified),
INDEX flagged (flagged),
INDEX deleted (deleted),
INDEX created_at (created_at),
INDEX updated_at (updated_at)
INDEX created (created),
INDEX updated (updated)
);

CREATE TABLE proposals (
Expand Down
16 changes: 8 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1289,10 +1289,10 @@
resolved "https://registry.yarnpkg.com/@snapshot-labs/prettier-config/-/prettier-config-0.1.0-beta.7.tgz#c8e07e7e9baabee245020a72ac05835b65139823"
integrity sha512-k/FUf4VWhwLFUmKuWs2mNvmPe691hqhvCJuujD4TfbIivWysmL1TqthwfdQUrQEAQUqVQ2ZKEiGkbufp5J27eQ==

"@snapshot-labs/snapshot-metrics@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@snapshot-labs/snapshot-metrics/-/snapshot-metrics-1.3.1.tgz#e282b443380434c454ff5800e7524c8a62d02acb"
integrity sha512-Dim9V/YgG/A4OJxpnyIBS2h43P/CBfFf5fvpDGwazJNb0DcyJPbRuw5kb48U/DCQ7aATM9XNG0s8Lj239VwTOw==
"@snapshot-labs/snapshot-metrics@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@snapshot-labs/snapshot-metrics/-/snapshot-metrics-1.4.0.tgz#f0831f9a8100c6196e8422e1366a8e5440dd6b16"
integrity sha512-WujRGrTkmNkd8kysCriOTiTqs2z4xfSuX4gqudlGw7IGxIHxyyj7zyaY6H1Y75usG3rs3ECUf+hOUSOUxA83pQ==
dependencies:
express-prom-bundle "^6.6.0"
node-fetch "^2.7.0"
Expand All @@ -1305,10 +1305,10 @@
dependencies:
"@sentry/node" "^7.60.1"

"@snapshot-labs/snapshot.js@^0.7.9":
version "0.7.9"
resolved "https://registry.yarnpkg.com/@snapshot-labs/snapshot.js/-/snapshot.js-0.7.9.tgz#6e0117b8601c794af7aefd1ad1daf4d354c8c078"
integrity sha512-YcQXQ7CeEemPHwR1HfDPNN3qdGL3xPsmP/iKiVcD93pJzl7vXOoTmKjiBW8qRPNS2JI65OY72V+5Zt4geaBWaw==
"@snapshot-labs/snapshot.js@^0.8.3":
version "0.8.3"
resolved "https://registry.yarnpkg.com/@snapshot-labs/snapshot.js/-/snapshot.js-0.8.3.tgz#a514d879d472e9da7a762c610371e65ceef3f604"
integrity sha512-BU3esG+M7NP3qAdE+Kzlxgg+mgQFsjaxCGh6MKGWLLwgkA8Jg6i/EpYf9cBhhXQSdgPuBxfXC+OtK1ATnAzRkQ==
dependencies:
"@ensdomains/eth-ens-namehash" "^2.0.15"
"@ethersproject/abi" "^5.6.4"
Expand Down

0 comments on commit 504817e

Please sign in to comment.