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); }); }) };