Skip to content

feat(indexer): BFT multi-node consensus protocol with state roots and gossip (#536)#578

Open
MarcusDavidG wants to merge 5 commits into
Epta-Node:mainfrom
MarcusDavidG:feat/indexer-bft-consensus-536
Open

feat(indexer): BFT multi-node consensus protocol with state roots and gossip (#536)#578
MarcusDavidG wants to merge 5 commits into
Epta-Node:mainfrom
MarcusDavidG:feat/indexer-bft-consensus-536

Conversation

@MarcusDavidG

Copy link
Copy Markdown
Contributor

Summary

Closes #536

Implements a Byzantine-fault-tolerant multi-node indexer consensus protocol using optimistic replication with cryptographic state roots.

Changes

Phase 1 — ADR

  • docs/ADR-003-indexer-consensus.md: evaluates Raft, PBFT/Tendermint, and optimistic replication; selects the state-root gossip approach, justified by Soroban events being deterministically totally ordered. Includes CAP trade-off analysis and failure mode coverage table.

Phase 2 — Cryptographic State Root

  • migrations/006_indexer_state.sql: indexer_state(ledger_sequence, state_root) table
  • src/stateRoot.ts: deterministic Merkle tree over sorted row hashes for posts/follows/profiles/pools; computeStateRoot, saveStateRoot, getStateRoot
  • src/api/routes/stateRoot.ts: GET /api/state-root?ledger=N
  • src/cli/verify-state.ts: re-derives root from DB and compares to --trusted-root or --peer; exits 1 on mismatch

Phase 3 — Gossip & Divergence Detection

  • src/gossip.ts: HTTP gossip loop broadcasting (ledger, state_root) to INDEXER_PEERS; emits DIVERGENCE_DETECTED structured log; binary-search reconciliation; self-fences with SELF_FENCED alert when >= DIVERGENCE_THRESHOLD peers disagree
  • src/api/index.ts: 503 SELF_FENCED middleware blocks all /api traffic when fenced
  • src/index.ts: saves state root after each new ledger; starts gossip background loop

Tests

  • src/__tests__/stateRoot.test.ts: determinism (same data → same root twice), different data → different root, merkleRoot edge cases
  • src/__tests__/gossip.test.ts: divergence detection logs DIVERGENCE_DETECTED, self-fencing sets isFenced() and logs SELF_FENCED, fenced node returns 503

Test Results

All 66 tests pass (8 suites).

…#536)

- docs/ADR-003-indexer-consensus.md: evaluates Raft, PBFT/Tendermint and
  optimistic replication; selects state-root gossip approach given Soroban
  events are already totally ordered and deterministic

- migrations/006_indexer_state.sql: indexer_state(ledger_sequence, state_root)

- src/stateRoot.ts: deterministic Merkle state root over posts/follows/
  profiles/pools; saveStateRoot persists per ledger; getStateRoot retrieves

- src/gossip.ts: HTTP gossip loop broadcasting latest (ledger, state_root)
  to INDEXER_PEERS; emits DIVERGENCE_DETECTED on mismatch; self-fences
  (SELF_FENCED) when >= DIVERGENCE_THRESHOLD peers disagree

- src/api/routes/stateRoot.ts: GET /api/state-root?ledger=N

- src/api/index.ts: mounts state-root route; blocks all /api traffic with
  503 SELF_FENCED when node is fenced

- src/index.ts: saves state root after each new ledger; starts gossip loop

- src/cli/verify-state.ts: re-derives state root and compares to --trusted-root
  or --peer; exits 1 on mismatch

- src/__tests__/stateRoot.test.ts: determinism tests for merkleRoot and
  computeStateRoot

- src/__tests__/gossip.test.ts: divergence detection, self-fencing, and
  503 API response tests

All 66 tests pass.
@devJaja devJaja self-requested a review June 20, 2026 02:36
@devJaja

devJaja commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Solid implementation @MarcusDavidG
fix the CI checks error then ready to merge

@MarcusDavidG

Copy link
Copy Markdown
Contributor Author

cl checks error fixed @devJaja we're good to go

@devJaja devJaja left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @MarcusDavidG
The architecture and overall design are correct. The ADR reasoning is sound. The self-fencing mechanism, 503 middleware, and saveStateRoot idempotency are all good. But the reconciliation stub is the one thing that could mislead an operator into thinking their node recovered when it hasn't — that needs to be either implemented or explicitly surfaced as a known limitation with a tracking issue. The md5/sha256 mismatch also needs fixing before the state root can be considered trustworthy for its stated purpose.

Request changes on the reconciliation stub and md5 issue. All other notes are non-blocking.

@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

@MarcusDavidG is attempting to deploy a commit to the Jaja's projects Team on Vercel.

A member of the Team first needs to authorize it.

@MarcusDavidG

Copy link
Copy Markdown
Contributor Author

thanks @devJaja ...requested changes have been addressed, please review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(indexer): design and implement a Byzantine-fault-tolerant multi-node indexer consensus protocol for Linkora's social graph state

2 participants