Contract Upgradability#

GenVM provides a native contract upgradability system that allows contracts to be modified after deployment while maintaining security guarantees and clear access controls.

Data that is necessary for this process resides in Root Slot.

Upgrade Control Mechanism#

The upgrade system works through access control during write transactions:

  1. At start of execution GenVM reads the upgraders list of Root Slot.

    It does not lead to RAM Consumption

  2. If the sender is not in the upgraders list of Root Slot,

    GenVM reads locked_slots and will prevent writing to them. It implies \(32*n\) RAM Consumption, where \(n\) is the number of locked slots (bounded by locked_slots). This memory is never released.

  3. GenVM loads the contract code as the entry runner. This is a runner

    load action; its RAM Consumption is specified in Runners and released when the sub-VM finishes.

Locked Slots in Nested Calls#

The upgraders and locked_slots lists are read once per execution, for the top-level contract and the top-level sender, before any sub-VM exists; the resulting charge is the one in Upgrade Control Mechanism and no sub-VM pays it again.

One set suffices because only the top-level contract’s storage is ever writable within an execution:

  1. A CallContract Message child cannot write storage at all (see Sub-VM Creation), so the callee’s own locked_slots are never consulted, and being an upgrader of the callee grants a calling contract nothing.

  2. A Sandbox Message child that was granted write_storage writes the same contract’s storage as its parent, under the same sender — the top-level set applies to it unchanged.

  3. A RunNondet Message child cannot write storage.

Contract Major Version#

The major field of Root Slot is at major and stores the public-ABI major version that the contract was built against. Top-level and runner loads compare this byte to CURRENT_MAJOR and fail with invalid_contract major_mismatch when it differs. CallContract Message may instead delegate the callee to another executor selected by the host.

The value is not modifiable by the contract itself: it is written once at deploy time by the host from a value detected in the contract package (a genvm.version custom WASM section, a version file inside a zip-packaged runner, or a leading // vX.Y.Z comment in single-file text contracts). The host detection flow and the on-the-wire endpoint (POST /contract/detect-version) are described in the impl-spec.

Implications for upgrades:

  • Replacing code with a binary of a different public-ABI major leaves major stale; the contract will fail to load on subsequent invocations. Upgraders MUST update major in the same transaction when crossing a major boundary.

  • Bumping major to a value the running GenVM does not support produces a load-time error; the upgrade transaction must be performed on a host that already supports the target major.