Skip to content

Signed bundle version upgrades without migration are silently ignored #2060

Description

@rtb-12

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

  1. Developer changes internal function logic (no state schema change)
  2. Bumps version in manifest (e.g., 1.0.01.1.0)
  3. Builds and installs new signed bundle → same ApplicationId, new WASM binary, new ApplicationMeta.version
  4. Calls context update-application without providing a migration function
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions