Skip to content

Feat/349 352 v2 evidence verification disputes events - #387

Merged
dDevAhmed merged 4 commits into
DigiNodes:mainfrom
abdoolyaro:feat/349-352-v2-evidence-verification-disputes-events
Aug 31, 2026
Merged

Feat/349 352 v2 evidence verification disputes events#387
dDevAhmed merged 4 commits into
DigiNodes:mainfrom
abdoolyaro:feat/349-352-v2-evidence-verification-disputes-events

Conversation

@abdoolyaro

Copy link
Copy Markdown
Contributor

closes #349
closes #350
closes #351
closes #352

…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).
@dDevAhmed
dDevAhmed merged commit 85fc094 into DigiNodes:main Aug 31, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants