Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Allow shutter in proposal privacy if space is using shutter privacy #475

Merged
merged 22 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bada402
fix: update privacy check logic in proposal
ChaituVR Dec 5, 2024
cfe4691
remove unwanted log
ChaituVR Dec 5, 2024
ca5306e
fix
ChaituVR Dec 5, 2024
0fa0bc0
Merge branch 'master' into fix-privacy-check
ChaituVR Dec 6, 2024
d4587b7
Merge branch 'master' into fix-privacy-check
ChaituVR Dec 7, 2024
a9ef94e
test: add tests for proposal privacy
wa0x6e Dec 8, 2024
6647ceb
Merge branch 'fix-privacy-check' of https://github.com/snapshot-labs/…
wa0x6e Dec 8, 2024
ff07b9f
Merge branch 'master' into fix-privacy-check
ChaituVR Dec 8, 2024
a5faf82
Merge branch 'master' of https://github.com/snapshot-labs/snapshot-se…
ChaituVR Dec 9, 2024
71b1fa4
Merge branch 'master' into fix-privacy-check
ChaituVR Jan 4, 2025
f828941
Merge branch 'fix-privacy-check' of https://github.com/snapshot-labs/…
ChaituVR Jan 4, 2025
2b4c67f
fix tests
ChaituVR Jan 4, 2025
b848c06
Update test/schema.sql
ChaituVR Jan 7, 2025
7220696
Remove duplicate test
ChaituVR Jan 7, 2025
534877e
Merge branch 'master' of https://github.com/snapshot-labs/snapshot-se…
ChaituVR Jan 7, 2025
ffafd19
test: remove superfluous assertions
wa0x6e Jan 7, 2025
129bf30
Merge branch 'fix-privacy-check' of https://github.com/snapshot-labs/…
ChaituVR Jan 7, 2025
6cf8c8c
Merge branch 'master' into fix-privacy-check
ChaituVR Jan 7, 2025
98c1f40
Merge branch 'fix-privacy-check' of https://github.com/snapshot-labs/…
ChaituVR Jan 7, 2025
6ed7a53
fix handle both undefined and empty string cases in proposal
ChaituVR Jan 7, 2025
66585e0
test: update test for new privacy behavior when missing
wa0x6e Jan 7, 2025
adf757c
Merge branch 'fix-privacy-check' of https://github.com/snapshot-labs/…
wa0x6e Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ingestor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export default async function ingestor(req) {
body: message.body,
discussion: message.discussion || '',
choices: message.choices,
privacy: message.privacy || '',
privacy: message.privacy,
labels: message.labels || [],
start: message.start,
end: message.end,
Expand All @@ -169,7 +169,7 @@ export default async function ingestor(req) {
name: message.title,
body: message.body,
discussion: message.discussion || '',
privacy: message.privacy || '',
privacy: message.privacy,
choices: message.choices,
labels: message.labels || [],
metadata: {
Expand Down
9 changes: 6 additions & 3 deletions src/writer/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ export async function verify(body): Promise<any> {
if (msg.payload.type !== space.voting.type) return Promise.reject('invalid voting type');
}

if (space.voting?.privacy !== 'any' && msg.payload.privacy) {
const spacePrivacy = space.voting?.privacy ?? 'any';
const proposalPrivacy = msg.payload.privacy;

if (proposalPrivacy !== undefined && spacePrivacy !== 'any' && spacePrivacy !== proposalPrivacy) {
return Promise.reject('not allowed to set privacy');
}

Expand Down Expand Up @@ -210,9 +213,9 @@ export async function action(body, ipfs, receipt, id): Promise<void> {
const plugins = JSON.stringify(metadata.plugins || {});
const spaceNetwork = spaceSettings.network;
const proposalSnapshot = parseInt(msg.payload.snapshot || '0');
let privacy = spaceSettings.voting?.privacy || '';
let privacy = spaceSettings.voting?.privacy ?? 'any';
if (privacy === 'any') {
privacy = msg.payload.privacy;
privacy = msg.payload.privacy ?? '';
}

let quorum = spaceSettings.voting?.quorum || 0;
Expand Down
9 changes: 6 additions & 3 deletions src/writer/update-proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ export async function verify(body): Promise<any> {

if (proposal.author !== body.address) return Promise.reject('Not the author');

if (space.voting?.privacy !== 'any' && msg.payload.privacy) {
const spacePrivacy = space.voting?.privacy ?? 'any';
const proposalPrivacy = msg.payload.privacy;

if (proposalPrivacy !== undefined && spacePrivacy !== 'any' && spacePrivacy !== proposalPrivacy) {
return Promise.reject('not allowed to set privacy');
}

Expand All @@ -68,9 +71,9 @@ export async function action(body, ipfs): Promise<void> {
const metadata = msg.payload.metadata || {};
const plugins = JSON.stringify(metadata.plugins || {});
const spaceSettings = await getSpace(msg.space);
let privacy = spaceSettings.voting?.privacy || '';
let privacy = spaceSettings.voting?.privacy ?? 'any';
if (privacy === 'any') {
privacy = msg.payload.privacy;
privacy = msg.payload.privacy ?? '';
}

const proposal = {
Expand Down
136 changes: 126 additions & 10 deletions test/unit/writer/proposal.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import omit from 'lodash/omit';
import * as writer from '../../../src/writer/proposal';
import input from '../../fixtures/writer-payload/proposal.json';
import { spacesGetSpaceFixtures } from '../../fixtures/space';
import {
ACTIVE_PROPOSAL_BY_AUTHOR_LIMIT,
ECOSYSTEM_SPACE_PROPOSAL_DAY_LIMIT,
FLAGGED_SPACE_PROPOSAL_DAY_LIMIT,
SPACE_PROPOSAL_DAY_LIMIT,
VERIFIED_SPACE_PROPOSAL_DAY_LIMIT,
ECOSYSTEM_SPACE_PROPOSAL_MONTH_LIMIT,
FLAGGED_SPACE_PROPOSAL_DAY_LIMIT,
FLAGGED_SPACE_PROPOSAL_MONTH_LIMIT,
SPACE_PROPOSAL_MONTH_LIMIT,
VERIFIED_SPACE_PROPOSAL_MONTH_LIMIT,
MAINNET_ECOSYSTEM_SPACES,
ACTIVE_PROPOSAL_BY_AUTHOR_LIMIT,
SPACE_PROPOSAL_DAY_LIMIT,
SPACE_PROPOSAL_MONTH_LIMIT,
TURBO_SPACE_PROPOSAL_DAY_LIMIT,
TURBO_SPACE_PROPOSAL_MONTH_LIMIT
TURBO_SPACE_PROPOSAL_MONTH_LIMIT,
VERIFIED_SPACE_PROPOSAL_DAY_LIMIT,
VERIFIED_SPACE_PROPOSAL_MONTH_LIMIT
} from '../../../src/helpers/limits';
import * as writer from '../../../src/writer/proposal';
import { spacesGetSpaceFixtures } from '../../fixtures/space';
import input from '../../fixtures/writer-payload/proposal.json';

const FLAGGED_ADDRESSES = ['0x0'];

Expand Down Expand Up @@ -69,6 +69,12 @@ mockGetProposalsCount.mockResolvedValue([
}
]);

function updateInputPayload(input: any, payload: any) {
const msg = JSON.parse(input.msg);

return { ...input, msg: JSON.stringify({ ...msg, payload: { ...msg.payload, ...payload } }) };
}

describe('writer/proposal', () => {
afterEach(jest.clearAllMocks);

Expand Down Expand Up @@ -294,6 +300,116 @@ describe('writer/proposal', () => {
expect(mockSnapshotUtilsValidate).toHaveBeenCalledTimes(0);
});
});

describe('when the space is using ANY privacy', () => {
beforeEach(() => {
mockGetSpace.mockResolvedValueOnce({
...spacesGetSpaceFixtures,
voting: { privacy: 'any' }
});
});

it('accepts a proposal with shutter privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
).resolves.toBeUndefined();
});

it('accepts a proposal with undefined privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: undefined }))
).resolves.toBeUndefined();
});

it('accepts a proposal with no privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: '' }))
).resolves.toBeUndefined();
});
});

// Fallback as if { privacy: 'any' }
describe('when the space is missing the privacy settings', () => {
beforeEach(() => {
mockGetSpace.mockResolvedValueOnce({
...spacesGetSpaceFixtures,
voting: undefined
});
});

it('accepts a proposal with shutter privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
).resolves.toBeUndefined();
});

it('accepts a proposal with undefined privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: undefined }))
).resolves.toBeUndefined();
});

it('accepts a proposal with no privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: '' }))
).resolves.toBeUndefined();
});
});

describe('when the space is using NO privacy', () => {
beforeEach(() => {
mockGetSpace.mockResolvedValueOnce({
...spacesGetSpaceFixtures,
voting: { privacy: '' }
});
});

it('rejects a proposal with shutter privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
).rejects.toMatch('not allowed to set privacy');
});

it('accepts a proposal with undefined privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: undefined }))
).resolves.toBeUndefined();
});

it('accepts a proposal with no privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: '' }))
).resolves.toBeUndefined();
});
});

describe('when the space is using SHUTTER privacy', () => {
beforeEach(() => {
mockGetSpace.mockResolvedValueOnce({
...spacesGetSpaceFixtures,
voting: { privacy: 'shutter' }
});
});

it('accepts a proposal with shutter privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: 'shutter' }))
).resolves.toBeUndefined();
});

it('accepts a proposal with undefined privacy', () => {
return expect(
writer.verify(updateInputPayload(input, { privacy: undefined }))
).resolves.toBeUndefined();
});

it('rejects a proposal with privacy empty string', async () => {
expect.assertions(1);
await expect(writer.verify(updateInputPayload(input, { privacy: '' }))).rejects.toMatch(
'not allowed to set privacy'
);
});
});
});

it('rejects if the snapshot is in the future', async () => {
Expand Down
Loading