Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
262589e
update
Kailai-Wang Nov 13, 2025
5860c8d
Merge branch 'dev' into p-1728-runtime-upgrade-simulation-ci-fails
Kailai-Wang Nov 20, 2025
2832d81
feat: migrate from Mocha to Vitest for better TS/ESM support
Kailai-Wang Nov 20, 2025
a527efb
chore: update Vitest to v3.2.4 and refresh pnpm-lock.yaml
Kailai-Wang Nov 20, 2025
438bb1f
upgrade to vitest 4
Kailai-Wang Nov 20, 2025
0060c99
try to fix ci
Kailai-Wang Nov 20, 2025
8ee0c8f
try to fix ci
Kailai-Wang Nov 20, 2025
4df22bc
try to fix more ci
Kailai-Wang Nov 20, 2025
3ec0511
Merge p-1728-vitest-migration into runtime-upgrade-simulation branch
Kailai-Wang Nov 20, 2025
2853655
Migrate runtime upgrade test to single-chain mode without relaychain
Kailai-Wang Nov 20, 2025
6eb1c43
Fix event subscription timing issue in council and democracy votes
Kailai-Wang Nov 20, 2025
2be3bba
Add Chopsticks database files to .gitignore
Kailai-Wang Nov 20, 2025
5de32db
Fix runtime upgrade by using dev_setStorage in standalone mode
Kailai-Wang Nov 20, 2025
8f8e134
Use scheduler injection instead of direct storage for runtime upgrade
Kailai-Wang Nov 20, 2025
4252cca
Revert to XCM mode with relaychain for runtime upgrades
Kailai-Wang Nov 20, 2025
a91fe01
Add diagnostic logging for runtime upgrade
Kailai-Wang Nov 20, 2025
009836f
try to use wasm-override
Kailai-Wang Nov 24, 2025
0b1ada0
Merge branch 'dev' into p-1728-runtime-upgrade-simulation-ci-fails
Kailai-Wang Nov 24, 2025
13d8e73
try to fix ci
Kailai-Wang Nov 24, 2025
81914ea
try again
Kailai-Wang Nov 24, 2025
41d33fa
remove
Kailai-Wang Nov 25, 2025
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
25 changes: 24 additions & 1 deletion .github/workflows/check-runtime-upgrade.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ jobs:
try-runtime:
runs-on: ubuntu-22.04
needs:
- check-condition
- runtime-matrix
strategy:
fail-fast: false
Expand Down Expand Up @@ -122,12 +121,36 @@ jobs:
cd parachain
cargo build --profile production -p ${{ matrix.runtime.package }} --features try-runtime -q --locked

- name: Install subwasm ${{ env.SUBWASM_VERSION }}
run: |
wget https://github.com/chevdor/subwasm/releases/download/v${{ env.SUBWASM_VERSION }}/subwasm_linux_amd64_v${{ env.SUBWASM_VERSION }}.deb
sudo dpkg -i subwasm_linux_amd64_v${{ env.SUBWASM_VERSION }}.deb
subwasm --version

- name: Check migrations
run: |
cd parachain
PACKAGE_NAME=${{ matrix.runtime.package }}
RUNTIME_BLOB_NAME=$(echo $PACKAGE_NAME | sed 's/-/_/g').compact.compressed.wasm
RUNTIME_BLOB_PATH=./target/production/wbuild/$PACKAGE_NAME/$RUNTIME_BLOB_NAME

# Check if upgrade is needed
release_version=$(subwasm --json info "$RUNTIME_BLOB_PATH" | jq .core_version.specVersion)
URI="${{ matrix.runtime.uri }}"
onchain_version=$(curl -s -H 'Content-Type: application/json' -d '{"id":1, "jsonrpc":"2.0", "method": "state_getRuntimeVersion", "params": [] }' ${URI//wss/https} | jq .result.specVersion)

echo "On-chain version: $onchain_version"
echo "Built runtime version: $release_version"

if [ "$onchain_version" -ge "$release_version" ]; then
echo "On-chain runtime version ($onchain_version) >= built version ($release_version)"
echo "Skipping migration checks - chain is already up to date"
exit 0
fi

echo "Upgrade needed: $onchain_version -> $release_version"
echo "Running migration checks..."

export RUST_LOG=remote-ext=debug,runtime=debug

COMMAND="./try-runtime \
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ rococo-fork.json
**/__pycache__/
**/.env

# Chopsticks database files
*.sqlite
*.sqlite-shm
*.sqlite-wal

rust-analyzer.json

*.local.md
Expand Down
19 changes: 12 additions & 7 deletions parachain/scripts/chopsticks/heima.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
endpoint: wss://rpc.heima-parachain.heima.network
port: 9944
db: ./new-db.sqlite
db: ./heima-db.sqlite
runtime-log-level: 3
allow-unresolved-imports: true
mock-signature-host: true
wasm-override: /tmp/runtime.wasm

import-storage:
System:
Account:
-
-
- "4BCh5fGornubJSotBzw9fJakxmdedQN6JJc5RsY4hsixpYQh"
- "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" # Alice
- providers: 1
data:
free: "100000000000000000000"
free: "1000000000000000000000000"
-
-
- "49dXob6fj4uh9SKNm4yCuxfnrvcmArxkoUTkNWQPdtoj3Xvn"
- "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" # Bob
- providers: 1
data:
free: "100000000000000000000"
free: "1000000000000000000000000"
TechnicalCommittee:
Members: ["4BCh5fGornubJSotBzw9fJakxmdedQN6JJc5RsY4hsixpYQh"]
Members: ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"] # Alice
Council:
Members: ["4BCh5fGornubJSotBzw9fJakxmdedQN6JJc5RsY4hsixpYQh", "49dXob6fj4uh9SKNm4yCuxfnrvcmArxkoUTkNWQPdtoj3Xvn"]
Members: ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"] # Alice, Bob

# Override block weights for testing - allow heavier proposals
# This simulates what would happen on a relay chain upgrade or with async backing
build-block-mode: Instant
15 changes: 10 additions & 5 deletions parachain/scripts/chopsticks/paseo.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
endpoint: wss://rpc.paseo-parachain.heima.network
port: 9944
db: ./new-db.sqlite
db: ./paseo-db.sqlite
runtime-log-level: 3
allow-unresolved-imports: true
mock-signature-host: true
wasm-override: /tmp/runtime.wasm

import-storage:
System:
Account:
-
-
- "4BCh5fGornubJSotBzw9fJakxmdedQN6JJc5RsY4hsixpYQh"
- "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" # Alice
- providers: 1
data:
free: "100000000000000000000"
-
-
- "49dXob6fj4uh9SKNm4yCuxfnrvcmArxkoUTkNWQPdtoj3Xvn"
- "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" # Bob
- providers: 1
data:
free: "100000000000000000000"
TechnicalCommittee:
Members: ["4BCh5fGornubJSotBzw9fJakxmdedQN6JJc5RsY4hsixpYQh"]
Members: ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"] # Alice
Council:
Members: ["4BCh5fGornubJSotBzw9fJakxmdedQN6JJc5RsY4hsixpYQh", "49dXob6fj4uh9SKNm4yCuxfnrvcmArxkoUTkNWQPdtoj3Xvn"]
Members: ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"] # Alice, Bob

# Override block weights for testing - allow heavier proposals
# This simulates what would happen on a relay chain upgrade or with async backing
build-block-mode: Instant
24 changes: 17 additions & 7 deletions parachain/scripts/runtime-upgrade.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ function print_divider() {
# Download runtime wasm
print_divider
echo "Download $1-runtime.compact.compressed.wasm from release tag $3 ..."
gh release download "$3" -p "$1-runtime.compact.compressed.wasm" -O "$new_wasm" || true
gh release download "$3" -p "$1-runtime.compact.compressed.wasm" || true
mv "$1-runtime.compact.compressed.wasm" "$new_wasm"

if [ -f "$new_wasm" ] && [ -s "$new_wasm" ]; then
ls -l "$new_wasm"
Expand All @@ -45,17 +46,22 @@ echo "On-chain: $onchain_version"
echo "Release: $release_version"

if [ "$onchain_version" -ge "$release_version" ]; then
echo "Current On-chain runtime is up to date, quit"
exit 1
echo "On-chain runtime version ($onchain_version) >= release version ($release_version)"
echo "Skipping runtime upgrade test - chain is already up to date"
exit 0
fi

# Start Chopsticks to fork the chain
echo "Upgrade needed: $onchain_version -> $release_version"

# Start Chopsticks to fork the parachain in standalone mode
# Standalone mode is simpler and sufficient for runtime upgrade testing
print_divider
echo "Forking parachain with Chopsticks ..."
echo "Forking parachain with Chopsticks in standalone mode ..."
npx @acala-network/chopsticks@latest --config=$ROOTDIR/parachain/scripts/chopsticks/$1.yml &
chopsticks_pid=$!
echo "Chopsticks fork parachain PID: $chopsticks_pid"
sleep 30 # Wait for Chopsticks to initialize
echo "Chopsticks PID: $chopsticks_pid"
echo "Parachain ($1) endpoint: ws://localhost:9944"
sleep 20 # Wait for Chopsticks to initialize

# Check if Chopsticks is running
if ! ps -p $chopsticks_pid > /dev/null; then
Expand All @@ -69,8 +75,12 @@ echo "Performing runtime upgrade ..."

cd "$ROOTDIR/parachain/ts-tests"
echo "NODE_ENV=ci" > .env
echo "PARACHAIN_NAME=$1" >> .env
pnpm install && pnpm run test-runtime-upgrade 2>&1

# Cleanup
print_divider
echo "Stopping Chopsticks..."
kill $chopsticks_pid 2>/dev/null || true
wait $chopsticks_pid 2>/dev/null || true
echo "Runtime upgrade succeed!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Chopsticks Runtime Upgrade Investigation

## Summary

Runtime upgrades cannot be fully tested in Chopsticks due to two bugs:

1. **Scheduler doesn't execute scheduled calls** - `Dispatched` event fires but calls never run
2. **`applyAuthorizedUpgrade` fails with `FailedToExtractRuntimeVersion`** (error `0x02000000`) when `checkVersion: true`

## Test Results

| Approach | Result | Error | Notes |
|----------|--------|-------|-------|
| 2MB `remarkWithEvent` | ✅ Works | - | Proves size NOT the issue |
| Scheduler injection | ❌ Fails | Call not executed | Scheduler bug |
| `applyAuthorizedUpgrade` (checkVersion: true) | ❌ Fails | `FailedToExtractRuntimeVersion` | Version extraction bug |
| `applyAuthorizedUpgrade` (checkVersion: false) | ⚠️ Partial | - | Triggers parachain validation flow |

## Core Test Snippets

### Proof: 2MB Extrinsics Work
```typescript
// Proves Chopsticks CAN handle 2MB payloads
const wasm = fs.readFileSync('/tmp/runtime.wasm'); // 1.9MB
const wasmHex = '0x' + wasm.toString('hex');

const remarkTx = api.tx.system.remarkWithEvent(wasmHex);
await remarkTx.signAndSend(alice, ({ status, events }) => {
// Result: ✅ ExtrinsicSuccess - 2MB extrinsic works fine!
});
```

### Bug: Version Extraction Fails
```typescript
// Authorize upgrade
await api.rpc('dev_setStorage', {
System: {
AuthorizedUpgrade: { codeHash: codeHash, checkVersion: true }
}
});

// Apply upgrade
const applyTx = api.tx.system.applyAuthorizedUpgrade(wasmHex);
await applyTx.signAndSend(alice, ({ events }) => {
// Result: ❌ ExtrinsicFailed
// Error: Module { index: 0, error: 0x02000000 }
// Decoded: System.FailedToExtractRuntimeVersion
//
// The WASM is valid (verified with subwasm), but Chopsticks
// cannot extract the runtime version during upgrade execution.
});
```

### Workaround: Disable Version Check
```typescript
// Use checkVersion: false to bypass version extraction
await api.rpc('dev_setStorage', {
System: {
AuthorizedUpgrade: { codeHash: codeHash, checkVersion: false }
}
});

const applyTx = api.tx.system.applyAuthorizedUpgrade(wasmHex);
await applyTx.signAndSend(alice, ({ events }) => {
// Result: ✅ ExtrinsicSuccess
// Events: parachainSystem.ValidationFunctionStored
//
// BUT: Runtime not immediately updated - uses parachain validation
// flow which requires relay chain coordination and multiple blocks.
});
```

## Root Causes

### Issue 1: Scheduler Bug
- `scheduler.schedule()` and storage-injected agendas both fail
- `scheduler.Dispatched` event fires but calls don't execute
- Affects all scheduled operations, not just runtime upgrades

### Issue 2: Version Extraction Bug
- Occurs when Substrate calls `Core_version` on new WASM
- Chopsticks fails to extract version even though WASM is valid
- Verified with `subwasm info runtime.wasm` - version metadata exists
- Only happens during `applyAuthorizedUpgrade` execution

## Recommendation

**For CI testing**: Use `wasm-override` in Chopsticks config to test that new runtime launches successfully:

```yaml
# chopsticks/heima.yml
endpoint: wss://rpc.heima-parachain.heima.network
wasm-override: /path/to/new-runtime.wasm
mock-signature-host: true

import-storage:
System:
Account:
- [["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"],
{providers: 1, data: {free: "1000000000000000000"}}]
```

Then test that the upgraded runtime works:
```typescript
test('New runtime launches and works', async () => {
// Connect to Chopsticks with new runtime
const api = await ApiPromise.create({ provider: new WsProvider('ws://localhost:9944') });

// Verify version bumped
const version = await api.rpc.state.getRuntimeVersion();
expect(version.specVersion.toNumber()).toBe(9251); // New version

// Test basic operations work
const tx = api.tx.system.remark('test');
await tx.signAndSend(alice, ({ status }) => {
expect(status.isInBlock).toBe(true);
});

// Runtime upgrade successful! ✅
});
```

This approach:
- ✅ Tests new runtime can initialize
- ✅ Tests basic operations work with new runtime
- ✅ Bypasses Chopsticks upgrade bugs
- ✅ Validates what matters: runtime compatibility

Democracy governance flow should be tested separately (if needed) up to authorization only.
Loading