About ACL Oracles

In this forum post I want to explain why I’m not a big fan of our ACL Oracles. I don’t even like that much ACL with params, but that would belong to separate and broader discussion. Although technically are impressive, I think they are not so useful. I guess they are aligned with Aragon initial position, trying to have a scope as wide as possible. At the technical level, more abstraction is not always better.

How it works

First of all, an introduction to explain how ACL Oracles work. To use such type of ACL you need two components:

  • A contract implementing the ACLOracle interface.
  • A contract using an ACL with parameters modifier in the function header.

Then you assign the role declared in that modifier to the address of the deployed Oracle contract. To do that you must grant the permission with params, using the ORACLE_PARAM_ID (203), the operator EQ (1) and the address of your oracle. You can see an example here:

function _setOracle(ACL _acl, address _who, address _where, bytes32 _what, address _oracle) private {
    uint256[] memory params = new uint256[](1);
    params[0] = _paramsTo256(ORACLE_PARAM_ID, uint8(Op.EQ), uint240(_oracle));

    _acl.grantPermissionP(_who, _where, _what, params);
}

Note that _who will be ANY_ENTITY here, as access in general won’t depend on who calls the protected function.
When the function protected by that role gets called, the authP modifier calls function canPerform, which fetches the Kernel and calls hasPermission on it. The Kernel gets its ACL and calls the homonym function on it. We are finally on the ACL, we are getting there. The ACL goes through _evalParams, which holds the core logic, and after checking that the param id is ORACLE_PARAM_ID calls checkOracle and from there with some assembly magic we finally reach canPerform function in the Oracle.

At this point I feel I could stop this post here, as this feels enough to make my point, but let’s move on ;-P.

Example

I’m going to use first example of usage of these oracles that I know of, by the 1Hive team: the Dandelion apps. You can check it here and here. First of all I want to clarify that 1Hive did an incredible job here. By no means this is a critic to their job, actually the opposite: precisely because that hard job was needed I think ACL Oracles are not the best idea. They were working under the assumption that they would use them, and that assumption is what I’m challenging.
Let’s check the Token Balance one. In my opinion, ACL system should include the notion of group defined in the interface, like for instance ACL in file permissions in POSIX systems.

But still, here this is as simple as:

function canPerform(address _sender, address _token, uint256 _minBalance) {
    return _token.balanceOf(_sender) >= _minBalance;
}

I wouldn’t use something as complicated as an ACL Oracle for a one line thing like this one. We could discuss where to put this, if we need to define it as an interface, etc., but in the end it’s just an easy check that belongs to the business logic of the app, so it doesn’t make sense to be abstracted and hidden behind an ACL.

I know that this would move it out of the Permissions of the DAO, but we don’t have a UI for ACL Oracles yet. Actually what you will see now in the Permissions UI of you DAO is that the role is assigned to ANY_ENTITY which will be confusing if not scary.

If we look at the other one, although a little bit more complex, the situation is similar to the other one. You just need to put this inside the DandelionVoting app itself:

function canPerform(address _who, uint64 _dissentWindowBlocks) external view returns (bool) {

    // We check hasNeverVotedYea for the edge case where the chains current block number is less than the
    // dissentWindowBlocks and canPerform would return false even if "who" has not voted, when it should return true.
    bool hasNeverVotedYea = lastYeaVoteBlock(_who) == 0;
    bool lastYeaVoteOutsideDissentWindow = lastYeaVoteBlock(_who).add(dissentWindowBlocks) < getBlockNumber64();

    return hasNeverVotedYea || lastYeaVoteOutsideDissentWindow;
}

The Redeem app could call that function. Again, you could put this in an interface, add some checks to be able to disable it and then rely on regular ACLs, etc.

Besides this Oracle is already tightly coupled to the Voting app. (As a side note, this Oracle was initially built as a separate contract, but then moved to the DandelionVoting app because of the close relationship).

Another argument for using this is the role assignment being dynamic, like maybe at some point you remove the Oracle and set the permission to a regular account or a forwarder app. Does this make any sense? It doesn’t seem likely. And even if you want to allow it, you can still do something like auth(YOUR_ROLE) || ...

Target

An important question that arises when thinking about ACL Oracles is: Who are we trying to help, users or developers?

Developers

Without UI it can only be developers (so far there’s none). See the introduction in the intro post on 1Hive blog:
“We expected this to be fairly straightforward. Turns out it’s not. In order to make Dandelion Orgs work we dug into some of the lesser known features of the aragonOS like ACL Oracles.”
It should be straightforward, ACL Oracles are the problem, not the solution.
The fact that you need to pass msg.sender as a param is counter-intiuitive and cumbersome, as usually msg.sender is the _who param (See this code).

Aragon documentation says:

“The possibilities for customizing an organization or protocol’s governance model are truly endless and there is no need to write any actual Solidity.”

But I would argue this is not true, at least for Oracles, as in these examples a quite complex Solidity code had to be written.

If the purpose of ACL Oracles is reusability, precisely because use cases will be about business logic, often it will be hard to decouple it from other parts of the app and make it truly reusable. See for instance the aforementioned example of the Dandelion voting app.

Users

In order for usrs to be able to benefit from this, we need them to be able to easily install Oracle apps in the app store, and a Permissions UI that supports params and oracles. I don’t think the latter is coming short/mid term, and I think it’s a huge challenge to build such a UI in a user friendly manner.
So right now it’s not useful for users, nor I expect it will be soon.
Furthermore, I don’t think it’s a good idea even if we had those frontend missing pieces. See section below to see why.

Security

In this ecosystem security plays an highly critical role, more than in other IT industries, no need to explain why. That’s why all projects spend a lot of resources auditing the smart contracts. Those audits, when analyzing Aragon projects, pay special attention to how permissions are set up. Allowing to use ACL Oracles is opening a big hole in governance/security, you can later inject there absolutely any logic. In my opinion the kind of logic that those oracles will hold should be fixed and predefined as regular apps.
Yes, you can argue that upgradeability opens up the same problems, and it’s true, but first of all the purpose of upgradeability is not to change the core business logic but mainly to fix bugs and maybe also adding new features. Anyway, I would argue the opposite: that’s what we have upgradeability for, to address major changes if needed. ACL Oracles are not the right place to be.
Flexibility and security are usually opposed. Upgradeability represents a trade-off (and there are a lot of people who are against it), but we can not assume that more flexibility is always good.

Another point regarding security. ACL system is audited, so it seems that using it should be the safest way to go. But there are two caveats here: first thing ACL’s with params and Oracles is a complex code that has never been used before, so even if audited could have flaws; second, the code needed to write is bigger and more complex using ACL Oracles than without, so you are opening doors to security issues. Besides you are moving a lot of the complexity (and therefore of the security risk) to the deployment phase.

Gas

I haven’t had time to measure it, but looking at all the hops needed I’m pretty sure ACL Oracles are not the most efficient way to implement those logic pieces.

Analogy

I would like to make an analogy with mobile OSes and app centers. Power of a programming language in a user friendly UI sounds like alchemy to me. I’m pretty sure there are tons of repeated functionalities in different apps in all the popular mobile app stores, but that’s fine. Being more generic usually comes at a price too. So I don’t think it’s that bad if we cannot reuse all the lines of code written within the Aragon ecosystem.

Saving Solidity?

Going back to that quote above, “…and there is no need to write any actual Solidity”
Assuming we don’t have a UI yet, I don’t think replacing Solidity by something like this is a good idea (except for testing). I’d rather have this in a template which shoud definitely be audited for any serious purpose. It’s a hole to put security critical logic into DAOs.

Gas limitation

If all the above is not enough, then we currently have a severe gas limitation of 30k gas. The first time this has been used this has been a problem (with a little help from Istanbul fork). Will Griff is doing a great job to fix this:

https://github.com/aragon/aragonOS/pull/565

Conclusion

I would like to insist on suggesting you to watch that talk by Cheng Lou, specially at minute ~11’. Finding the right spot, the sweet balance, as always in life, is quite hard, but my gut feeling is that ACL Oracles don’t belong to it.

4 Likes

Great post! :raised_hands: Thanks for sharing your Solidity magic :sparkles: and I agree that those concerns are valid and wise

1 Like

Thanks for the writeup @bingen! I know we have discussed at length previously but will add some comments which you may have already heard for the sake of any one else who may be reading.

Why I like the ACL (and ACL Oracles):
I’m not a developer, but have a fairly technical background. And helped come up with the design for Dandelion that leverages ACL Oracles.

I find that having the access control logic contained within a single place to be drastically easier to understand and reason about than if there were to be a complex web of checks across various different applications. It’s also much easier to visualize for a user in a generalizable way.

I think the tradeoffs in terms of gas justify that logical simplicity, if we are coming from the perspective of promoting an experience where Aragon applications and the Client are modular and composable rather than tightly integrated suites. If we are not prioritizing that experience, we should probably question what advantage aragonOS and ACL architecture more generally provide.

As it stands now I still think that leveraging ACL oracles was the right approach for Dandelion. If the ACL had natively supported groups, we would not have needed to create the token balance oracle, and perhaps in the future an updated ACL will allow us to remove that component. However, because of the nature of aragonOS we were able to add that functionality without making significant changes to core, battle-tested code.

Additionally, the Dandelion oracle that enables ragequit functionality could have been implemented outside of the ACL as you suggest, but it would also be less functional and would be pretty much impossible to expose that information in the Permissions app in the future. This would mean that the permissions app becomes only a partial view into how the organization functions and basically useless, whereas with the current implementation we can relatively easily provide atleast a visual indication that the permission is there and has been parameterized. https://github.com/aragon/aragon/issues/821

Improving the ACL by getting rid of the ACL entirely

One of the major arguments here is that changes to business logic should be rare and that we should not optimize for flexibility there. I’m not sure exactly where I stand on this as I find the flexibility of the permissions system and the client quite useful… and many of the most interesting organization do not use the out of the box templates but are still well served by the Client… but I do think that it is probably unlikely that we will typical see users making significant changes to their permissions, installing apps, or uninstalling apps, on a regular and causal basis. We even have our templates professionally audited… which is counterintuitive since a template is just a configuration and deployment shortcut.

If the general practice is that most users prefer using a stable template/configuration and do not intend to modify that configuration while using the organization, we may not need an ACL at all. We could focus instead of building a simple library of components, without necessarily even having a Canonical Client UI, and then each “template” would provide its own interface which summarizes the “permissions” structure of the organization.

2 Likes

Thanks for your insights Luke!
I want to make a small comment about this:

As I said in my original post, this is not completely true, see this PR:

:stuck_out_tongue_winking_eye:

Although in this specific case, the fix was required due to a mistake on our part when we moved from using a call to a staticcall to the ACL oracle.

1 Like

Ok, but the more complexity you have the more prone to error it will be.