Skip to content

Question: Gateway workflow design vs contract access control (E2E finding) #26

Description

@gilbertsahumada

Background

The gateway has 236 unit/integration tests, but they all mock contract interactions. This means any mismatch between what the gateway expects and what the contracts actually do goes undetected until a real deployment.

To close this gap, I'm building an E2E test suite that deploys the full ChaosChain contract stack on an Anvil local testnet and runs gateway workflows against it. The approach is to dockerize both the gateway and the blockchain (Anvil), so tests exercise the real contract calls end-to-end without mocks. Running against real contracts exposed one design question and two gateway-side bugs.

Related: #25 (ABI alignment fix — first finding from this effort)

Before proposing fixes, I need to clarify the gateway's intended role.


Core question: Who runs the gateway?

The RegisterWorkStep and RegisterValidatorStep in the gateway call registerWork() and registerValidator() on RewardsDistributor. These functions are onlyOwner by design.

This means the gateway's signer must be the RewardsDistributor owner for these steps to succeed. Today the workflow uses input.signer_address (the worker/validator) for these calls, which will always revert unless that address happens to be the owner.

Two possible interpretations:

  1. The gateway is always operated by ChaosChain (the owner) — the workflow should use the gateway's own signer (which is the owner) for registerWork/registerValidator calls, not the worker/validator signer.
  2. The gateway is meant to be run by anyone — the contract access control would need to change to allow this, but that's a contract design decision, not a gateway bug.

Which is the intended model? The answer determines how to fix the workflow.


Additional finding (gateway-side bug)

This is an issue in the gateway's own logic, independent of the question above.

1. RegisterValidatorStep precondition blocks direct mode

Where: src/workflows/score-submission.ts#L955

The step checks if (!progress.reveal_confirmed) before proceeding. In commit-reveal mode this works — AwaitRevealConfirmStep sets reveal_confirmed = true. But in direct mode, AwaitScoreConfirmStep sets score_confirmed = true instead. Since reveal_confirmed is never set, the precondition always fails and the workflow ends in FAILED.

Suggested fix: if (!progress.reveal_confirmed && !progress.score_confirmed)

2. Reconciliation ADVANCE_TO_STEP skips progress state

Where: src/workflows/reconciliation.ts#L227 and #L653

When reconciliation detects work already exists on-chain and advances from SUBMIT_WORK_ONCHAIN to REGISTER_WORK, it updates the step but doesn't set progress.onchain_confirmed = true. Any downstream logic that checks onchain_confirmed won't see it, even though the work is confirmed on-chain.


How this was found

Local E2E testing with:

  • Anvil local testnet (port 8546, --block-time 1)
  • Full contract deployment via Foundry script: MockIdentityRegistry → ChaosChainRegistry → RewardsDistributor → StudioProxyFactory → ChaosCore → PredictionMarketLogic
  • Gateway with EthersChainAdapter, InMemoryWorkflowPersistence, MockArweaveAdapter

Result

Test suite Result
Unit/integration (236 tests) All pass
E2E (4 tests) 1 pass, 3 STALLED

The E2E infrastructure (Docker Compose with Anvil + gateway + PostgreSQL) will be added in a follow-up PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions