fix: comment report content mismatch (#639)

* fix: include postId with reporting comment

* refactor: enforce postId in report to be either empty string or with value

* build: pass postId to relay to create report

* chore: fix lint

* chore: adjust test for new report history schema

* refactor: return correct content of comment report

* chore: adjust report history with new schema

* fix: typo

* fix: add test for post report with non-empty postId

* chore: fix typo
This commit is contained in:
ryanycw.eth
2025-02-11 18:14:07 +08:00
committed by GitHub
parent 5e4ecbdbac
commit 4ed050df0a
11 changed files with 101 additions and 38 deletions

View File

@@ -40,12 +40,14 @@ export class ReportService extends RelayApiService {
async createReport({
type,
objectId,
postId,
reason,
category,
identityNonce,
}: {
type: ReportType
objectId: string
postId?: string
reason: string
category: number
identityNonce: number
@@ -65,6 +67,7 @@ export class ReportService extends RelayApiService {
_reportData: {
type,
objectId,
postId: postId ?? '',
reason,
category,
reportEpoch: Number(epoch),

View File

@@ -78,6 +78,7 @@ export interface ReportPostData {
export interface ReportCommentData {
commentId: string
postId: string
epoch?: number
epochKey?: string
}

View File

@@ -10,11 +10,11 @@ import {
useUserState,
useUserStateTransition,
} from '@/features/core'
import { useSendNotification } from '@/features/notification/stores/useNotificationStore'
import { NotificationType } from '@/types/Notifications'
import { ReportType } from '@/types/Report'
import { getEpochKeyNonce } from '@/utils/helpers/getEpochKeyNonce'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useSendNotification } from '@/features/notification/stores/useNotificationStore'
import { NotificationType } from '@/types/Notifications'
export function useReportComment() {
const queryClient = useQueryClient()
@@ -50,6 +50,7 @@ export function useReportComment() {
const { epoch, epochKey } = await reportService.createReport({
type: ReportType.COMMENT,
objectId: commentId,
postId,
reason,
category,
identityNonce,
@@ -64,6 +65,7 @@ export function useReportComment() {
onMutate: (variables) => {
const reportCommentData: ReportCommentData = {
commentId: variables.commentId,
postId: variables.postId,
epoch: undefined,
epochKey: undefined,
}
@@ -86,6 +88,7 @@ export function useReportComment() {
if (context?.actionId) {
succeedActionById(context.actionId, {
commentId: data.commentId,
postId: data.postId,
epoch: data.epoch,
epochKey: data.epochKey,
})

View File

@@ -11,6 +11,7 @@ export interface ReportHistory {
reportId: string
type: ReportType // 0: Post, 1: Comment
objectId: string // PostId or CommentId
postId: string // PostId of the reported object, should be empty for post report
object: RelayRawPost | RelayRawComment
reportorEpochKey: string // Epoch Key of the person who reported
reportorClaimedRep?: boolean // TRUE: claimed, FALSE: not claimed

View File

@@ -304,6 +304,7 @@ export async function relayReport({
proof,
type,
objectId,
postId,
reportorEpochKey,
reason,
category,
@@ -312,6 +313,7 @@ export async function relayReport({
proof: EpochKeyLiteProof
type: ReportType
objectId: string
postId?: string
reportorEpochKey: string
reason: string
category: number
@@ -325,6 +327,7 @@ export async function relayReport({
_reportData: {
type,
objectId,
postId: postId ?? '',
reportorEpochKey,
reason,
category,

View File

@@ -147,6 +147,7 @@ const _schema = [
['reportId', 'String'],
['type', 'Int'],
['objectId', 'String'], // PostId or CommentId
['postId', 'String'], // PostId of the reported object
['reportorEpochKey', 'String'], // Epoch Key of the person who reported
{
name: 'reportorClaimedRep',

View File

@@ -40,6 +40,7 @@ export class ReportService {
): Promise<ReportHistory> {
// 1.a Check if the post / comment exists is not reported already(post status = 1 / comment status = 1)
if (reportData.type === ReportType.POST) {
if (reportData.postId) throw Errors.INVALID_POST_ID()
if (!Validator.isValidNumber(reportData.objectId))
throw Errors.INVALID_POST_ID()
@@ -52,6 +53,7 @@ export class ReportService {
throw Errors.POST_REPORTED()
reportData.respondentEpochKey = post.epochKey
} else if (reportData.type === ReportType.COMMENT) {
if (!reportData.postId) throw Errors.MISSING_POST_ID()
if (!Validator.isValidNumber(reportData.objectId))
throw Errors.INVALID_COMMENT_ID()
const comment = await commentService.fetchSingleComment(
@@ -92,6 +94,7 @@ export class ReportService {
reportId: reportId,
type: reportData.type,
objectId: reportData.objectId,
postId: reportData.postId,
reportorEpochKey: reportData.reportorEpochKey,
respondentEpochKey: reportData.respondentEpochKey,
reason: reportData.reason,
@@ -641,10 +644,13 @@ export class ReportService {
// Fetch the associated object (post or comment)
const tableName = report.type == ReportType.POST ? 'Post' : 'Comment'
const whereClause: any = {
[`${tableName.toLowerCase()}Id`]: report.objectId,
}
if (report.postId) whereClause.postId = report.postId
const object = await db.findOne(tableName, {
where: {
[`${tableName.toLowerCase()}Id`]: report.objectId,
},
where: whereClause,
})
// Return the report with its associated object

View File

@@ -159,6 +159,7 @@ export const Errors = {
400,
'REPORT_OBJECT_TYPE_NOT_EXISTS'
),
MISSING_POST_ID: createErrorType('Missing postId', 400, 'MISSING_POST_ID'),
INVALID_REPORT_STATUS: createErrorType(
'Invalid report status',
400,

View File

@@ -29,6 +29,7 @@ export interface ReportHistory {
reportId?: string
type: number // 0: Post, 1: Comment
objectId: string // PostId or CommentId
postId: string // PostId of the reported object
reportorEpochKey: string // Epoch Key of the person who reported
reportorClaimedRep?: boolean // TRUE: claimed, FALSE: not claimed
respondentEpochKey?: string // Epoch Key of the person who was reported

View File

@@ -139,12 +139,45 @@ describe('POST /api/report', function () {
await stopServer('report', snapshot, sync, express)
})
it('should fail to create a report on post with non-empty postId', async function () {
const postId = '0'
const userState = await genUserState(users[0].id, app, prover)
const reportData: ReportHistory = {
type: ReportType.POST,
objectId: postId,
postId: postId,
reportorEpochKey: 'epochKey1',
reason: 'Inappropriate content',
category: ReportCategory.SPAM,
reportEpoch: sync.calcCurrentEpoch(),
}
const reputationProof = await userState.genProveReputationProof({
epkNonce: nonce,
})
await express
.post('/api/report')
.set('content-type', 'application/json')
.send(
stringifyBigInts({
_reportData: reportData,
publicSignals: reputationProof.publicSignals,
proof: reputationProof.proof,
})
)
.then((res) => {
expect(res).to.have.status(400)
expect(res.body.error).to.be.equal('Invalid postId')
})
})
it('should create a report and update post status', async function () {
const postId = '0'
const userState = await genUserState(users[0].id, app, prover)
const reportData: ReportHistory = {
type: ReportType.POST,
objectId: postId,
postId: '',
reportorEpochKey: 'epochKey1',
reason: 'Inappropriate content',
category: ReportCategory.SPAM,
@@ -196,9 +229,12 @@ describe('POST /api/report', function () {
it('should fail to create a report with invalid proof', async function () {
const userState = await genUserState(users[0].id, app, prover)
const commentId = '0'
const postId = '0'
const reportData: ReportHistory = {
type: ReportType.COMMENT,
objectId: '0',
objectId: commentId,
postId: postId,
reportorEpochKey: 'epochKey1',
reason: 'Spam',
category: ReportCategory.SPAM,
@@ -227,12 +263,47 @@ describe('POST /api/report', function () {
})
})
it('should fail to create a report on comment with empty postId', async function () {
const userState = await genUserState(users[0].id, app, prover)
const commentId = '0'
const postId = ''
const reportData: ReportHistory = {
type: ReportType.COMMENT,
objectId: commentId,
postId: postId,
reportorEpochKey: 'epochKey1',
reason: 'Spam',
category: ReportCategory.SPAM,
reportEpoch: sync.calcCurrentEpoch(),
}
const reputationProof = await userState.genProveReputationProof({
epkNonce: nonce,
})
await express
.post('/api/report')
.set('content-type', 'application/json')
.send(
stringifyBigInts({
_reportData: reportData,
publicSignals: reputationProof.publicSignals,
proof: reputationProof.proof,
})
)
.then((res) => {
expect(res).to.have.status(400)
expect(res.body.error).to.be.equal('Missing postId')
})
})
it('should create a report and update comment status', async function () {
const commentId = '0'
const postId = '0'
const userState = await genUserState(users[0].id, app, prover)
const reportData: ReportHistory = {
type: ReportType.COMMENT,
objectId: commentId,
postId: postId,
reportorEpochKey: 'epochKey1',
reason: 'Spam',
category: ReportCategory.SPAM,
@@ -297,6 +368,7 @@ describe('POST /api/report', function () {
const reportData: ReportHistory = {
type: ReportType.POST,
objectId: '0',
postId: '',
reportorEpochKey: 'epochKey1',
reason: 'Inappropriate content',
category: ReportCategory.SPAM,
@@ -327,6 +399,7 @@ describe('POST /api/report', function () {
const reportData: ReportHistory = {
type: ReportType.POST,
objectId: 'non-existent',
postId: '',
reportorEpochKey: 'epochKey1',
reason: 'Non-existent post',
category: ReportCategory.SPAM,

View File

@@ -56,13 +56,11 @@ describe('Reputation Claim', function () {
let repoterUserState: UserState
let repoterEpochKey: EpochKeyLiteProof
let posterEpochKey: EpochKeyLiteProof
let voterEpochKey: EpochKeyLiteProof
let attesterId: bigint
let reportedEpochKey: bigint
let nullifier: BigInt
let voter2: IdentityObject
let voter2UserState: UserState
let voter2EpochKey: EpochKeyLiteProof
let nullifier2: BigInt
const EPOCH_LENGTH = 3000
@@ -89,26 +87,6 @@ describe('Reputation Claim', function () {
: [newAdjudicator]
}
async function findReportWithNullifier(
db: DB,
epoch: number,
nullifier: string,
status: ReportStatus
) {
const reports = await db.findMany('ReportHistory', {
where: {
reportEpoch: epoch,
status: status,
},
})
return reports.find((report) =>
report.adjudicatorsNullifier.some(
(adj) => adj.nullifier === nullifier
)
)
}
before(async function () {
this.timeout(6000000)
snapshot = await ethers.provider.send('evm_snapshot', [])
@@ -127,9 +105,7 @@ describe('Reputation Claim', function () {
sync = synchronizer
unirep = _unirep
// poster = await userService.getLoginUser(db, 'poster', undefined)
poster = createRandomUserIdentity()
const wallet = ethers.Wallet.createRandom()
posterUserState = await signUp(poster, {
app,
db,
@@ -142,9 +118,7 @@ describe('Reputation Claim', function () {
expect(hasSignedUp).equal(true)
console.log('poster register success...')
// reporter = await userService.getLoginUser(db, 'reporter', undefined)
reporter = createRandomUserIdentity()
const wallet2 = ethers.Wallet.createRandom()
repoterUserState = await signUp(reporter, {
app,
db,
@@ -157,9 +131,7 @@ describe('Reputation Claim', function () {
expect(hasSignedUp2).equal(true)
console.log('reporter register success...')
// voter = await userService.getLoginUser(db, 'voter', undefined)
voter = createRandomUserIdentity()
const wallet3 = ethers.Wallet.createRandom()
voterUserState = await signUp(voter, {
app,
db,
@@ -173,7 +145,6 @@ describe('Reputation Claim', function () {
console.log('voter register success...')
voter2 = createRandomUserIdentity()
const wallet4 = ethers.Wallet.createRandom()
voter2UserState = await signUp(voter2, {
app,
db,
@@ -186,13 +157,10 @@ describe('Reputation Claim', function () {
expect(hasSignedUp4).equal(true)
console.log('voter2 register success...')
voter2EpochKey = await voter2UserState.genEpochKeyLiteProof()
chainId = await unirep.chainid()
repoterEpochKey = await repoterUserState.genEpochKeyLiteProof()
posterEpochKey = await posterUserState.genEpochKeyLiteProof()
voterEpochKey = await voterUserState.genEpochKeyLiteProof()
attesterId = posterUserState.sync.attesterId
@@ -211,6 +179,7 @@ describe('Reputation Claim', function () {
reportId,
type: ReportType.POST,
objectId: postId,
postId: '',
reportorEpochKey: repoterEpochKey.epochKey.toString(),
respondentEpochKey: posterEpochKey.epochKey.toString(),
reason: 'Test reason',
@@ -291,6 +260,7 @@ describe('Reputation Claim', function () {
reportId: reportId2,
type: ReportType.POST,
objectId: postId2,
postId: '',
reportorEpochKey: repoterEpochKey.epochKey.toString(),
respondentEpochKey: posterEpochKey.epochKey.toString(),
reason: 'Test reason for failed report',