mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
## Era-targeted validator set submission with dedicated submitter role > **Note:** This PR includes a detailed specification at [`specs/validator-set-submission/validator-set-submission.md`](https://github.com/datahaven-xyz/datahaven/blob/feat/validator-set-submitter/specs/validator-set-submission/validator-set-submission.md) that covers the design rationale, submission lifecycle, era-targeting rules, and failure modes. Reading the spec first will make the contract, pallet, and daemon changes easier to follow. ### Summary - Introduce a dedicated `validatorSetSubmitter` role on `DataHavenServiceManager`, separating validator set submission authority from the contract owner - Replace the unscoped `sendNewValidatorSet` with `sendNewValidatorSetForEra`, which encodes a `targetEra` into the Snowbridge message payload - Add server-side era validation in the `external-validators` pallet to reject stale, duplicate, or out-of-range submissions - Add a long-running TypeScript daemon that watches session changes and automatically submits each era's validator set at the right time ### Contract changes (`contracts/`) - **New `validatorSetSubmitter` storage slot** — set during `initialize` and rotatable via `setValidatorSetSubmitter` (owner-only). The storage gap is decremented accordingly. - **`sendNewValidatorSet` → `sendNewValidatorSetForEra`** — accepts a `uint64 targetEra` parameter and is restricted to `onlyValidatorSetSubmitter` instead of `onlyOwner`. - **`buildNewValidatorSetMessageForEra`** — the `NewValidatorSetPayload.externalIndex` is now caller-supplied instead of hardcoded to `0`. - **New events** — `ValidatorSetSubmitterUpdated`, `ValidatorSetMessageSubmitted`. - **New error** — `OnlyValidatorSetSubmitter`. - **New test suite** — `ValidatorSetSubmitter.t.sol` covering submitter set/rotate, access control, era encoding, and legacy function removal. ### Pallet changes (`operator/`) - **`validate_target_era`** in `external-validators` — enforces `activeEra < targetEra <= activeEra + 1` and `targetEra > ExternalIndex` (dedup guard). - **New errors** — `TargetEraTooOld`, `TargetEraTooNew`, `DuplicateOrStaleTargetEra`. - **Tests** — five new test cases for era boundary conditions (next-era acceptance, old-era rejection, too-new rejection, duplicate rejection, genesis behavior). Existing `era_hooks_with_external_index` test updated to use valid target eras. - **Runtime test fixes** — `external_index: 0` → `1` in mainnet/stagenet/testnet EigenLayer message processor tests to satisfy the new validation. ### Validator set submitter daemon (`test/tools/validator-set-submitter/`) - Event-driven service that subscribes to finalized `Session.CurrentIndex` via Polkadot-API `watchValue`. - Submits once per era during the last session, targeting `ActiveEra + 1`. - Tracks submitted eras to avoid duplicates; skips if `ExternalIndex` already covers the target. - Startup self-checks: Ethereum connectivity, DataHaven connectivity, on-chain submitter authorization. - Supports `--dry-run` mode and YAML configuration. - Graceful shutdown on `SIGINT`/`SIGTERM`. ### Test & tooling updates - **E2E test** (`validator-set-update.test.ts`) — calls `sendNewValidatorSetForEra` with a computed `targetEra` and filters the substrate event by `external_index`. - **`update-validator-set.ts` script** — accepts `--target-era` flag; defaults to era 1 for fresh networks. - **CLI launch** — wires validator set update as an interactive step after relayer launch. - **`package.json`** — new `submitter` and `submitter:dry-run` scripts. - Regenerated contract bindings, PAPI metadata, state-diff, and storage layout snapshots. ### Test plan - [x] `forge test` — passes, including new `ValidatorSetSubmitter.t.sol` - [x] `cargo test` — passes, including new era-validation tests in `external-validators` - [x] `bun test:e2e` — validator-set-update suite passes with era-targeted flow - [x] Manual: run submitter daemon against local network (`bun submitter`), verify it submits once per era at the correct session ## ⚠️ Breaking Changes ⚠️ - **`sendNewValidatorSet` removed** — replaced by `sendNewValidatorSetForEra(uint64 targetEra, ...)`. Callers must now supply a `targetEra` parameter. - **Access control changed** — validator set submission is now restricted to the `validatorSetSubmitter` role instead of the contract `owner`. The submitter address is set during `initialize` and rotatable via `setValidatorSetSubmitter` (owner-only). - **`external-validators` pallet now validates `targetEra`** — messages with a stale, duplicate, or out-of-range `external_index` are rejected on-chain. Existing integrations sending `external_index: 0` will fail validation. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
89 lines
4.3 KiB
JSON
89 lines
4.3 KiB
JSON
{
|
|
"name": "@datahaven/e2e-test",
|
|
"module": "index.ts",
|
|
"type": "module",
|
|
"private": true,
|
|
"scripts": {
|
|
"cli": "bun run cli/index.ts",
|
|
"fmt": "biome check .",
|
|
"fmt:fix": "biome check --write .",
|
|
"build:docker:operator": "docker build --no-cache --platform linux/amd64 -t datahavenxyz/datahaven:local -f ../docker/datahaven-dev.Dockerfile ../.",
|
|
"generate:wagmi": "wagmi generate",
|
|
"generate:snowbridge-cfgs": "bun -e \"import {generateSnowbridgeConfigs} from './scripts/gen-snowbridge-cfgs.ts'; await generateSnowbridgeConfigs()\"",
|
|
"generate:contracts": "bun -e \"import {generateContracts} from './scripts/generate-contracts.ts'; await generateContracts()\"",
|
|
"generate:types": "(cd ../operator && cargo build --release) && bun x papi add --wasm \"../operator/target/release/wbuild/datahaven-stagenet-runtime/datahaven_stagenet_runtime.wasm\" datahaven",
|
|
"generate:types:fast": "(cd ../operator && cargo build --release --features fast-runtime) && bun x papi add --wasm \"../operator/target/release/wbuild/datahaven-stagenet-runtime/datahaven_stagenet_runtime.wasm\" datahaven",
|
|
"start:e2e:verified": "bun cli launch --verified --blockscout --deploy-contracts --setup-validators --update-validator-set --fund-validators",
|
|
"start:e2e:verified:relayers": "bun cli launch --verified --blockscout --deploy-contracts --setup-validators --update-validator-set --fund-validators --relayer --datahaven",
|
|
"start:e2e:local": "LOG_LEVEL=debug bun start:e2e:ci --bd",
|
|
"start:e2e:ci": "bun cli launch --datahaven --no-build-datahaven --launch-kurtosis --deploy-contracts --fund-validators --setup-validators --update-validator-set --set-parameters --relayer --clean-network --slot-time 1",
|
|
"start:e2e:minrelayer": "bun cli launch --relayer --deploy-contracts --no-setup-validators --no-update-validator-set --no-fund-validators --datahaven",
|
|
"stop:docker:datahaven": "docker rm -f $(docker ps -aq --filter name='^datahaven-') 2>/dev/null || true; docker network rm datahaven-net || true",
|
|
"stop:docker:relayer": "docker rm -f $(docker ps -aq --filter name='^snowbridge-') 2>/dev/null || true",
|
|
"stop:e2e": "bun cli stop --all",
|
|
"stop:dh": "bun cli stop --datahaven --no-enclave --no-relayer ",
|
|
"stop:sb": "bun cli stop --relayer --no-datahaven --no-enclave",
|
|
"stop:eth": "bun cli stop --enclave --no-datahaven --no-relayer",
|
|
"stop:engine": "bun cli stop --kurtosisEngine --no-datahaven --no-relayer --no-enclave",
|
|
"test:e2e": "bun test ./e2e/suites --timeout 900000",
|
|
"compile:contracts": "bun x tsx scripts/compile-contracts.ts compile",
|
|
"test:e2e:parallel": "bun scripts/test-parallel.ts",
|
|
"moonwall:test": "moonwall test dev_datahaven",
|
|
"moonwall:run": "moonwall run dev_datahaven",
|
|
"submitter": "bun run tools/validator-set-submitter/main.ts run",
|
|
"submitter:dry-run": "bun run tools/validator-set-submitter/main.ts run --dry-run",
|
|
"typecheck": "tsc --noEmit",
|
|
"tsgo": "tsgo tsc --noEmit --pretty --skipLibCheck",
|
|
"postinstall": "papi"
|
|
},
|
|
"devDependencies": {
|
|
"@types/bun": "latest",
|
|
"@types/yargs": "^17.0.33",
|
|
"@typescript/native-preview": "^7.0.0-dev.20250618.1"
|
|
},
|
|
"peerDependencies": {
|
|
"typescript": "^5.8.3"
|
|
},
|
|
"dependencies": {
|
|
"@biomejs/biome": "^2.0.0",
|
|
"@commander-js/extra-typings": "^13.1.0",
|
|
"@dotenvx/dotenvx": "^1.44.2",
|
|
"@inquirer/prompts": "^7.5.3",
|
|
"@moonwall/cli": "^5.15.0",
|
|
"@moonwall/util": "^5.15.0",
|
|
"@noble/curves": "^1.9.2",
|
|
"@noble/hashes": "^1.8.0",
|
|
"@polkadot-api/descriptors": "file:.papi/descriptors",
|
|
"@types/dockerode": "^3.3.41",
|
|
"@types/node": "^22.15.32",
|
|
"@wagmi/cli": "^2.3.1",
|
|
"@wagmi/core": "^2.17.3",
|
|
"chalk": "^5.4.1",
|
|
"commander": "^13.1.0",
|
|
"dockerode": "^4.0.7",
|
|
"dotenv": "^16.5.0",
|
|
"ethers": "^6.13.4",
|
|
"octokit": "^4.1.4",
|
|
"ora": "^8.2.0",
|
|
"pino": "^9.7.0",
|
|
"pino-pretty": "^13.0.0",
|
|
"polkadot-api": "^1.15.1",
|
|
"solc": "^0.8.30",
|
|
"tiny-invariant": "^1.3.3",
|
|
"viem": "^2.31.3",
|
|
"wagmi": "^2.15.6",
|
|
"yaml": "^2.8.0",
|
|
"yargs": "^18.0.0",
|
|
"zod": "^3.25.67"
|
|
},
|
|
"trustedDependencies": [
|
|
"@biomejs/biome",
|
|
"bufferutil",
|
|
"cpu-features",
|
|
"esbuild",
|
|
"keccak",
|
|
"protobufjs",
|
|
"ssh2",
|
|
"utf-8-validate"
|
|
]
|
|
}
|