Skip to content

Commit

Permalink
Merge pull request #462 from social-tw/feat/UST-138-claim-reputaion-i…
Browse files Browse the repository at this point in the history
…n-background

[UST-138] [Frontend] Claim reputation in background
  • Loading branch information
Xiawpohr authored Sep 9, 2024
2 parents 5b258c5 + 2692baa commit 1e4b692
Show file tree
Hide file tree
Showing 14 changed files with 400 additions and 6 deletions.
5 changes: 5 additions & 0 deletions packages/frontend/src/constants/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export enum QueryKeys {
PostHistory = 'post_history',
CommentHistory = 'comment_history',
VoteHistory = 'vote_history',
ReportHistory = 'ReportHistory',
ReportsWaitingForTransaction = 'ReportsWaitingForTransaction',
}

export enum MutationKeys {
Expand All @@ -36,4 +38,7 @@ export enum MutationKeys {
ReportComment = 'report_comment',
Adjudicate = 'adjudicate',
CheckIn = 'check_in',
ClaimReputation = 'claim_reputation',
ClaimAdjudicatorReputation = 'claim_adjudicator_reputation',
ClaimEpochKeyReputation = 'claim_epoch_key_reputation',
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { RelayCreateReportResponse } from '@/types/api'
import {
RelayRawReportCategory,
ReportCategory,
ReportStatus,
ReportType,
} from '@/types/Report'
import { stringifyBigInts } from '@unirep/utils'
Expand Down Expand Up @@ -108,4 +109,26 @@ export class ReportService extends RelayApiService {
)
return response.data
}

async fetchWaitFotTransactionReports() {
const client = this.getAuthClient()
const userState = this.getUserState()
const { publicSignals, proof } = await userState.genEpochKeyLiteProof()

const searchParams = new URLSearchParams()
searchParams.append(
'status',
ReportStatus.WAITING_FOR_TRANSACTION.toString(),
)
searchParams.append(
'publicSignals',
JSON.stringify(stringifyBigInts(publicSignals)),
)
searchParams.append('proof', JSON.stringify(stringifyBigInts(proof)))
const response = await client.get<ReportHistory[]>(
`/report?${searchParams.toString()}`,
)

return response.data
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { UserState } from '@unirep/core'
import { toDecString } from '@unirep/core/src/Synchronizer'
import { UnirepSocialCircuit } from '@unirep-app/circuits/dist/src/types'
import { stringifyBigInts } from '@unirep/utils'
import { ReportNonNullifierProof } from '@unirep-app/circuits'

export async function genReportNonNullifierProof(
userState: UserState,
params: {
reportId: string
reportedEpochKey: bigint
reportedEpoch: number
nonce: number
},
options: {
epoch?: number
attesterId?: bigint | string
} = {},
) {
const attesterId = toDecString(
options.attesterId ?? userState.sync.attesterId,
)
const epoch =
options.epoch ?? (await userState.latestTransitionedEpoch(attesterId))

const identitySecret = userState.id.secret

const circuitInputs = {
reported_epoch_key: params.reportedEpochKey,
identity_secret: identitySecret,
reported_epoch: params.reportedEpoch,
current_epoch: epoch,
current_nonce: params.nonce,
attester_id: attesterId,
chain_id: userState.chainId,
}

const results = await userState.prover.genProofAndPublicSignals(
UnirepSocialCircuit.reportNonNullifierProof,
stringifyBigInts(circuitInputs),
)

return new ReportNonNullifierProof(
results.publicSignals,
results.proof,
userState.prover,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { UserState } from '@unirep/core'
import { toDecString } from '@unirep/core/src/Synchronizer'
import { genReportNullifier } from '@/features/core'
import { UnirepSocialCircuit } from '@unirep-app/circuits/dist/src/types'
import { stringifyBigInts } from '@unirep/utils'
import { ReportNullifierProof } from '@unirep-app/circuits'

export async function genReportNullifierProof(
userState: UserState,
params: {
reportId: string
nonce: number
},
options: {
epoch?: number
attesterId?: bigint | string
} = {},
) {
const reportId = params.reportId

const attesterId = toDecString(
options.attesterId ?? userState.sync.attesterId,
)
const epoch =
options.epoch ?? (await userState.latestTransitionedEpoch(attesterId))

const identitySecret = userState.id.secret

const reportNullifier = genReportNullifier(identitySecret, reportId)

const circuitInputs = {
report_nullifier: reportNullifier,
identity_secret: identitySecret,
report_id: reportId,
current_epoch: epoch,
current_nonce: params.nonce,
attester_id: attesterId,
chain_id: userState.chainId,
}

const results = await userState.prover.genProofAndPublicSignals(
UnirepSocialCircuit.reportNullifierProof,
stringifyBigInts(circuitInputs),
)

return new ReportNullifierProof(
results.publicSignals,
results.proof,
userState.prover,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useUserState } from '@/features/core'
import { RepUserType } from '@/types/Report'
import { useCallback, useEffect } from 'react'
import { useWaitForTransactionReport } from '@/features/reporting/hooks/useGetWaitForTransactReport/useWaitForTransactionReport'
import { useReportAdjucatorsReputation } from '@/features/reporting/hooks/useReportAdjicatorsReputation/useReportAdjucatorsReputation'
import { useReportEpochKeyRepuation } from '@/features/reporting/hooks/useReportEpochKeyReputation/useReportEpochKeyRepuation'
import { isMyEpochKey } from '@/utils/helpers/epochKey'
import { isMyAdjudicateNullifier } from '@/features/reporting/utils/helpers'

export function useBackgroundReputationClaim() {
const { data: reports } = useWaitForTransactionReport()
const { mutateAsync: claimAdjucatorRepuation } =
useReportAdjucatorsReputation()
const { mutateAsync: claimEpochKeyRepuation } = useReportEpochKeyRepuation()
const { userState } = useUserState()

const processReports = useCallback(async () => {
if (!reports || !userState) return

for (const report of reports) {
if (!report.respondentEpochKey) continue
if (
isMyEpochKey(
userState,
report.reportEpoch,
report.reportorEpochKey,
)
) {
if (!report.reportorClaimedRep) {
await claimEpochKeyRepuation({
reportId: report.reportId,
reportedEpochKey: BigInt(report.reportorEpochKey),
reportedEpoch: report.reportEpoch,
repUserType: RepUserType.REPORTER,
})
}
} else if (
isMyEpochKey(
userState,
report.object.epoch,
report.respondentEpochKey,
)
) {
if (!report.respondentClaimedRep) {
await claimEpochKeyRepuation({
reportId: report.reportId,
reportedEpochKey: BigInt(report.respondentEpochKey),
reportedEpoch: report.object.epoch,
repUserType: RepUserType.POSTER,
})
}
} else if (
report.adjudicatorsNullifier?.some(
(adj) =>
isMyAdjudicateNullifier(
userState,
report.reportId,
adj.nullifier,
) && !adj.claimed,
)
) {
await claimAdjucatorRepuation(report.reportId)
}
}
}, [reports, userState, claimEpochKeyRepuation, claimAdjucatorRepuation])

useEffect(() => {
processReports()
}, [reports, processReports])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useQuery } from '@tanstack/react-query'
import { QueryKeys } from '@/constants/queryKeys'
import { ReportService, useEpoch, useUserState } from '@/features/core'

export function useWaitForTransactionReport() {
const { getGuaranteedUserState } = useUserState()
const { currentEpoch } = useEpoch()
return useQuery({
queryKey: [QueryKeys.ReportsWaitingForTransaction, currentEpoch],
queryFn: async () => {
const userState = await getGuaranteedUserState()
const reportService = new ReportService(userState)
return reportService.fetchWaitFotTransactionReports()
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { MutationKeys, QueryKeys } from '@/constants/queryKeys'
import {
useActionCount,
useUserState,
useUserStateTransition,
useWeb3Provider,
} from '@/features/core'
import { genReportNullifierProof } from '@/features/core/utils/genReportNullifierProof'
import { getEpochKeyNonce } from '@/utils/helpers/getEpochKeyNonce'
import { relayClaimReputation } from '@/utils/api'
import { RepUserType } from '@/types/Report'

export function useReportAdjucatorsReputation() {
const { stateTransition } = useUserStateTransition()
const { getGuaranteedUserState } = useUserState()
const actionCount = useActionCount()
const { getGuaranteedProvider } = useWeb3Provider()
const queryClient = useQueryClient()

return useMutation({
mutationKey: [MutationKeys.ClaimAdjudicatorReputation],
mutationFn: async (reportId: string) => {
await stateTransition()
const userState = await getGuaranteedUserState()
const nonce = getEpochKeyNonce(actionCount)
const { publicSignals, proof } = await genReportNullifierProof(
userState,
{ reportId, nonce: nonce },
)
const result = await relayClaimReputation(
reportId,
RepUserType.VOTER,
publicSignals,
proof,
)
const provider = getGuaranteedProvider()
await provider.waitForTransaction(result.message.txHash)
await userState.waitForSync()

return result
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QueryKeys.ReputationScore],
})
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { MutationKeys, QueryKeys } from '@/constants/queryKeys'
import {
useActionCount,
useUserState,
useUserStateTransition,
useWeb3Provider,
} from '@/features/core'
import { genReportNonNullifierProof } from '@/features/core/utils/genReportNonNullifierProof'
import { getEpochKeyNonce } from '@/utils/helpers/getEpochKeyNonce'
import { relayClaimReputation } from '@/utils/api'
import { RepUserType } from '@/types/Report'

export function useReportEpochKeyRepuation() {
const { stateTransition } = useUserStateTransition()
const { getGuaranteedUserState } = useUserState()
const actionCount = useActionCount()
const { getGuaranteedProvider } = useWeb3Provider()
const queryClient = useQueryClient()

return useMutation({
mutationKey: [MutationKeys.ClaimEpochKeyReputation],
mutationFn: async ({
reportId,
reportedEpochKey,
reportedEpoch,
repUserType,
}: {
reportId: string
reportedEpochKey: bigint
reportedEpoch: number
repUserType: RepUserType
}) => {
await stateTransition()
const userState = await getGuaranteedUserState()
const nonce = getEpochKeyNonce(actionCount)

const { publicSignals, proof } = await genReportNonNullifierProof(
userState,
{
reportId,
reportedEpochKey,
reportedEpoch,
nonce,
},
)
const result = await relayClaimReputation(
reportId,
repUserType,
publicSignals,
proof,
)
const provider = getGuaranteedProvider()
await provider.waitForTransaction(result.message.txHash)
await userState.waitForSync()

return result
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QueryKeys.ReputationScore],
})
},
})
}
3 changes: 3 additions & 0 deletions packages/frontend/src/features/reporting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ export { useFetchReportCategories } from './hooks/useFetchReportCategories/useFe
export { useNotifyCheckIn } from './hooks/useNotifyCheckIn/useNotifyCheckIn'
export { usePendingReports } from './hooks/usePendingReports/usePendingReports'
export { useReputationScore } from './hooks/useReputationScore/useReputationScore'
export { useBackgroundReputationClaim } from './hooks/useBackgroundReputationClaim/useBackgroundReputationClaim'
export { useReportAdjucatorsReputation } from './hooks/useReportAdjicatorsReputation/useReportAdjucatorsReputation'
export { useReportEpochKeyRepuation } from './hooks/useReportEpochKeyReputation/useReportEpochKeyRepuation'
3 changes: 3 additions & 0 deletions packages/frontend/src/routes/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
useMatch,
useNavigate,
} from 'react-router-dom'
import { useBackgroundReputationClaim } from '@/features/reporting/hooks/useBackgroundReputationClaim/useBackgroundReputationClaim'

function NotificationContainer({ children }: { children: React.ReactNode }) {
return (
Expand All @@ -46,6 +47,8 @@ export default function AppLayout() {

const navigate = useNavigate()

useBackgroundReputationClaim()

const headerTextOnDesktop = getDesktopHeaderTextByPath(location.pathname)
const headerTextOnMobile = getMobileHeaderTextByPath(location.pathname)

Expand Down
Loading

0 comments on commit 1e4b692

Please sign in to comment.