Invariants & Error Handling Discussion

Welcome to the discussion thread about this lecture section. Here you can feel free to discuss the topic at hand and ask questions.

2 Likes

Would an assertion have prevented some of the big hacks like the DAO hack? There are many checks you can build into the code, but this code can quickly become larger than the code that actually does the work. It’s kinda like building unit tests into the production code.

For the DAO hack you could check that the contract balance was only reduced by the amount being withdrawn for the split, but this would require extra code to store the old balance and calculate approximately what the new balance should be (the gas cost makes this tricky). Is that too much work and why it may not have been done?

The underflow attack in the batchTransfer method of Beauty coin could have been reverted with something more simple like:

assert(balances[msg.sender] < oldBalance)

This would have caught that nothing was deducted from the sender (because the amount under flowed to zero).

I like the idea of invariants, but how often do they get used in practice and have they actually prevented a disaster?

2 Likes

In the code, do you know why the invariant is:
assert(address(this).balance >= totalSupply);

Why is it balance instead of balanceOf?:

assert(address(this).balanceOf >= totalSupply);

pragma solidity 0.5.12;

contract Token{

mapping(address => uint) public balanceOf;

uint public totalSupply;

function deposit() public payable{
    balanceOf[msg.sender] += msg.value;
    totalSupply += msg.value;
    assert(address(this).balance >= totalSupply);
    assert(balanceOf[msg.sender] >= msg.value);
}

}

1 Like

Hi @andersbraath1
address(this).balance is a global variable available in every contract, it shows the contract balance.
balanceOf is a mapping created by the user, address(this).balance is automatically created when initializing a smart contract

1 Like

balanceOf() is used to get the balance of a particular wallet, but, balance, and like expressed by the code address(this).balance is specifically referring to the contract address total balance. Two very different use cases.

would it make sense to create a modifier for checking common invariants?

    modifier checkTotalSupply() {
        _;
        assert(this.balance >= totalSupply); // is this checked after the function?
    }
    
    function deposit() public payable checkTotalSupply {
        balanceOf[msg.sender] += msg.value;
        totalSupply += msg.value;
    }

1 Like

I’m getting this error in the example.
Capturar

Solved!.. Forgot to add “pragma solidity ^…”;

1 Like

Hey guys,

I understand then that it is a good idea to set some invariants and use ‘assert’ to check. But despite I find logic when or how to use ‘require’ statements I wonder 'how can we decide when it is worth using an assert?. Like, should we try to use as many asserts as possible? I guess not, since they will cost some gas, or maybe I am wrong and they are far more valuable than some gas. Or is any kind of rule of thumb like using an invariant every function or a number of invariants per smart contract, etc.?

Thanks!