feat(sdk): fail strict mainnet readiness on deployment drift - #463
Merged
karagozemin merged 2 commits intoOct 1, 2026
Merged
Conversation
Readiness checked operator-supplied expectations, so `mainnet:ready --strict` and `mainnet:verify` could pass against a drifted deployment: wrong contract, testnet passphrase, old WASM, or a swapped escrow SAC all still went green. - Commit the mainnet deployment as data: packages/sdk/mainnet-artifacts.json is the source of truth, parsed by parseMainnetManifest with a closed schema. - Compare the live deployment against the manifest through the read-only client: contractId, networkPassphrase, wasmHash, tokenContract. Mismatch or unreadable blocks; the report names the field, fingerprints the passphrase, and redacts anything shaped like a secret key. - Replay a recorded snapshot with no RPC (`--fixture`), committed under packages/sdk/fixtures and exercised in CI, so each mismatch field is proven to fail without touching mainnet. - mainnet-ready/mainnet-verify exit non-zero on drift and need no secret keys; balance checks take public keys. Refs Sub-Rosa-Issue#383
|
@Hamda-gbade Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #383.
mainnet:ready --strictandmainnet:verifyused expectations supplied by the caller, so a green run did not mean the deployment matched what this repo believes is mainnet. A different contract, a testnet passphrase, a rebuilt contract, or a swapped escrow SAC all passed. The expected values are now committed data, and readiness compares the live network against them.Committed manifest
packages/sdk/mainnet-artifacts.jsonis the source of truth: contract id, network passphrase, rpc url, wasm hash, the mainnet XLM SAC used as escrow token, plus the settled-round 1 expectations.parseMainnetManifestenforces a closed schema, so a typo or an unexpected key is an error rather than a silently ignored field, and a test pins the JSON to the existingMAINNET_*constants.The four compared fields
contractIdnetworkPassphrasegetNetworkwasmHashtokenContractusdcin the deployedGlobalConfig(the escrow SAC)Each is a blocking check with its own id (
contract-id,network-passphrase,wasm-hash,token-contract). Mismatch blocks. A field that cannot be read also blocks: an unverifiable field must never read as a pass, which is the gap that let this drift hide. Config drift is also caught before any RPC call, so pointingNETWORK_PASSPHRASE/ROUND_CONTRACT_IDat testnet fails instead of reporting a healthy check against the wrong network.Output stays shareable
mainnet-readyandmainnet-verifyexit non-zero and name the disagreeing field. The passphrase is printed as a short sha256 fingerprint and anything shaped like anS...secret key is redacted, so a failing readiness report can be pasted into an issue. Neither command needs a secret key;--with-balancesnow takesOPERATOR_PUBLIC_KEY/KEEPER_PUBLIC_KEY/BIDDER_PUBLIC_KEY.CI-safe fixture mode
--fixturereplays a recorded deployment through the same comparison with no mainnet RPC:The recording is committed at
packages/sdk/fixtures/mainnet-readiness.jsonand a new step in the TypeScript coverage workflow runs both scripts. Tests assert every mismatch field fails, a matching recording passes, and the source of both scripts contains no_SECRETreference.One footgun found while testing this:
--fixturewith no value used to fall through to the live path, so a CI run would have quietly hit mainnet. A bare--fixturenow replays the committed recording, and both flags ignore a following value that is another flag.Tests
pnpm --filter @sub-rosa/sdk test— 250 pass (49 inmainnet-readiness.test.ts)pnpm --filter @sub-rosa/sdk typecheck, plus typecheck forkeeper,receipt-cli,agent,webkeeper73,receipt-cli19,agent38,tlock67,time17,drand-tools5,round-bindings17logging:check,errors:normalize:check,errors:check,snapshot:check,threat-model:check,time:guard,docs:check,docs:check-linksNotes for review
wasmHash/contractIdinmainnet-artifacts.jsonand the parity test forces the constant inmainnet-artifacts.tsto move with it.src/mainnet-manifest.tsis Node-only and deliberately not re-exported from the barrel, so browser consumers do not pullnode:fsinto the web bundle.MainnetReadinessInputno longer takesexpectedWasmHash; the manifest owns it.mainnet-settleandmainnet-microwere updated to the new shape.