Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bc0218d
chore: replace the code owners with the people who can approve
re-gius Sep 21, 2026
3fc0638
chore: own the repository as a team, not as two names
re-gius Sep 21, 2026
77f8932
Merge branch 'master' into chore/codeowners
GHkrishna Sep 22, 2026
6bbd97d
Upgrade testnet manifests
re-gius Sep 22, 2026
1a82064
chore: replace the code owners with the people who can approve (#314)
re-gius Sep 22, 2026
2dd93fb
Revert "Upgrade testnet manifests"
re-gius Sep 22, 2026
027e763
chore: record the live testnet manifests (#315)
re-gius Sep 22, 2026
14f55a6
fix(security): minor security bugs (#307)
GHkrishna Sep 23, 2026
408f75e
ci: gate the release genesis key behind the releases environment (#316)
re-gius Sep 23, 2026
bcfec88
chore: add SPDX copyright attribution and a NOTICE file (#317)
re-gius Sep 23, 2026
9f15b8b
chore: ci optimisations (#318)
GHkrishna Sep 24, 2026
1a6d5b4
feat(release): build a Next V2 genesis and publish its address set (#…
mordamax Sep 28, 2026
7516913
fix(release): ignore embedded metadata in changedset, list breaking c…
GHkrishna Sep 30, 2026
9a84fa7
feat: rename lite/full to device and personhood names, with legacy ga…
re-gius Sep 30, 2026
bbcd737
ci(release): publish only from tag pushes, fix Usage and Deployments …
re-gius Oct 2, 2026
1652e2e
Merge master into chore/paseo-v1.0.0-upgrade
re-gius Oct 5, 2026
62a8c2c
chore(upgrades): replace the v0.8.0 campaign with v0.8.0 snapshots fr…
re-gius Oct 5, 2026
73a6e3d
feat(upgrades): v1.0.0 campaign scripts and fork tests
re-gius Oct 5, 2026
2bd3a8c
ci(upgrades): console steps for the v1.0.0 campaign
re-gius Oct 5, 2026
d444806
docs(upgrades): v1.0.0 runbook
re-gius Oct 5, 2026
2088bda
fix(upgrades): validate the PoP resolver layout with no override
re-gius Oct 5, 2026
c23fcea
forge fmt
re-gius Oct 5, 2026
69f2bd2
fix(upgrades): allow partial-step retries and check keys before decla…
re-gius Oct 5, 2026
2e3dfd1
ci: skip live manifests carried unchanged from master
re-gius Oct 5, 2026
28b140f
fix(upgrades): run DeclareRelease through declare
re-gius Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ DOTNS_TLD=dot
DEPLOYMENT_NETWORK=

# Optional. Address of a pre-deployed CREATE3 factory to reuse. When set,
# DeployCore adopts it instead of minting a new one, so every DotNS address is
# DeployCore adopts it instead of minting a new one, so every dotNS address is
# pinned to that factory and stays the same across chain resets. `bun run
# deploy:all` sets this automatically from the factory step; set it by hand only
# when running `bun run deploy` (the pipeline on its own).
Expand Down
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @waytwotall @sphamjoli
* @paritytech/dotns
1 change: 1 addition & 0 deletions .github/abi-contracts.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ IPopRules
IDotnsProtocolRegistry
IDotnsNameEscrow
IDotnsPopController
IDotnsPopControllerLegacy
IDotnsPopResolver
IDotnsPopLens
IDotnsController
Expand Down
2 changes: 1 addition & 1 deletion .github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@

- name: "dotns-sdk"
color: "5319e7"
description: "DotNS SDK (UI, CLI, libraries)"
description: "dotNS SDK (UI, CLI, libraries)"

- name: "smartcontracts"
color: "e3a21a"
Expand Down
51 changes: 50 additions & 1 deletion .github/workflows/contract-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,40 @@ jobs:

- run: bun install

# `cache/` and `out/` are forge's own incremental-compile state: it hashes each
# source file and only recompiles what changed since the hash it last saw. A
# restored cache from an unrelated commit is always safe to build on top of.
# Forge just recompiles whatever the content hashes say is dirty, so a partial
# match via `restore-keys` still saves most of a cold `via_ir` build. This has
# to sit after `bun install`: its postinstall (`setup.bash`) force-reclones
# `lib/`'s pinned dependencies and reapplies the OZ patches on top, so hashing
# `lib/` any earlier would key the cache off content these steps are about to
# replace.
#
# Forge also drops persisted fuzz/invariant/test failure state under
# `cache/fuzz`, `cache/invariant`, and `cache/test-failures`, and replays it
# on the next run before exploring anything new (see foundry.toml's
# `[profile.ci.invariant]` comment). That state is cleared below, both before
# this job's `forge coverage` run and again right before the job ends, so it
# never rides along in what gets restored or saved here.
#
# This job keys its cache under `foundry-build-coverage-`, with `coverage`
# ahead of the OS on purpose. `forge coverage --ir-minimum` compiles under
# different settings from the test job and leaves different content in
# `cache/`/`out/`, so the two must never restore each other's builds. The
# test job restores anything starting with `foundry-build-${{ runner.os }}-`
# and GitHub picks the newest match, so any key that starts with that
# prefix (even `foundry-build-${{ runner.os }}-coverage-...`) would get
# picked up by it. Putting `coverage` first keeps it out of that prefix.
- uses: actions/cache@v4
with:
path: |
cache
out
key: foundry-build-coverage-${{ runner.os }}-${{ hashFiles('foundry.toml', 'remappings.txt', 'contracts/**/*.sol', 'test/**/*.sol', 'scripts/**/*.sol', 'lib/**/*.sol') }}
restore-keys: |
foundry-build-coverage-${{ runner.os }}-

- name: Build contracts
env:
FOUNDRY_DISABLE_NIGHTLY_WARNING: "1"
Expand All @@ -78,10 +112,17 @@ jobs:
FOUNDRY_DISABLE_NIGHTLY_WARNING: "1"
run: |
set +e

# A restored cache can carry a fuzz/invariant/test failure persisted by
# an earlier, unrelated run. Forge replays those before running anything
# new, so a stale one would fail this job even after the real bug behind
# it was fixed. Clear it before running coverage.
rm -rf cache/fuzz cache/invariant cache/test-failures

forge coverage --ir-minimum --report summary \
--no-match-coverage "test/|lib/" \
2>&1 | tee coverage.log
CODE=$?
CODE=${PIPESTATUS[0]}

python3 scripts/reports/coverage-report.py coverage.log
exit $CODE
Expand Down Expand Up @@ -231,6 +272,14 @@ jobs:
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}

# Last step in the job on purpose: `actions/cache`'s save only runs once
# every step above has finished, and it caches whatever is on disk at that
# point, not just what "Run coverage" left behind. Clearing here too means
# nothing this job did can leave a persisted failure in the saved cache.
- name: Clear persisted Foundry failure state before cache save
if: always()
run: rm -rf cache/fuzz cache/invariant cache/test-failures

cleanup:
runs-on: ubuntu-latest
if: github.event.action == 'closed'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ jobs:
fi

# This CI deploy reproduces the expected address set. The canonical factory
# was deployed above, and every DotNS address is a pure function of that
# was deployed above, and every dotNS address is a pure function of that
# factory plus a fixed salt, so the freshly deployed manifest must equal the
# committed expected set. Assert that, print the expected-vs-actual table, then
# rerun the pipeline to prove the deploy is resumable: a rerun adopts every
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/format-lint-product.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ permissions:
pull-requests: write
issues: write

# Keyed by PR number so a new push cancels this PR's still-running lint job instead of
# queuing behind it; every other content-triggered workflow in this repo does the same.
concurrency:
group: format-lint-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
Expand Down
48 changes: 40 additions & 8 deletions .github/workflows/gas-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,31 @@ jobs:

- run: bun install

- name: Start revive-eth-rpc (paseo_local fork target)
run: |
docker compose up -d --build
bash scripts/shell/wait-for-eth-rpc.sh
# `cache/` and `out/` are forge's own incremental-compile state: it hashes each
# source file and only recompiles what changed since the hash it last saw. A
# restored cache from an unrelated commit is always safe to build on top of.
# Forge just recompiles whatever the content hashes say is dirty, so a partial
# match via `restore-keys` still saves most of a cold `via_ir` build. This has
# to sit after `bun install`, not just after the master shard's patch step
# above: `bun install`'s postinstall (`setup.bash`) force-reclones `lib/`'s
# pinned dependencies for both shards and reapplies the OZ patches on top,
# so hashing `lib/` any earlier would key the cache off content that step
# is about to replace.
#
# Forge also drops persisted fuzz/invariant/test failure state under
# `cache/fuzz`, `cache/invariant`, and `cache/test-failures`, and replays it
# on the next run before exploring anything new (see foundry.toml's
# `[profile.ci.invariant]` comment). That state is cleared below, both before
# this job's `forge test` run and again right before the job ends, so it
# never rides along in what gets restored or saved here.
- uses: actions/cache@v4
with:
path: |
cache
out
key: foundry-build-${{ runner.os }}-${{ matrix.target.id }}-${{ hashFiles('foundry.toml', 'remappings.txt', 'contracts/**/*.sol', 'test/**/*.sol', 'scripts/**/*.sol', 'lib/**/*.sol') }}
restore-keys: |
foundry-build-${{ runner.os }}-${{ matrix.target.id }}-

- name: Generate gas report
env:
Expand All @@ -136,6 +157,13 @@ jobs:
# while preserving the solc compilation cache under `cache/`.
rm -rf out/build-info
forge build

# A restored cache can carry a fuzz/invariant/test failure persisted by
# an earlier, unrelated run. Forge replays those before running anything
# new, so a stale one would show up in this gas report even after the
# real bug behind it was fixed. Clear it before testing.
rm -rf cache/fuzz cache/invariant cache/test-failures

# Skip invariant tests in the gas report: invariant runs dominate
# the wall-clock for ~20% of the gas signal (most invariants exercise
# the same paths the unit/fuzz suites already cost). Keep the
Expand All @@ -146,10 +174,6 @@ jobs:
--no-match-path 'test/fork/**' \
2>&1 | tee gas-report.txt

- name: Tear down revive-eth-rpc
if: always()
run: docker compose down --volumes --remove-orphans

- name: Stage shard output
if: always()
run: |
Expand All @@ -163,6 +187,14 @@ jobs:
path: shard
retention-days: 7

# Last step in the job on purpose: `actions/cache`'s save only runs once
# every step above has finished, and it caches whatever is on disk at that
# point, not just what "Generate gas report" left behind. Clearing here too
# means nothing this job did can leave a persisted failure in the saved cache.
- name: Clear persisted Foundry failure state before cache save
if: always()
run: rm -rf cache/fuzz cache/invariant cache/test-failures

report:
name: Gas Report Diff
needs: generate
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/genesis-extractor-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ on:
permissions:
contents: read

# Keyed by PR number so a new push cancels this PR's still-running extractor check
# instead of queuing behind it; every content-triggered workflow in this repo does
# the same.
concurrency:
group: genesis-extractor-test-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
genesis-extractor-test:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/issue-add-to-project.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Add Issues to DotNS Project
name: Add Issues to dotNS Project

on:
issues:
Expand Down
54 changes: 25 additions & 29 deletions .github/workflows/paseo-upgrade-step.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ name: Paseo Upgrade Step
# The gate is the `testnet-upgrades` environment: every run pauses until a required reviewer
# approves it, whoever added the label. The label is the request; the approval is the decision.
# Labels are also how retries work: the job removes its label when it finishes, so re-adding it
# re-runs the step. The rotation script skips what already moved and the twelve swaps are
# idempotent; step 13 is the exception, and its runbook section says to inspect, not re-add.
# re-runs the step. Re-running one that already landed is harmless: a single-proxy upgrade swaps
# in the same code again, and the other steps skip what is already on chain.
#
# Key handling. Step 0 signs with the repository-level `DOTNS_ADMIN_KEY`, because its value
# cannot be read by anyone, so it cannot be re-entered as an environment secret. That exposure
# is time-boxed: step 0's whole purpose is to make that key powerless. The fresh key the
# operator generates IS entered as an environment secret under the same name, which shadows the
# repository one for every later step, and the repository copy is then deleted. Runner logs on
# this public repository are world-readable, so nothing here may ever echo key material; the
# password below only encrypts a keystore that lives for one run.
# Key handling. Every step signs with `DOTNS_ADMIN_KEY` from the `testnet-upgrades`
# environment, the owner of every proxy and of the protocol registry. Runner logs on this public
# repository are world-readable, so nothing here may ever echo key material; the password below
# only encrypts a keystore that lives for one run.

on:
pull_request:
Expand Down Expand Up @@ -57,17 +54,20 @@ jobs:
run: |
set -euo pipefail
SCRIPT="${LABEL#run:}"
# Each step names the contracts it upgrades, whose snapshots are checked against the
# chain right before it broadcasts. Steps that upgrade no proxy name none.
case "$SCRIPT" in
RotateOwnership|UpgradeProtocolRegistry|UpgradeRegistry|UpgradeRegistrar|\
UpgradeRegistrarController|UpgradePopController|UpgradePopRules|\
UpgradeNameEscrow|UpgradeNameWhitelist|UpgradeResolver|UpgradeReverseResolver|\
UpgradeContentResolver|UpgradePopResolver|MigrateStoreFactory|DeclareRelease) ;;
UpgradeRegistry) SUBJECTS="DotnsRegistry" ;;
UpgradePopRules) SUBJECTS="PopRules" ;;
UpgradePopResolverAndController) SUBJECTS="DotnsPopResolver DotnsPopController" ;;
RedeployPopLens|DeclareRelease) SUBJECTS="" ;;
*)
echo "::error::'$SCRIPT' is not a runbook step; see docs/PASEO-V080-RUNBOOK.md"
echo "::error::'$SCRIPT' is not a runbook step; see docs/PASEO-V100-RUNBOOK.md"
exit 1
;;
esac
echo "script=$SCRIPT" >> "$GITHUB_OUTPUT"
echo "subjects=$SUBJECTS" >> "$GITHUB_OUTPUT"

# The head commit, not the merge ref. A pull_request checkout defaults to the PR merged
# into its base, and master merged into this branch is not the code that was reviewed or
Expand All @@ -93,41 +93,37 @@ jobs:
docker compose up --build -d eth-rpc
scripts/shell/wait-for-eth-rpc.sh http://127.0.0.1:8545

# Only before anything has been broadcast: the moment step 1 lands, the snapshots stop
# matching the chain by design, so this gate is pinned to step 0 instead of pretending to
# be a mid-flight health check.
# Scoped to the contracts this step upgrades. Every proxy an earlier step upgraded has
# moved past its snapshot by design; the ones this step is about to swap must still match,
# and a mismatch here means the code changed underneath the campaign.
- name: Verify snapshots against the deployed bytecode
if: steps.step.outputs.script == 'RotateOwnership'
if: steps.step.outputs.subjects != ''
env:
RPC_URL: http://127.0.0.1:8545
SNAPSHOT_SUBJECTS: ${{ steps.step.outputs.subjects }}
run: scripts/shell/verify-snapshots.sh

- name: Broadcast ${{ steps.step.outputs.script }}
env:
SCRIPT: ${{ steps.step.outputs.script }}
RPC_URL: http://127.0.0.1:8545
# `_account.sh` imports this into a run-local keystore when none exists. Environment
# secrets shadow repository ones for jobs that declare the environment, which is what
# switches every post-rotation step onto the fresh key without touching this file.
# `_account.sh` imports this into a run-local keystore when none exists.
PRIVATE_KEY: ${{ secrets.DOTNS_ADMIN_KEY }}
# Encrypts a keystore that exists for the lifetime of this runner. Masked because no
# log line should print even throwaway credentials in cleartext.
ACCOUNT_PASSWORD: testnet-upgrades-run-${{ github.run_id }}
# Step parameters, configured as environment variables on `testnet-upgrades`:
# DOTNS_NEW_OWNER for step 0, DOTNS_RELEASE_TAG for step 14, and optionally
# DOTNS_OLD_STORE_FACTORY as the step 13 cross-check.
DOTNS_NEW_OWNER: ${{ vars.DOTNS_NEW_OWNER }}
# Step parameter, configured as an environment variable on `testnet-upgrades`: the
# release tag `RedeployPopLens` scopes its salt to and `DeclareRelease` declares.
DOTNS_RELEASE_TAG: ${{ vars.DOTNS_RELEASE_TAG }}
DOTNS_OLD_STORE_FACTORY: ${{ vars.DOTNS_OLD_STORE_FACTORY }}
run: |
set -euo pipefail
echo "::add-mask::$ACCOUNT_PASSWORD"
./scripts/deploy/upgrade.sh

# The broadcast record and the manifest are the two things a step changes on disk. The
# manifest cannot be pushed back from here: the branch requires signed commits, which a
# runner cannot produce, so step 13's manifest change is committed by the operator from
# the artifact. Everything else leaves the workspace unchanged.
# runner cannot produce, so the manifest `RedeployPopLens` writes is committed by the
# operator from the artifact. Everything else leaves the workspace unchanged.
- name: Keep the broadcast record
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -151,5 +147,5 @@ jobs:
run: |
set -euo pipefail
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" \
--body "**${SCRIPT}**: ${RESULT}. [Run](${RUN_URL}). Broadcast record and manifest attached as artifacts. Re-add \`${LABEL}\` to retry; for MigrateStoreFactory, read the runbook before retrying anything."
--body "**${SCRIPT}**: ${RESULT}. [Run](${RUN_URL}). Broadcast record and manifest attached as artifacts. Re-add \`${LABEL}\` to retry."
gh pr edit "$PR" --repo "$GITHUB_REPOSITORY" --remove-label "$LABEL"
7 changes: 7 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ permissions:
pull-requests: write
issues: write

# Keyed by PR number so a new push (or label change) cancels this PR's still-running
# title/label check instead of queuing behind it; every content-triggered workflow in
# this repo does the same.
concurrency:
group: pr-title-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
title:
name: PR Title
Expand Down
Loading
Loading