Problem
For signed bundles, ApplicationId = hash(package, signer_id) — it stays the same across version upgrades. The UpdateApplicationRequest handler uses ApplicationId as the sole discriminator for detecting changes:
// core/crates/context/src/handlers/update_application.rs:57-67
if migration.is_none() {
if let Some(ref context) = context_meta {
if application_id == context.application_id {
return ActorResponse::reply(Ok(()));
}
}
}
This means: same ApplicationId + no migration = silently skipped. The update never reaches the contract, the WASM cache is never invalidated, and the new binary is never loaded.
Affected scenario
- Developer changes internal function logic (no state schema change)
- Bumps version in manifest (e.g.,
1.0.0 → 1.1.0)
- Builds and installs new signed bundle → same
ApplicationId, new WASM binary, new ApplicationMeta.version
- Calls
context update-application without providing a migration function
- Result: handler returns
Ok(()) immediately — new WASM is never loaded
This forces developers to always provide a migration function (even a trivial pass-through) for any code change to signed bundles, even when no state transformation is needed.
Impact
- Affects the context-level update path (
UpdateApplicationRequest handler)
- Also affects the group upgrade propagation flow (same pattern in
upgrade_group.rs and execute.rs)
- Only affects signed bundles — unsigned/raw WASM apps produce a different
ApplicationId for every code change, so the check doesn't trigger
Suggested fix
The data to detect version changes already exists in ApplicationMeta.version. The handler could compare the installed version against the new version:
if migration.is_none() {
if let Some(ref context) = context_meta {
if application_id == context.application_id {
// Check if the version actually changed
let version_changed = /* compare ApplicationMeta.version */;
if !version_changed {
return ActorResponse::reply(Ok(()));
}
// Version changed but no migration — proceed with cache invalidation
// and WASM reload without running a migration function
}
}
}
Alternatively, compare blob hashes (ApplicationMeta.blob) to detect bytecode changes regardless of version strings.
Workaround
Provide a trivial migration function in the WASM that returns the existing state unchanged, and specify --migration <method> during the upgrade.
Problem
For signed bundles,
ApplicationId = hash(package, signer_id)— it stays the same across version upgrades. TheUpdateApplicationRequesthandler uses ApplicationId as the sole discriminator for detecting changes:This means: same ApplicationId + no migration = silently skipped. The update never reaches the contract, the WASM cache is never invalidated, and the new binary is never loaded.
Affected scenario
1.0.0→1.1.0)ApplicationId, new WASM binary, newApplicationMeta.versioncontext update-applicationwithout providing a migration functionOk(())immediately — new WASM is never loadedThis forces developers to always provide a migration function (even a trivial pass-through) for any code change to signed bundles, even when no state transformation is needed.
Impact
UpdateApplicationRequesthandler)upgrade_group.rsandexecute.rs)ApplicationIdfor every code change, so the check doesn't triggerSuggested fix
The data to detect version changes already exists in
ApplicationMeta.version. The handler could compare the installed version against the new version:Alternatively, compare blob hashes (
ApplicationMeta.blob) to detect bytecode changes regardless of version strings.Workaround
Provide a trivial migration function in the WASM that returns the existing state unchanged, and specify
--migration <method>during the upgrade.