(Part 22) Ethereum Solidity - Fixing Multisig Exploit, Halting User Account(PT 22)

in #utopian-io6 years ago

Repository

https://github.com/igormuba/EthereumSolidityClasses/tree/master/class22

What Will I Learn?

  • Fix for 2 exploits on the multisig contract upgrader(changer)
  • Halting users accounts
  • Blockig halted users to send tokens/vote/submit changes

Requirements

  • Internet connection
  • Code editor
  • Browser

Difficulty

  • Advanced

Tutorial Contents

On the previous two tutorials, we have turned an ERC20 token contract into an ERC223 token contract. But what we have built was not a simple ERC223 contract, but a mutable ERC223 one, by mutable I mean that the code of the contract can be "changed" down the road. In case a new exploit or bug is discovered, it is a nice feature to be able to fix your contract, although, contracts are, by default immutable.

After implementing the mutability in our contract, I have explained about the "moral" discussion behind mutable contracts. Some people argue that it is not morally correct giving the power to the developer to change the contract in the future however he likes. While I abstain from this discussion on my tutorials, I have shown how can you give your contract such a feature while still pleasing, even if just a little, both sides of the discussion. The solution is implemented by requiring a consensus of the token holders before a change is approved.

So, the creator of the contract/app can deploy the original code, but we have designed it that he can only deploy it once, and any new change proposals require that the token holders vote on it, so any change would only be made if the holders agree with it.

But that generates a few issues and exploits. Today I am going to cover a little bit of an exploit that would allow the same tokens to vote multiple times. Again, as I have explained in other tutorials, but I need to say again to make it clear, we are working with lots of contracts that connect to each other in too many ways, and bugs are inherent to such a design.

While it would be impossible for me to cover every single "failure point" of the design, I can cover the biggest one. In a real contract using this design, you would need at least 1 week of bug hunting and exploit fixing for each day of coding, as each new contract and interaction adds another level of complexity, which makes the difficulty of pen testing exponentially harder.

Halting voter tokens

A simple solution I came out with, to avoid users from exploiting the system and voting multiple times with the same tokens by transferring it to a new account, is to halt the tokens of an account.

This is a similar feature from the ICO contract we have implemented in another class.

For this, I will introduce a mapping that stores the information of whether the users account is halted or not. We want to not allow him to vote and transfer tokens if his account is halted, so, on the ERC223Variables.sol contract we add the mapping:

mapping (address=>bool) private haltedAccount;//mapping of halted accounts

And the function to halt, "unhalt" and check the status of the account.

Halting an account:

    //halts an account
    function haltAccount(address _haltedAccount) public{
        require(msg.sender==changeManagerAddress);//requires caller change manager
        require(!haltedAccount[_haltedAccount]);//requires account is not yet halted
        haltedAccount[_haltedAccount]=true;//sets the account as halted
    }

In this case, we want the code to throw an error if, for whatever reason, the function tries to halt an already halted account, because obviously, if someone is trying to halt an already halted account probably he is trying to exploit something and do something he shouldn't be doing, that is why the "require(!haltedAccount[_haltedAccount])".

On the function to release an account from the halt we do something similar:

    //unhalts an account
    function unhaltAccount(address _haltedAccount) public{
        require(msg.sender==changeManagerAddress);//requires caller change manager
        require(haltedAccount[_haltedAccount]);//requires account is halted
        haltedAccount[_haltedAccount]=false;//frees the account from the halt
    }

But on the example above we confirm that the account is actually halted.

Now, we just need to implement the view function, that is gonna be used on a modifier, that will throw an error is a halted user tries to vote or transfer his coins.

    //checks halt status of an account
    function checkAccountHalt(address _haltedAccount) public view returns(bool){
        require(msg.sender==changeManagerAddress);//requires caller change manager
        return haltedAccount[_haltedAccount];//returns the status of the account
    }

Custom modifier to deny halted accounts

Now, on the change manager contract, we will add a custom modifier. This modifier will be used in functions on the change manager that requires an user to not have a halted account.
The modifier:

    //modifier to block halted accounts
    modifier notHalted {
        bool isHalted = variablesManager.checkAccountHalt(msg.sender); //loads the halt status
        require (!isHalted);//throws if account is halted
        _; //means this modifier is executed before the code of the function
    }

Now, still on the change manager, we will implement the logic that checks the halt status of the user before calling the functions to create proposals, and the logic that halts the accounts after a vote is casted. We won't, however, implement is this tutorial the logic to unhalt accounts to keep things simple. Unhalting accounts is not that simple because it is not possible to iterate through a mapping, so that would require a deeper refactor of the code to implement address tracking, and is, for now, out of the scope of the class.

On the function transferChangeProposal of the change manager, add the modifier we have just created notHalted, so it looks like:

function transferChangeProposal(address newTransferManager) public notHalted returns (uint thisProposalId){}

And on the else, in case the caller does not have enough balance to approve a change, add the logic to call the halt function from the variables manager:

        if(myVote>(variablesManager.totalSupply()/2)){
            proposals[_currentId].approved=true;
            coreManager.setTransferManager(newTransferManager, address(this));
            variablesManager.setTransferManager(newTransferManager);
        }else{
            proposals[_currentId].approved=false;
            variablesManager.haltAccount(msg.sender);//halts user account if not enough balance
        }

Same thing applies to the approveChange function.

Avoiding halted users to transfer tokens

The first exploit, above, was the one that allowed users vote more than once on the same account, and was more obvious. The second exploit I am covering on this section is more subtle. If a halted user transfers his tokens to another account, that would allow him to vote with the same tokens but from multiple accounts, maybe allowing him to approve the change of a malicious code.

To fix it, on the transfer manager contract we also need to implement some logic to prevent halted users from making transfer. It is not difficult, just need to implement the same custom modifier from the change manager and add it to the 4 transfer functions in that contract.

The only exception is the transferFrom function with data. This function already has a very complex logic and too many variables. Chances are that when you add that modifier there you will face an error saying "The stack is too deep".

The fix for the above issue is to simply implement the logic from the modifier to the first line of code without creating any new variables:

function transferFrom(address _caller, address _sender, address _receiver, uint _amount) public notHalted returns (bool){
        require(!variablesManager.checkAccountHalt(msg.sender));
//more code below hidden...
}

Testing

Let us now see if the user indeed can't vote more than once. I have deployed the contracts as always, so I won't go in too deep on the deployment process. I just want to say that you need to be aware of all connections between the contracts. The reason why we allow the creator to deploy and set the initial contracts and connect them without a consensus is that, without the proper initial configuration the code simply won't work.

Using a secondary account, not the owner, the user can submit a change proposal, as expected.

Captura de Tela 20190121 às 19.25.20.png

However, if the same person tries to approve anything else, or submit any other proposal, Ethereum will throw, as the users account is halted.

Captura de Tela 20190121 às 19.28.13.png

Curriculum

First tutorial

Latest tutorial

Classes related to this one

Beneficiaries

This post has as beneficiaries
@utopian.pay with 5%
using the SteemPeak beneficiary tool
image.png

Sort:  

Thank you for your contribution @igormuba.

  • Be careful of spaces in code sections.
    steemit.png

As already is the habit a good tutorial. Thank you for your work in developing these tutorials.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Chat with us on Discord.

[utopian-moderator]

Hi, thank you for your attention on reviewing my tutorial. I indeed forgot to remove the spaces, will be more careful.

Thank you for your work and patience

Thank you for your review, @portugalcoin! Keep up the good work!

Great post. I will be keeping an eye open for more.I enjoy your posts

Hi @igormuba!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Hey, @igormuba!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.17
TRX 0.13
JST 0.027
BTC 59046.50
ETH 2654.73
USDT 1.00
SBD 2.50