Help Ukrainian Ukraine economy and refugees by hiring Ukrainian Software Developers - we donate a lot to charities and volunteer foundations

Ukraine

Ethereum Smart Contracts: Security Starts with Architecture

Ethereum Smart Contracts: Security Starts with Architecture
Table of Contents

    When you create Blockchain, you should decide on the right platform to deploy it. While Blockchains are secure just by their properties, there can be some known vulnerabilities that can exist in your architecture and they can be manipulated by hackers and wrong-doers.

    The architecture you use should have integrated security features that can do the following:

    • Prevent anyone from accessing root and sensitive information. This includes even administrators and root users.
    • Any illicit attempt to alter the data should be thwarted. Unauthorized access should be denied and reported immediately.
    • Encryption keys should be carefully guarded using high-security standards. This will ensure that the keys are not misappropriated.

    When your architecture has these features, your network will get the additional protection that’s needed to avert any attacks – whether they are from inside or outside.

     

    Test checklist

    If you want to test a Blockchain project, how do you do it? And what limitations are there in this technology? Before we delve into these topics, keep in mind that Blockchain isn’t limited to Bitcoin. While Bitcoin is also a Blockchain based technology, Blockchain in itself is a much broader term. Let’s go through the parameters for testing.

     

    Block size limit

    For Ethereum block size limit means the same as the gas size limit. The gas limit is a cap on both processing and storage because the cost of a transaction is fixed in units of gas for each type of instruction.

    The upper gas limit of a block is 1MB. When Bitcoin was introduced, for the first 18 months, the average block size was under 30KB. Towards the end of 2017, it was around 1MB. It’s not yet decided what will happen once the block size reaches over 1MB. Since there can be multiple transactions in a single block, the block size can increase.

     

    Load

    Since there are many people on the Blockchain, the load is an important parameter to be tested. If we take the example of Bitcoin, there are about 3-4 transactions per second. This works as of now, but what if the load increases? It’s important to maintain the performance of the Blockchain even under heavy loads.

     

    Performance

    Performance can go down with load. The performance of the Blockchain should be optimal and it shouldn’t take too long for a query to return with results. If a query has to go through every block in the chain, how long will it take? This should be considered while developing the project.

     

    Collision

    What happens when there is a collision? If there is a split in the chain, what action will be taken? The chain is peer reviewed and there should be a prompt action to decide which branch of the chain will continue and which will be discarded. There can be other instances when a collision occurs in the chain. The system should be able to handle exceptional conditions effectively.

     

    Security

    Security is a critical aspect of blockchain technology, especially in the context of Ethereum smart contracts. Given the decentralized nature of blockchain, where numerous miners are involved, ensuring robust security is challenging yet essential.
    A multi-layered security system is vital to protect against various cyber threats. However, beyond the blockchain layers, it's also crucial to consider other cybersecurity measures.
    For instance, using a Virtual Private Network (VPN) can add an extra layer of security, especially when accessing or managing smart contracts remotely. A VPN encrypts your internet connection, safeguarding your online activities against potential cyber threats."

     

    Investigation dependency Gas Usage from Amount of Investors/Website operators

    Gas refers to a fee paid for a transaction. Each transaction can’t be done for free and needs some capacity (hardware, power, maintenance etc.) in order to be executed.  

    This way we have a price for every transaction which is calculated in Gas if we talk about Ethereum operations.

    Since Ethereum itself has no correlation with ever-changing infrastructure prices, gas is a so-called equivalent for the transaction price which should be approved by the investors.

    Users can also set limits for the gas amount to be transferred to avoid users’ wallets going empty because of multiple transactions taking place simultaneously.

    In the test cases below we aimed at finding the maximum amount of investors and optimal gas price for a transaction to be performed.

     

    Gas usage for 10 investors:

    ethereum-smart-contracts-testing-gas-usage-10-investors

    Gas usage for 50 investors:

    ethereum smart contracts testing gas usage 50 investors

    Gas usage for 100 investors:

    ethereum smart contracts testing gas usage 100 investors

    Gas usage for 150 investors:

    ethereum smart contracts testing gas usage 150 investors

    Gas usage for 200 investors:

    ethereum smart contracts testing gas usage 200 investors

    Gas usage for 250 investors:

    ethereum smart contracts testing gas usage 250 investors

    Gas usage for 251 investors:

    ethereum smart contracts testing gas usage 251 investors

    The investigation process described above resulted in the fact, that the transaction limit is 250 investors per one transaction.

     

    The Security Checklist Criteria

    It’s a common mistake to rely on a single random testing tool and hope you’ve delivered a secure Ethereum-based solution. Instead, our expert development team has worked out a list of key parameters a truly secure smart contract should meet:

    1. Confusion of the different method calling possibilities: send(), transfer(), and call.value()
    2. Missing error handling of external calls
    3. Erroneous control flow assumptions after external calls
    4. The use of push over pull for external calls
    5. Lack of enforcement of invariants with assert() and require()
    6. Rounding errors in integer division
    7. Fallback functions with a higher gas limit than 2300
    8. Functions and state variables without explicit visibility
    9. Missing pragmas to for compiler version
    10. Race conditions, such as contract Reentrancy
    11. Transaction front running
    12. Timestamp dependence
    13. Integer overflow and underflow
    14. Code blocks that consume non-constant amount of gas, that grows over block gas limit.
    15. Denial of Service attacks
    16. Suspicious code or underhanded code.
    17. Error prone code constructs
    18. Race conditions

    Based on these criteria it is possible to evaluate some really useful tools you can use to test your smart contract. Let’s see which ones our team has decided to use.

     

    What Are the Tools?

    Reliable smart contacts can’t be written without proper testing. In order to make sure your code has no vulnerabilities and is reliable, you’ll need to use special tools that help detect all the issues. We’ve gone through a number of “bug-hunters” and now share the top of our shortlist:

    1. Smart Contracts code checker: for bad practices and vulnerabilities
    2. Mythril: a security analysis tool for Ethereum smart contract
    3. Visualize Solidity control flow for smart contract security analysis

    In this article, we share the test cases for Smart Contracts code checker and Mythril security analysis tool.

     

    Checking code by Mythril security tool

    Integer Overflow

    Type: Warning

    Contract: ERC20FreezableImplementation

    Function name: _function_transferBulk

    PC address: 1224

    A possible integer overflow exists in the function `_function_transferBulk`.

    The addition or multiplication may result in a value higher than the maximum representable integer.

    --------------------

    function transferBulk(address[] _toAccounts, uint256[] _tokenAmount) onlyOwner public {

       require(_toAccounts.length == _tokenAmount.length);

       for(uint i=0; i<_toAccounts.length; i++) {

         balances[msg.sender] = balances[msg.sender].sub(_tokenAmount[i]);

         balances[_toAccounts[i]] = balances[_toAccounts[i]].add(_tokenAmount[i]);

         if(!isInvestor[_toAccounts[i]]){

           isInvestor[_toAccounts[i]] = true;

           investors.push(_toAccounts[i]);

         }

       }

     }

    --------------------

    Resolution:This overflow can be, when _toAccounts.length will be > 2**256+1. It is not possible because of block gas limit. Also, we are using safeMath library, which prevents all of this.

     

    Exception state

    Type: Informational

    Contract: ERC20FreezableImplementation

    Function name: _function_0xd73dd623

    PC address: 9071

    A reachable exception has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.

    --------------------

    In file: contracts/SafeMath.sol:45

    assert(c >= a)

    --------------------

    Resolution:Just information that exception could be. It’s expected behavior.

     

    Exception state

    Type: Informational

    Contract: ERC20FreezableImplementation

    Function name: _function_0x9316c3e7

    PC address: 9040

    A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.

    --------------------

    In file: contracts/SafeMath.sol:36

    assert(b <= a)

    --------------------

    Resolution:Just information that exception could be. It’s expected behavior.

     

    Exception state

    Type: Informational

    Contract: ERC20FreezableImplementation

    Function name: _function_0xd880ae89

    PC address: 8396

    A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.

    --------------------

    investors[i]

    --------------------

     

    ==== Integer Overflow  ====

    Type: Warning

    Contract: ERC20FreezableImplementation

    Function name: _function_0xb2f5a54c

    PC address: 7009

    A possible integer overflow exists in the function `_function_0xb2f5a54c`.

    The addition or multiplication may result in a value higher than the maximum representable integer.

    --------------------

    return investors

    --------------------

     

    Checking code by SmartCheck security tool

    Reentrancy

    Any interaction from a contract (A) with another contract (B) and any transfer of Ether hands over control to that contract (B). This makes it possible for B to call back into A before this interaction is completed.

    This pattern is experimental and can report false issues. This pattern might also be triggered when

    • accessing the structure's field;
    • using enumeration element.

    To avoid reentrancy, you can use the Checks-Effects-Interactions pattern as outlined further below:

    pragma solidity ^0.4.11;
    contract Fund {
    /// Mapping of ether shares of the contract.
    mapping(address => uint) shares;
    /// Withdraw your share.
    function withdraw() {
    var share = shares[msg.sender];
    shares[msg.sender] = 0;
    msg.sender.transfer(share);
    }
    }

    Note that reentrancy is not only an effect of Ether transfer but of any function call on another contract. Furthermore, you also have to take multi-contract situations into account. A called contract could modify the state of another contract you depend on.

    Resolution:We avoid this problem with a line like:
    balances[_to] = balances[_to].add(_value);
    In the mining moment of the transaction, we are getting the actual value of the balance in the current moment.

     

    Compiler version not fixed

    Solidity source files indicate the versions of the compiler they can be compiled with.


    pragma solidity ^0.4.17; // bad: compiles w 0.4.17 and above
    pragma solidity 0.4.17; // good : compiles w 0.4.17 only
     

    Resolution: It is recommended to follow the latter example, as future compiler versions may handle certain language constructions in a way the developer did not foresee.

     

    No payable fallback function

    The contract does not have a payable fallback. All attempts to transfer or send ether to this contract will be reverted.

    Resolution:This is an expected behavior.

     

    Integration testing

    We can’t make full integration testing with ganache-cli because it's a bug with using call() method in web3js. It doesn’t allow call function which is not using gas with gas limit > then block size limit.

    Resolution: So we need to launch our personal node of the test network for full integration testing.

    Tests which should be made:

    1) Check TransferBack method (This method is used for manually solving possible transfer errors. It transfers tokens from a given address back to the token owner) with different scenarios:

    1. When address of transaction sender is not owner ⇒ should fail;
    2. When address parameter is "0x0000000000000000000000000000000000000000" ⇒ should fail;
    3. When value > account.balance ⇒ should fail;
    4. When owner calls these methods with real parameters and address balance >= value ⇒ should pass;

    2) Check TransferBulk (Transfer tokens from owner to array of addresses) method with different scenarios:

    1. When array of recipients and array of token amounts for recipients are different ⇒ should fail;
    2. When address of transaction sender is not owner ⇒ should fail;
    3. When owner calls these methods with real parameters ⇒ should pass;

    3) Check getInvestors method:

    1. Should return array with all investors;

    4) Check getInvestorsBalances method:

    1. Should return array with investor balances without any gas usage;

    5) Check freeze() method:

    1. When address of transaction sender is not owner ⇒ should fail;
    2. Should lock all transfer methods except transferBack and transferBulk, when user tries to call transfer method, should be revert;

    6) Check unFreeze() method:

    1. When address of transaction sender is not owner ⇒ should fail;
    2. Should unlock all transfer methods which was locked by freeze() method;

     

    ERC20 Testing Cases

    In order to extend your understanding of smart contracts testing, we’ve run a number of tests for ERC20. Yes, we know a number of tests had already been done and there’s no reason to run tests ourselves. But there’s only one team we can trust in terms of high-quality development and security, so we did them on our own. You can find a list of test cases we went through using the tools described above.

     


    ethereum smart contracts Test Case 1 balanceOf

    ethereum smart contracts Test Case 2 approve

    ethereum smart contracts Test Case 3 totalSupply

    ethereum smart contracts Test Case 4 transferethereum-smart-contracts-Test-Case-4-Transfer_2

    ethereum smart contracts exampleethereum smart contracts Test Case 5 TransferForm_2ethereum smart contracts Test Case 5 TransferForm_3

    ethereum smart contracts Test Case 6 Allowance

    ethereum smart contracts Test Case 7 transferOwnershipethereum smart contracts Test Case 7 transferOwnership_2

    ethereum smart contracts Test Case 8 increaseApprove-decreaseApprove

    ethereum smart contracts Test Case 9 burn

     

    Hooray! We congratulate you for having learned the basics of smart contracts testing. Now you’ll definitely be ahead of your competitors in terms of safety and reliability.

    Have some questions about smart contracts or got a mind-blowing project idea? Just drop us a line and speak with one of our Blockchain experts!

    image description

    Roman Korzh

    VP of Development

    image description

    Anna Slipets

    Business Development Manger

    Let's Talk