EIP3561 - Trust Minimized Upgradeability Proxy

# Simple Summary

Additional storage slots for upgradeability proxy to decrease trust in interaction with upgradeable smart contracts.

# Abstract

Removing trust from upgradeability proxy is required for anonymous developers. To achieve that, disallowing sudden, potentially malicious upgrades is required. This EIP introduces a time lock, a period of one month, before defined by proxy admin implementation becomes active implementation.

# Motivation

It's usually not possible for anonymous developers who use upgradeability proxies to gain community trust.

Fairer, better future for humanity absolutely requires some developers to stay anonymous while still attract vital attention to solutions they propose and at the same time leverage the benefits of possible upgradeability. For example, a project like Collateralized Asset Protocol allows to trade stocks and securities on the blockchain in a purely decentralized way making the developers of such projects potentially risking their freedom. Aletheo was born to fight tyranny.

Extinction is more likely with closed internet borders. As long as internet has eyes everywhere, devastating conventional warfare is less likely to happen regardless of power vacuum or exponential increase in existential threats.

# Specification

The specification is an addition to the standard EIP-1967 transparent proxy design. The specification focuses on the slots it adds. All admin interactions with trust minimized proxy must emit an event to make admin actions trackable, and all admin actions must be guarded with onlyAdmin() modifier.

# Next Logic Contract Address

Storage slot 0xe8c186b11a4be12af079b0a5c235146db6f3615b2a8b1b47f9bfe3a956337ef9 (obtained as bytes32(uint256(keccak256('eip3561.proxy.next_implementation')) - 1)). Logic address must be first defined as next logic, before it can function as actual logic implementation stored in EIP-1967 IMPLEMENTATION_SLOT. Admin interactions with next logic contract address correspond with these methods and events:

// sets next logic contract address and initializes it. Emits NextLogicDefined
// 0x as calldata is an equivalent of proposeTo()
function proposeToAndCall(address implementation, bytes calldata data) external onlyAdmin;
// sets the address stored as next implementation as current IMPLEMENTATION_SLOT
// as soon UPGRADE_BLOCK_SLOT allows
function upgrade() external onlyAdmin;
// cancelling is possible for as long as upgrade() for given next logic was not called
// emits NextLogicCanceled
function cancelUpgrade() external onlyAdmin;

event NextLogicDefined(address indexed nextLogic, uint earliestArrivalBlock); // important to have
event NextLogicCanceled(address indexed oldLogic);

# Upgrade Block

Storage slot 0xd366e20ef9f21888e3d225d6a18f0bceb0ce0008a1e881be9a0467a0293afc96 (obtained as bytes32(uint256(keccak256('eip3561.proxy.upgrade_block')) - 1)). On/after this block next logic contract address can be set to EIP-1967 IMPLEMENTATION_SLOT or, in other words, start to function as current logic. Updated automatically and is shown as earliestArrivalBlock in the event NextLogicDefined.

# Propose Block

Storage slot 0x1e166c9744902ecbb9f589bbc9e7da5f078e553ad162c2ee62c71827916db75f (obtained as bytes32(uint256(keccak256('eip3561.proxy.propose_block')) - 1)). Defines after/on which block proposing next logic is possible. Required for convenience, for example can be manually set to a year from given time, so that the users can have peace of mind during the year. Can be set to maximum number to completely seal the code, must not overflow. Admin interactions with this slot correspond with this method and event:

function prolongLock(uint n) external onlyAdmin;
event UpgradesRestrictedUntil(uint block);

# Deadline Block

Storage slot 0x560a0645803da084600e483b1076fd54059fdee9045033b88b51d342faf2b2af (obtained as bytes32(uint256(keccak256('eip3561.proxy.deadline')) - 1)). Defines after/on which block it becomes impossible to upgrade the contract, can be set in constructor if used or can be set many times as long as next value is lower than previous. Required in case of probability that the project development can be compromised, unlike PROPOSE_BLOCK_SLOT, deadline is less likely to require any urgent transaction like PROPOSE_BLOCK_SLOT. Admin interactions with this slot should correspond with this method and event:

function setDeadline(uint block) external onlyAdmin;
event DeadlineSet(uint block);

# Trust_minimized Boolean

Storage slot 0x17dc395ab915c140774b2c05da17fd5b71a2d193e53bc9732d6b62d419a46635 (obtained as bytes32(uint256(keccak256('eip3561.proxy.trust_minimized')) - 1)). False by default and can only ever be set true. While it is false, then the proxy operates exactly as standard EIP-1967 transparent proxy, which allows to correct potential mistakes of first deployment. After set to true, all above specification becomes active. Admin interactions with this slot should correspond with this method and event:

function removeTrust() external onlyAdmin;
event TrustRemoved();

# Rationale

An argument "just don't make such contracts upgadeable at all" fails when it comes to complex systems. It might be impossible to model complex systems right on first try.

A proxy is easily abusable by anonymous soulless scammers without a time delay before an actual upgrade(change of current implementation address) goes live. A time delay is probably unavoidable, even if it means that inexperienced developers might not have confidence using it. Albeit this is a downside of this EIP, it's a critically important option to have in smart contract development today.

Propose block adds to convenience if used, so should be kept. An ability to cancel next logic can also be important for the same reason. Deadline block, while can be omitted, can be critically important for some projects, it would be reasonable to rather keep it too. Unless developers always write correct automatic deployment scripts, trustless boolean is required from my own experience, because I once failed to execute 15 deployment steps correctly.

# Implementation

The following implementation is an example which lacks trustless boolean slot and uses NEXT_LOGIC_BLOCK_SLOT instead of UPGRADE_BLOCK_SLOT to get farther slot value: Aletheo repository (opens new window).

Copyright and related rights waived via CC0 (opens new window).

▲ Powered by Vercel