Introduction

Between June 1st and June 14th, zkSecurity was tasked by Provable to audit the upcoming “Program Upgradability” update for Aleo. Two consultants worked over two weeks to review the codebase for potential security issues and provide design feedback and recommendations.

Prior to the engagement, the team spent two weeks getting acquainted with the SnarkVM codebase and the Aleo ecosystem. In the last several days of the audit, we also verified the fixes for all findings reported in this assessment.

Aleo Program Upgradability

Prior to this upgrade all Aleo programs were immutable: a program was deployed once and could not be modified or upgraded hereafter. With the upcoming “Program Upgradability” update, programs may now change after initial deployment. This has interesting implications throughout the system which may have relied on the immutability of programs up until this point, we explore these and note a number of security implications/considerations with the proposed design and implementation (prior to the release).

Glossary

For the reader’s convenience, we include a brief glossary of central terms used within the Aleo SnarkVM:

  • Program : Collection of functions, mappings, records, closures. Called a “contract” in other systems.
  • Program ID : Unique program identifier, composed of a name (e.g. example.aleo) and a network identifier.
  • Transition : Call to a single function in a program.
  • Execution : Sequence of transitions, for the root call and any internal calls.
  • Deployment : Deployment of a new program or (after this update) an upgrade of an existing program.
  • Transaction : Execution or a deployment.
  • Constructor (new) : Function run during deployment; restricts upgradability of the program.

Constructors

This upgrade introduces the ability to upgrade programs on-chain, by redeploying them. Every time a program is upgraded, the edition of the program must increment by one, prior to the update, the edition was not exposed to the snarkVM and internally fixed to zero. When and under which conditions a program can be upgraded is controlled by a method added to all newly deployed programs called “the constructor”.

For instance, the following constructor disallows any upgrades, by requiring that the edition of the new program to be zero:

program example.aleo;

constructor:
  assert.eq example.aleo/edition 0u16;

Note that the constructor is also run during the initial deployment of the program, and that the constructor above can only be satisfied during the initial deployment. As a result, note that e.g.

program example.aleo;

constructor:
  assert.eq false true;

Is an undeployable program.

The following constructor requires that program and any upgrades are deployed by a specific address:

program example.aleo;

constructor:
  assert.eq example.aleo/program_owner <ADDRESS>;

Constructors have access to the mappings of a program and hence the rules for upgrading a program can be controlled dynamically by manipulating the mappings using the other functions in the program. However, the constructor itself is immutable and cannot be modified or upgraded. All legacy programs, which do not have a constructor are immutable.

Permissible Upgrades

Upgrades are only allowed to expand or leave unchanged the interface of a program, e.g. by only adding new functions or new mappings – which can be read externally. This is important to avoid breaking any dependent program which call methods of the upgraded program: such programs would not “type” after the upgrade, referencing e.g. functions which no longer exist. Note however that functions can be “functionally” deleted by making them trivially unsatisfiable, and may otherwise change their behavior in arbitrary ways and thus there is no guarantee that the dependent program will remain satisfiable after the upgrade.

Program Owner

The program owner is the address which deployed (the latest edition of) a program. Depending on the constructor logic, this party may have special privileges and the program owner is used in the constructor to identify the party deploying the upgrade, allowing the constructor to check if the party is eligible to deploy the program. Cryptographically, the program owner is bound to the deployment by signing the “deployment id” which is meant to uniquely identify the program being deployed in the transition.