From e3f521b0bf4f17d091288d1462c24e7bd7837c7d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Fri, 28 Nov 2025 16:16:02 +0100 Subject: [PATCH 1/7] Update integration tests for battery station runtime and add relay timestamp checks --- integration-tests/README.md | 4 +- ...itgeist.json => zombieBatteryStation.json} | 0 integration-tests/moonwall.config.json | 6 +- integration-tests/package.json | 1 + integration-tests/pnpm-lock.yaml | 3 + .../test-zombienet-runtime-upgrade.ts | 175 +++++++++++++++++- 6 files changed, 182 insertions(+), 7 deletions(-) rename integration-tests/configs/{zombieZeitgeist.json => zombieBatteryStation.json} (100%) diff --git a/integration-tests/README.md b/integration-tests/README.md index 982a91515..c3de38a91 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -86,10 +86,10 @@ Unable to map [u8; 32] to a lookup index [16:36:17.240] INFO (xcm/24440): Connected relaychain 'Polkadot' with parachain 'Zeitgeist' ``` -#### Test the upgrade to the WASM from `./target/release/wbuild/zeitgeist-runtime` on zombienet: +#### Test the upgrade to the WASM from `./target/release/wbuild/battery-station-runtime` on zombienet: ```bash -pnpm exec moonwall test zombienet_zeitgeist_upgrade --runInBand +pnpm exec moonwall test zombienet_battery_station_upgrade --runInBand ``` #### Test the upgrade to the WASM from `./target/release/wbuild/zeitgeist-runtime` on the live main-net fork using chopsticks: diff --git a/integration-tests/configs/zombieZeitgeist.json b/integration-tests/configs/zombieBatteryStation.json similarity index 100% rename from integration-tests/configs/zombieZeitgeist.json rename to integration-tests/configs/zombieBatteryStation.json diff --git a/integration-tests/moonwall.config.json b/integration-tests/moonwall.config.json index 2e33b7c36..55d5e5571 100644 --- a/integration-tests/moonwall.config.json +++ b/integration-tests/moonwall.config.json @@ -4,7 +4,7 @@ "scriptsDir": "scripts/", "environments": [ { - "name": "zombienet_zeitgeist_upgrade", + "name": "zombienet_battery_station_upgrade", "testFileDir": ["tests/rt-upgrade-zombienet"], "runScripts": [ "build-node.sh", @@ -17,10 +17,10 @@ "binPath": "../target/release/zeitgeist" } ], - "rtUpgradePath": "../target/release/wbuild/zeitgeist-runtime/zeitgeist_runtime.compact.compressed.wasm", + "rtUpgradePath": "../target/release/wbuild/battery-station-runtime/battery_station_runtime.compact.compressed.wasm", "type": "zombie", "zombieSpec": { - "configPath": "./configs/zombieZeitgeist.json" + "configPath": "./configs/zombieBatteryStation.json" } }, "connections": [ diff --git a/integration-tests/package.json b/integration-tests/package.json index 08f6df946..7d8749ebb 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -12,6 +12,7 @@ "@polkadot/api": "16.5.2", "@polkadot/keyring": "13.5.2", "@polkadot/types": "16.5.2", + "@polkadot/util": "13.5.2", "@polkadot/util-crypto": "13.5.2", "@types/node": "22.10.2", "debug": "4.3.4", diff --git a/integration-tests/pnpm-lock.yaml b/integration-tests/pnpm-lock.yaml index e31e1a4b3..00ad9c999 100644 --- a/integration-tests/pnpm-lock.yaml +++ b/integration-tests/pnpm-lock.yaml @@ -57,6 +57,9 @@ importers: '@polkadot/types': specifier: 16.5.2 version: 16.5.2 + '@polkadot/util': + specifier: 13.5.2 + version: 13.5.2 '@polkadot/util-crypto': specifier: 13.5.2 version: 13.5.2(@polkadot/util@13.5.2) diff --git a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts index 247790815..fff5421ff 100644 --- a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts +++ b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts @@ -24,6 +24,8 @@ import { } from "@moonwall/cli"; import { KeyringPair } from "@moonwall/util"; import { ApiPromise, Keyring } from "@polkadot/api"; +import { u8aConcat } from "@polkadot/util"; +import { blake2AsHex, xxhashAsU8a } from "@polkadot/util-crypto"; import fs from "node:fs"; import { RuntimeVersion } from "@polkadot/types/interfaces"; @@ -83,6 +85,12 @@ describeSuite({ const codeString = currentCode.toString(); const moonwallContext = await MoonwallContext.getContext(); + const specVersion = ( + paraApi.consts.system.version as unknown as RuntimeVersion + ).specVersion.toNumber(); + log( + `Parachain specVersion=${specVersion}, block=${blockNumberBefore}, rtUpgradePath=${moonwallContext.rtUpgradePath}` + ); log( "Moonwall Context providers: " + moonwallContext.providers.map((p) => p.name).join(", ") @@ -109,16 +117,179 @@ describeSuite({ ); } - await context.upgradeRuntime({ from: alice, logger: log }); + const txStatus = async (tx: any, label: string) => + new Promise((resolve, reject) => { + let unsubscribe: (() => void) | undefined; + tx.signAndSend(alice, (result: any) => { + if (result.dispatchError) { + // Dispatch errors won't throw, so surface them explicitly. + const errText = result.dispatchError.toString(); + log(`${label} dispatchError=${errText}`); + reject(new Error(`${label} failed: ${errText}`)); + unsubscribe?.(); + return; + } + log( + `${label} status=${result.status?.type ?? "unknown"}, events=${result.events + ?.map((ev: any) => `${ev.event.section}.${ev.event.method}`) + .join(",")}` + ); + if (result.status?.isInBlock || result.status?.isFinalized) { + unsubscribe?.(); + resolve(); + } + }) + .then((unsub: () => void) => { + unsubscribe = unsub; + }) + .catch(reject); + }); + + const findCall = (callName: string) => { + for (const [section, calls] of Object.entries(paraApi.tx)) { + const typedCalls = calls as Record; + if (typedCalls?.[callName]) { + return { call: typedCalls[callName], section }; + } + } + return undefined; + }; + + const upgradeCallLocation = { + authorize: undefined as string | undefined, + enact: undefined as string | undefined, + }; + + const authorizeUpgradeResult = findCall("authorizeUpgrade"); + if (authorizeUpgradeResult) { + upgradeCallLocation.authorize = authorizeUpgradeResult.section; + } + + // On this SDK the enact call lives in frame-system as applyAuthorizedUpgrade. + const applyAuthorizedUpgradeResult = findCall("applyAuthorizedUpgrade"); + if (applyAuthorizedUpgradeResult) { + upgradeCallLocation.enact = applyAuthorizedUpgradeResult.section; + } + + const authorizeUpgrade = authorizeUpgradeResult?.call; + const applyAuthorizedUpgrade = applyAuthorizedUpgradeResult?.call; + const upgradeAvailable = authorizeUpgrade && applyAuthorizedUpgrade; + + const upgradeSections = Object.keys(paraApi.tx).filter((section) => + /parachain|upgrade|system/i.test(section) + ); + log(`tx sections matching /parachain|upgrade|system/: ${upgradeSections.join(",")}`); + + if (upgradeAvailable) { + // Zeitgeist runtime blocks `setCode`, so use the authorized upgrade flow. + const wasmHash = blake2AsHex(wasm); + log("Authorizing runtime upgrade via system.authorizeUpgrade"); + const authorizeTx = + authorizeUpgrade.meta.args.length === 1 + ? authorizeUpgrade(wasmHash) + : authorizeUpgrade(wasmHash, true); + log( + `authorizeUpgrade located in section=${upgradeCallLocation.authorize}, args=${authorizeUpgrade.meta.args.length}` + ); + await txStatus( + paraApi.tx.sudo.sudo(authorizeTx), + "authorizeUpgrade" + ); + + log("Waiting for validation function approval"); + await context.waitBlock(2); + + log("Enacting authorized upgrade"); + log( + `applyAuthorizedUpgrade located in section=${upgradeCallLocation.enact}, args=${applyAuthorizedUpgrade.meta.args.length}` + ); + await txStatus( + paraApi.tx.sudo.sudo(applyAuthorizedUpgrade(rtHex)), + "applyAuthorizedUpgrade" + ); + } else { + throw new Error( + "Runtime upgrade calls missing in metadata; expected system.authorizeUpgrade/applyAuthorizedUpgrade" + ); + } + await context.waitBlock(2); const blockNumberAfter = ( await paraApi.rpc.chain.getBlock() ).block.header.number.toNumber(); - log(`Before: #${blockNumberBefore}, After: #${blockNumberAfter}`); + const codeAfter = (await paraApi.rpc.state.getStorage(":code"))?.toString(); + log( + `Before: #${blockNumberBefore}, After: #${blockNumberAfter}, code changed=${ + codeAfter !== codeString + }` + ); + log( + `Code (before): ${codeString.slice(0, 10)}...${codeString.slice(-10)}, code (after): ${ + codeAfter ? codeAfter.slice(0, 10) + "..." + codeAfter.slice(-10) : "undefined" + }` + ); expect( blockNumberAfter, "Block number did not increase" ).to.be.greaterThan(blockNumberBefore); + expect(codeAfter, "Runtime code should match upgraded wasm").to.equal(rtHex); + }, + }); + + it({ + id: "T03", + title: "Relay timestamp (from relay proof) is present and increases across blocks", + timeout: 120000, + test: async function () { + const runtimePrefix = xxhashAsU8a("runtime", 128); + const tsPrefix = xxhashAsU8a("RelayTimestampNow", 128); + const relayTsKey = u8aConcat(runtimePrefix, tsPrefix); + + const readRelayTs = async (): Promise => { + const raw = await paraApi.rpc.state.getStorage(relayTsKey); + expect(raw, "RelayTimestampNow storage should exist").to.not.be.null; + const rawHex = raw?.toHex(); + expect(rawHex, "RelayTimestampNow should decode to hex").to.not.be.undefined; + log(`RelayTimestampNow raw=${rawHex}`); + return paraApi.createType("u64", rawHex ?? 0).toNumber(); + }; + + let tsRelay1 = 0; + let retries = 0; + while (tsRelay1 === 0 && retries < 5) { + log(`Attempt ${retries + 1}: reading RelayTimestampNow`); + tsRelay1 = await readRelayTs(); + if (tsRelay1 === 0) { + await context.waitBlock(1); + } + retries++; + } + + const tsPara1 = (await paraApi.query.timestamp.now()).toNumber(); + const block1 = ( + await paraApi.rpc.chain.getBlock() + ).block.header.number.toNumber(); + + expect(tsRelay1, "Initial relay timestamp should be non-zero").to.be.greaterThan(0); + expect( + tsPara1, + "Parachain timestamp should mirror relay timestamp" + ).to.equal(tsRelay1); + + await context.waitBlock(2); + + const tsRelay2 = await readRelayTs(); + const tsPara2 = (await paraApi.query.timestamp.now()).toNumber(); + const block2 = ( + await paraApi.rpc.chain.getBlock() + ).block.header.number.toNumber(); + + expect(block2, "Block height should advance").to.be.greaterThan(block1); + expect( + tsRelay2, + "Relay timestamp should increase with new relay proofs" + ).to.be.greaterThan(tsRelay1); + expect(tsPara2, "Parachain timestamp should mirror relay timestamp").to.equal(tsRelay2); }, }); }, From 297f2d66f689e25bc422ceb9ba947f43136516f1 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Sun, 30 Nov 2025 19:17:52 +0100 Subject: [PATCH 2/7] Update relay timestamp test to use direct storage key for retrieval --- .../test-zombienet-runtime-upgrade.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts index fff5421ff..c2211021f 100644 --- a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts +++ b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts @@ -241,12 +241,11 @@ describeSuite({ title: "Relay timestamp (from relay proof) is present and increases across blocks", timeout: 120000, test: async function () { - const runtimePrefix = xxhashAsU8a("runtime", 128); - const tsPrefix = xxhashAsU8a("RelayTimestampNow", 128); - const relayTsKey = u8aConcat(runtimePrefix, tsPrefix); + const relayTsStorageKey = + "0x54dbd40f5201dbc18b0eed4b2ecd9cc67e2cdf745d68eeb295336330e3a1a063"; const readRelayTs = async (): Promise => { - const raw = await paraApi.rpc.state.getStorage(relayTsKey); + const raw = await paraApi.rpc.state.getStorage(relayTsStorageKey); expect(raw, "RelayTimestampNow storage should exist").to.not.be.null; const rawHex = raw?.toHex(); expect(rawHex, "RelayTimestampNow should decode to hex").to.not.be.undefined; @@ -259,6 +258,10 @@ describeSuite({ while (tsRelay1 === 0 && retries < 5) { log(`Attempt ${retries + 1}: reading RelayTimestampNow`); tsRelay1 = await readRelayTs(); + const rawDirect = await paraApi.rpc.state.getStorage( + relayTsStorageKey + ); + log(`RelayTimestampNow direct RPC read: ${rawDirect?.toHex() ?? "null"}`); if (tsRelay1 === 0) { await context.waitBlock(1); } From 720edbf2be487dfa7d120d11184f9e6255db4a6a Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Sun, 30 Nov 2025 19:27:27 +0100 Subject: [PATCH 3/7] Update relay timestamp test to use bigint for timestamp values --- .../test-zombienet-runtime-upgrade.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts index c2211021f..1ed2663d1 100644 --- a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts +++ b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts @@ -244,36 +244,36 @@ describeSuite({ const relayTsStorageKey = "0x54dbd40f5201dbc18b0eed4b2ecd9cc67e2cdf745d68eeb295336330e3a1a063"; - const readRelayTs = async (): Promise => { + const readRelayTs = async (): Promise => { const raw = await paraApi.rpc.state.getStorage(relayTsStorageKey); expect(raw, "RelayTimestampNow storage should exist").to.not.be.null; const rawHex = raw?.toHex(); expect(rawHex, "RelayTimestampNow should decode to hex").to.not.be.undefined; log(`RelayTimestampNow raw=${rawHex}`); - return paraApi.createType("u64", rawHex ?? 0).toNumber(); + return paraApi.createType("u64", rawHex ?? 0).toBigInt(); }; - let tsRelay1 = 0; + let tsRelay1 = 0n; let retries = 0; - while (tsRelay1 === 0 && retries < 5) { + while (tsRelay1 === 0n && retries < 5) { log(`Attempt ${retries + 1}: reading RelayTimestampNow`); tsRelay1 = await readRelayTs(); const rawDirect = await paraApi.rpc.state.getStorage( relayTsStorageKey ); log(`RelayTimestampNow direct RPC read: ${rawDirect?.toHex() ?? "null"}`); - if (tsRelay1 === 0) { + if (tsRelay1 === 0n) { await context.waitBlock(1); } retries++; } - const tsPara1 = (await paraApi.query.timestamp.now()).toNumber(); + const tsPara1 = (await paraApi.query.timestamp.now()).toBigInt(); const block1 = ( await paraApi.rpc.chain.getBlock() ).block.header.number.toNumber(); - expect(tsRelay1, "Initial relay timestamp should be non-zero").to.be.greaterThan(0); + expect(tsRelay1, "Initial relay timestamp should be non-zero").to.be.greaterThan(0n); expect( tsPara1, "Parachain timestamp should mirror relay timestamp" @@ -282,7 +282,7 @@ describeSuite({ await context.waitBlock(2); const tsRelay2 = await readRelayTs(); - const tsPara2 = (await paraApi.query.timestamp.now()).toNumber(); + const tsPara2 = (await paraApi.query.timestamp.now()).toBigInt(); const block2 = ( await paraApi.rpc.chain.getBlock() ).block.header.number.toNumber(); From 8d81301d76e8d4001511fe39925ce1dddd0b1367 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Sun, 30 Nov 2025 19:33:38 +0100 Subject: [PATCH 4/7] Refactor relay timestamp test to decode storage value as bigint --- .../rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts index 1ed2663d1..b9df079a7 100644 --- a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts +++ b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts @@ -26,6 +26,7 @@ import { KeyringPair } from "@moonwall/util"; import { ApiPromise, Keyring } from "@polkadot/api"; import { u8aConcat } from "@polkadot/util"; import { blake2AsHex, xxhashAsU8a } from "@polkadot/util-crypto"; +import { u8aToBigInt } from "@polkadot/util"; import fs from "node:fs"; import { RuntimeVersion } from "@polkadot/types/interfaces"; @@ -250,7 +251,9 @@ describeSuite({ const rawHex = raw?.toHex(); expect(rawHex, "RelayTimestampNow should decode to hex").to.not.be.undefined; log(`RelayTimestampNow raw=${rawHex}`); - return paraApi.createType("u64", rawHex ?? 0).toBigInt(); + // Storage encodes u64 little-endian; decode explicitly. + const ts = u8aToBigInt(raw?.toU8a(true) ?? new Uint8Array(), true); + return ts; }; let tsRelay1 = 0n; From 648d32437ac4063dbc333b4f14d79ced47981e30 Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Sun, 30 Nov 2025 19:37:32 +0100 Subject: [PATCH 5/7] Update relay timestamp test to allow for small drift between parachain and relay timestamps --- .../test-zombienet-runtime-upgrade.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts index b9df079a7..cd953206b 100644 --- a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts +++ b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts @@ -277,10 +277,11 @@ describeSuite({ ).block.header.number.toNumber(); expect(tsRelay1, "Initial relay timestamp should be non-zero").to.be.greaterThan(0n); - expect( - tsPara1, - "Parachain timestamp should mirror relay timestamp" - ).to.equal(tsRelay1); + const drift = tsPara1 > tsRelay1 ? tsPara1 - tsRelay1 : tsRelay1 - tsPara1; + const driftLimitMs = 5_000n; // allow small drift between local timestamp inherent and relay value + expect(drift, "Parachain timestamp should be close to relay timestamp").to.be.lte( + driftLimitMs + ); await context.waitBlock(2); @@ -295,7 +296,10 @@ describeSuite({ tsRelay2, "Relay timestamp should increase with new relay proofs" ).to.be.greaterThan(tsRelay1); - expect(tsPara2, "Parachain timestamp should mirror relay timestamp").to.equal(tsRelay2); + const drift2 = tsPara2 > tsRelay2 ? tsPara2 - tsRelay2 : tsRelay2 - tsPara2; + expect(drift2, "Parachain timestamp should be close to relay timestamp").to.be.lte( + driftLimitMs + ); }, }); }, From 5614e63a7ca7ed2f20c1e37900f648a538b9f2fe Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 1 Dec 2025 10:56:10 +0100 Subject: [PATCH 6/7] Add zombieZeitgeist configuration for zombienet upgrade tests --- .../configs/zombieZeitgeist.json | 45 +++++++++++++++++++ integration-tests/moonwall.config.json | 33 ++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 integration-tests/configs/zombieZeitgeist.json diff --git a/integration-tests/configs/zombieZeitgeist.json b/integration-tests/configs/zombieZeitgeist.json new file mode 100644 index 000000000..8849784b7 --- /dev/null +++ b/integration-tests/configs/zombieZeitgeist.json @@ -0,0 +1,45 @@ +{ + "settings": { + "timeout": 1000, + "provider": "native" + }, + "relaychain": { + "chain": "polkadot-local", + "default_command": "./tmp/polkadot", + "default_args": ["--no-hardware-benchmarks", "-lparachain=debug", "--database=paritydb", "--no-beefy", "--detailed-log-output"], + "nodes": [ + { + "name": "charlie", + "rpc_port": 9957, + "validator": true + }, + { + "name": "bob", + "rpc_port": 9960, + "validator": true + } + ] + }, + "parachains": [ + { + "id": 2092, + "chain": "zeitgeist", + "collators": [ + { + "name": "alice", + "command": "../target/release/zeitgeist", + "rpc_port": 9954, + "p2p_port": 33059, + "args": ["-lparachain=debug", "--force-authoring", "--detailed-log-output"] + } + ] + } + ], + "types": { + "Header": { + "number": "u64", + "parent_hash": "Hash", + "post_state": "Hash" + } + } +} diff --git a/integration-tests/moonwall.config.json b/integration-tests/moonwall.config.json index 55d5e5571..fb37099a6 100644 --- a/integration-tests/moonwall.config.json +++ b/integration-tests/moonwall.config.json @@ -36,6 +36,39 @@ } ] }, + { + "name": "zombienet_zeitgeist_upgrade", + "testFileDir": ["tests/rt-upgrade-zombienet"], + "runScripts": [ + "build-node.sh", + "build-zeitgeist-spec.sh", + "download-polkadot.sh" + ], + "foundation": { + "launchSpec": [ + { + "binPath": "../target/release/zeitgeist" + } + ], + "rtUpgradePath": "../target/release/wbuild/zeitgeist-runtime/zeitgeist_runtime.compact.compressed.wasm", + "type": "zombie", + "zombieSpec": { + "configPath": "./configs/zombieZeitgeist.json" + } + }, + "connections": [ + { + "name": "Relay", + "type": "polkadotJs", + "endpoints": ["ws://127.0.0.1:9957"] + }, + { + "name": "parachain", + "type": "polkadotJs", + "endpoints": ["ws://127.0.0.1:9954"] + } + ] + }, { "name": "chopsticks_zeitgeist_upgrade", "testFileDir": ["tests/rt-upgrade-zeitgeist-chopsticks"], From d9ae9d298f19742c5d380dc8eb1046b2566efb6d Mon Sep 17 00:00:00 2001 From: Chralt98 Date: Mon, 1 Dec 2025 15:22:00 +0100 Subject: [PATCH 7/7] Update integration tests and configurations for zombienet upgrades --- integration-tests/README.md | 6 ++ integration-tests/configs/hydradx.yml | 6 +- .../configs/zombieZeitgeist.json | 90 ++++++++++--------- .../scripts/build-zeitgeist-spec.sh | 62 ++++++++++++- .../scripts/download-polkadot.sh | 4 + .../test-zombienet-runtime-upgrade.ts | 3 +- 6 files changed, 126 insertions(+), 45 deletions(-) diff --git a/integration-tests/README.md b/integration-tests/README.md index c3de38a91..771579e4a 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -86,6 +86,12 @@ Unable to map [u8; 32] to a lookup index [16:36:17.240] INFO (xcm/24440): Connected relaychain 'Polkadot' with parachain 'Zeitgeist' ``` +#### Test the upgrade to the WASM from `./target/release/wbuild/zeitgeist-runtime` on zombienet: + +```bash +pnpm exec moonwall test zombienet_zeitgeist_upgrade --runInBand +``` + #### Test the upgrade to the WASM from `./target/release/wbuild/battery-station-runtime` on zombienet: ```bash diff --git a/integration-tests/configs/hydradx.yml b/integration-tests/configs/hydradx.yml index 1837b3d00..a18a0d186 100644 --- a/integration-tests/configs/hydradx.yml +++ b/integration-tests/configs/hydradx.yml @@ -1,4 +1,8 @@ -endpoint: wss://rpc.hydradx.cloud/public-ws +endpoint: + - wss://hydration-rpc.n.dwellir.com + - wss://hydration.ibp.network + - wss://rpc.helikon.io/hydradx + - wss://hydration.dotters.network mock-signature-host: true block: ${env.HYDRADX_BLOCK_NUMBER} db: ./tmp/hydradx_db_mba.sqlite diff --git a/integration-tests/configs/zombieZeitgeist.json b/integration-tests/configs/zombieZeitgeist.json index 8849784b7..16d66cd49 100644 --- a/integration-tests/configs/zombieZeitgeist.json +++ b/integration-tests/configs/zombieZeitgeist.json @@ -1,45 +1,55 @@ { - "settings": { - "timeout": 1000, - "provider": "native" - }, - "relaychain": { - "chain": "polkadot-local", - "default_command": "./tmp/polkadot", - "default_args": ["--no-hardware-benchmarks", "-lparachain=debug", "--database=paritydb", "--no-beefy", "--detailed-log-output"], - "nodes": [ - { - "name": "charlie", - "rpc_port": 9957, - "validator": true - }, - { - "name": "bob", - "rpc_port": 9960, - "validator": true - } - ] - }, - "parachains": [ - { - "id": 2092, - "chain": "zeitgeist", - "collators": [ - { - "name": "alice", - "command": "../target/release/zeitgeist", - "rpc_port": 9954, - "p2p_port": 33059, - "args": ["-lparachain=debug", "--force-authoring", "--detailed-log-output"] - } - ] - } + "settings": { + "timeout": 1000, + "provider": "native" + }, + "relaychain": { + "chain": "rococo-local", + "default_command": "./tmp/polkadot", + "default_args": [ + "--no-hardware-benchmarks", + "-lparachain=debug", + "--database=paritydb", + "--no-beefy", + "--detailed-log-output" ], - "types": { - "Header": { - "number": "u64", - "parent_hash": "Hash", - "post_state": "Hash" + "nodes": [ + { + "name": "charlie", + "rpc_port": 9957, + "validator": true + }, + { + "name": "bob", + "rpc_port": 9960, + "validator": true + } + ] + }, + "parachains": [ + { + "id": 2092, + "chain": "./zeitgeist-parachain-2092.json", + "collators": [ + { + "name": "alice", + "command": "../target/release/zeitgeist", + "rpc_port": 9954, + "p2p_port": 33059, + "args": [ + "-lparachain=debug", + "--force-authoring", + "--detailed-log-output" + ] } + ] + } + ], + "types": { + "Header": { + "number": "u64", + "parent_hash": "Hash", + "post_state": "Hash" } + } } diff --git a/integration-tests/scripts/build-zeitgeist-spec.sh b/integration-tests/scripts/build-zeitgeist-spec.sh index 6a3d6dd4b..e1b970424 100755 --- a/integration-tests/scripts/build-zeitgeist-spec.sh +++ b/integration-tests/scripts/build-zeitgeist-spec.sh @@ -2,10 +2,68 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Exit on any error -set -e +set -euo pipefail # Always run the commands from the "integration-tests" dir cd $(dirname $0)/.. mkdir -p specs -../target/release/zeitgeist build-spec --chain=zeitgeist --raw > specs/zeitgeist-parachain-2092.json \ No newline at end of file +# Start from the dev plain spec so zombienet can customize it. Avoid parsing +# and re-stringifying the whole JSON (which can switch large integers into +# exponent form) by doing targeted string replacements. +tmp_spec="$(mktemp)" +../target/release/zeitgeist build-spec --chain=dev --disable-default-bootnode > "${tmp_spec}" + +out_path="$(pwd)/zeitgeist-parachain-2092.json" + +node - "${tmp_spec}" "${out_path}" <<'NODE' +const fs = require("fs"); +const path = require("path"); +const [,, inPath, outPath] = process.argv; +if (!inPath || !outPath) { + console.error("spec path arg missing"); + process.exit(1); +} + +let text = fs.readFileSync(inPath, "utf8"); +const replace = (pattern, replacement, label) => { + const next = text.replace(pattern, replacement); + if (next === text) { + console.error(`WARN: pattern not replaced (${label})`); + } + text = next; +}; + +replace(/"name"\s*:\s*"[^"]*"/, '"name": "Zeitgeist Rococo Local"', "name"); +replace(/"id"\s*:\s*"[^"]*"/, '"id": "zeitgeist_rococo_local"', "id"); +replace(/"relay_chain"\s*:\s*"[^"]*"/, '"relay_chain": "rococo-local"', "relay_chain"); +replace(/"parachain_id"\s*:\s*\d+/, '"parachain_id": 2092', "parachain_id"); +replace(/"chainType"\s*:\s*"[^"]*"/, '"chainType": "Local"', "chainType"); +replace(/"bootNodes"\s*:\s*\[[\s\S]*?\]/, '"bootNodes": []', "bootNodes"); +replace(/"telemetryEndpoints"\s*:\s*(\[[\s\S]*?\]|null)/, '"telemetryEndpoints": null', "telemetryEndpoints"); +replace(/"protocolId"\s*:\s*(null|"[^"]*")/, '"protocolId": "zeitgeist-rococo"', "protocolId"); +replace(/"ss58Format"\s*:\s*\d+/, '"ss58Format": 73', "ss58Format"); +replace(/"tokenDecimals"\s*:\s*[^,}\n]+/, '"tokenDecimals": 10', "tokenDecimals"); +replace(/"tokenSymbol"\s*:\s*"[^"]*"/, '"tokenSymbol": "ZTG"', "tokenSymbol"); +replace(/("parachainInfo"\s*:\s*\{\s*"parachainId"\s*:\s*)\d+/, "$1 2092", "parachainInfo.parachainId"); + +fs.writeFileSync(outPath, text); +console.log(`Wrote ${outPath}`); + +// Keep the runtime wasm that Moonwall will upload in sync with the code baked +// into the genesis so the upgrade test compares against the correct artifact. +try { + const spec = JSON.parse(text); + const codeHex = spec?.genesis?.runtimeGenesis?.code || spec?.genesis?.runtime?.code; + if (typeof codeHex === "string" && codeHex.startsWith("0x")) { + const wasmPath = path.resolve(process.cwd(), "../target/release/wbuild/zeitgeist-runtime/zeitgeist_runtime.compact.compressed.wasm"); + fs.mkdirSync(path.dirname(wasmPath), { recursive: true }); + fs.writeFileSync(wasmPath, Buffer.from(codeHex.slice(2), "hex")); + console.log(`Wrote runtime wasm to ${wasmPath}`); + } else { + console.error("WARN: runtime code not found in spec; skipping wasm write"); + } +} catch (err) { + console.error("WARN: failed to parse spec JSON to write wasm", err?.message || err); +} +NODE diff --git a/integration-tests/scripts/download-polkadot.sh b/integration-tests/scripts/download-polkadot.sh index dcd9b6fac..b5cb4cbb4 100755 --- a/integration-tests/scripts/download-polkadot.sh +++ b/integration-tests/scripts/download-polkadot.sh @@ -190,6 +190,10 @@ delete_if_not_binary "tmp/polkadot-prepare-worker" if [[ -x tmp/polkadot && -x tmp/polkadot-execute-worker && -x tmp/polkadot-prepare-worker ]]; then POLKADOT_VERSION=$(tmp/polkadot --version || true) + if [[ -n "${POLKADOT_SKIP_DOWNLOAD:-}" ]]; then + echo "POLKADOT_SKIP_DOWNLOAD set; using existing polkadot binaries (${POLKADOT_VERSION:-unknown})." + exit 0 + fi if [[ "${POLKADOT_RELEASE}" == "latest" || "${POLKADOT_VERSION}" == *"${POLKADOT_RELEASE}"* ]]; then echo "Polkadot binaries already match the requested release." exit 0 diff --git a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts index cd953206b..b93ac0e7e 100644 --- a/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts +++ b/integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts @@ -24,8 +24,7 @@ import { } from "@moonwall/cli"; import { KeyringPair } from "@moonwall/util"; import { ApiPromise, Keyring } from "@polkadot/api"; -import { u8aConcat } from "@polkadot/util"; -import { blake2AsHex, xxhashAsU8a } from "@polkadot/util-crypto"; +import { blake2AsHex } from "@polkadot/util-crypto"; import { u8aToBigInt } from "@polkadot/util"; import fs from "node:fs"; import { RuntimeVersion } from "@polkadot/types/interfaces";