From 6cbfc8815968b39d0d9ae6255d7aea2a979053be Mon Sep 17 00:00:00 2001 From: bumi Date: Wed, 14 Mar 2018 19:12:03 +0100 Subject: [PATCH] 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. --- contracts/Token1.sol | 9 ++-- contracts/upgradeable/IRegistry.sol | 21 +++++++-- contracts/upgradeable/Registry.sol | 46 ++++++++++++++----- contracts/upgradeable/UpgradeabilityProxy.sol | 19 +++++--- .../upgradeable/UpgradeabilityStorage.sol | 3 ++ migrations/1519035571_registry.js | 5 ++ migrations/1520798600_setup.js | 14 ++---- migrations/1520802793_upgrade.js | 2 +- 8 files changed, 80 insertions(+), 39 deletions(-) create mode 100644 migrations/1519035571_registry.js diff --git a/contracts/Token1.sol b/contracts/Token1.sol index 6759f7f..6dfc6d3 100644 --- a/contracts/Token1.sol +++ b/contracts/Token1.sol @@ -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; + } + } diff --git a/contracts/upgradeable/IRegistry.sol b/contracts/upgradeable/IRegistry.sol index 55223e8..1a55c69 100644 --- a/contracts/upgradeable/IRegistry.sol +++ b/contracts/upgradeable/IRegistry.sol @@ -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); + } diff --git a/contracts/upgradeable/Registry.sol b/contracts/upgradeable/Registry.sol index f80247b..c5755e1 100644 --- a/contracts/upgradeable/Registry.sol +++ b/contracts/upgradeable/Registry.sol @@ -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; } } diff --git a/contracts/upgradeable/UpgradeabilityProxy.sol b/contracts/upgradeable/UpgradeabilityProxy.sol index e279e79..ca75b25 100644 --- a/contracts/upgradeable/UpgradeabilityProxy.sol +++ b/contracts/upgradeable/UpgradeabilityProxy.sol @@ -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); } } diff --git a/contracts/upgradeable/UpgradeabilityStorage.sol b/contracts/upgradeable/UpgradeabilityStorage.sol index 08ad454..627073d 100644 --- a/contracts/upgradeable/UpgradeabilityStorage.sol +++ b/contracts/upgradeable/UpgradeabilityStorage.sol @@ -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 diff --git a/migrations/1519035571_registry.js b/migrations/1519035571_registry.js new file mode 100644 index 0000000..9628775 --- /dev/null +++ b/migrations/1519035571_registry.js @@ -0,0 +1,5 @@ +var Registry = artifacts.require('./upgradeable/Registry.sol'); + +module.exports = function(deployer) { + deployer.deploy(Registry); +}; diff --git a/migrations/1520798600_setup.js b/migrations/1520798600_setup.js index 500db01..1271d2a 100644 --- a/migrations/1520798600_setup.js +++ b/migrations/1520798600_setup.js @@ -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); }); }); }; diff --git a/migrations/1520802793_upgrade.js b/migrations/1520802793_upgrade.js index 92ec36c..c90a7fb 100644 --- a/migrations/1520802793_upgrade.js +++ b/migrations/1520802793_upgrade.js @@ -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); }); }) };