The Self-Custody Series, Part II
StarkEx is a self-custodial scalability engine. We’ve described the different facets of a self-custodial system, with upgradability being one of them. This post describes the upgradability mechanism we’ve built to ensure that StarkEx remains a self-custodial system throughout.
There are two reasons for upgrading the code of a smart contract: new features, or security (bugs). The challenge is to introduce such upgrades without obviating the self-custodial nature of the system. A “naive” (in fact: malicious) replacement of the StarkEx logic, would allow the application operator to take hold of all user assets. StarkEx includes a novel upgradability mechanism to safeguard users’ assets. In broad strokes, this mechanism makes use of timelocks, and different sorts of temporal cascading of smart contracts (further details below).
The StarkEx Application Contract: an Overview
The StarkEx Application Smart Contract (ASC) uses the Proxy Pattern (first suggested by openZepplin), and as such has several sub-components. A Proxy Contract holds all the storage and funds, and has the address of (i.e., a pointer to) a Logic Contract, that defines the functionality. From the Proxy Contract, we call the Logic Contract using delegate calls (which runs in the context of the Proxy Contract). The StarkEx ASC logic is thus defined by the Logic Contract. The StarkEx ASC logic is therefore upgraded by means of deploying a new contract and updating the pointer in the Proxy Contract. The addresses of both the Verifier contract and the Data Availability Committee (DAC) contract are referenced in the Logic Contract, rather than in the Proxy Contract (if you’d like a refresher on the different components of StarkEx, here’s an overview).
Upgrade Mechanisms & Safeguarding Self-Custody
In Phase I, we have a separate upgrade mechanism for each of these three contracts: Logic, Verifier, and DAC. All upgrade operations can only be done by permissioned addresses.
- Logic Contract Upgrade:
- Upgrade: The Proxy Contract keeps a list of Logic Contract version addresses. A new version is time-locked for a period we denote as upgrade_activation_delay (set to 28 days) before that version can become active. upgrade_activation_delay is set to be significantly greater than the grace period of the StarkEx Escape Hatch. Only one of these versions is active at a certain point in time, but the authorized administrator can immediately switch between unlocked versions.
- Self-Custody: The time period before a new version is unlocked allows the users to review the new version. The users can opt out of StarkEx if they find the new version unacceptable, and alert other users as well. Since upgrade_activation_delay is significantly larger than the grace period, users are guaranteed to be able to withdraw their funds in a timely fashion
Let us discuss the remaining contracts, both which provide a verification service: one is verification of STARK proofs (the Verifier Contract), and the other is for verification of data availability (the DAC Contract). They are similar in nature, and so are employed by the Logic Contract, and upgraded, in an identical fashion.
2. Verifier Contract Upgrade:
- Mechanism: The Logic Contract keeps a list of verifier contracts. A proof is considered valid only if accepted by all verifiers. Adding a new verifier to the contract is immediate. Removing a verifier is time-locked for the duration of upgrade_activation_delay.
- Self-Custody: The verifier’s purpose is to accept valid proofs and to reject invalid proofs. For security reasons, we allow an immediate addition (sans delay) of a new verifier — note it is an immediate addition, not replacement. Since a proof has to be accepted by all verifiers, invalid proofs still cannot be accepted. The time-lock guarantees that already-deployed verifiers can’t be removed immediately, thus allowing the community to review a newly added contract.
3. DAC Contract Upgrade: identical pattern and reasoning to the Verifier Contracts.
To better understand the cumulative nature of these verification contracts, consider the sieve metaphor: a verifier is like a very fine “sieve”, which lets through only valid statements. In StarkEx we don’t simply replace one “sieve” with another, but rather require that a statement pass through both preexisting “sieves” and the new “sieve” deployed. The new “sieve” cannot validate an invalid statement; it can only further restrict the set of statements deemed valid. This, for example, is exactly what happened with most of Bitcoin’s protocol upgrades, which were soft forks: once a soft fork is enacted, previously invalid blocks remain invalid, and hitherto-valid blocks can now become invalid too.
Security Risks & Their Mitigation
As we mention above, one primary motivation to upgrade smart contracts is the detection of a security risk. We can categorize three types of security risks:
- Locked Funds:
- Risk: user funds deposited to a smart contract might become locked due to a bug (e.g. the Parity bug).
- Mitigation: Adding a new Logic Contract version that fixes the bug.
2. Stolen Funds:
- Risk: A bug in the contract might allow malicious users to abuse the API and steal funds from the contract. The StarkEx application operator needs to be able to fix this ASAP, as time literally means money.
- Mitigation: The ability to switch immediately to another Logic Contract version (once passed the time-lock duration) addresses this risk. Specifically, the system will have a pre-loaded skeletal withdrawal-only version. The skeletal version is an effective means to shut down all contract functionality, except for user withdrawals.
3. Verifier Soundness Bug: This type of bug, which can lead naturally to locked or stolen funds, is worthy of a separate discussion, as StarkEx is a zkp-based system.
- Risk: This type of bug would result in the Verifier Contract accepting a proof for an invalid state transition. This could tamper with users’ balances, and in particular, allow theft of funds.
- Mitigation: Soundness bugs are solved by adding a new Verifier Contract immediately (with no time-lock), since each state-transition must be accepted by all the deployed verifiers: the new Verifier Contract, which presumably was deployed in order to reject that invalid transition, will suffice to have it rejected by the system.