Refactor to support multiple contracts in the registry

This allows to manage different contracts (Token, Collaborator, etc.)
with one registry.
The registry serves as main source for all contracts and versions.
Versions are independently deployed and added to the registry. From
there the proxy contract's implementation can be upgraded to the new
version.
Currently no access control is implemented.
This commit is contained in:
bumi 2018-03-14 19:12:03 +01:00
parent 4051093f72
commit 6cbfc88159
8 changed files with 80 additions and 39 deletions

View File

@ -4,13 +4,14 @@ import './upgradeable/Upgradeable.sol';
contract Token1 is Upgradeable {
uint public value;
function Token() public {
value = 1;
}
uint public value = 0;
function mint() public {
value += 10;
}
function initialize(address sender) public payable {
value = 1;
}
}

View File

@ -7,28 +7,39 @@ pragma solidity ^0.4.18;
interface IRegistry {
/**
* @dev This event will be emitted every time a new proxy is created
* @param name of the contract, as specified in the registry
* @param proxy representing the address of the proxy created
*/
event ProxyCreated(address proxy);
event ProxyCreated(bytes32 name, address proxy);
/**
* @dev This event will be emitted every time a new implementation is registered
* @param name of the contract, as specified in the registry
* @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);
event VersionAdded(bytes32 name, uint 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 name of the contract, as specified in the registry
* @param implementation representing the address of the new implementation to be registered
*/
function addVersion(string version, address implementation) public;
function addVersion(bytes32 name, address implementation) public;
/**
* @dev Tells the address of the implementation for a given version
* @param name of the contract, as specified in the registry
* @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);
function getVersion(bytes32 name, uint version) public view returns (address);
/**
* @dev Tells the latest address of the implementation
* @param name of the contract, as specified in the registry
* @return address of the implementation registered for the latest version
*/
function getLatestVersion(bytes32 name) public view returns (address);
}

View File

@ -9,38 +9,60 @@ import './UpgradeabilityProxy.sol';
* @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;
// mapping of contract names to versions to implementation
// "Token" => "1.0.0" => "0x123"
mapping(bytes32 => mapping(uint => address)) public versions;
// current version for a certain contract
mapping(bytes32 => uint) public currentVersions;
// mapping of the contract names to the proxy addresses
mapping(bytes32 => address) public proxies;
/**
* @dev Registers a new version with its implementation address
* @param version representing the version name of the new implementation to be registered
* @param name of the contract
* @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);
function addVersion(bytes32 name, address implementation) public {
currentVersions[name] = currentVersions[name] + 1;
uint version = currentVersions[name];
require(versions[name][version] == 0x0);
versions[name][version] = implementation;
VersionAdded(name, version, implementation);
}
/**
* @dev Tells the address of the implementation for a given version
* @param name of the contract
* @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];
function getVersion(bytes32 name, uint version) public view returns (address) {
return versions[name][version];
}
function getLatestVersion(bytes32 name) public view returns (address) {
uint current = currentVersions[name];
return getVersion(name, current);
}
function upgrade(bytes32 name, uint version) public {
UpgradeabilityProxy(proxies[name]).upgradeTo(version);
}
/**
* @dev Creates an upgradeable proxy
* @param name of the contract
* @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);
function createProxy(bytes32 name, uint version) public payable returns (UpgradeabilityProxy) {
require(proxies[name] == 0x0);
UpgradeabilityProxy proxy = new UpgradeabilityProxy(name, version);
proxies[name] = address(proxy);
Upgradeable(proxy).initialize.value(msg.value)(msg.sender);
ProxyCreated(proxy);
ProxyCreated(name, proxy);
return proxy;
}
}

View File

@ -10,10 +10,8 @@ import './UpgradeabilityStorage.sol';
*/
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
/**
* @dev Constructor function
*/
function UpgradeabilityProxy(string _version) public {
function UpgradeabilityProxy(bytes32 _name, uint _version) public {
_proxiedContractName = _name;
registry = IRegistry(msg.sender);
upgradeTo(_version);
}
@ -22,8 +20,17 @@ contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
* @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);
function upgradeTo(uint _version) public {
require(msg.sender == address(registry));
_implementation = registry.getVersion(_proxiedContractName, _version);
}
/**
* @dev Upgrades the implementation to the latest version
*/
function upgradeToLatest() public {
require(msg.sender == address(registry));
_implementation = registry.getLatestVersion(_proxiedContractName);
}
}

View File

@ -13,6 +13,9 @@ contract UpgradeabilityStorage {
// Address of the current implementation
address internal _implementation;
// contract name
bytes32 public _proxiedContractName;
/**
* @dev Tells the address of the current implementation
* @return address of the current implementation

View File

@ -0,0 +1,5 @@
var Registry = artifacts.require('./upgradeable/Registry.sol');
module.exports = function(deployer) {
deployer.deploy(Registry);
};

View File

@ -1,20 +1,12 @@
var Registry = artifacts.require('./upgradeable/Registry.sol');
var Registry = artifacts.require('./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) {
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]);
});
registry.addVersion('Token', Token.address);
});
});
};

View File

@ -8,7 +8,7 @@ module.exports = function(deployer) {
}).then(function(token) {
Registry.deployed().then(function(registry) {
console.log('Token address: ', Token.address);
registry.addVersion('Token_2.0', Token.address);
registry.addVersion('Token', Token.address);
});
})
};