Feat/349 352 v2 evidence verification disputes events - #387
Merged
dDevAhmed merged 4 commits intoAug 31, 2026
Merged
Conversation
…Nodes#352, V2-BE-011) Implements the V2-BE-011 boundary: contracts emit facts, this pipeline decodes and normalizes them into v2_canonical_events, and every downstream V2 read model consumes only that table. - ArtifactRegistryService: fail-closed lookup of the approved ABI for a (chainId, contractAddress) pair. No row or an unapproved row means the address is never decoded. - EventDecoderService: decode + normalize a raw log. Explicit outcomes for unknown signature, decode error, and artifact drift (ABI decodes an event name with no canonical schema mapping yet), never a silent drop. - CanonicalEventsService: orchestrates ingest -> idempotent persist -> same-transaction checkpoint advance. Replaying an identical (chainId, txHash, logIndex) is a no-op; the safe-block checkpoint only ever moves forward. - CanonicalEventQueryService: deterministic (blockNumber, logIndex) ordered reads for downstream projectors. - Migration for v2_canonical_events, v2_contract_artifacts, v2_event_checkpoints, v2_event_quarantine. Audit of overlapping current code: found three pre-existing, inconsistent indexer paths (src/blockchain/{indexer-checkpoint,processed-event} + blockchain-indexer.service.ts; src/entities/{indexed-event,indexing-state} + src/indexer/event-indexer.service.ts, which isn't even wired into app.module.ts) plus an API-authoritative Evidence/Claim module. None of those paths are touched or removed by this commit; V2-BE-011 is additive and namespaced under src/v2/ so the legacy paths keep running unmodified until their own superseding issues land and a maintainer decides how to retire them. This commit only documents the audit finding. Dependencies and assumed interfaces (flagged for review): V2-BE-011 depends on V2-BE-008 (approved artifact import) and V2-BE-010 (reorg-safe checkpoint store), neither of which has merged or has a frozen interface. ContractArtifact and EventCheckpoint here are deliberately minimal stand-ins scoped to exactly what this pipeline needs to fail closed and stay replay-safe. They are expected to be reconciled or replaced once V2-BE-008/010 land. The per-event-name field mapping in event-schema-registry.ts assumes argument names (actor/claimId/roundId/ asset/amount) following the vocabulary used in the V2 issue text itself, since no frozen ABI exists yet to read real argument names from; this is the highest-risk assumption in this PR and should be checked against the real approved ABI as soon as it exists. Tests: 17 passing (6 decoder unit, 5 artifact-registry unit, 6 integration against a real sqlite-backed DB covering idempotency, quarantine on every branch, and checkpoint monotonicity).
…igiNodes#349, V2-BE-013) Implements V2-BE-013 on top of the V2-BE-011 canonical event pipeline. - ProjectEvidence: current-state read model (currentVersion, status, contentDigest) keyed by evidenceId. - ProjectEvidenceVersion: append-only version history. EvidenceReplaced always appends a new row rather than mutating one, so removal never destroys prior versions and history is always fully reconstructable from events alone. - EvidenceProjectorService consumes EvidenceRegistered/Replaced/Removed canonical events via CanonicalEventQueryService, using a shared ProjectorCursor for resumable, ordered consumption. Idempotent: a unique constraint on (eventTxHash, eventLogIndex) makes replaying an already applied event a no-op rather than a duplicate version row. - EvidenceQueryService + EvidenceController: read-only GET endpoints only (list current evidence for a claim, keyset-paginated version history). No POST/PUT/DELETE exists on this controller by design, satisfying the "no backend-authoritative protocol mutation" acceptance criterion; a controller test asserts only GET handlers exist. - Migration for v2_project_evidence, v2_project_evidence_version, and the shared v2_projector_cursors table used by this and future V2 projectors. Audit: supersedes the legacy `evidences`/`evidence_versions` tables (src/claims/entities/evidence*.entity.ts), which are directly writable via EvidenceController/EvidenceService and are therefore the "backend authoritative" pattern this issue's scope replaces. Not removed in this commit -- the legacy Evidence module stays live and unmodified pending a maintainer decision on cutover/retirement, since removing it is a consumer-facing breaking change outside this issue's stated scope. Flagged assumption (dependency risk): treats evidenceId == claimId (one evidence slot per claim), matching the legacy Evidence entity's 1:1 Claim<->Evidence shape, since V2-BE-012's frozen claim-projection interface (a stated dependency of this issue) does not exist yet and no event payload field for a distinct evidence slot id was available to project from. If the approved protocol supports multiple evidence slots per claim, this projector's evidenceId derivation will need revisiting once V2-BE-012 lands. Also inherits event-schema-registry.ts's argument-name assumptions from DigiNodes#352. Tests: 8 passing (5 projector integration against a real sqlite-backed DB covering registration, replacement, removal, replay-safety, and keyset pagination; 3 controller unit tests).
…ights (DigiNodes#350, V2-BE-014) Implements V2-BE-014 on top of the V2-BE-011 canonical event pipeline. - ProjectVerificationRound: isolates first-round and appeal-round records via roundType + a per-type roundNumber sequence, rather than sharing one undifferentiated counter, per this issue's AC. - ProjectParticipantPosition: stake, reputation input, and effective weight stored verbatim from the event, never recomputed here -- reimplementing protocol outcome logic in the API is an explicit non-goal. All three are decimal strings. - VerificationProjectorService detects and records, rather than silently dropping or overwriting: * duplicates -- a second event committing a position for a participant who already has one in that round (unique constraint on (roundId, participant) distinguished from a same-event replay via the (eventTxHash, eventLogIndex) unique constraint) * out-of-order arrivals -- a PositionCommitted whose round hasn't been projected yet Both are written to the new shared v2_indexing_anomalies table (sourceModule-tagged so V2-BE-016 and future projectors can reuse it instead of each needing their own anomaly table). - Read-only query service + controller (rounds by claim, split first/appeal; positions by round). - Migration for v2_project_verification_round, v2_project_participant_position, and v2_indexing_anomalies. Flagged assumption (dependency risk): payload keys read for round/position detail (roundType, roundNumber, deadline, stake, reputationInput, effectiveWeight, verdict) follow the vocabulary in the V2-BE-014 issue text itself, since V2-BE-008's approved ABI has not landed. Documented in event-schema-registry.ts and at the top of verification-projector.service.ts; expect to reconcile against the real ABI once V2-BE-008 lands. Tests: 5 integration tests against a real sqlite-backed DB: round isolation, position projection with verbatim stake/weight, duplicate detection, out-of-order detection, and replay-safety.
…, V2-BE-016)
Implements V2-BE-016 on top of the V2-BE-011 canonical event pipeline.
- ProjectDispute: disputeId is derived deterministically as
`${claimId}:${originalRoundId}` rather than taken from an assumed event
field, so it is always reconstructable from the DisputeRaised event
alone and links deterministically to the original claim and provisional
outcome, per this issue's AC.
- DisputesProjectorService implements an explicit, fail-closed state
machine: RAISED -> RESOLVED, RAISED -> EXPIRED. Any other transition
(resolving/expiring a dispute that was never raised, or one that's
already terminal) is rejected and recorded as an INVALID_TRANSITION
anomaly in the shared v2_indexing_anomalies table rather than silently
applied, per the AC to represent invalid transitions explicitly.
Duplicate DisputeRaised events for the same claim/round are similarly
detected and recorded rather than overwriting the original.
- challengeBond/challengeBondAsset/resolvedOutcome are stored verbatim
from events, never recomputed.
- Read-only query service + controller (list disputes for a claim; fetch
by original round).
- Migration for v2_project_dispute.
Flagged assumption (dependency risk): this issue also depends on
V2-BE-012 (claim projection) and V2-BE-015 (stake/bond model), neither of
which has merged. claimId and challengeBond/asset are treated as opaque
values taken verbatim from canonical events rather than joined against a
real claim or stake projection, since neither exists yet. appealRoundId
correlation (from DisputeRaised.payload.appealRoundId) is also flagged:
no frozen ABI exists to confirm whether/how the protocol correlates a
dispute to its appeal round; this may need rework once V2-BE-008/012/015
land. Inherits event-schema-registry.ts's other argument-name assumptions.
Tests: 7 integration tests against a real sqlite-backed DB covering
raise/resolve/expire, both invalid-transition cases, duplicate-raise
detection, and replay-safety.
---
This completes the four-issue slice on this branch:
- DigiNodes#352 (V2-BE-011): canonical event decode/normalize pipeline
- DigiNodes#349 (V2-BE-013): evidence read models
- DigiNodes#350 (V2-BE-014): verification rounds/positions/weights
- DigiNodes#351 (V2-BE-016): disputes and appeal lifecycle
37 tests passing total, lint and typecheck clean (excluding one
pre-existing, unrelated baseline failure in src/claims/evidence.service.ts
already present on main -- see PR description).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #349
closes #350
closes #351
closes #352