Skip to content

Commit b3b8974

Browse files
chore(funder): derive dedicated funder at bare root; fix CI comment order
Follow-up to the env-secret funder change: - funder.ts: derive the dedicated funder at the bare root (empty derivation path) instead of //0, so the funded account matches the signing path; replace the address cast with a real null narrow in FUNDER_CHAIN - funder.test.ts: pin the derivation path with a frozen bare-root vector so a path regression fails loudly - e2e/e2e-release/e2e-post-release workflows: reword comments to describe the dedicated funder as primary and Alice as the fallback (matches the new order) - docs/e2e-bootstrap.md + changeset: reflect the bare-root primary funder
1 parent fc1ddd3 commit b3b8974

7 files changed

Lines changed: 45 additions & 29 deletions

File tree

.changeset/funder-seed-env.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
The dedicated testnet funder seed is no longer hardcoded in the binary. It is
66
read from the `MASTER_FUNDER_SEED` environment variable (CI injects it from a
7-
repository secret); when unset, funding falls back to Alice alone. This affects
8-
only the E2E suite and operator tooling — no end-user command funds from this
9-
chain.
7+
repository secret); when unset, funding falls back to Alice alone. The dedicated
8+
funder is now the primary account (drawn down ahead of public Alice) and is
9+
derived at the bare root (empty derivation path). This affects only the E2E
10+
suite and operator tooling — no end-user command funds from this chain.

.github/workflows/e2e-post-release.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ jobs:
9090
env:
9191
DOT_TAG: e2e-ci-post-release
9292
DOT_TELEMETRY: "1"
93-
# Dedicated-funder seed for globalSetup's SIGNER top-up
94-
# fallback. Absent → Alice only.
93+
# Dedicated-funder seed — the primary account globalSetup
94+
# tops up SIGNER from, ahead of the Alice fallback. Absent →
95+
# Alice only.
9596
MASTER_FUNDER_SEED: ${{ secrets.MASTER_FUNDER_SEED }}
9697

9798
- name: Upload forensic artefacts

.github/workflows/e2e-release.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,9 @@ jobs:
7979
DOT_E2E_BINARY: ${{ github.workspace }}/bin/dot-linux-x64
8080
DOT_TAG: e2e-ci-release
8181
DOT_TELEMETRY: "1"
82-
# Dedicated-funder seed for globalSetup's SIGNER top-up
83-
# fallback. Absent → Alice only.
82+
# Dedicated-funder seed — the primary account globalSetup
83+
# tops up SIGNER from, ahead of the Alice fallback. Absent →
84+
# Alice only.
8485
MASTER_FUNDER_SEED: ${{ secrets.MASTER_FUNDER_SEED }}
8586

8687
- name: Upload forensic artefacts

.github/workflows/e2e.yml

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ jobs:
7070
DOT_DEPLOY_VERBOSE: "1"
7171
DOT_TAG: ${{ steps.setup.outputs.tag }}
7272
DOT_TELEMETRY: "1"
73-
# Seed for the dedicated funder, the fallback globalSetup uses
74-
# to top up SIGNER once Alice drains. Absent → Alice only.
73+
# Seed for the dedicated funder, the primary account
74+
# globalSetup tops up SIGNER from (drawn down before public
75+
# Alice, the fallback). Absent → Alice only.
7576
MASTER_FUNDER_SEED: ${{ secrets.MASTER_FUNDER_SEED }}
7677

7778
- name: Surface failure detail
@@ -168,8 +169,9 @@ jobs:
168169
DOT_DEPLOY_VERBOSE: "1"
169170
DOT_TAG: ${{ steps.setup.outputs.tag }}
170171
DOT_TELEMETRY: "1"
171-
# Seed for the dedicated funder, the fallback globalSetup uses
172-
# to top up SIGNER once Alice drains. Absent → Alice only.
172+
# Seed for the dedicated funder, the primary account
173+
# globalSetup tops up SIGNER from (drawn down before public
174+
# Alice, the fallback). Absent → Alice only.
173175
MASTER_FUNDER_SEED: ${{ secrets.MASTER_FUNDER_SEED }}
174176

175177
- name: Surface failure detail
@@ -231,8 +233,9 @@ jobs:
231233
DOT_DEPLOY_VERBOSE: "1"
232234
DOT_TAG: ${{ steps.setup.outputs.tag }}
233235
DOT_TELEMETRY: "1"
234-
# Seed for the dedicated funder, the fallback globalSetup uses
235-
# to top up SIGNER once Alice drains. Absent → Alice only.
236+
# Seed for the dedicated funder, the primary account
237+
# globalSetup tops up SIGNER from (drawn down before public
238+
# Alice, the fallback). Absent → Alice only.
236239
MASTER_FUNDER_SEED: ${{ secrets.MASTER_FUNDER_SEED }}
237240
# nightly-deploy-moddable creates a fresh public repo in
238241
# paritytech/ each run + pushes the fixture into it. Other
@@ -315,8 +318,9 @@ jobs:
315318
DOT_DEPLOY_VERBOSE: "1"
316319
DOT_TAG: ${{ steps.setup.outputs.tag }}
317320
DOT_TELEMETRY: "1"
318-
# Seed for the dedicated funder, the fallback globalSetup uses
319-
# to top up SIGNER once Alice drains. Absent → Alice only.
321+
# Seed for the dedicated funder, the primary account
322+
# globalSetup tops up SIGNER from (drawn down before public
323+
# Alice, the fallback). Absent → Alice only.
320324
MASTER_FUNDER_SEED: ${{ secrets.MASTER_FUNDER_SEED }}
321325

322326
- name: Surface failure detail

docs/e2e-bootstrap.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ After a registry-contract redeploy, the next CI run restores them automatically.
6262

6363
`MASTER_FUNDER_SEED` is a BIP-39 mnemonic read at runtime by
6464
`src/utils/account/funder.ts` (env var, never hardcoded). It derives the
65-
dedicated funder at `//0`, which is the **primary** funder — the chain draws it
66-
down first and falls back to public Alice only when it's empty. When unset the
67-
chain is just Alice — fine for offline/local runs, but CI must set it. Provide
68-
it to local runs with `export MASTER_FUNDER_SEED=…`.
65+
dedicated funder at the bare root (empty derivation path), which is the
66+
**primary** funder — the chain draws it down first and falls back to public
67+
Alice only when it's empty. When unset the chain is just Alice — fine for
68+
offline/local runs, but CI must set it. Provide it to local runs with
69+
`export MASTER_FUNDER_SEED=…`.
6970

7071
`GITHUB_TOKEN` (auto-provided) is used by the report job and cleanup cron.
7172

@@ -115,7 +116,7 @@ Nothing to do manually. `globalSetup` (`e2e/cli/setup/fund.ts`) and
115116
Alice as the fallback.
116117

117118
If both the dedicated funder and Alice are empty, re-fund the dedicated funder
118-
account (the `//0`-derived address — run `bun run scripts/check-funder-balance.ts`
119+
account (the bare-root address — run `bun run scripts/check-funder-balance.ts`
119120
with the seed exported to print it) on Paseo, and update the secret if the seed
120121
itself changed.
121122

src/utils/account/funder.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ import { describe, it, expect, afterEach, vi } from "vitest";
2828

2929
const VALID_MNEMONIC = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
3030

31+
// Frozen bare-root (empty derivation path) address of VALID_MNEMONIC. Pins the
32+
// derivation path so a regression back to `//0` (or any other junction) fails
33+
// loudly: the funder is funded at the bare root, and a path drift would silently
34+
// move the signer to an unfunded account. `//0` of this mnemonic is a DIFFERENT
35+
// address (5D34dL5…), so this vector genuinely guards the path.
36+
const VALID_MNEMONIC_BARE_ROOT = "5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV";
37+
3138
afterEach(() => {
3239
delete process.env.MASTER_FUNDER_SEED;
3340
vi.resetModules();
@@ -50,8 +57,9 @@ describe("FUNDER_CHAIN dedicated-funder gating", () => {
5057

5158
// Dedicated funder is tried first; Alice is the fallback.
5259
expect(FUNDER_CHAIN.map((f) => f.name)).toEqual(["dedicated", "Alice"]);
53-
// Address is derived from the seed and matches the leading chain entry.
54-
expect(DEDICATED_FUNDER_ADDRESS).toMatch(/^5/);
60+
// Derived at the bare root (empty path) and matches the leading entry.
61+
// The frozen vector pins the derivation path against `//0` regressions.
62+
expect(DEDICATED_FUNDER_ADDRESS).toBe(VALID_MNEMONIC_BARE_ROOT);
5563
expect(FUNDER_CHAIN[0]?.address).toBe(DEDICATED_FUNDER_ADDRESS);
5664
});
5765

src/utils/account/funder.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ import { ss58Encode } from "@parity/product-sdk-address";
4141
import { type Env, getChainConfig } from "../../config.js";
4242

4343
/**
44-
* Dedicated testnet funder, derived at `//0` from the `MASTER_FUNDER_SEED`
45-
* env var (a BIP-39 mnemonic). `null` when the var is unset/blank — callers
46-
* then fall back to Alice alone. Derived once at module load so we don't
47-
* re-run BIP-39 + sr25519 on every access.
44+
* Dedicated testnet funder, derived at the bare root (empty derivation path)
45+
* from the `MASTER_FUNDER_SEED` env var (a BIP-39 mnemonic). `null` when the
46+
* var is unset/blank — callers then fall back to Alice alone. Derived once at
47+
* module load so we don't re-run BIP-39 + sr25519 on every access.
4848
*/
4949
const dedicatedSeed = process.env.MASTER_FUNDER_SEED?.trim();
50-
const dedicated = dedicatedSeed ? seedToAccount(dedicatedSeed, "//0") : null;
50+
const dedicated = dedicatedSeed ? seedToAccount(dedicatedSeed, "") : null;
5151
const dedicatedAddress = dedicated ? ss58Encode(dedicated.publicKey) : null;
5252

5353
export interface Funder {
@@ -68,11 +68,11 @@ export interface Funder {
6868
* `MASTER_FUNDER_SEED` is configured; without it the chain is Alice-only.
6969
*/
7070
export const FUNDER_CHAIN: readonly Funder[] = [
71-
...(dedicated
71+
...(dedicated && dedicatedAddress
7272
? [
7373
{
7474
name: "dedicated",
75-
address: dedicatedAddress as string,
75+
address: dedicatedAddress,
7676
signer: dedicated.signer,
7777
},
7878
]

0 commit comments

Comments
 (0)