Skip to content

WNE-41: reconnect bootstrap-window authorization on OPPInbound.epochIn - #82

Open
brianjohnson5972 wants to merge 1 commit into
masterfrom
wne-41_corrected
Open

WNE-41: reconnect bootstrap-window authorization on OPPInbound.epochIn#82
brianjohnson5972 wants to merge 1 commit into
masterfrom
wne-41_corrected

Conversation

@brianjohnson5972

Copy link
Copy Markdown

wire-tools-ts — response to wire-ethereum PR #191 (WNE-41) review, finding 1

This is the paired change the wire-ethereum review called for:

"Finding 1 needs a paired wire-tools-ts change before this can merge without
breaking the e2e gate."

Confirmed, and this is it. The two PRs should land together — wire-ethereum #191
alone leaves epochIn callable by nobody on a test cluster.


The finding

OPPInbound.epochIn authorizes against the AccessManager for as long as the
genesis bootstrap window is open (rosterInitialized == false), because the
batch-operator roster it would otherwise consult does not exist yet.
OutpostManager.setupOPPRoles registers the epochIn selector under
OPP_INBOUND_ROLE and grants that role to nobody, so the window is closed by
default and a deployment must name its genesis deliverer.

deployLocal.ts grants the hardhat deployer. On a WIRE cluster that is the wrong
address. Verified on all three legs:

  • wire-sysio/plugins/outpost_ethereum_client_plugindeliver_outbound_envelope
    calls epoch_in through a client whose signer is the daemon's own key
    (get_signer_address()), and the ladder-B gate reads msg.sender.
  • ClusterBuildDefaults — batch operators get ethereumHdIndex: index + 1; the
    deployer is HD 0.
  • grep -rn "opp_inbound\|grantRole" packages/*/src/ — nothing. The harness had
    no grant path and never calls epochIn itself.

Result: epoch 1 reverts AccessManagedUnauthorized, nextEpochIndex stays 1,
the depot's chkcons never fires advance, and every flow-* dies on an epoch
stall.

Why the fix could not live in wire-ethereum

Two independent reasons, either one sufficient:

  1. Ordering. The outpost deploys in Cluster Prerequisites; operators are
    provisioned later, in Cluster Post Contract Deployment. The accounts do not
    exist when deployLocal.ts runs, so it cannot grant them.
  2. SSM. Under a KEY signature provider the operator EM keys derive from
    EthereumOutpostBootstrapper.AnvilMnemonic, so an HD-band grant would have
    worked. Under SSM they derive from a generated cluster mnemonic anvil has
    never seen — which is exactly why BatchOperatorEthereumFundingWei exists.
    No band of anvil-derived addresses covers that mode, so an HD-band grant would
    have fixed KEY-mode clusters and left SSM-mode ones silently broken.

The grant therefore has to happen in the harness, after provisioning, keyed on
the operators' real addresses.


The change

packages/cluster-tool/src/tools/ethereum/EthereumOutpostManagerTool.ts (new)

The OutpostManager AccessManager-administration surface. One Step factory
today:

  • planGrantBootstrapDelivery(actor, name, description, options, operatorLabel)
    ClusterBuildStep (:89)
  • runGrantBootstrapDelivery(ctx, input, signal) — the named runner (:112).
    Resolves the operator from ctx.keyStore, asserts it carries an Ethereum
    address, and performs exactly one grantRole(OPP_INBOUND_ROLE, <EOA>)
    write with an explicitly resolved nonce.
  • loadOutpostManager(ctx) (:149) — a pure value helper binding the deployed
    OutpostManager from this cluster's outpost-addrs.json to
    ctx.ethereum.wallet.signer. That signer is anvil HD 0, the deploy owner and
    the only identity holding ADMIN_ROLE on the outpost authority — which is what
    grantRole's restricted modifier requires.

One write per Step, so each grant lands in the Report as its own validated
checkpoint rather than disappearing into a loop.

packages/cluster-tool/src/orchestration/ClusterBuildDefaults.ts:940-958

A GrantBootstrapDelivery phase inside postContractDeployment, carrying one
Step per batch operator. Placement is load-bearing in both directions:

  • After Create batchops & uws — the accounts must exist to be granted.
  • Before OperatorNodes — the operators must hold the role before their
    daemons start delivering.

Gated on !isExternalOutpost. In external mode this run deployed no outpost and
holds no admin authority over the ones it talks to; those deliverers are
authorized out of band.

Underwriters are deliberately not granted — only batch-operator daemons run
the outpost_ethereum_client delivery path.


Tests

packages/cluster-tool/tests/tools/ethereum/EthereumOutpostManagerTool.test.ts

  • planGrantBootstrapDelivery captures the typed input and binds the named
    runner.
  • loadOutpostManager binds the artifact ABI to the deployed address and is
    bound to the deploy owner by identity, not merely by address — a
    same-address signer on another provider would submit to the wrong chain.
  • Throws loudly when OutpostManager is absent from the address map.
  • The runner refuses an operator with no Ethereum address before touching the
    chain (granting address(0) would leave epochIn callable by nobody,
    silently), refuses an unknown label, and honours an already-aborted signal.

packages/cluster-tool/tests/orchestration/ClusterBuildDefaultsBootstrapDelivery.test.ts

  • One grant Step per batch operator in local mode — count and uniqueness both
    asserted, so a future refactor into a single looping Step fails here.
  • Registered after operator provisioning and before both OperatorNodes and
    EpochBootstrap.
  • Absent in external-outpost mode.

Verification: pnpm build clean, full-repo eslint . clean, full
cluster-tool jest 131 suites / 1167 tests passing, including the 9 new
cases.


Two things reviewers should know

No flow ran end to end. This change is verified by composition tests and unit
tests, not against a live cluster — the real proof for finding 1 is a green
flow-* run, and that needs a wire-sysio build the authoring machine did not
have. Worth running one flow (or the e2e gate with both branches) before merging
the pair.

Pre-existing build errors in the checkout, unrelated to this change. pnpm build reports 13 errors in cluster-tool-shared and the flow-swap-* packages.
Confirmed pre-existing by stashing this change and rebuilding to an identical
error set; none are in touched files. Root cause is environmental: with no
wire-sysio/build/opp/typescript present, @wireio/opp-typescript-models
resolves from the registry into
wire-libraries-ts/packages/sdk-core/node_modules/, producing a TS2883
portability error on ClusterConfigSchema that cascades into ClusterConfig not
being exported. That is also a sighting of the dep edge cross-repo.md calls a
red flag (opp-*-models under a wire-libraries-ts path) — worth tracing
separately, but it is this checkout's install state rather than anything in this
PR.

Change-Id: Ie1b3a354da8d05f78c8e4b670076d6e6cd076a54
@brianjohnson5972
brianjohnson5972 requested review from heifner, huangminghuang and jglanz and removed request for heifner and huangminghuang August 21, 2026 20:34
@brianjohnson5972

Copy link
Copy Markdown
Author

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.

1 participant