(Part 21) Ethereum Solidity - Allowing 2 Types Of New Contract And Introduction To Bug Hunting(PT 21)

in #utopian-io5 years ago

Repository

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

What Will I Learn?

  • How to implement the variable manager change on the change manager
  • Refactoring the code
  • "Rounding" the contract (improving functions)

Requirements

  • Internet connection
  • Code editor
  • Browser

Difficulty

  • Advanced

Tutorial Contents

Last tutorial was about creating another contract to manage the change of contracts. The creator of the contract and core, then, was allowed to set the contracts connected to it once and only once, and after the initial setup, required for the network to work before people reach consensus on the changes, only the change manager was allowed to change contracts, and it is designed to only change when the token holders find a consensus on what change they want to approve.

I also introduced you one of the many bugs of multisig wallets (our change manager works similarly to a multisig), which is, trying to not allow users to vote more than once. This tutorial still won't be about fixing the vulnerability that allows users and tokens to vote twice, I want to make a standalone tutorial covering that more in depth.

Adding the variable manager change to the change manager

First, obviously, we need to import the variable manager object to the change manager, outside the contract, so that we can give it the address and then be able to call his functions:

import "browser/ERC223Transfers.sol";//import transfers manager object

After that, now inside the contract, we declare the object variables and the boolean to track if the creator has already set the starting contract (he can do that only once to ensure that the contract works):

ERC223Transfers private transfersManager;//creates transfer manager object
bool private transfersManagerSet; //tracks if creator set initial transfer manager

The transfer manager on the change manager

We haven't set on the change manager the transfer manager on the last tutorial because we only require the managers object and address to be able to call their functions, now that we are implementing the variable manager we need to set the object of the transfer manager to call the function to change the variable manager on the transfer manager.
Yes, I realize that it is getting complex. When you are making mutable contracts that are interconnected, the changes you need to make grow exponentially, but I have made a neat mind-map so you can understand better.

image.png
(yes, software engineers use mental maps daily, there are even professional daily mapping tools, search about "UML")

Variable change proposal

Very similar to the last post, now we have to create a proposal object and store so other people can vote it:

//proposal to change the variable manager contract
function variableChangeProposal(address newVariableManager) public returns (uint thisProposalId){
     uint myVote = variablesManager.balanceOf(msg.sender); //weight of my vote
     changeProposal memory variableChange; //creates proposal structure object in memory       
     proposals[currentId]=variableChange; //saves the object above in the position of currentId on the mapping
     uint _currentId=currentId; //saves current ID for later use
     currentId++; //increases the currentId number for the next proposal
     proposals[_currentId].newAddress=newVariableManager; //stores the address of the proposed new variable manager
     proposals[_currentId].contractID=2;//id 2 means variable manager
     proposals[_currentId].votes[msg.sender]=myVote;//automatically ads my tokens to the voting
     if(myVote>(variablesManager.totalSupply()/2)){//if my voter is greater than half of the total tokens
        proposals[_currentId].approved=true;//set status as approved
        coreManager.setVariablesManager(newVariableManager);//tells the core what is the new contract
        transfersManager.setVariablesManager(newVariableManager);//tells the transfer manager the new variable manager
     }else{ //if not enough voting power
         proposals[_currentId].approved=false; //sets approval status as not yet approved
     }
      thisProposalId=_currentId; //returns the id of the proposal
    }

function variableChangeProposal(address newVariableManager) receives the address for the new variable manager contract.
returns (uint thisProposalId) returns the ID of the proposal.
uint myVote = variablesManager.balanceOf(msg.sender);//set my vote weight as the amount of tokens I have.

The following chunk of code is responsible for creating a proposal object and storing it on the mapping on the position of the current ID:

changeProposal memory variableChange; //creates proposal structure object in memory
proposals[currentId]=variableChange; //saves the object above in the position of currentId on the mapping

And this chunk of code is responsible for saving is a temporary variable what is the current ID and then increase it so the next user can use the new ID without repetition:

uint _currentId=currentId; //saves current ID for later use
currentId++; //increases the currentId number for the next proposal

The next ones are just to set the variables on the struct according to the given address, the contract ID (2 for variable contract) and my vote:

proposals[_currentId].newAddress=newVariableManager; //stores the address of the proposed new variable manager
proposals[_currentId].contractID=2;//id 2 means variable manager
proposals[_currentId].votes[msg.sender]=myVote;//automatically ads my tokens to the voting

And if my vote is bigger than half of the supply:

if(myVote>(variablesManager.totalSupply()/2))

The proposal is considered automatically accepted and calls the changers from the other contracts to update them on what is the new variable manager:

proposals[_currentId].approved=true;//set status as approved
coreManager.setVariablesManager(newVariableManager);//tells the core what is the new contract
transfersManager.setVariablesManager(newVariableManager);//tells the transfer manager the new variable manager

But if my tokens are not enough to consider it a consensus the status is set as "not yet approved":

}else{ //if not enough voting power
    proposals[_currentId].approved=false; //sets approval status as not yet approved
}

Refactoring the approve proposal

The approve was done, previously, to only work with the transaction proposals, now we will refactor it. It is not complex like the previous categories. I am highlighting the changes with comments:

function approveChange(uint proposalId) public{
     proposals[proposalId].votes[msg.sender]=variablesManager.balanceOf(msg.sender);
     proposals[proposalId].total+=variablesManager.balanceOf(msg.sender);
     if(proposals[proposalId].total>(variablesManager.totalSupply()/2)){
        proposals[proposalId].approved=true;
        if(proposals[proposalId].contractID==1){//if type is transfer (id=1)
            coreManager.setTransferManager(proposals[proposalId].newAddress); //tells core the new transfer contract
            variablesManager.setTransferManager(proposals[proposalId].newAddress); //tells variable manager new transfer contract
         } else if (proposals[proposalId].contractID==2){//if type is varaible (id=2)
             coreManager.setVariablesManager(proposals[proposalId].newAddress);//tells core the new variable manager
             transfersManager.setVariablesManager(proposals[proposalId].newAddress);//tells transfer the new variable manager
         }
    }
}

As you can see the changes are only that I made an if/else if statement to see if the proposal is for a new transaction manager or a variable manger.

Allowing change manager to modify other contracts

The last step is to allow, on the core and on the transfer manager, the change manager to change their code.

On both, the core and the transfer manager, the code will be similar, so I will explain just one, but the new code should be added to both.

Ensure you have the change and variable manager and the respective boolean for them to block the creator for changing them more than one:

ERC223Variables private variablesManager;
bool private variablesManagerSet;
address private changeManagerAddress;
bool private chageManagerSet; 

Ensure you have the functions that set the variable manager:

//change variable manager
function setVariablesManager(address _variablesManager) public{
    require((msg.sender==_creator&&(!variablesManagerSet))||msg.sender==changeManagerAddress);
    variablesManagerSet=true;
    variablesManager=ERC223Variables(_variablesManager);
}

msg.sender==_creator&&(!variablesManagerSet)) the creator can set it but only once
msg.sender==changeManagerAddress or the change manager can set it

The function to set the change manager address (to allow it to change the contract)

function setChangeManager(address _changeManager) public{
    require((msg.sender==_creator&&(!chageManagerSet)));
    chageManagerSet=true;
    changeManagerAddress=_changeManager;
}

Small details

At this point, the contract is "ready", by that I mean, it works. I will finish now by showing one of the points we can "round up" to avoid bugs. It is impossible for me to fix every bug that this contract may have. On a real-world project you would spend 1 day coding and 1 week bug hunting because the complexity grows exponentially, each new contract is another window open for bugs to enter (great analogy), this is why I had to change the difficulty of the last tutorials to advanced, a mutable decentralized contract is not something a beginner would be able to implement, even someone that has an intermediary level of knowledge of Ethereum would also maybe leave lots of rooms for bugs and exploits. Really, to cover all bugs I would need many more tutorials, but I I gave you the tools you need to found them.

On the next tutorial I will cover the main exploits, that allow users to vote twice and allows the same tokens to vote twice on different accounts, but as said before, that by itself deserves a standalone tutorial. So, here comes the bonus, use it as a guide so you know where to start bug hunting.

Giving the change manager address to new contracts

One of the corners we can round is that, when someone submits a proposal, we expect that he gives the change manager address to his contract, but that would require the users to verify not just the code, but the history of transactions to check if he indeed set the real changer address.

The fix I found is simple, whenever we call the changer on the core, the core sets the new contract changer address.

See below on the setter of the transfer manager on the core contract:

function setTransferManager(address _transferManager, address _changeManager) public{
    require((msg.sender==_creator&&(!transferManagerSet))||msg.sender==changeManagerAddress);
    transferManagerSet=true;
    transferManager = ERC223Transfers(_transferManager);
    transferManager.setChangeManager(_changeManager); //sets the change manager of the new created contract right away
}

So, this also adds another layer of security:
The creator of the new contract MUST implement this change and allow the core to call the function to set the changer as the contract we already know.

Curriculum

First tutorial

Latest tutorial

Beneficiaries

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

Sort:  

Thank you for your contribution @igormuba.

Tutorial very well explained and with quite interesting content.

Thank you very much for following my suggestions. I'm happy to see you getting better at developing your 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]

Well, I think I should thank you, and every other reviewer, for your patience and effort on helping me improve.

Posted using Partiko Android

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

Congratulations @igormuba! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You received more than 8000 upvotes. Your next target is to reach 9000 upvotes.

Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word STOP

Support SteemitBoard's project! Vote for its witness and get one more award!

Que conteúdo hein! Um prato cheio para os programadores!

Posted using Steeve, an AI-powered Steem interface

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.32
TRX 0.11
JST 0.034
BTC 66269.58
ETH 3204.67
USDT 1.00
SBD 4.24