-
Notifications
You must be signed in to change notification settings - Fork 44
Update integration test for valid relay chain timestamp in parachain storage #1456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
e3f521b
297f2d6
720edbf
8d81301
648d324
5614e63
d9ae9d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,6 +24,9 @@ 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 { u8aToBigInt } from "@polkadot/util"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import fs from "node:fs"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { RuntimeVersion } from "@polkadot/types/interfaces"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -83,6 +86,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 +118,184 @@ describeSuite({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await context.upgradeRuntime({ from: alice, logger: log }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const txStatus = async (tx: any, label: string) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new Promise<void>((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); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+120
to
+146
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add timeout protection to prevent indefinite hangs. The Consider adding a timeout mechanism: const txStatus = async (tx: any, label: string) =>
new Promise<void>((resolve, reject) => {
let unsubscribe: (() => void) | undefined;
+ const timeout = setTimeout(() => {
+ unsubscribe?.();
+ reject(new Error(`${label} timed out after 60s`));
+ }, 60000);
tx.signAndSend(alice, (result: any) => {
if (result.dispatchError) {
const errText = result.dispatchError.toString();
log(`${label} dispatchError=${errText}`);
+ clearTimeout(timeout);
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) {
+ clearTimeout(timeout);
unsubscribe?.();
resolve();
}
})
.then((unsub: () => void) => {
unsubscribe = unsub;
})
- .catch(reject);
+ .catch((err) => {
+ clearTimeout(timeout);
+ reject(err);
+ });
});🤖 Prompt for AI AgentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a risk of indefinite hangs here if the TX fails to reach isInBlock or isFinalized?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be fixed by: #1459 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const findCall = (callName: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const [section, calls] of Object.entries(paraApi.tx)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const typedCalls = calls as Record<string, any>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+183
to
+214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Search for authorizeUpgrade and applyAuthorizedUpgrade definitions in runtime
# First, find runtime-related directories
echo "=== Locating runtime directories ==="
fd -t d "runtime" | head -20
echo -e "\n=== Searching for authorizeUpgrade definitions ==="
rg -n "authorizeUpgrade\|applyAuthorizedUpgrade" \
--type rust \
-B 2 -A 5 \
2>/dev/null | head -100Repository: zeitgeistpm/zeitgeist Length of output: 338 🏁 Script executed: #!/bin/bash
# Read the test file to understand the full context
echo "=== Reading test file (lines 150-240 for context) ==="
sed -n '150,240p' integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts
echo -e "\n=== Checking Cargo.toml for SDK version ==="
find . -name "Cargo.toml" -path "*/integration-tests/*" | head -1 | xargs cat | grep -A 5 -B 5 "polkadot"Repository: zeitgeistpm/zeitgeist Length of output: 3696 🏁 Script executed: #!/bin/bash
# Find integration-tests Cargo.toml and check SDK version
echo "=== Finding integration-tests Cargo.toml ==="
find . -path "*/integration-tests/Cargo.toml" -exec cat {} \;
echo -e "\n=== Searching for other meta.args.length patterns ==="
rg -n "meta\.args\.length" --type typescript --type javascript -B 2 -A 2
echo -e "\n=== Searching for waitBlock patterns to understand timing ==="
rg -n "waitBlock" --type typescript -B 2 -A 2 | head -50Repository: zeitgeistpm/zeitgeist Length of output: 314 🏁 Script executed: #!/bin/bash
# Better approach to find integration-tests files
echo "=== Integration-tests directory structure ==="
ls -la integration-tests/ 2>/dev/null | head -20
echo -e "\n=== Finding Cargo.toml files ==="
find integration-tests -name "Cargo.toml" 2>/dev/null
echo -e "\n=== Search for meta.args.length (no type filter) ==="
rg -n "meta\.args\.length" -B 2 -A 2 | head -60
echo -e "\n=== Search for waitBlock (no type filter) ==="
rg -n "waitBlock" -B 2 -A 2 | head -60Repository: zeitgeistpm/zeitgeist Length of output: 5027 🏁 Script executed: #!/bin/bash
# Check package.json for SDK versions and find waitBlock implementation
echo "=== Integration-tests package.json ==="
cat integration-tests/package.json
echo -e "\n=== Search for context.waitBlock definition/implementation ==="
rg -n "waitBlock" --type typescript -A 5 -B 2 | grep -A 10 "function\|const.*waitBlock\|=>" | head -40
echo -e "\n=== Search for moonwall context/framework files ==="
rg -n "class.*Context\|function.*context" integration-tests -B 2 -A 5 | head -60Repository: zeitgeistpm/zeitgeist Length of output: 1621 🏁 Script executed: #!/bin/bash
# Search for test-zombienet-runtime-upgrade.ts full content to understand context better
echo "=== Full test file content (part 1 - imports and setup) ==="
head -100 integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts
echo -e "\n=== Check for comments about timing or block requirements ==="
rg -n "block\|timing\|wait" integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts -i | head -40
echo -e "\n=== Search for authorizeUpgrade in Polkadot docs or comments ==="
rg -n "authorizeUpgrade\|applyAuthorizedUpgrade\|SDK\|version" integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts -i -B 1 -A 1Repository: zeitgeistpm/zeitgeist Length of output: 3789 🏁 Script executed: #!/bin/bash
# Search for more context about the meta.args check and what it's handling
echo "=== Full context around meta.args.length check (lines 170-220) ==="
sed -n '170,220p' integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts
echo -e "\n=== Search for any SDK or version-related comments in test ==="
rg -n "SDK\|version\|meta\|args" integration-tests/tests/rt-upgrade-zombienet/test-zombienet-runtime-upgrade.ts | head -30Repository: zeitgeistpm/zeitgeist Length of output: 2331 🌐 Web query:
💡 Result: Summary (@polkadot/api v16.5.2)
Sources:
🏁 Script executed: #!/bin/bash
# Search for authorizeUpgrade definition in battery-station runtime
echo "=== Searching for authorizeUpgrade in battery-station runtime ==="
rg -n "authorizeUpgrade" runtime/battery-station -B 3 -A 3 --type rust
echo -e "\n=== Checking runtime/zeitgeist as well ==="
rg -n "authorizeUpgrade" runtime/zeitgeist -B 3 -A 3 --type rust
echo -e "\n=== Search for pallet_system config or extensions ==="
rg -n "pallet_system\|System" runtime/battery-station/src/lib.rs -B 2 -A 10 | head -80Repository: zeitgeistpm/zeitgeist Length of output: 229 🏁 Script executed: #!/bin/bash
# Check battery-station runtime configuration
echo "=== Reading battery-station runtime/lib.rs ==="
wc -l runtime/battery-station/src/lib.rs
echo -e "\n=== Grep for System pallet config ==="
rg -n "impl.*frame_system\|frame_system::Config\|System" runtime/battery-station/src/lib.rs -B 2 -A 5 | head -100
echo -e "\n=== Check for any comments about upgrade or args ==="
rg -n "upgrade\|args\|variant" runtime/battery-station/src/lib.rs -i | head -30Repository: zeitgeistpm/zeitgeist Length of output: 243 🏁 Script executed: #!/bin/bash
# Read the full battery-station runtime lib.rs
echo "=== Full battery-station/src/lib.rs ==="
cat -n runtime/battery-station/src/lib.rs
echo -e "\n=== Check runtime structure for other files ==="
find runtime/battery-station/src -type f -name "*.rs" | head -20Repository: zeitgeistpm/zeitgeist Length of output: 7574 🌐 Web query:
💡 Result: Short answer — how authorizeUpgrade + applyAuthorizedUpgrade work, timing and requirements:
Sources: polkadot.js extrinsics docs (system.authorizeUpgrade / applyAuthorizedUpgrade). [1] Substrate frame_system implementation and OnSetCode behavior (set_code / set_code_without_checks / OnSetCode trait). [2][3] The meta.args.length check is dead code; 2-block wait is reasonable but undocumented. 1. Lines 189-190 — Unnecessary conditional: The check for 2. Line 200 — 2-block wait is reasonable: The Consider:
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+220
to
+235
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add explicit null check before code comparison. The optional chaining at line 220 could result in const codeAfter = (await paraApi.rpc.state.getStorage(":code"))?.toString();
+ expect(codeAfter, "Failed to read :code storage after upgrade").to.not.be.undefined;
log(
`Before: #${blockNumberBefore}, After: #${blockNumberAfter}, code changed=${
codeAfter !== codeString📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "T03", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: "Relay timestamp (from relay proof) is present and increases across blocks", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout: 120000, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test: async function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const relayTsStorageKey = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "0x54dbd40f5201dbc18b0eed4b2ecd9cc67e2cdf745d68eeb295336330e3a1a063"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+244
to
+245
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Hardcoded storage key is fragile. The storage key Replace the hardcoded key with dynamic derivation: - const relayTsStorageKey =
- "0x54dbd40f5201dbc18b0eed4b2ecd9cc67e2cdf745d68eeb295336330e3a1a063";
+ // Derive storage key: twox_128("RelayDataProvider") + twox_128("RelayTimestampNow")
+ const relayTsStorageKey = u8aConcat(
+ xxhashAsU8a("RelayDataProvider", 128),
+ xxhashAsU8a("RelayTimestampNow", 128)
+ );This explains the previously imported but unused 📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it okay to hardcode this value?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just a storage key called pallet Exactly what we have in struct RelayTimestampNowPrefix;
impl StorageInstance for RelayTimestampNowPrefix {
const STORAGE_PREFIX: &'static str = "RelayTimestampNow";
fn pallet_prefix() -> &'static str {
"runtime"
}
}
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const readRelayTs = async (): Promise<bigint> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Storage encodes u64 little-endian; decode explicitly. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ts = u8aToBigInt(raw?.toU8a(true) ?? new Uint8Array(), true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ts; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let tsRelay1 = 0n; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let retries = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 === 0n) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await context.waitBlock(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| retries++; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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(0n); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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()).toBigInt(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||


Uh oh!
There was an error while loading. Please reload this page.