Skip to content

Commit

Permalink
Revert last commit (#735)
Browse files Browse the repository at this point in the history
* Use AdminUpgadablilityProxy

* add oz application dir to arc

* improve test covers

* lint

* more tests
  • Loading branch information
orenyodfat authored Apr 21, 2020
1 parent 012d1bc commit dc45775
Show file tree
Hide file tree
Showing 12 changed files with 541 additions and 25 deletions.
120 changes: 120 additions & 0 deletions contracts/registry/App.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
pragma solidity 0.5.17;

import "./ImplementationProvider.sol";
import "./Package.sol";
import "@openzeppelin/upgrades/contracts/upgradeability/AdminUpgradeabilityProxy.sol";
import "@openzeppelin/upgrades/contracts/ownership/Ownable.sol";


/**
* @title App
* @dev Contract for upgradeable applications.
* It handles the creation of proxies.
*/
contract App is OpenZeppelinUpgradesOwnable {
/**
* @dev Emitted when a new proxy is created.
* @param proxy Address of the created proxy.
*/
event ProxyCreated(address proxy);

/**
* @dev Emitted when a package dependency is changed in the application.
* @param providerName Name of the package that changed.
* @param package Address of the package associated to the name.
* @param version Version of the package in use.
*/
event PackageChanged(string providerName, address package, uint64[3] version);

/**
* @dev Tracks a package in a particular version, used for retrieving implementations
*/
struct ProviderInfo {
Package package;
uint64[3] version;
}

/**
* @dev Maps from dependency name to a tuple of package and version
*/
mapping(string => ProviderInfo) internal providers;

/**
* @dev Sets a package in a specific version as a dependency for this application.
* Requires the version to be present in the package.
* @param packageName Name of the package to set or overwrite.
* @param package Address of the package to register.
* @param version Version of the package to use in this application.
*/
function setPackage(string memory packageName, Package package, uint64[3] memory version) public onlyOwner {
require(package.hasVersion(version), "The requested version must be registered in the given package");
providers[packageName] = ProviderInfo(package, version);
emit PackageChanged(packageName, address(package), version);
}

/**
* @dev Unsets a package given its name.
* Reverts if the package is not set in the application.
* @param packageName Name of the package to remove.
*/
function unsetPackage(string memory packageName) public onlyOwner {
require(address(providers[packageName].package) != address(0), "Package to unset not found");
delete providers[packageName];
emit PackageChanged(packageName, address(0), [uint64(0), uint64(0), uint64(0)]);
}

/**
* @dev Creates a new proxy for the given contract and forwards a function call to it.
* This is useful to initialize the proxied contract.
* @param packageName Name of the package where the contract is contained.
* @param contractName Name of the contract.
* @param admin Address of the proxy administrator.
* @param data Data to send as msg.data to the corresponding implementation to initialize the proxied contract.
* It should include the signature and the parameters of the function to be called, as described in
* https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
* @return Address of the new proxy.
*/
function create(string memory packageName, string memory contractName, address admin, bytes memory data)
public
payable
returns (AdminUpgradeabilityProxy) {
address implementation = getImplementation(packageName, contractName);
AdminUpgradeabilityProxy proxy = (new AdminUpgradeabilityProxy).value(msg.value)(implementation, admin, data);
emit ProxyCreated(address(proxy));
return proxy;
}

/**
* @dev Returns the implementation address for a given contract name, provided by the `ImplementationProvider`.
* @param packageName Name of the package where the contract is contained.
* @param contractName Name of the contract.
* @return Address where the contract is implemented.
*/
function getImplementation(string memory packageName, string memory contractName) public view returns (address) {
ImplementationProvider provider = getProvider(packageName);
if (address(provider) == address(0)) return address(0);
return provider.getImplementation(contractName);
}

/**
* @dev Returns the provider for a given package name, or zero if not set.
* @param packageName Name of the package to be retrieved.
* @return The provider.
*/
function getProvider(string memory packageName) public view returns (ImplementationProvider provider) {
ProviderInfo storage info = providers[packageName];
if (address(info.package) == address(0)) return ImplementationProvider(0);
return ImplementationProvider(info.package.getContract(info.version));
}

/**
* @dev Returns information on a package given its name.
* @param packageName Name of the package to be queried.
* @return A tuple with the package address and pinned version given a package name, or zero if not set
*/
function getPackage(string memory packageName) public view returns (Package, uint64[3] memory) {
ProviderInfo storage info = providers[packageName];
return (info.package, info.version);
}
}
77 changes: 77 additions & 0 deletions contracts/registry/ImplementationDirectory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
pragma solidity 0.5.17;

import "./ImplementationProvider.sol";
import "@openzeppelin/upgrades/contracts/ownership/Ownable.sol";
import "@openzeppelin/upgrades/contracts/utils/Address.sol";


/**
* @title ImplementationDirectory
* @dev Implementation provider that stores contract implementations in a mapping.
*/
contract ImplementationDirectory is ImplementationProvider, OpenZeppelinUpgradesOwnable {
/**
* @dev Emitted when the implementation of a contract is changed.
* @param contractName Name of the contract.
* @param implementation Address of the added implementation.
*/
event ImplementationChanged(string contractName, address indexed implementation);

/**
* @dev Emitted when the implementation directory is frozen.
*/
event Frozen();

/// @dev Mapping where the addresses of the implementations are stored.
mapping (string => address) internal implementations;

/// @dev Mutability state of the directory.
bool public frozen;

/**
* @dev Modifier that allows functions to be called only before the contract is frozen.
*/
modifier whenNotFrozen() {
require(!frozen, "Cannot perform action for a frozen implementation directory");
_;
}

/**
* @dev Makes the directory irreversibly immutable.
* It can only be called once, by the owner.
*/
function freeze() public onlyOwner whenNotFrozen {
frozen = true;
emit Frozen();
}

/**
* @dev Sets the address of the implementation of a contract in the directory.
* @param contractName Name of the contract.
* @param implementation Address of the implementation.
*/
function setImplementation(string memory contractName, address implementation) public onlyOwner whenNotFrozen {
require(OpenZeppelinUpgradesAddress.isContract(implementation),
"Cannot set implementation in directory with a non-contract address");
implementations[contractName] = implementation;
emit ImplementationChanged(contractName, implementation);
}

/**
* @dev Removes the address of a contract implementation from the directory.
* @param contractName Name of the contract.
*/
function unsetImplementation(string memory contractName) public onlyOwner whenNotFrozen {
implementations[contractName] = address(0);
emit ImplementationChanged(contractName, address(0));
}

/**
* @dev Returns the implementation address of a contract.
* @param contractName Name of the contract.
* @return Address of the implementation.
*/
function getImplementation(string memory contractName) public view returns (address) {
return implementations[contractName];
}
}
15 changes: 15 additions & 0 deletions contracts/registry/ImplementationProvider.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity 0.5.17;


/**
* @title ImplementationProvider
* @dev Abstract contract for providing implementation addresses for other contracts by name.
*/
contract ImplementationProvider {
/**
* @dev Abstract function to return the implementation address of a contract.
* @param contractName Name of the contract.
* @return Implementation address of the contract.
*/
function getImplementation(string memory contractName) public view returns (address);
}
141 changes: 141 additions & 0 deletions contracts/registry/Package.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
pragma solidity 0.5.17;

import "@openzeppelin/upgrades/contracts/ownership/Ownable.sol";


/**
* @title Package
* @dev A package is composed by a set of versions, identified via semantic versioning,
* where each version has a contract address that refers to a reusable implementation,
* plus an optional content URI with metadata. Note that the semver identifier is restricted
* to major, minor, and patch, as prerelease tags are not supported.
*/
contract Package is OpenZeppelinUpgradesOwnable {
/**
* @dev Emitted when a version is added to the package.
* @param semanticVersion Name of the added version.
* @param contractAddress Contract associated with the version.
* @param contentURI Optional content URI with metadata of the version.
*/
event VersionAdded(uint64[3] semanticVersion, address contractAddress, bytes contentURI);

struct Version {
uint64[3] semanticVersion;
address contractAddress;
bytes contentURI;
}

mapping (bytes32 => Version) internal versions;
mapping (uint64 => bytes32) internal majorToLatestVersion;
uint64 internal latestMajor;

/**
* @dev Adds a new version to the package. Only the Owner can add new versions.
* Reverts if the specified semver identifier already exists.
* Emits a `VersionAdded` event if successful.
* @param semanticVersion Semver identifier of the version.
* @param contractAddress Contract address for the version, must be non-zero.
* @param contentURI Optional content URI for the version.
*/
function addVersion(uint64[3] memory semanticVersion, address contractAddress, bytes memory contentURI)
public
onlyOwner {
require(contractAddress != address(0), "Contract address is required");
require(!hasVersion(semanticVersion), "Given version is already registered in package");
require(!semanticVersionIsZero(semanticVersion), "Version must be non zero");

// Register version
bytes32 versionId = semanticVersionHash(semanticVersion);
versions[versionId] = Version(semanticVersion, contractAddress, contentURI);

// Update latest major
uint64 major = semanticVersion[0];
if (major > latestMajor) {
latestMajor = semanticVersion[0];
}

// Update latest version for this major
uint64 minor = semanticVersion[1];
uint64 patch = semanticVersion[2];
uint64[3] storage latestVersionForMajor = versions[majorToLatestVersion[major]].semanticVersion;
if (semanticVersionIsZero(latestVersionForMajor) // No latest was set for this major
|| (minor > latestVersionForMajor[1]) // Or current minor is greater
|| (minor == latestVersionForMajor[1] && patch > latestVersionForMajor[2]) // Or current patch is greater
) {
majorToLatestVersion[major] = versionId;
}

emit VersionAdded(semanticVersion, contractAddress, contentURI);
}

/**
* @dev Checks whether a version is present in the package.
* @param semanticVersion Semver identifier of the version.
* @return true if the version is registered in this package, false otherwise.
*/
function hasVersion(uint64[3] memory semanticVersion) public view returns (bool) {
Version storage version = versions[semanticVersionHash(semanticVersion)];
return address(version.contractAddress) != address(0);
}

/**
* @dev Returns the version with the highest semver identifier registered in the package.
* For instance, if `1.2.0`, `1.3.0`, and `2.0.0` are present, will always return `2.0.0`, regardless
* of the order in which they were registered. Returns zero if no versions are registered.
* @return Semver identifier, contract address, and content URI for the version, or zero if not exists.
*/
function getLatest()
public
view
returns (uint64[3] memory semanticVersion, address contractAddress, bytes memory contentURI) {
return getLatestByMajor(latestMajor);
}

/**
* @dev Returns the version with the highest semver identifier for the given major.
* For instance, if `1.2.0`, `1.3.0`, and `2.0.0` are present, will return `1.3.0` for major `1`,
* regardless of the order in which they were registered. Returns zero if no versions are registered
* for the specified major.
* @param major Major identifier to query
* @return Semver identifier, contract address, and content URI for the version, or zero if not exists.
*/
function getLatestByMajor(uint64 major)
public
view
returns (uint64[3] memory semanticVersion, address contractAddress, bytes memory contentURI) {
Version storage version = versions[majorToLatestVersion[major]];
return (version.semanticVersion, version.contractAddress, version.contentURI);
}

/**
* @dev Returns a version given its semver identifier.
* @param semanticVersion Semver identifier of the version.
* @return Contract address and content URI for the version, or zero if not exists.
*/
function getVersion(uint64[3] memory semanticVersion)
public
view
returns (address contractAddress, bytes memory contentURI) {
Version storage version = versions[semanticVersionHash(semanticVersion)];
return (version.contractAddress, version.contentURI);
}

/**
* @dev Returns a contract for a version given its semver identifier.
* This method is equivalent to `getVersion`, but returns only the contract address.
* @param semanticVersion Semver identifier of the version.
* @return Contract address for the version, or zero if not exists.
*/
function getContract(uint64[3] memory semanticVersion) public view returns (address contractAddress) {
Version storage version = versions[semanticVersionHash(semanticVersion)];
return version.contractAddress;
}

function semanticVersionHash(uint64[3] memory version) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(version[0], version[1], version[2]));
}

function semanticVersionIsZero(uint64[3] memory version) internal pure returns (bool) {
return version[0] == 0 && version[1] == 0 && version[2] == 0;
}
}
4 changes: 2 additions & 2 deletions contracts/schemes/UpgradeScheme.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import "@daostack/infra-experimental/contracts/votingMachines/VotingMachineCallb
import "../votingMachines/VotingMachineCallbacks.sol";
import "../libs/Bytes32ToStr.sol";
import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "@openzeppelin/upgrades/contracts/application/Package.sol";
import "@openzeppelin/upgrades/contracts/application/ImplementationProvider.sol";
import "../registry/Package.sol";
import "../registry/ImplementationProvider.sol";


/**
Expand Down
Loading

0 comments on commit dc45775

Please sign in to comment.