Upgradable experiments
This commit is contained in:
commit
9efe30afda
|
@ -0,0 +1 @@
|
|||
build
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
## Development
|
||||
|
||||
For local development it is recommended to use [ganache-cli](https://github.com/trufflesuite/ganache-cli) to run a local development chain.
|
||||
|
||||
We default to port 7545 for development to not get in conflict with the default Ethereum RPC port.
|
||||
|
||||
$ ganache-cli -p 7545
|
|
@ -0,0 +1,23 @@
|
|||
pragma solidity ^0.4.17;
|
||||
|
||||
contract Migrations {
|
||||
address public owner;
|
||||
uint public last_completed_migration;
|
||||
|
||||
modifier restricted() {
|
||||
if (msg.sender == owner) _;
|
||||
}
|
||||
|
||||
function Migrations() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function setCompleted(uint completed) public restricted {
|
||||
last_completed_migration = completed;
|
||||
}
|
||||
|
||||
function upgrade(address new_address) public restricted {
|
||||
Migrations upgraded = Migrations(new_address);
|
||||
upgraded.setCompleted(last_completed_migration);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
pragma solidity ^0.4.4;
|
||||
|
||||
import './upgradeable/Upgradeable.sol';
|
||||
|
||||
contract Token1 is Upgradeable {
|
||||
|
||||
uint public value;
|
||||
function Token() public {
|
||||
value = 1;
|
||||
}
|
||||
|
||||
function mint() public {
|
||||
value += 10;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
pragma solidity ^0.4.4;
|
||||
|
||||
import './Token1.sol';
|
||||
|
||||
contract Token2 is Token1 {
|
||||
|
||||
function mint() public {
|
||||
value += 20;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
pragma solidity ^0.4.18;
|
||||
|
||||
/**
|
||||
* @title IRegistry
|
||||
* @dev This contract represents the interface of a registry contract
|
||||
*/
|
||||
interface IRegistry {
|
||||
/**
|
||||
* @dev This event will be emitted every time a new proxy is created
|
||||
* @param proxy representing the address of the proxy created
|
||||
*/
|
||||
event ProxyCreated(address proxy);
|
||||
|
||||
/**
|
||||
* @dev This event will be emitted every time a new implementation is registered
|
||||
* @param version representing the version name of the registered implementation
|
||||
* @param implementation representing the address of the registered implementation
|
||||
*/
|
||||
event VersionAdded(string version, address implementation);
|
||||
|
||||
/**
|
||||
* @dev Registers a new version with its implementation address
|
||||
* @param version representing the version name of the new implementation to be registered
|
||||
* @param implementation representing the address of the new implementation to be registered
|
||||
*/
|
||||
function addVersion(string version, address implementation) public;
|
||||
|
||||
/**
|
||||
* @dev Tells the address of the implementation for a given version
|
||||
* @param version to query the implementation of
|
||||
* @return address of the implementation registered for the given version
|
||||
*/
|
||||
function getVersion(string version) public view returns (address);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
pragma solidity ^0.4.17;
|
||||
|
||||
contract Migrations {
|
||||
address public owner;
|
||||
uint public last_completed_migration;
|
||||
|
||||
modifier restricted() {
|
||||
if (msg.sender == owner) _;
|
||||
}
|
||||
|
||||
function Migrations() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function setCompleted(uint completed) public restricted {
|
||||
last_completed_migration = completed;
|
||||
}
|
||||
|
||||
function upgrade(address new_address) public restricted {
|
||||
Migrations upgraded = Migrations(new_address);
|
||||
upgraded.setCompleted(last_completed_migration);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
pragma solidity ^0.4.18;
|
||||
|
||||
/**
|
||||
* @title Proxy
|
||||
* @dev Gives the possibility to delegate any call to a foreign implementation.
|
||||
*/
|
||||
contract Proxy {
|
||||
|
||||
/**
|
||||
* @dev Tells the address of the implementation where every call will be delegated.
|
||||
* @return address of the implementation to which it will be delegated
|
||||
*/
|
||||
function implementation() public view returns (address);
|
||||
|
||||
/**
|
||||
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
|
||||
* This function will return whatever the implementation call returns
|
||||
*/
|
||||
function () payable public {
|
||||
address _impl = implementation();
|
||||
require(_impl != address(0));
|
||||
bytes memory data = msg.data;
|
||||
|
||||
assembly {
|
||||
let result := delegatecall(gas, _impl, add(data, 0x20), mload(data), 0, 0)
|
||||
let size := returndatasize
|
||||
|
||||
let ptr := mload(0x40)
|
||||
returndatacopy(ptr, 0, size)
|
||||
|
||||
switch result
|
||||
case 0 { revert(ptr, size) }
|
||||
default { return(ptr, size) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
pragma solidity ^0.4.18;
|
||||
|
||||
import './IRegistry.sol';
|
||||
import './Upgradeable.sol';
|
||||
import './UpgradeabilityProxy.sol';
|
||||
|
||||
/**
|
||||
* @title Registry
|
||||
* @dev This contract works as a registry of versions, it holds the implementations for the registered versions.
|
||||
*/
|
||||
contract Registry is IRegistry {
|
||||
// Mapping of versions to implementations of different functions
|
||||
mapping (string => address) internal versions;
|
||||
|
||||
/**
|
||||
* @dev Registers a new version with its implementation address
|
||||
* @param version representing the version name of the new implementation to be registered
|
||||
* @param implementation representing the address of the new implementation to be registered
|
||||
*/
|
||||
function addVersion(string version, address implementation) public {
|
||||
require(versions[version] == 0x0);
|
||||
versions[version] = implementation;
|
||||
VersionAdded(version, implementation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Tells the address of the implementation for a given version
|
||||
* @param version to query the implementation of
|
||||
* @return address of the implementation registered for the given version
|
||||
*/
|
||||
function getVersion(string version) public view returns (address) {
|
||||
return versions[version];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Creates an upgradeable proxy
|
||||
* @param version representing the first version to be set for the proxy
|
||||
* @return address of the new proxy created
|
||||
*/
|
||||
function createProxy(string version) public payable returns (UpgradeabilityProxy) {
|
||||
UpgradeabilityProxy proxy = new UpgradeabilityProxy(version);
|
||||
Upgradeable(proxy).initialize.value(msg.value)(msg.sender);
|
||||
ProxyCreated(proxy);
|
||||
return proxy;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
pragma solidity ^0.4.18;
|
||||
|
||||
import './Proxy.sol';
|
||||
import './IRegistry.sol';
|
||||
import './UpgradeabilityStorage.sol';
|
||||
|
||||
/**
|
||||
* @title UpgradeabilityProxy
|
||||
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
|
||||
*/
|
||||
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
|
||||
|
||||
/**
|
||||
* @dev Constructor function
|
||||
*/
|
||||
function UpgradeabilityProxy(string _version) public {
|
||||
registry = IRegistry(msg.sender);
|
||||
upgradeTo(_version);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Upgrades the implementation to the requested version
|
||||
* @param _version representing the version name of the new implementation to be set
|
||||
*/
|
||||
function upgradeTo(string _version) public {
|
||||
_implementation = registry.getVersion(_version);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
pragma solidity ^0.4.18;
|
||||
|
||||
import './IRegistry.sol';
|
||||
|
||||
/**
|
||||
* @title UpgradeabilityStorage
|
||||
* @dev This contract holds all the necessary state variables to support the upgrade functionality
|
||||
*/
|
||||
contract UpgradeabilityStorage {
|
||||
// Versions registry
|
||||
IRegistry internal registry;
|
||||
|
||||
// Address of the current implementation
|
||||
address internal _implementation;
|
||||
|
||||
/**
|
||||
* @dev Tells the address of the current implementation
|
||||
* @return address of the current implementation
|
||||
*/
|
||||
function implementation() public view returns (address) {
|
||||
return _implementation;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
pragma solidity ^0.4.18;
|
||||
|
||||
import './UpgradeabilityStorage.sol';
|
||||
|
||||
/**
|
||||
* @title Upgradeable
|
||||
* @dev This contract holds all the minimum required functionality for a behavior to be upgradeable.
|
||||
* This means, required state variables for owned upgradeability purpose and simple initialization validation.
|
||||
*/
|
||||
contract Upgradeable is UpgradeabilityStorage {
|
||||
/**
|
||||
* @dev Validates the caller is the versions registry.
|
||||
* THIS FUNCTION SHOULD BE OVERRIDDEN CALLING SUPER
|
||||
* @param sender representing the address deploying the initial behavior of the contract
|
||||
*/
|
||||
function initialize(address sender) public payable {
|
||||
require(msg.sender == address(registry));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
var Registry = artifacts.require('./upgradeable/Registry.sol');
|
||||
|
||||
var Token = artifacts.require('./Token1.sol');
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(Registry).then(function() {
|
||||
return Registry.deployed();
|
||||
}).then(function(registry) {
|
||||
return deployer.deploy(Token);
|
||||
}).then(function(token) {
|
||||
console.log('Registry address: ', Registry.address);
|
||||
console.log('Token address: ', Token.address);
|
||||
Registry.deployed().then(function(registry) {
|
||||
registry.addVersion('Token_1.0', Token.address);
|
||||
registry.createProxy('Token_1.0').then(function(r) {
|
||||
console.log(r.logs[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
var Token = artifacts.require('./Token2.sol');
|
||||
var Registry = artifacts.require('./upgradeable/Registry');
|
||||
var UpgradeabilityProxy = artifacts.require('./upgradeable/UpgradeabilityProxy');
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(Token).then(function(t) {
|
||||
return Token.deployed();
|
||||
}).then(function(token) {
|
||||
Registry.deployed().then(function(registry) {
|
||||
console.log('Token address: ', Token.address);
|
||||
registry.addVersion('Token_2.0', Token.address);
|
||||
});
|
||||
})
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
var Migrations = artifacts.require("./Migrations.sol");
|
||||
|
||||
module.exports = function(deployer) {
|
||||
deployer.deploy(Migrations);
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
module.exports = {
|
||||
// See <http://truffleframework.com/docs/advanced/configuration>
|
||||
networks: {
|
||||
development: {
|
||||
host: "127.0.0.1",
|
||||
port: 7545,
|
||||
network_id: "*" // Match any network id
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue