Echidna
WEB3DEV Team
# Echidna
Exemplos de fuzzing com Echidna (opens new window).
- Salve o contrato de solidity como
TestEchidna.sol
- Na pasta onde seu contrato está armazenado execute o seguinte comando.
docker run -it --rm -v $PWD:/code trailofbits/eth-security-toolbox
- Veja os comentários abaixo e execute
echidna-test
comandos.
// SPDX-License-Identifier: MIT
pragma 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ções
function test_assert(uint _i) external {
assert(_i < 10);
}
// Exemplo mais complexo
function 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 do timestamp é definido na configuração. O padrão é 7 dias.
callers de contrato também podem ser definidos na configuração. As contas padrão são
0x10000
0x20000
0x00a329C0648769a73afAC7F9381e08fb43DBEA70
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/*
docker run -it --rm -v $PWD:/code trailofbits/eth-security-toolbox
echidna-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 delay
Caso 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 falhar
address[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 contador
function 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;
}
}