PR C: wire real NotaryRegistry on Chain 138 (arch step 4) #7

Merged
nsatoshi merged 0 commits from devin/1776875569-notary-chain-wiring into main 2026-04-22 10:11:54 -07:00
Owner

Implements step 4 from the architecture gap-analysis — the ledger-anchor layer (arch §4.5 + §5.7) stops being a mock and starts writing to the deployed NotaryRegistry on Chain 138. Independent of PRs A/B; can land in any order.

What lands

services/notaryChain.ts (new)

ethers-v6 adapter. Minimal ABI (two functions, two events, one view) lifted from contracts/interfaces/INotaryRegistry.sol:

fn purpose
registerPlan(bytes32 planId, Step[] steps, address creator) anchor planHash on-chain
finalizePlan(bytes32 planId, bool success) record commit/abort disposition
getPlan(bytes32) read-side for GET /api/plans/:id/proof (wired later)

Public surface:

  • anchorPlan(plan){ mode: 'chain'|'mock', txHash?, planHash, blockNumber?, contractAddress? }
  • finalizeAnchor(planId, success){ mode, txHash?, receiptHash?, blockNumber? }
  • computePlanHash(plan) and planIdToBytes32(planId) — deterministic helpers so the mock and chain paths publish identical hashes for identical inputs.

A singleton contract/wallet is cached after first use, keyed on contractAddress, and can be reset via __resetForTests().

Graceful mock fallback

If any of CHAIN_138_RPC_URL, NOTARY_REGISTRY_ADDRESS, ORCHESTRATOR_PRIVATE_KEY is unset, both functions return mode: 'mock' and log the reason. Unit tests, CI, and local dev require no extra wiring.

services/notary.ts (refactored)

Still the single entry point for ExecutionCoordinator; now delegates to notaryChain and surfaces the extra on-chain fields (mode, txHash, blockNumber, contractAddress, receiptHash) to callers. If the chain call throws, we log + fall back to mock so a momentary RPC outage cannot take the workflow down.

config/env.ts

CHAIN_138_RPC_URL          url, optional    (e.g. https://rpc.d-bis.org)
CHAIN_138_CHAIN_ID         digits, optional (default 138)
NOTARY_REGISTRY_ADDRESS    0x-40 hex, optional
ORCHESTRATOR_PRIVATE_KEY   0x-64 hex, optional

All four regex-validated. Missing envs do not fail startup.

Tests

tests/unit/notaryChain.test.ts — 6 cases: planIdToBytes32 determinism + collision-resistance, computePlanHash determinism, and the three mock-fallback paths (empty config, partial config, finalizeAnchor).

Verification

$ npx tsc --noEmit     # clean
$ npx jest             # 51 passed, 4 suites (45 pre-existing + 6 new)

Not in this PR

  • No on-chain test: the integration needs a funded signer on Chain 138; that's a manual smoke once an ORCHESTRATOR_PRIVATE_KEY is provisioned.
  • Steps encoding: registerPlan is called with an empty Step[] for now. PR E (SWIFT gateway) introduces stable step IDs, after which we can serialise the real steps.
  • getNotaryProof still returns a deterministic mock — the on-chain read will be wired in PR D alongside the events table (so a single query reconstructs { onChainProof, eventTrail }).

Series order

A → B → C → D → E → F → G → H.

Implements **step 4** from the architecture gap-analysis — the ledger-anchor layer (arch §4.5 + §5.7) stops being a mock and starts writing to the deployed `NotaryRegistry` on Chain 138. Independent of PRs A/B; can land in any order. ## What lands ### `services/notaryChain.ts` (new) ethers-v6 adapter. Minimal ABI (two functions, two events, one view) lifted from `contracts/interfaces/INotaryRegistry.sol`: | fn | purpose | | --- | --- | | `registerPlan(bytes32 planId, Step[] steps, address creator)` | anchor planHash on-chain | | `finalizePlan(bytes32 planId, bool success)` | record commit/abort disposition | | `getPlan(bytes32)` | read-side for `GET /api/plans/:id/proof` (wired later) | Public surface: - `anchorPlan(plan)` → `{ mode: 'chain'|'mock', txHash?, planHash, blockNumber?, contractAddress? }` - `finalizeAnchor(planId, success)` → `{ mode, txHash?, receiptHash?, blockNumber? }` - `computePlanHash(plan)` and `planIdToBytes32(planId)` — deterministic helpers so the mock and chain paths publish identical hashes for identical inputs. A singleton contract/wallet is cached after first use, keyed on `contractAddress`, and can be reset via `__resetForTests()`. ### Graceful mock fallback If **any** of `CHAIN_138_RPC_URL`, `NOTARY_REGISTRY_ADDRESS`, `ORCHESTRATOR_PRIVATE_KEY` is unset, both functions return `mode: 'mock'` and log the reason. Unit tests, CI, and local dev require no extra wiring. ### `services/notary.ts` (refactored) Still the single entry point for `ExecutionCoordinator`; now delegates to `notaryChain` and surfaces the extra on-chain fields (`mode`, `txHash`, `blockNumber`, `contractAddress`, `receiptHash`) to callers. If the chain call throws, we log + fall back to mock so a momentary RPC outage cannot take the workflow down. ### `config/env.ts` ``` CHAIN_138_RPC_URL url, optional (e.g. https://rpc.d-bis.org) CHAIN_138_CHAIN_ID digits, optional (default 138) NOTARY_REGISTRY_ADDRESS 0x-40 hex, optional ORCHESTRATOR_PRIVATE_KEY 0x-64 hex, optional ``` All four regex-validated. Missing envs do **not** fail startup. ### Tests `tests/unit/notaryChain.test.ts` — 6 cases: `planIdToBytes32` determinism + collision-resistance, `computePlanHash` determinism, and the three mock-fallback paths (empty config, partial config, finalizeAnchor). ## Verification ``` $ npx tsc --noEmit # clean $ npx jest # 51 passed, 4 suites (45 pre-existing + 6 new) ``` ## Not in this PR - No on-chain test: the integration needs a funded signer on Chain 138; that's a manual smoke once an ORCHESTRATOR_PRIVATE_KEY is provisioned. - Steps encoding: `registerPlan` is called with an empty `Step[]` for now. PR E (SWIFT gateway) introduces stable step IDs, after which we can serialise the real steps. - `getNotaryProof` still returns a deterministic mock — the on-chain read will be wired in PR D alongside the events table (so a single query reconstructs `{ onChainProof, eventTrail }`). ## Series order A → B → **C** → D → E → F → G → H.
nsatoshi merged commit 3e1fb9ef7e into main 2026-04-22 10:11:54 -07:00
nsatoshi deleted branch devin/1776875569-notary-chain-wiring 2026-04-22 10:11:56 -07:00
Sign in to join this conversation.