If the contract issuer wants to have a way to upgrade the contract code, so that account data and other things carry over, can Ethereum provide for this? Also can this be done without changing the contract address or does one always need to issue a new address?

Do "annex" mechanisms exist to add some new functionality to a contract without a total rewrite?

10 Answers 10

up vote 122 down vote accepted

Once a contract is in the blockchain, it is final and cannot be changed. Certain parameters, of course, can be changed if they are allowed to change via the original code.

One method of updating contracts is to use a versioning system. For example, you could have an entryway contract that just forwards all calls to the most recent version of the contract, as defined by an updatable address parameter. You could also use a name registry, and update that to point to the most recent contract version.

Another method is to put your logic code in a library, then use the CALLCODE feature, via libraries in Solidity, to call the code located at a specified, updatable, address. This way, user data persists between versions. This has the limitation that the ABI of the logic contract must stay the same over time.

Homestead Edit:

Starting with the Homestead release, there is now a DELEGATECALL opcode. This allows you to essentially forward calls to a separate contract while maintaining msg.sender and all storage.

For example, you could have a contract that maintains the same address and storage, but forward all calls to an address stored in a variable:

contract Relay {
    address public currentVersion;
    address public owner;

    function Relay(address initAddr){
        currentVersion = initAddr;
        owner = msg.sender;
    }

    function update(address newAddress){
        if(msg.sender != owner) throw;
        currentVersion = newAddress;
    }

    function(){
        if(!currentVersion.delegatecall(msg.data)) throw;
    }
}

Here's an old gist I used to demonstrate data/code segregation a while ago.

  • It might be worth mentioning name resolvers, too. – Nick Johnson May 19 '16 at 8:51
  • 1
    Here's a great example of this idea fully fleshed out: gist.github.com/Arachnid/4ca9da48d51e23e5cfe0f0e14dd6318f – Tjaden Hess Jan 29 '17 at 20:33
  • This is so smart, and is a form of dependency injection: the entryway contact is dependent on the current version's address. – Jossie Calderon Jun 12 '17 at 23:44
  • Hey Tjaden Hess, Jossie Calderon ! I'm a bit confused about how to actually implement this concept. I've made a gist with a simple example with a single contract Donation that needs to be updated. Would someone be kind enough to take a look at it and tell me if I'm completely misunderstanding the concept? gist.github.com/fabdarice/d513d620d9355312d085c7a68e6c6118 Appreciate this a lot, thanks ! – fabdarice Jun 14 '17 at 18:56
  • Hey Tjaden Hess nice example, How can I deal with this contract through web3, Suppose I have contract named contract DemoVersion1.sol which contain following code contract DemoVersion1 { function checkVersion() returns (uint){ return 1; } } and I have to upgrade the contract to version two which contain following code contract DemoVersion2 { function checkVersion() returns (uint){ return 2; } } how can I handle calling the methods of contract , can any one explain or point to the suitable example. – SwapnilKumbhar Jun 15 '17 at 9:02

One method is to use a System of Contracts as outlined below:

  1. Contract "Register" - will contain pairs "name - address" for all contracts of your system;
  2. Contract Backend;
  3. Contract Frontend using Backend;
  4. Deploy Register & get address of it;
  5. Deploy Backend & register address of Backend into already deployed Register;
  6. Hardcode the address of Register into source of Backend. Before any call Backend from Frontend you should call your Register and get the actual address of Backend.

Then you can update your Backend contract any time - simply deploy the new one and re-register them in the Register.

Calling external contract: solidity.readthedocs.org...

Also see forum discussion: forum.ethereum.org...


UPD: Same but more efficient way (maybe)

First deploy:

  1. Write contract Register that can deploy other contracts with itself address as constructor argument;
  2. Write all other contracts - "upgradable" contracts with constructors requiring Register's address;
    • maybe that contracts should be disablable or have subside method
  3. Deploy Register giving to its constructor data - all other contract from step 2.

Upgrade:

  1. Deploy new version of "upgradable" contract with same address of Register;
    • Or maybe if your Register can deploy other contracts - git it to him
  2. (optional) disable/kill old version of "upgradable" contract;
  3. Register address of the new version of "upgradable" contract in the Register.
  • Great! If anyone has existing coded examples please add here :) – Mikko Ohtamaa Mar 31 '16 at 13:44
  • I am moving the correct answer mark, because the other answer contains updated information on regarding how to do a relay contract on Homestead . – Mikko Ohtamaa Jul 7 '16 at 15:12
  • 1
    Question: In the case of multiple contracts, wouldn't it be more efficient to first register all the contracts, save all their addresses and then deploy the Register contract (passing all the saved addresses as constructor arguments)? Might save a lot of function calls that way and corresponding gas? – Bharat Mallapur Jul 7 at 7:45
  • 1
    @BharatMallapur, yes off course – Alex Koz. Jul 7 at 17:50

Contract code is immutable, the storage is mutable, but you cannot execute code placed into storage, at least for now.

Bugfixes to contracts

As for bugfixes, the common pattern is to have proxy or lookup contracts to be a gateway to the real one, which in case of a change or bugfix would be replaced. Replacing it also means losing the old storage contents.

Keeping storage

If you want the ability to upgrade code, while keeping storage, you could think of separating storage and logic. Have a dedicated storage contract, which accepts write calls from trusted addresses (e.g. the logic contracts). All important storage should be associated with this one.

Accessing storage after selfdestruct

As of today there is no real pruning implemented even in the case of selfdestruct, but that should definitely come in the future. There are several EIPs discussing this.

Even if pruning is implemented, it shouldn't happen in an instant and you should be able to read storage from the last state. It is also planned to have archive nodes to keep states indefinitely -- not sure that is feasible without limitations just by judging at the growth of the blockchain.

Redeploying at same address

In short: practically this is not possible. The contract addresses are calculated from the sender and the nonce. The nonce is sequential, there cannot be any gaps and there cannot be duplicates.

In theory it is possible to arrive at the same hash with a different nonce and address combination, but the likelyhood is small.

Contracts deployed on a blockchain are immutable, so this means:

  • address and code of a deployed contract cannot be changed
  • deploying a newer (or even identical) contract will create a new address
  • code cannot be added to a deployed contract

If the contract issues want to have a way to upgrade the contract code, so that account data and other things carry over what means Ethereum provides for this?

A simple way to extend a contract C1, is to make sure that C1 has functions / accessors that return all data it has. A new contract C2 can be written, that calls the C1 functions and does additional or corrected logic. (Note, that if C1 and C2 have foo, where C1's foo is buggy and C2's foo is corrected, there is no way to disable C1 foo from being called.)

A registry can be used, as described by @Alexander's answer, so that other DApps and contracts query the registry for the address of contractC, so that when C1 is "replaced" by C2, no DApp code needs to change. Using a registry in this way prevents hardcoding the address of C1 (so that C2, C3, C4 can take its place when needed), but the DApp does need to hardcode the address of the registry.


EDIT: The ENS, Ethereum Name Service, was just deployed on the testnet (Ropsten).

See the ENS wiki for a quickstart and other details. Here's an introduction:

ENS is the Ethereum Name Service, a distributed, extensible naming system based on the Ethereum blockchain.

ENS can be used to resolve a wide variety of resources. The initial standard for ENS defines resolution for Ethereum addresses, but the system is extensible by design, allowing more resource types to be resolved in future without the core components of ENS requiring upgrades.

ENS is deployed on the Ropsten testnet at 0x112234455c3a32fd11230c42e7bccd4a84e02010.

Initial discussion here.

  • 1
    And the registry, as any indirection system, has its own bugs, security issues, and problems. If you want to upgrade the contract because of a security risk, this is something to keep in mind. If The DAO had an upgrade system, no doubt it would have been used for a hack... – bortzmeyer Jul 6 '16 at 10:15
  • @bortzmeyer Agree, any upgrade mechanism has risks that could be exploited itself and those risks should be considered. – eth Nov 24 '16 at 7:34

@Nick Johnson has a base contract for upgradeable contracts.

As he says, before using one should be "fully understanding the limitations and drawbacks."

/**
 * Base contract that all upgradeable contracts should use.
 * 
 * Contracts implementing this interface are all called using delegatecall from
 * a dispatcher. As a result, the _sizes and _dest variables are shared with the
 * dispatcher contract, which allows the called contract to update these at will.
 * 
 * _sizes is a map of function signatures to return value sizes. Due to EVM
 * limitations, these need to be populated by the target contract, so the
 * dispatcher knows how many bytes of data to return from called functions.
 * Unfortunately, this makes variable-length return values impossible.
 * 
 * _dest is the address of the contract currently implementing all the
 * functionality of the composite contract. Contracts should update this by
 * calling the internal function `replace`, which updates _dest and calls
 * `initialize()` on the new contract.
 * 
 * When upgrading a contract, restrictions on permissible changes to the set of
 * storage variables must be observed. New variables may be added, but existing
 * ones may not be deleted or replaced. Changing variable names is acceptable.
 * Structs in arrays may not be modified, but structs in maps can be, following
 * the same rules described above.
 */
contract Upgradeable {
    mapping(bytes4=>uint32) _sizes;
    address _dest;

    /**
     * This function is called using delegatecall from the dispatcher when the
     * target contract is first initialized. It should use this opportunity to
     * insert any return data sizes in _sizes, and perform any other upgrades
     * necessary to change over from the old contract implementation (if any).
     * 
     * Implementers of this function should either perform strictly harmless,
     * idempotent operations like setting return sizes, or use some form of
     * access control, to prevent outside callers.
     */
    function initialize();

    /**
     * Performs a handover to a new implementing contract.
     */
    function replace(address target) internal {
        _dest = target;
        target.delegatecall(bytes4(sha3("initialize()")));
    }
}

/**
 * The dispatcher is a minimal 'shim' that dispatches calls to a targeted
 * contract. Calls are made using 'delegatecall', meaning all storage and value
 * is kept on the dispatcher. As a result, when the target is updated, the new
 * contract inherits all the stored data and value from the old contract.
 */
contract Dispatcher is Upgradeable {
    function Dispatcher(address target) {
        replace(target);
    }

    function initialize() {
        // Should only be called by on target contracts, not on the dispatcher
        throw;
    }

    function() {
        bytes4 sig;
        assembly { sig := calldataload(0) }
        var len = _sizes[sig];
        var target = _dest;

        assembly {
            // return _dest.delegatecall(msg.data)
            calldatacopy(0x0, 0x0, calldatasize)
            delegatecall(sub(gas, 10000), target, 0x0, calldatasize, 0, len)
            return(0, len)
        }
    }
}

contract Example is Upgradeable {
    uint _value;

    function initialize() {
        _sizes[bytes4(sha3("getUint()"))] = 32;
    }

    function getUint() returns (uint) {
        return _value;
    }

    function setUint(uint value) {
        _value = value;
    }
}
  • This is my favorite solution. the answr should be upvoted more! With the last fork, there is also no need for specifiying the sizes of the return values (_sizes[bytes4(sha3("getUint()"))] = 32). – ivicaa Feb 27 at 16:05
  • I covered this topic here: youtube.com/watch?v=KBqDYF5jw-0 – rstormsf Jun 8 at 7:24

Coming to one of the basic principles at Ethereum that is a Smart Contract cannot be modified after deployment.

BUT, you can still have Upgradable Smart Contracts if you put the following into consideration

This has to be planed from the start. The key point is number 4. But all the others are essential to have a real and smooth Smart Contract Upgrade.

So, you will need to design your smart contract taking in consideration the following 5 points:

  1. Keep your smart contracts modular and fairly separate rules and logic from data structure. So if you will need to change something, you will change just the related contract and you will not need to change many or all contracts.
  2. You should be prepared by having an emergency stop or circuit-breaker to be able to stop all operations during any migration. Because you do not want to be in a situation where people can still update/insert data to the old version of the smart contract while you are migrating and thereafter.
  3. You should previously provided the ability to read all the data from your smart contract. Of course you can do a permissioned read by restricting reading all data to the owner or any other trusted user or even another smart contract. You will need to read from the old version of your smart contract and insert in the new version.
  4. You will use of of the following strategies to communicate with your smart contract. I copied them from Smart Contact Best Practices:

Upgrading Broken Contracts

Code will need to be changed if errors are discovered or if improvements need to be made. It is no good to discover a bug, but have no way to deal with it

...

However, there are two basic approaches that are most commonly used. The simpler of the two is to have a registry contract that holds the address of the latest version of the contract. A more seamless approach for contract users is to have a contract that forwards calls and data onto the latest version of the contract.

Example 1: Use a registry contract to store latest version of a contract

In this example, the calls aren't forwarded, so users should fetch the current address each time before interacting with it.

contract SomeRegister {
    address backendContract;
    address[] previousBackends;
    address owner;

    function SomeRegister() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner)
        _;
    }

    function changeBackend(address newBackend) public
    onlyOwner()
    returns (bool)
    {
        if(newBackend != backendContract) {
            previousBackends.push(backendContract);
            backendContract = newBackend;
            return true;
        }

        return false;
    }
}

There are two main disadvantages to this approach:

  1. Users must always look up the current address, and anyone who fails to do so risks using an old version of the contract

  2. You will need to think carefully about how to deal with the contract data when you replace the contract

The alternate approach is to have a contract forward calls and data to the latest version of the contract:

Example 2: Use a DELEGATECALL to forward data and calls

contract Relay {
    address public currentVersion;
    address public owner;

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function Relay(address initAddr) {
        currentVersion = initAddr;
        owner = msg.sender; // this owner may be another contract with multisig, not a single contract owner
    }

    function changeContract(address newVersion) public
    onlyOwner()
    {
        currentVersion = newVersion;
    }

    function() {
        require(currentVersion.delegatecall(msg.data));
    }
}

This approach avoids the previous problems but has problems of its own. You must be extremely careful with how you store data in this contract. If your new contract has a different storage layout than the first, your data may end up corrupted. Additionally, this simple version of the pattern cannot return values from functions, only forward them, which limits its applicability. (More complex implementations attempt to solve this with in-line assembly code and a registry of return sizes.)

Regardless of your approach, it is important to have some way to upgrade your contracts, or they will become unusable when the inevitable bugs are discovered in them.

However, I also recommend checking Proxy Libraries in Solidity that is posted by Zeppelin Solutions and Aragon. There is a planning to make an industry standard for this matter.

  1. You have to have a good testing strategies and tactics. Because the cost of updating the your smart contract can really ruin your life.

I created a story on Medium for this with title: Essential Design Consideration for Ethereum dApps (1): Upgradeable Smart Contracts and I provided a sample for every point of the above 5.

We(I and My team) have recently worked on Upgradable Contracts problem after referring to colony.io's post on Upgradable Contracts. So, we came with a solution in which we have different layers of contract rather than having one single Contract.

If I describe it briefly then one need to make the storage part very generic so that once you create it, you can store every type of data into it (with the help of setter methods) and access it (with the help of getter methods). That makes your data storage eternal, which you don't need to change in future.

Look at this datastore contract to understand it better - https://goo.gl/aLmvJ5

Second layer should be the main contract with your functions, which can be upgraded at later point and in order to use the old data store, you should make the contract in way so that you can point your newly deployed contract to the existing(old) data store and then you can kill the old contract, after the new contract correctly talks to the old datastore.

Look at our codebase to understand how we have implemented upgradable contract - https://goo.gl/p5zGEv

Note: in above GitHub repo, we are using three layers of contracts because of our use case. However, it is possible to make contract upgradable with two layers only.

Hope this helps.

Allows you to have a contract with a stable address, but fully controllable and upgradeable behavior.

https://github.com/u2/ether-router

https://github.com/ConsenSys/smart-contract-best-practices#upgrading-broken-contracts

  • 1
    Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference. – Afri Dec 30 '16 at 8:29

zos introduced a framework for us to easily implement an upgradable smart contract

PTAL: https://docs.zeppelinos.org/docs/start.html

The real problem in upgradable smart contract is to migrate stored values from contract.

A much better way to build upgradable smart contract is to differentiate your storage and logics in different contracts.

Save all of your contract data in one smart contract which only accepts calls from your logic contract.

Keep changing the logics of your logic contract. However, you need be very visionary while defining the variables of storage contract.

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.