Nesse artigo iremos aprender a como realizar testes em um contrato inteligente com Echidna.
Echidna
Exemplos de fuzzing com Echidna.
- Salve o contrato de Solidity como por exemplo
TestEchidna.sol
- Na pasta onde seu contrato está armazenado, execute o seguinte comando:
docker run -it --rm -v $PWD:/code trailofbits/eth-security-toolbox
Dentro do docker, seu código será armazenado em /code
3. Veja os comentários abaixo e execute o comando echidna-test
.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;/*echidna-test TestEchidna.sol --contract TestCounter*/contract Counter {uint public count;function inc() external {count += 1;}function dec() external {count -= 1;}}contract TestCounter is Counter {function echidna_test_true() public view returns (bool) {return true;}function echidna_test_false() public view returns (bool) {return false;}function echidna_test_count() public view returns (bool) {// Aqui estamos testando que Counter.count deve sempre ser <= 5.// O teste falhará. Echidna é inteligente o suficiente para chamar Counter.inc() mais// do que 5 vezes.return count <= 5;}}/*echidna-test TestEchidna.sol --contract TestAssert --check-asserts*/contract TestAssert {// Asserts não detectadas em 0.8.// Muda para 0.7 para testar asserçõesfunction test_assert(uint _i) external {assert(_i < 10);}// Exemplo mais complexofunction abs(uint x, uint y) private pure returns (uint) {if (x >= y) {return x - y;}return y - x;}function test_abs(uint x, uint y) external {uint z = abs(x, y);if (x >= y) {assert(z <= x);} else {assert(z <= y);}}}
Tempo de teste e remetente
Echidna pode fuzz timestamp. O intervalo de carimbo de data/hora é definido na configuração. O padrão é 7 dias.
Chamadores de contrato também podem ser definidos na configuração.
As contas padrão são:
0x10000
0x20000
0x00a329C0648769a73afAC7F9381e08fb43DBEA70
// SPDX-License-Identifier: MITpragma solidity ^0.8;/*docker run -it --rm -v $PWD:/code trailofbits/eth-security-toolboxechidna-test EchidnaTestTimeAndCaller.sol --contract EchidnaTestTimeAndCaller*/contract EchidnaTestTimeAndCaller {bool private pass = true;uint private createdAt = block.timestamp;/*teste falhará se Echidna puder chamar setFail()teste vai passar caso contrário*/function echidna_test_pass() public view returns (bool) {return pass;}function setFail() external {/*Echidna pode chamar esta função se delay <= max block delayCaso contrário, o Echidna não poderá chamar esta função.O atraso máximo do bloco pode ser estendido especificando-o em um arquivo de configuração.*/uint delay = 7 days;require(block.timestamp >= createdAt + delay);pass = false;}// Remetentes padrão// Altere os endereços para ver o teste falharaddress[3] private senders = [address(0x10000),address(0x20000),address(0x00a329C0648769a73afAC7F9381e08fb43DBEA70)];address private sender = msg.sender;// Passe _sender como entrada e exija msg.sender == _sender// para ver _sender para exemplo de contadorfunction setSender(address _sender) external {require(_sender == msg.sender);sender = msg.sender;}// Verifique os remetentes padrão.// O remetente deve ser uma das 3 contas padrão.function echidna_test_sender() public view returns (bool) {for (uint i; i < 3; i++) {if (sender == senders[i]) {return true;}}return false;}}