Skip to content
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

A Mechanism for Decentralised Proxy Contracts #235

Open
kaiynne opened this issue Sep 15, 2019 · 2 comments

Comments

@kaiynne
Copy link
Member

commented Sep 15, 2019

If you don’t care about the context and just want to read about the mechanism, jump down to the Proposed Solution section.

Recently there has been some debate about the decision to use proxies for the Synthetix smart contract suite. While the decision was not made lightly, it has been a concern of mine for a while. Not because I believe our team to be malicious, but because I don’t want anyone using the system to need to even consider this before using it. This weekend while thinking about the issue I came up with a potential solution that I have not previously seen, it may have been proposed earlier though and I missed it. In any case, this post will offer a solution to the proxy contract issue, but first I will provide context on what a proxy contract is and why Synthetix uses them.

Introduction to proxy contracts

Smart contracts are intended to be trustless, which means that the code that is verifiable by anyone and that you can determine how the contract will behave when you interact with it. This doesn’t make smart contracts perfectly safe — there can still be bugs or other issues that can cause you to lose funds when interacting with them. But interacting with a smart contract should be deterministic. This is where the real power arises, because once deployed the contract treats every transaction the same.* So users are empowered to interact with the contracts without fear that they may be modified or captured, and this is obviously not true of other platforms where a central authority controls them and can change them at will.

Proxy contracts break this assumption, because they grant certain privileged parties the ability to change the behaviour of the contracts after they are deployed. But before we explore this let’s discuss how they do this.

There are a few different proxy contract architectures, but the basic one as described by Nick Johnson can be found here. Essentially it splits a standard contract into three parts, Storage, Logic and Registry (the “proxy”). By doing this we can deploy a new logic contract and tell the registry contract to connect to this new logic rather than the old logic. One of the immediate benefits of this is that the contract address for the proxy can remain the same but the logic can be upgraded.

So why would you want to break the assumption of logical immutability when dealing with a deployed smart contract? The answer is that it allows us to upgrade the contracts to improve the functionality. But the reality is that you can already do this fairly easily. You can just deploy an entirely new contract suite whenever you need to upgrade, then users can use a migration contract to convert their old tokens to the new tokens and take advantage of the new logic.

This is definitely a better solution from a security perspective, but it is simply not practical from a usability perspective for two reasons. The first is that if you are rapidly iterating on a system you may make 2-3 upgrades per year. This would require a token swap 2-3 times per year, which is a lot of friction for users. It also means you will likely have tokens spread across four or more generations of contracts for ever. The second reason it’s not practical is that it means external services must regularly update the addresses they use to contract to the contracts, which is even less practical.

The smart contract trilemma

With all this in mind, any smart contract project is presented with a trilemma: pick two from Usability, Security and Speed (of iteration). We chose usability and speed of iteration, but we believe this was the pragmatic choice for two reasons. The first is that you need to trust the team initially anyway. This argument is somewhat akin to the PoW miner collusion argument: yes, miners could collude and launch a 51% attack, but it would destroy their investment in mining equipment. Now, this is not a perfect argument, but in this case our view is that in the early stages it would be unprofitable for the team to collude to steal the tokens or destroy their value, especially (and I am just being frank here) when the treasury—which is the real value in the early stages—was already controlled by the team. But the more important point is that the trilemma can be flipped later! You can actually decide to replace Speed with Security once speed is less important. And this is a critical point. Speed of iteration is so much more valuable than security early on in a project’s life that without it you are seriously lowering the expected outcomes for the project, so increased trust is a false promise.

OK, so two questions follow from this argument, the first is when do you flip the trilemma and more importantly how?

Let’s start with when. Personally, I believe you do it when the community demands it. But you can also do it slowly rather than all at once. There are numerous bridges in the Synthetix system that can be cut to remove privileges from the engineering team. I think we are getting close to a point where we need to start doing this. To that end we will be releasing a roadmap for the second half of the year and it will lay out the aspects we intend to make immutable. There is obviously risk here, in that if something goes wrong we will be forced to use the token swap route, but that is not the worst fallback to have.

So now, finally, to the point. Apologies for so much context, but I think these kinds of decisions need to be viewed holistically rather than in isolation. For more context on my approach to decentralisation see this article.

Proposed Solution

The fundamental issue with the proxy design is that the owner of the proxy can replace the logic contract with arbitrary logic of their choosing. In practice the owner is usually a multisig or some other mechanism that requires multiple parties to sign the transaction. But in practice this not really going to prevent the system being captured. So I am proposing a solution that uses a signalling contract as the owner of the proxies. This contract will serve two purposes. The first is that it will send the transactions required to rewire the proxy contracts, thus replacing a multisig controlled by several individuals. The second and more important function is that it will allow anyone to send a transaction that specifies a proposal to rewire the contracts to new logic contracts. This is important as it means there is no privileged owner of the system. Anyone can propose a change and that change can then be voted on by token holders. Now some of you (Vlad Zamfir) are probably screaming “on-chain governance: burn it!” But we are essentially just replicating the previously discussed token migration method for upgrades. Not perfectly, of course, because if you vote against the change then the upgrade could still take place, but in the scenario where you elect to not migrate your token it will most likely become worthless, but if everyone chooses to not migrate then the upgrade will essentially not proceed until the issues are resolved.

Open Questions

There are some questions here of course around the actual implementation, specifically how will voting work. I have some initial thoughts but I think that some threshold must be set whereby if after a certain period some percentage of tokens have not signalled to change then the upgrade is rejected. Of course you could say “well, no one will vote!” But I am inclined to disagree because this is not the same thing as a vote to spend funds in a DAO where non-voting is unlikely to impact you. In this case, if you don’t vote then the upgrade doesn’t proceed, which will likely have an impact on the value of your tokens. But we also have the option of forcing users to vote before they can claim fees, given that more than 80% of the outstanding tokens claim every few weeks this would be extremely effective in ensuring voter turnout.

There are other solutions like quadratic voting, which while potentially subject to sybil attacks is less of a concern in the Synthetix system, due to the staking mechanics where users have to stake and they earn rewards that are escrowed but can be used to earn further rewards. This means that if you split your tokens into multiple wallets you will then have to manage those fragmented wallets, which has a high cost in both gas and effort.

In any case the specifics of the signalling mechanism are probably less important than the fact that with this architecture you end up with slightly reduced security (the contracts are not immutable) for slightly slower iteration speed (you need to build consensus for upgrades) and slightly more friction (you have to vote to signal your approval for a change). Given these adjusted trade-offs I think implementing a mechanism like this for Synthetix is worth considering and I will be writing up a SIP (Synthetix Improvement Proposal) for it shortly.

@john-light

This comment has been minimized.

Copy link

commented Sep 16, 2019

Hi @kaiynne you may be interested in looking at aragonOS as a tool to use for your decentralized proxy solution.

For example, Melon protocol has used this as a solution for decentralized control over upgrades to their smart contract system (described here).

Happy to answer any questions about how this could work for Synthetix!

@kaiynne

This comment has been minimized.

Copy link
Member Author

commented Sep 17, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.