diff --git a/.github/workflows/publish-prerelease.yml b/.github/workflows/publish-prerelease.yml index 7b42df55..f4f73dad 100644 --- a/.github/workflows/publish-prerelease.yml +++ b/.github/workflows/publish-prerelease.yml @@ -29,10 +29,10 @@ jobs: # Gated: this job reads the genesis owner key, which lives only on the `releases` # environment, so every run waits for a reviewer's approval there. environment: releases - # Baked into the genesis registry, so it decides which networks the artifact suits. - # Job-level: five steps name the file derived from it and must not disagree. + # One genesis per TLD, since the TLD is baked into the registry: `.testnet` for previewnet, + # `.paseo` for Paseo Asset Hub Next V2. Job-level: several steps name the files it yields. env: - DOTNS_TLD: testnet + DOTNS_TLDS: testnet paseo steps: # On workflow_dispatch the tag does not exist yet; the release step creates it # from the branch this run was started on. Read through an env var rather than @@ -155,7 +155,12 @@ jobs: FOUNDRY_DISABLE_NIGHTLY_WARNING: "1" FACTORY_DEPLOYER_KEY: ${{ secrets.FACTORY_DEPLOYER_KEY }} DOTNS_ADMIN_KEY: ${{ secrets.DOTNS_ADMIN_KEY }} - run: bash scripts/genesis/build-genesis.sh release + run: | + for tld in $DOTNS_TLDS; do + DOTNS_TLD="$tld" bash scripts/genesis/build-genesis.sh release + done + # The address set every genesis above carries, which the builder asserted. + cp deployments/expected.json release/dotns-genesis-addresses.json - name: Extract ABIs run: | @@ -194,7 +199,7 @@ jobs: TAG="$RELEASE_TAG" cd release zip -r "../dotns-abis-${TAG}.zip" abis/ release-manifest.json codehashes.json \ - "dotns-genesis-${DOTNS_TLD}.json" + dotns-genesis-*.json - name: Generate release body env: @@ -251,7 +256,12 @@ jobs: echo "not the previous live deployment's." echo "" echo "- **Release contents:** [release-manifest.json]($ASSET_BASE/release-manifest.json)" - echo "- [dotns-genesis-${DOTNS_TLD}.json]($ASSET_BASE/dotns-genesis-${DOTNS_TLD}.json) — pallet-revive genesis, \`.${DOTNS_TLD}\` TLD, for ${DOTNS_TLD} networks only." + for tld in $DOTNS_TLDS; do + echo "- [dotns-genesis-${tld}.json]($ASSET_BASE/dotns-genesis-${tld}.json) - pallet-revive genesis, \`.${tld}\` TLD, for .${tld} networks only." + done + echo "- [dotns-genesis-addresses.json]($ASSET_BASE/dotns-genesis-addresses.json) - the addresses those genesis files carry." + echo "" + echo "Previewnet uses \`.testnet\`, Paseo Asset Hub Next V2 uses \`.paseo\`." } >> release-body.md - name: ABI diff since previous release @@ -285,7 +295,7 @@ jobs: release/release-manifest.json release/codehashes.json release/abi-diff.json - release/dotns-genesis-${{ env.DOTNS_TLD }}.json + release/dotns-genesis-*.json body_path: release-body.md draft: true prerelease: true @@ -304,7 +314,8 @@ jobs: echo "release-manifest.json" echo "codehashes.json" echo "abi-diff.json" - echo "dotns-genesis-${DOTNS_TLD}.json" + echo "dotns-genesis-addresses.json" + for tld in $DOTNS_TLDS; do echo "dotns-genesis-${tld}.json"; done } | sort > "$RUNNER_TEMP/wanted-assets.txt" if ! diff -u "$RUNNER_TEMP/wanted-assets.txt" "$RUNNER_TEMP/actual-assets.txt"; then echo "::error::Draft pre-release $TAG does not match the expected asset set; delete the draft and re-run." diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 9d0a18dd..88cbbcb4 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -29,10 +29,10 @@ jobs: # Gated: this job reads the genesis owner key, which lives only on the `releases` # environment, so every run waits for a reviewer's approval there. environment: releases - # Baked into the genesis registry, so it decides which networks the artifact suits. - # Job-level: five steps name the file derived from it and must not disagree. + # One genesis per TLD, since the TLD is baked into the registry: `.testnet` for previewnet, + # `.paseo` for Paseo Asset Hub Next V2. Job-level: several steps name the files it yields. env: - DOTNS_TLD: testnet + DOTNS_TLDS: testnet paseo steps: # On workflow_dispatch the tag does not exist yet; the release step creates it # from the branch this run was started on. Read through an env var rather than @@ -151,7 +151,12 @@ jobs: FOUNDRY_DISABLE_NIGHTLY_WARNING: "1" FACTORY_DEPLOYER_KEY: ${{ secrets.FACTORY_DEPLOYER_KEY }} DOTNS_ADMIN_KEY: ${{ secrets.DOTNS_ADMIN_KEY }} - run: bash scripts/genesis/build-genesis.sh release + run: | + for tld in $DOTNS_TLDS; do + DOTNS_TLD="$tld" bash scripts/genesis/build-genesis.sh release + done + # The address set every genesis above carries, which the builder asserted. + cp deployments/expected.json release/dotns-genesis-addresses.json - name: Extract ABIs run: | @@ -188,7 +193,7 @@ jobs: TAG="$RELEASE_TAG" cd release zip -r "../dotns-abis-${TAG}.zip" abis/ deployments.json release-manifest.json \ - codehashes.json "dotns-genesis-${DOTNS_TLD}.json" + codehashes.json dotns-genesis-*.json - name: Generate release body env: @@ -253,8 +258,13 @@ jobs: echo "Pallet-revive genesis state, for a chain that should carry DotNS from block zero" echo "rather than deploying it afterwards." echo "" - echo "- [dotns-genesis-${DOTNS_TLD}.json]($ASSET_BASE/dotns-genesis-${DOTNS_TLD}.json) — registry initialised with the **\`.${DOTNS_TLD}\` TLD**, so it suits **${DOTNS_TLD} networks only** (this is what previewnet uses). Not for a production chain." - echo "- A chain booted from this genesis carries the fresh-deploy address set: \`deployments/expected.json\` in the repository at this tag. [deployments.json]($ASSET_BASE/deployments.json) records the live networks instead, and the two can differ where a live network has moved a contract." + for tld in $DOTNS_TLDS; do + echo "- [dotns-genesis-${tld}.json]($ASSET_BASE/dotns-genesis-${tld}.json) - registry initialised with the **\`.${tld}\` TLD**, so it suits **.${tld} networks only**. Not for a production chain." + done + echo "" + echo "Previewnet uses \`.testnet\`, Paseo Asset Hub Next V2 uses \`.paseo\`." + echo "" + echo "- [dotns-genesis-addresses.json]($ASSET_BASE/dotns-genesis-addresses.json) - the address set a chain booted from any of these genesis files carries. [deployments.json]($ASSET_BASE/deployments.json) records the live networks instead, and the two can differ where a live network has moved a contract." } >> release-body.md # `gh release view` with no tag resolves through /releases/latest, which skips @@ -302,7 +312,7 @@ jobs: release/release-manifest.json release/codehashes.json release/abi-diff.json - release/dotns-genesis-${{ env.DOTNS_TLD }}.json + release/dotns-genesis-*.json body_path: release-body.md draft: true prerelease: false @@ -322,7 +332,8 @@ jobs: echo "release-manifest.json" echo "codehashes.json" echo "abi-diff.json" - echo "dotns-genesis-${DOTNS_TLD}.json" + echo "dotns-genesis-addresses.json" + for tld in $DOTNS_TLDS; do echo "dotns-genesis-${tld}.json"; done } | sort > "$RUNNER_TEMP/wanted-assets.txt" if ! diff -u "$RUNNER_TEMP/wanted-assets.txt" "$RUNNER_TEMP/actual-assets.txt"; then echo "::error::Draft release $TAG does not match the expected asset set; delete the draft and re-run." diff --git a/DEPLOYMENTS.md b/DEPLOYMENTS.md index 442d8608..4c764fc0 100644 --- a/DEPLOYMENTS.md +++ b/DEPLOYMENTS.md @@ -285,7 +285,7 @@ If the deployment was intended to update a public environment, update the addres `deployments//.json` is a **network record**: what is deployed on that live network right now. It is updated only by a real deploy or migration on that network, never by a code change. Everything that answers for reality reads these files: releases copy their addresses verbatim, and pointing tooling or the wire stage at an address with nothing behind it breaks whatever reads it. The CREATE3 parity gates compare `expected.json` rather than these files, so the rule that a code change leaves them alone is enforced in CI instead: `release-metadata.yml` fails a pull request that edits one unless it carries the `deployment-record` label. -`deployments/expected.json` is the **expected set**: the addresses a fresh deploy of the current revision lands through the pinned CREATE3 factory. It is a property of the code, not of any network; the CI deploy job and `scripts/genesis/build-genesis.sh` verify against it, and releases never publish it. +`deployments/expected.json` is the **expected set**: the addresses a fresh deploy of the current revision lands through the pinned CREATE3 factory. It is a property of the code, not of any network; the CI deploy job and `scripts/genesis/build-genesis.sh` verify against it, and releases publish it only as `dotns-genesis-addresses.json`, the address set of their genesis files. The expected set can legitimately disagree with a network manifest: after a code change moves an address, the expected set carries the new address while every network manifest keeps the old one until that network actually redeploys. The difference between them is the migration backlog, readable as a diff, and it is resolved per network by the event that relocates the contract: a wipe-and-redeploy on a test network, a deliberate migration on one that never wipes. diff --git a/RELEASE_ARTIFACTS.md b/RELEASE_ARTIFACTS.md index 584730ad..1a480c93 100644 --- a/RELEASE_ARTIFACTS.md +++ b/RELEASE_ARTIFACTS.md @@ -11,13 +11,15 @@ What each release publishes, what the files guarantee, and how to consume them. | `release-manifest.json` | What this release contains, machine readable | | `codehashes.json` | Stripped-metadata hash of each contract's built runtime bytecode | | `abi-diff.json` | Selector-level ABI changes since the previous release, machine readable | +| `dotns-genesis-.json` | pallet-revive genesis with DotNS deployed, one per TLD (`testnet` for previewnet, `paseo` for Paseo Asset Hub Next V2) | +| `dotns-genesis-addresses.json` | The addresses a chain booted from those genesis files carries, a copy of `deployments/expected.json` | | `dotns-abis-.zip` | The same files in one archive | -Every JSON asset is attached to the release individually, at the top level, with no folder. The zip holds the ABIs under `abis/` plus `deployments.json`, `release-manifest.json`, and `codehashes.json` at its root; `abi-diff.json` is generated together with the release body and attached individually. +Every JSON asset is attached to the release individually, at the top level, with no folder. The zip holds the ABIs under `abis/` plus `deployments.json`, `release-manifest.json`, `codehashes.json`, and the `dotns-genesis-*.json` files at its root; `abi-diff.json` is generated together with the release body and attached individually. The release surface is decided in `.github/abi-contracts.txt` so a contract reaches consumers only when it is listed there. -**Pre-releases carry no addresses.** A pre-release is cut in order to be deployed, so at that point the recorded addresses still belong to the previous deployment of different code. Publishing them under that tag would break the one thing `version` is for, namely that a release's addresses and its ABIs came from the same release. A pre-release therefore ships the ABIs, `release-manifest.json`, `codehashes.json`, and `abi-diff.json`, and the addresses arrive with the release that follows the deployment. `codehashes.json` is on the pre-release deliberately: deploys run from pre-release tags, and an upgrade diffs its build against the previous release's copy before touching a live network. +**Pre-releases carry no live addresses.** A pre-release is cut in order to be deployed, so at that point the recorded addresses still belong to the previous deployment of different code. Publishing them under that tag would break the one thing `version` is for, namely that a release's addresses and its ABIs came from the same release. A pre-release therefore ships the ABIs, `release-manifest.json`, `codehashes.json`, `abi-diff.json`, and the genesis files, whose addresses are this commit's own fresh deploy; the live addresses arrive with the release that follows the deployment. `codehashes.json` is on the pre-release deliberately: deploys run from pre-release tags, and an upgrade diffs its build against the previous release's copy before touching a live network. `deployments.json` and `release-manifest.json` were also added after this repository had already published releases, so an older release carries only the per-contract ABIs and the zip. That is expected rather than broken, and it cannot be corrected: releases here are immutable, so no asset can be attached after publication. Treat either file being missing as "this release predates it, or is a pre-release" and fall back, or pin a release you have checked. @@ -44,7 +46,7 @@ The release surface is decided in `.github/abi-contracts.txt` so a contract reac - One entry can serve more than one live network. `paseo-assethub` is the deployment that both previewnet and Paseo Asset Hub Next V2 run, because every network deployed through the shared CREATE3 factory lands on the same addresses. So expect entries named after deployments, not after every chain you might connect to. - The names under `contracts` (`DotnsRegistrar`, `PopRules`) do not change, and a name always means the same contract. Your code can depend on that. - Addresses are copied from the manifest verbatim, which the deploy pipeline writes EIP-55 checksummed. Compare them case-insensitively rather than relying on the casing. -- Only per-network manifests are published. `deployments/expected.json` — the fresh-deploy address set that CI and the genesis builder verify against (see `DEPLOYMENTS.md`) — is not a network and never appears here, so a release cut while an address move is awaiting its network's redeploy still advertises the addresses each live network actually runs. +- Only per-network manifests are published. `deployments/expected.json` — the fresh-deploy address set that CI and the genesis builder verify against (see `DEPLOYMENTS.md`) — is not a network and never appears here (it ships separately as `dotns-genesis-addresses.json`), so a release cut while an address move is awaiting its network's redeploy still advertises the addresses each live network actually runs. - `LabelStoreBeacon` and `UserStoreBeacon` appear when deployed but are not network-stable, because the `StoreFactory` initialiser deploys them. Read them from the factory rather than pinning them. ## `release-manifest.json` diff --git a/deployments/previewnet/README.md b/deployments/previewnet/README.md index 420ca959..73305551 100644 --- a/deployments/previewnet/README.md +++ b/deployments/previewnet/README.md @@ -1,7 +1,7 @@ # previewnet The Product Preview Network's Asset Hub (`wss://previewnet.substrate.dev/asset-hub`, -ETH RPC `https://previewnet.substrate.dev/eth-rpc`), TLD `.dot`. Deployed from a v0.8.0 +ETH RPC `https://previewnet.substrate.dev/eth-rpc`), TLD `.testnet`. Deployed from a v0.8.0 genesis, so every address matches the fresh-deploy set in `deployments/expected.json`. The chain id is `420420417`, the same as `paseo-assethub`: pallet-revive testnets share it, diff --git a/scripts/genesis/build-genesis.sh b/scripts/genesis/build-genesis.sh index 4044ac42..0ea53a0a 100755 --- a/scripts/genesis/build-genesis.sh +++ b/scripts/genesis/build-genesis.sh @@ -170,6 +170,10 @@ forge clean forge build echo "" +# The stages extend an existing manifest, and one left by an earlier build (for another TLD) +# describes a chain this anvil never saw. +rm -f "$DEPLOYMENT_FILE" + # Deploy the Create3Factory from the single-purpose key at nonce 0 so it lands on # the canonical address, then hand it to the stages via CREATE3_FACTORY (read by # BaseDeployer._configuredCreate3Factory).