Skip to content

Commit dc45775

Browse files
authored
Revert last commit (#735)
* Use AdminUpgadablilityProxy * add oz application dir to arc * improve test covers * lint * more tests
1 parent 012d1bc commit dc45775

File tree

12 files changed

+541
-25
lines changed

12 files changed

+541
-25
lines changed

contracts/registry/App.sol

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
pragma solidity 0.5.17;
2+
3+
import "./ImplementationProvider.sol";
4+
import "./Package.sol";
5+
import "@openzeppelin/upgrades/contracts/upgradeability/AdminUpgradeabilityProxy.sol";
6+
import "@openzeppelin/upgrades/contracts/ownership/Ownable.sol";
7+
8+
9+
/**
10+
* @title App
11+
* @dev Contract for upgradeable applications.
12+
* It handles the creation of proxies.
13+
*/
14+
contract App is OpenZeppelinUpgradesOwnable {
15+
/**
16+
* @dev Emitted when a new proxy is created.
17+
* @param proxy Address of the created proxy.
18+
*/
19+
event ProxyCreated(address proxy);
20+
21+
/**
22+
* @dev Emitted when a package dependency is changed in the application.
23+
* @param providerName Name of the package that changed.
24+
* @param package Address of the package associated to the name.
25+
* @param version Version of the package in use.
26+
*/
27+
event PackageChanged(string providerName, address package, uint64[3] version);
28+
29+
/**
30+
* @dev Tracks a package in a particular version, used for retrieving implementations
31+
*/
32+
struct ProviderInfo {
33+
Package package;
34+
uint64[3] version;
35+
}
36+
37+
/**
38+
* @dev Maps from dependency name to a tuple of package and version
39+
*/
40+
mapping(string => ProviderInfo) internal providers;
41+
42+
/**
43+
* @dev Sets a package in a specific version as a dependency for this application.
44+
* Requires the version to be present in the package.
45+
* @param packageName Name of the package to set or overwrite.
46+
* @param package Address of the package to register.
47+
* @param version Version of the package to use in this application.
48+
*/
49+
function setPackage(string memory packageName, Package package, uint64[3] memory version) public onlyOwner {
50+
require(package.hasVersion(version), "The requested version must be registered in the given package");
51+
providers[packageName] = ProviderInfo(package, version);
52+
emit PackageChanged(packageName, address(package), version);
53+
}
54+
55+
/**
56+
* @dev Unsets a package given its name.
57+
* Reverts if the package is not set in the application.
58+
* @param packageName Name of the package to remove.
59+
*/
60+
function unsetPackage(string memory packageName) public onlyOwner {
61+
require(address(providers[packageName].package) != address(0), "Package to unset not found");
62+
delete providers[packageName];
63+
emit PackageChanged(packageName, address(0), [uint64(0), uint64(0), uint64(0)]);
64+
}
65+
66+
/**
67+
* @dev Creates a new proxy for the given contract and forwards a function call to it.
68+
* This is useful to initialize the proxied contract.
69+
* @param packageName Name of the package where the contract is contained.
70+
* @param contractName Name of the contract.
71+
* @param admin Address of the proxy administrator.
72+
* @param data Data to send as msg.data to the corresponding implementation to initialize the proxied contract.
73+
* It should include the signature and the parameters of the function to be called, as described in
74+
* https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
75+
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
76+
* @return Address of the new proxy.
77+
*/
78+
function create(string memory packageName, string memory contractName, address admin, bytes memory data)
79+
public
80+
payable
81+
returns (AdminUpgradeabilityProxy) {
82+
address implementation = getImplementation(packageName, contractName);
83+
AdminUpgradeabilityProxy proxy = (new AdminUpgradeabilityProxy).value(msg.value)(implementation, admin, data);
84+
emit ProxyCreated(address(proxy));
85+
return proxy;
86+
}
87+
88+
/**
89+
* @dev Returns the implementation address for a given contract name, provided by the `ImplementationProvider`.
90+
* @param packageName Name of the package where the contract is contained.
91+
* @param contractName Name of the contract.
92+
* @return Address where the contract is implemented.
93+
*/
94+
function getImplementation(string memory packageName, string memory contractName) public view returns (address) {
95+
ImplementationProvider provider = getProvider(packageName);
96+
if (address(provider) == address(0)) return address(0);
97+
return provider.getImplementation(contractName);
98+
}
99+
100+
/**
101+
* @dev Returns the provider for a given package name, or zero if not set.
102+
* @param packageName Name of the package to be retrieved.
103+
* @return The provider.
104+
*/
105+
function getProvider(string memory packageName) public view returns (ImplementationProvider provider) {
106+
ProviderInfo storage info = providers[packageName];
107+
if (address(info.package) == address(0)) return ImplementationProvider(0);
108+
return ImplementationProvider(info.package.getContract(info.version));
109+
}
110+
111+
/**
112+
* @dev Returns information on a package given its name.
113+
* @param packageName Name of the package to be queried.
114+
* @return A tuple with the package address and pinned version given a package name, or zero if not set
115+
*/
116+
function getPackage(string memory packageName) public view returns (Package, uint64[3] memory) {
117+
ProviderInfo storage info = providers[packageName];
118+
return (info.package, info.version);
119+
}
120+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
pragma solidity 0.5.17;
2+
3+
import "./ImplementationProvider.sol";
4+
import "@openzeppelin/upgrades/contracts/ownership/Ownable.sol";
5+
import "@openzeppelin/upgrades/contracts/utils/Address.sol";
6+
7+
8+
/**
9+
* @title ImplementationDirectory
10+
* @dev Implementation provider that stores contract implementations in a mapping.
11+
*/
12+
contract ImplementationDirectory is ImplementationProvider, OpenZeppelinUpgradesOwnable {
13+
/**
14+
* @dev Emitted when the implementation of a contract is changed.
15+
* @param contractName Name of the contract.
16+
* @param implementation Address of the added implementation.
17+
*/
18+
event ImplementationChanged(string contractName, address indexed implementation);
19+
20+
/**
21+
* @dev Emitted when the implementation directory is frozen.
22+
*/
23+
event Frozen();
24+
25+
/// @dev Mapping where the addresses of the implementations are stored.
26+
mapping (string => address) internal implementations;
27+
28+
/// @dev Mutability state of the directory.
29+
bool public frozen;
30+
31+
/**
32+
* @dev Modifier that allows functions to be called only before the contract is frozen.
33+
*/
34+
modifier whenNotFrozen() {
35+
require(!frozen, "Cannot perform action for a frozen implementation directory");
36+
_;
37+
}
38+
39+
/**
40+
* @dev Makes the directory irreversibly immutable.
41+
* It can only be called once, by the owner.
42+
*/
43+
function freeze() public onlyOwner whenNotFrozen {
44+
frozen = true;
45+
emit Frozen();
46+
}
47+
48+
/**
49+
* @dev Sets the address of the implementation of a contract in the directory.
50+
* @param contractName Name of the contract.
51+
* @param implementation Address of the implementation.
52+
*/
53+
function setImplementation(string memory contractName, address implementation) public onlyOwner whenNotFrozen {
54+
require(OpenZeppelinUpgradesAddress.isContract(implementation),
55+
"Cannot set implementation in directory with a non-contract address");
56+
implementations[contractName] = implementation;
57+
emit ImplementationChanged(contractName, implementation);
58+
}
59+
60+
/**
61+
* @dev Removes the address of a contract implementation from the directory.
62+
* @param contractName Name of the contract.
63+
*/
64+
function unsetImplementation(string memory contractName) public onlyOwner whenNotFrozen {
65+
implementations[contractName] = address(0);
66+
emit ImplementationChanged(contractName, address(0));
67+
}
68+
69+
/**
70+
* @dev Returns the implementation address of a contract.
71+
* @param contractName Name of the contract.
72+
* @return Address of the implementation.
73+
*/
74+
function getImplementation(string memory contractName) public view returns (address) {
75+
return implementations[contractName];
76+
}
77+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pragma solidity 0.5.17;
2+
3+
4+
/**
5+
* @title ImplementationProvider
6+
* @dev Abstract contract for providing implementation addresses for other contracts by name.
7+
*/
8+
contract ImplementationProvider {
9+
/**
10+
* @dev Abstract function to return the implementation address of a contract.
11+
* @param contractName Name of the contract.
12+
* @return Implementation address of the contract.
13+
*/
14+
function getImplementation(string memory contractName) public view returns (address);
15+
}

contracts/registry/Package.sol

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
pragma solidity 0.5.17;
2+
3+
import "@openzeppelin/upgrades/contracts/ownership/Ownable.sol";
4+
5+
6+
/**
7+
* @title Package
8+
* @dev A package is composed by a set of versions, identified via semantic versioning,
9+
* where each version has a contract address that refers to a reusable implementation,
10+
* plus an optional content URI with metadata. Note that the semver identifier is restricted
11+
* to major, minor, and patch, as prerelease tags are not supported.
12+
*/
13+
contract Package is OpenZeppelinUpgradesOwnable {
14+
/**
15+
* @dev Emitted when a version is added to the package.
16+
* @param semanticVersion Name of the added version.
17+
* @param contractAddress Contract associated with the version.
18+
* @param contentURI Optional content URI with metadata of the version.
19+
*/
20+
event VersionAdded(uint64[3] semanticVersion, address contractAddress, bytes contentURI);
21+
22+
struct Version {
23+
uint64[3] semanticVersion;
24+
address contractAddress;
25+
bytes contentURI;
26+
}
27+
28+
mapping (bytes32 => Version) internal versions;
29+
mapping (uint64 => bytes32) internal majorToLatestVersion;
30+
uint64 internal latestMajor;
31+
32+
/**
33+
* @dev Adds a new version to the package. Only the Owner can add new versions.
34+
* Reverts if the specified semver identifier already exists.
35+
* Emits a `VersionAdded` event if successful.
36+
* @param semanticVersion Semver identifier of the version.
37+
* @param contractAddress Contract address for the version, must be non-zero.
38+
* @param contentURI Optional content URI for the version.
39+
*/
40+
function addVersion(uint64[3] memory semanticVersion, address contractAddress, bytes memory contentURI)
41+
public
42+
onlyOwner {
43+
require(contractAddress != address(0), "Contract address is required");
44+
require(!hasVersion(semanticVersion), "Given version is already registered in package");
45+
require(!semanticVersionIsZero(semanticVersion), "Version must be non zero");
46+
47+
// Register version
48+
bytes32 versionId = semanticVersionHash(semanticVersion);
49+
versions[versionId] = Version(semanticVersion, contractAddress, contentURI);
50+
51+
// Update latest major
52+
uint64 major = semanticVersion[0];
53+
if (major > latestMajor) {
54+
latestMajor = semanticVersion[0];
55+
}
56+
57+
// Update latest version for this major
58+
uint64 minor = semanticVersion[1];
59+
uint64 patch = semanticVersion[2];
60+
uint64[3] storage latestVersionForMajor = versions[majorToLatestVersion[major]].semanticVersion;
61+
if (semanticVersionIsZero(latestVersionForMajor) // No latest was set for this major
62+
|| (minor > latestVersionForMajor[1]) // Or current minor is greater
63+
|| (minor == latestVersionForMajor[1] && patch > latestVersionForMajor[2]) // Or current patch is greater
64+
) {
65+
majorToLatestVersion[major] = versionId;
66+
}
67+
68+
emit VersionAdded(semanticVersion, contractAddress, contentURI);
69+
}
70+
71+
/**
72+
* @dev Checks whether a version is present in the package.
73+
* @param semanticVersion Semver identifier of the version.
74+
* @return true if the version is registered in this package, false otherwise.
75+
*/
76+
function hasVersion(uint64[3] memory semanticVersion) public view returns (bool) {
77+
Version storage version = versions[semanticVersionHash(semanticVersion)];
78+
return address(version.contractAddress) != address(0);
79+
}
80+
81+
/**
82+
* @dev Returns the version with the highest semver identifier registered in the package.
83+
* For instance, if `1.2.0`, `1.3.0`, and `2.0.0` are present, will always return `2.0.0`, regardless
84+
* of the order in which they were registered. Returns zero if no versions are registered.
85+
* @return Semver identifier, contract address, and content URI for the version, or zero if not exists.
86+
*/
87+
function getLatest()
88+
public
89+
view
90+
returns (uint64[3] memory semanticVersion, address contractAddress, bytes memory contentURI) {
91+
return getLatestByMajor(latestMajor);
92+
}
93+
94+
/**
95+
* @dev Returns the version with the highest semver identifier for the given major.
96+
* For instance, if `1.2.0`, `1.3.0`, and `2.0.0` are present, will return `1.3.0` for major `1`,
97+
* regardless of the order in which they were registered. Returns zero if no versions are registered
98+
* for the specified major.
99+
* @param major Major identifier to query
100+
* @return Semver identifier, contract address, and content URI for the version, or zero if not exists.
101+
*/
102+
function getLatestByMajor(uint64 major)
103+
public
104+
view
105+
returns (uint64[3] memory semanticVersion, address contractAddress, bytes memory contentURI) {
106+
Version storage version = versions[majorToLatestVersion[major]];
107+
return (version.semanticVersion, version.contractAddress, version.contentURI);
108+
}
109+
110+
/**
111+
* @dev Returns a version given its semver identifier.
112+
* @param semanticVersion Semver identifier of the version.
113+
* @return Contract address and content URI for the version, or zero if not exists.
114+
*/
115+
function getVersion(uint64[3] memory semanticVersion)
116+
public
117+
view
118+
returns (address contractAddress, bytes memory contentURI) {
119+
Version storage version = versions[semanticVersionHash(semanticVersion)];
120+
return (version.contractAddress, version.contentURI);
121+
}
122+
123+
/**
124+
* @dev Returns a contract for a version given its semver identifier.
125+
* This method is equivalent to `getVersion`, but returns only the contract address.
126+
* @param semanticVersion Semver identifier of the version.
127+
* @return Contract address for the version, or zero if not exists.
128+
*/
129+
function getContract(uint64[3] memory semanticVersion) public view returns (address contractAddress) {
130+
Version storage version = versions[semanticVersionHash(semanticVersion)];
131+
return version.contractAddress;
132+
}
133+
134+
function semanticVersionHash(uint64[3] memory version) internal pure returns (bytes32) {
135+
return keccak256(abi.encodePacked(version[0], version[1], version[2]));
136+
}
137+
138+
function semanticVersionIsZero(uint64[3] memory version) internal pure returns (bool) {
139+
return version[0] == 0 && version[1] == 0 && version[2] == 0;
140+
}
141+
}

contracts/schemes/UpgradeScheme.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import "@daostack/infra-experimental/contracts/votingMachines/VotingMachineCallb
55
import "../votingMachines/VotingMachineCallbacks.sol";
66
import "../libs/Bytes32ToStr.sol";
77
import "@openzeppelin/upgrades/contracts/Initializable.sol";
8-
import "@openzeppelin/upgrades/contracts/application/Package.sol";
9-
import "@openzeppelin/upgrades/contracts/application/ImplementationProvider.sol";
8+
import "../registry/Package.sol";
9+
import "../registry/ImplementationProvider.sol";
1010

1111

1212
/**

0 commit comments

Comments
 (0)