Contract Security

Tutorial 1 of 4

Contract Security Tutorial

1. Introduction

In this tutorial, we aim to introduce you to the important topic of smart contract security. Smart contracts are self-executing contracts with the terms of the agreement directly written into code. They are increasingly common in blockchain technology. However, as with any software, they can have vulnerabilities.

By the end of this tutorial, you will have a basic understanding of common security vulnerabilities in smart contracts and how to address them. This tutorial assumes you already have a basic understanding of blockchain technology and some experience with programming.

2. Step-by-Step Guide

Concepts

1. Reentrancy

Reentrancy is a common vulnerability where an attacker repeatedly calls a contract before the first function call is finished. Each subsequent call can alter the state of the contract and lead to unexpected behavior.

2. Overflow and Underflow

These errors occur when a value exceeds the maximum or minimum limit that can be stored in a variable. An overflow error occurs when a value exceeds the maximum, while an underflow error occurs when a value goes below the minimum.

Best Practices

To avoid reentrancy, use the Checks-Effects-Interactions pattern. In this pattern, you first check the conditions, then alter the state of the contract, and finally, interact with other contracts.

To prevent overflow and underflow errors, use SafeMath library which provides functions for arithmetic operations that throw an error on overflow and underflow.

3. Code Examples

Example 1: Avoiding Reentrancy

contract Vulnerable {
    function withdraw(uint amount) public {
        if(msg.sender.call.value(amount)()) {
            balances[msg.sender] -= amount;
        }
    }
}

contract Secure {
    function withdraw(uint amount) public {
        require(amount <= balances[msg.sender]);
        balances[msg.sender] -= amount;
        msg.sender.call.value(amount)();
    }
}

In the Vulnerable contract, the balance is updated only after the funds have been sent. This could allow an attacker to repeatedly call the withdraw function before the balance is updated.

In the Secure contract, we first check that the amount is not greater than the balance (Checks), then we decrease the balance (Effects), and finally, we interact with the external contract (Interactions).

Example 2: Preventing Overflow and Underflow

contract Secure {
    using SafeMath for uint256;

    function add(uint256 a, uint256 b) public pure returns (uint256) {
        return a.add(b);
    }

    function subtract(uint256 a, uint256 b) public pure returns (uint256) {
        return a.sub(b);
    }
}

In this example, we use the SafeMath library to perform arithmetic operations. The add and sub methods will throw an error if an overflow or underflow occurs.

4. Summary

In this tutorial, we've learned about two common vulnerabilities in smart contracts: reentrancy and overflow/underflow errors. We've also seen how to use the Checks-Effects-Interactions pattern and the SafeMath library to prevent these vulnerabilities.

Next, you might want to learn about other smart contract vulnerabilities such as timestamp dependence and front-running. You can also study common security patterns such as rate limiting and commit-reveal.

5. Practice Exercises

  1. Write a function that transfers a specified amount of ether from one account to another. Make sure to prevent reentrancy.
  2. Write a function that multiplies two numbers. Use the SafeMath library to prevent overflow.

Solutions

  1. Here's an example solution using the Checks-Effects-Interactions pattern:
contract Secure {
    function transfer(address to, uint amount) public {
        require(amount <= balances[msg.sender]);
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}
  1. Here's an example solution using the SafeMath library:
contract Secure {
    using SafeMath for uint256;

    function multiply(uint256 a, uint256 b) public pure returns (uint256) {
        return a.mul(b);
    }
}

In the first exercise, we check that the amount to be transferred is not more than the balance, then we update the balances, and finally, we would interact with the to contract (if it were a contract and not just an address).

In the second exercise, we use the mul function from the SafeMath library to multiply two numbers and throw an error if an overflow occurs.