From November 28th to February 10th, Aragon Court's contracts were deployed to Ethereum mainnet for security researchers to review before they were activated and open to users.

In that time, two bugs were reported by renowned security researcher samczsun, which were fixed on February 9th, along with unrelated UX improvements.

In late February, a third bug was discovered by Bingen, a core contributor to Aragon Court from Aragon One, which was fixed on March 16th.

The bugs did not affect any users.

For complete details of the findings and upgrades, please read the full write-up below.


As developers, we all know there's no silver bullet for designing complex systems or writing perfectly secure code. That said, we put a lot of effort into designing the protocol, built an exhaustive test suite, and received a thorough security audit. But no matter what, we know the presence of bugs is always a possibility. That's why we designed the protocol to handle upgrades and prepared a contingency plan in the event we needed to fix issues or adjust the protocol to community needs.

Aragon Court's protocol was implemented following a module-based architecture where each module is in charge of a specific part of the protocol. The glue between all these modules is what we called the Controller. This architecture allows us to have the flexibility to plug, unplug, or swap these modules easily. The AN DAO currently governs these decisions, initially formed by a group of members trusted by the community on AGP-126, which will later be transitioned to a sovereign DAO controlled by ANT holders.

Aragon Court architecture diagram

During the onboarding period, we kept doing internal reviews, with the collaboration of external security researchers, resulting in a day-1 upgrade proposal to the community.

Jurors Registry

One of the most important fixes was a bug in the JurorsRegistry module of Aragon Court, found by samczsun. Sam responsibly disclosed this bug through the AN bug bounty program and helped us during the review process to make sure these issues were fixed correctly. It was a pleasure working with him! He is the first bug bounty hunter to claim a reward from our bug bounty program.

The main problem was related to the complex data structure Aragon Court maintains to ensure it can efficiently draft jurors under the block gas limit. Basically, we keep two data structures with jurors' information that must be updated simultaneously to reflect the same state. There were two edge cases in which this condition was not met:

  1. When a juror requests an ANJ deactivation, they have to wait one term before they can withdraw their ANJ from Aragon Court. This is because they could still be selected for a dispute in the same term they requested the deactivation. If this happens, the deactivation balance requested is decreased to ensure the juror has enough active ANJ to participate in the dispute. The problem was that we weren't reflecting this in both data structures, but only in one of them (see L634-L651). This enabled two possible exploit paths based on whether the juror was on the winning or the losing side of the dispute. In the case of a winning juror, it would have resulted in losing some ANJ because their balance was not updated correctly at the time of selection. In case of a losing juror, the dispute itself could be blocked from being settled if the juror didn't have enough ANJ left in Aragon Court to be penalized because the stored balance was reflecting an amount lower than the juror's actual balance.
  2. The second issue was specific to a dispute lasting until the final round, where all active jurors can become involved. We were updating the data structures differently depending on whether the juror had a deactivation request or not (see L377-L387). This would have been a problem in case a juror would have requested an ANJ deactivation while voting in the final round. It could have caused the same situations explained in the previous issue, depending on whether the juror was on the winning or losing side of the dispute.

Even though the probability of occurrence was low, the impact could have been considerable.

We resolved this issue before anyone could exploit it as part of the day 1 migration. A new version of the JurorsRegistry was deployed with the fix and we submitted a vote to the AN DAO to perform the swap. Since the old Registry already held tokens and information about jurors from the ANJ pre-activation phase ("Phase 1"), the migration involved more than a simple module swap. We ended up delaying the beginning of Aragon Court's first term to ensure we had enough time to migrate all the balances of the old instance to the new one, once the pre-activation phase ended.

Dispute Manager

Another important issue was discovered by Bingen, a member of Aragon One and one of the main contributors to Aragon Court. This issue was related to how the evidence submission period was handled in the DisputeManager module. Specifically, it resulted in a possible advantage to one side of the dispute when drafting jurors.

To summarize, disputes follow a lifecycle. Early on in a dispute's life, the protocol provides a window of time for the involved parties to submit any relevant evidence for jurors to evaluate later. In cases where all parties are done submitting evidence, Aragon Court allows the last submitter to close the submission process early and proceed to the next phase.

The problem was that in these cases, the protocol would use the current term's randomness value—an already known value—to draft the initial jurors (see L233). This would have allowed the party that closed the submission process to see what the draft outcome would be, and, if it wasn't favorable, wait for the next term. Although it would have been possible to do this for only a limited number of terms (currently 7), it still wasn't the desired behavior.

The fix was simple: when closing the submission period early, we changed the draft term to be the next term (in the future) to ensure its randomness was not known beforehand. Similar to what we did for the previous bug, we submitted a vote to the AN DAO to perform the module swap. This time, we didn't need to do any other action to complete the migration.

ANJ activation wrapper

Another issue pointed out by samczsun was related to the smart contract we built to simplify and decrease the number of transactions necessary for an account to become an active juror by obtaining and activating ANJ into Aragon Court. The issue was that any account with an existing ANT approval to the wrapper contract could have their approved amount activated by any other account (see L78).

Fortunately, the fix was simple, and we only needed to deploy a new instance of the wrapper contract which users can opt into and does not require approval from the AN DAO. We found no accounts with remaining approval balances for the old wrapper contracts.

UX enhancements

We seized the fact that we had to upgrade some of these modules and performed a few other changes to improve the overall user experience of Aragon Court's dashboard:

  • Improved some events emitted by the DisputeManager and JurorsRegistry modules (#244)
  • Allowed jurors to receive active ANJ from third parties (#245)
  • Allowed jurors to delegate their revealing process to third parties (#246)
  • Improved events related to jury rewards, emitted by the DisputeManager module (#257)

Acknowledgments

I want to send a huge thanks to samczsun for his professionalism in disclosing the issues described above and for being responsive during the resolution phase. Also, to the whole Aragon One development team, especially to Jorge, Brett, and Bingen for helping so much throughout the process.

Join the project

We welcome everyone to participate in our bug bounty program. If you are a hacker, we have a $250k rewards pool with tiers up to $50k per bug.

If you have any questions or want to contribute to the project, come and say hello! You can reach out to us on our chat and our forum.


This post was a collaboration between

Facu Spagnuolo, Aragon One

  • Facu Spagnuolo
  • Aragon One