-
Notifications
You must be signed in to change notification settings - Fork 115
Package registry PoC #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Package registry PoC #131
Conversation
1090a32
to
ff47999
Compare
443f3f0
to
7b42164
Compare
7b42164
to
c078540
Compare
Looks great @facuspagnuolo! I've yet to understand better the standards around ENS to comment in-depth about the PR, but the architecture looks good. I like having a base registrar, with different "interfaces" depending on the access control for zOS or aOS (though maybe we could switch from Ownable to RBAC down the road). I have one question pending regarding ENS: can we be sure that, under the new registrar to be deployed next year, there is no way for the domain backing the package registry to be lost? For instance, if we register Moving on to a different subject, which is the 1- We have an APMRegistry and a ZOSRegistry, both entirely different contracts, but they are both backed by the same Registrar, tied to the same domain. This would allow both organizations, at first, to share the domain where packages are registered, and then move forward by integrating the subsequent contracts. 2- We aim at a single Registry contract that covers the use cases for both. We can extend the APMRegistry contract to have Either way, this opens the door for arbitrary contracts to be registered in the registry, not just valid Another topic is the 1- If we modify 2- Otherwise, we accept the fact that, during the initial beta period, there will be two different kind of animals registered in the same registry, which have different interfaces. Note that (2) means that a lib developer who wants to develop for both aOS and zOS would need to register two different packages under two different names, at least during this beta phase. On the other hand, even if we do implement a shared interface, then the expected |
40e5a5d
to
dfd4422
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Left a handful of questions. My biggest concern is having an API too large for Package, just for historical reasons (acommodating for specific aOS and zOS needs).
* @param version Name of the version. | ||
* @param provider ImplementationProvider associated with the version. | ||
*/ | ||
function addVersion(uint16[3] version, ImplementationProvider provider) public onlyOwner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove this method altogether, I think we can work fine with newVersion
directly.
* @param version Name of the version. | ||
* @return The implementation provider of the version. | ||
*/ | ||
function getVersion(uint16[3] version) public view returns (ImplementationProvider) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove this method, and use getContract
directly, having the client cast it to an ImplementationProvider
return len > 0 ? len - 1 : 0; | ||
} | ||
|
||
function isValidBump(uint16[3] _oldVersion, uint16[3] _newVersion) public pure returns (bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would be removing this from the shared implementation, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so, we should discuss that
} | ||
|
||
function getLatest() public view returns (uint16[3], address, bytes) { | ||
return getByVersionId(versions.length - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried about this one. I believe we should allow for pushing a version 1.2.4, as a fix to an existing 1.2.3, even if there is a 2.0 running around. Picking up the last version in the array returns the last one uploaded, not the last one in terms of semver.
return (version.semanticVersion, version.contractAddress, version.contentURI); | ||
} | ||
|
||
function getContractByVersionId(uint256 _versionId) public view returns (address) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this method, and just have clients work with getByVersionId?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'm worried that users will need to know the version ID for a given version number, we can expose a method to tell that tho
/** | ||
* @title RepoPackage | ||
*/ | ||
contract RepoPackage { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a general comment, I feel like the API is a bit convoluted. For instance, are the getByVersionId
getters needed, or could we remove them? Same for getLatestForContractAddress
. I know that these methods are in the original AragonPackage, can we reach out to them and check if they do need all of them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally agree, the main idea of this PoC was to validate that Aragon's interface could be compatible with ours, we should now work together towards a common and better and minimum interface :)
* @dev Collection of contracts grouped into versions. | ||
* Contracts with the same name can have different implementation addresses in different versions. | ||
*/ | ||
contract NewZosPackage is Ownable, RepoPackage { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we start moving to RBAC?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree but I think that's not part of the PoC, do you agree?
rootNode = _rootNode; | ||
} | ||
|
||
function createName(bytes32 _label, address _owner) public returns (bytes32) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep these methods internal instead of public
Registrar.initialize(_registry, _rootNode); | ||
} | ||
|
||
function createName(bytes32 _label, address _owner) public onlyOwner returns (bytes32) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just noted I don't properly understand how access control to the registrar should be. Who should be the owner of the contract? How are we handling who can push new packages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was simlpy proving that we can reuse exactly the same implementation used by Aragon with another permission control flow. In this scenario, the owner
will be the only one allowed which doesn't make much sense. IMO, we can start with something public which means so anyone can register a package for ZeppelinOS
require(isValidBump(zeroVersion, _newSemanticVersion)); | ||
} | ||
|
||
uint256 versionId = versions.push(Version(_newSemanticVersion, contractAddress, _contentURI)) - 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how serious this is, but we're relying on transaction ordering to identify versions, and this might be unreliable because of reorgs.
Can we assess the severity of this and consider alternatives?
One rather efficient alternative would be the concatenation of the version components. This should be unique anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I hadn't thought of that. I was actually thinking about removing versionId altogether, given that there is already the semanticVersionHash
that could be used, which is similar to the concatenation you mention.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar but only probabilistically unique!
* @param version Name of the added version. | ||
* @param provider ImplementationProvider associated with the version. | ||
*/ | ||
event VersionAdded(uint16[3] version, ImplementationProvider indexed provider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it the responsibility of this contract to emit an event with the provider
? Should the base RepoPackage
be emitting this info along with the URI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, this should not be part of ZosPackage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, we should move that to RepoPackage
. Didn't want to do that in the PoC just to make sure there was an implementation satisfying both specs :)
event NewVersion(uint256 versionId, uint16[3] semanticVersion); | ||
|
||
struct Version { | ||
uint16[3] semanticVersion; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd consider using three uint64
. I know it sounds like a lot but I don't see a reason for using smaller numbers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually using uint64 allows for alternative version identifiers, such as time-based versions (20180925 does not fit in 16 bits), so +1 to this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that, this should not be a problem for Aragon since it is an array right? I mean, we won't need to perform any migrations or so, right?
Nice work @facuspagnuolo! I like the simplification work you have done. Other points I wanted to touch upon: Rich governanceI see most of the logic has been taken from APM, but without the
With just those 3 steps, we give ZEP token holders right to vote on new versions and repos inside the registry. You could get fancier and create new Token Manager instances for different groups of stakeholders, different Voting apps with different consensus thresholds for different repos, etc. This wouldn't be possible if the package manager wasn't a DAO. We recently wrote a blog post about this. Upgradeability of the reposRight now, the contracts for the repo and the registry are not upgradeable, right? I think they should be (no need to argue why upgradeability is important between us haha). Also one of the cool things you get for free if the package manager is a DAO is that you can upgrade all repos in a registry. Otherwise, you'd need to upgrade each one individually. But if it's a DAO, you can just upgrade the reference to the
|
Hey @luisivan! thanks a lot for your feedback! 😄 Regarding the GovernanceI really like the idea of decentrilizing the governance of the Upgradeability of the ReposAFAIK, given that a Aragon's
|
Got it, makes sense, thanks for the clarification @facuspagnuolo! Regarding governance and upgradeability, I think it doesn't need to be a part of the interface, but a part of any of the implementations. The minimum viable path is to work on a common interface and spec, which I think we are doing a good job at. But ideally we would also work on a canonical implementation that we can all use and build upon. For the spec and interface, we don't need to get into governance or upgradeability, but for the implementation, I think it's very necessary. |
Fixes #132