Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
src = "contracts"
test = "test-foundry"
solc_version = "0.8.17"

[invariant]
runs = 256
depth = 64
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "forge-std/Vm.sol";
import "forge-std/console.sol";

import "@contracts/HeapOrdering.sol";
import "./ICommonHeapOrdering.sol";
import "./helpers/IConcreteHeapOrdering.sol";

abstract contract CommonHeapOrdering is Test {
ICommonHeapOrdering internal heap;

Vm public hevm = Vm(HEVM_ADDRESS);
abstract contract TestCommonHeapOrdering is Test {
IConcreteHeapOrdering internal heap;

address[] public accounts;
uint256 public NB_ACCOUNTS = 50;
Expand Down Expand Up @@ -65,7 +62,7 @@ abstract contract CommonHeapOrdering is Test {
}

function testShouldNotInsertZeroAddress() public {
hevm.expectRevert(abi.encodeWithSignature("AddressIsZero()"));
vm.expectRevert(abi.encodeWithSignature("AddressIsZero()"));
update(address(0), 0, 10);
}

Expand Down Expand Up @@ -340,12 +337,12 @@ abstract contract CommonHeapOrdering is Test {
}

function testOverflowNewValue() public {
hevm.expectRevert("SafeCast: value doesn't fit in 96 bits");
vm.expectRevert("SafeCast: value doesn't fit in 96 bits");
update(accounts[0], 0, uint256(type(uint128).max));
}

function testOverflowFormerValue() public {
hevm.expectRevert("SafeCast: value doesn't fit in 96 bits");
vm.expectRevert("SafeCast: value doesn't fit in 96 bits");
update(accounts[0], uint256(type(uint128).max), 0);
}
}
12 changes: 5 additions & 7 deletions test-foundry/TestDoubleLinkedList.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import "@contracts/DoubleLinkedList.sol";
contract TestDoubleLinkedList is Test {
using DoubleLinkedList for DoubleLinkedList.List;

Vm public hevm = Vm(HEVM_ADDRESS);

uint256 public NDS = 50;
address[] public accounts;
address public ADDR_ZERO = address(0);
Expand All @@ -34,23 +32,23 @@ contract TestDoubleLinkedList is Test {
}

function testShouldNotInsertAccountWithZeroValue() public {
hevm.expectRevert(abi.encodeWithSignature("ValueIsZero()"));
vm.expectRevert(abi.encodeWithSignature("ValueIsZero()"));
list.insertSorted(accounts[0], 0, NDS);
}

function testShouldNotInsertZeroAddress() public {
hevm.expectRevert(abi.encodeWithSignature("AddressIsZero()"));
vm.expectRevert(abi.encodeWithSignature("AddressIsZero()"));
list.insertSorted(address(0), 10, NDS);
}

function testShouldNotRemoveAccountThatDoesNotExist() public {
hevm.expectRevert(abi.encodeWithSignature("AccountDoesNotExist()"));
vm.expectRevert(abi.encodeWithSignature("AccountDoesNotExist()"));
list.remove(accounts[0]);
}

function testShouldInsertSeveralTimesTheSameAccount() public {
list.insertSorted(accounts[0], 1, NDS);
hevm.expectRevert(abi.encodeWithSignature("AccountAlreadyInserted()"));
vm.expectRevert(abi.encodeWithSignature("AccountAlreadyInserted()"));
list.insertSorted(accounts[0], 2, NDS);
}

Expand Down Expand Up @@ -217,7 +215,7 @@ contract TestDoubleLinkedList is Test {
}

// Add last 10 accounts at the same value.
for (uint256 i = NDS - 10; i < NDS; i++) {
for (uint256 i = accounts.length - 10; i < NDS; i++) {
list.insertSorted(accounts[i], 10, newNDS);
}

Expand Down
11 changes: 4 additions & 7 deletions test-foundry/TestHeap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "forge-std/Vm.sol";

import "@contracts/Heap.sol";

contract TestHeap is Test {
using BasicHeap for BasicHeap.Heap;

Vm public hevm = Vm(HEVM_ADDRESS);

uint256 public TESTED_SIZE = 50;
address[] public accounts;
address public ADDR_ZERO = address(0);
Expand All @@ -36,25 +33,25 @@ contract TestHeap is Test {
}

function testShouldNotInsertAccountWithZeroValue() public {
hevm.expectRevert(abi.encodeWithSignature("WrongValue()"));
vm.expectRevert(abi.encodeWithSignature("WrongValue()"));
heap.insert(accounts[0], 0);

assertEq(heap.getSize(), 0);
}

function testShouldNotInsertZeroAddress() public {
hevm.expectRevert(abi.encodeWithSignature("AddressIsZero()"));
vm.expectRevert(abi.encodeWithSignature("AddressIsZero()"));
heap.insert(address(0), 10);
}

function testShouldInsertSeveralTimesTheSameAccount() public {
heap.insert(accounts[0], 1);
hevm.expectRevert(abi.encodeWithSignature("AccountAlreadyInserted()"));
vm.expectRevert(abi.encodeWithSignature("AccountAlreadyInserted()"));
heap.insert(accounts[0], 2);
}

function testShouldNotRemoveAccountThatDoesNotExist() public {
hevm.expectRevert(abi.encodeWithSignature("AccountDoesNotExist()"));
vm.expectRevert(abi.encodeWithSignature("AccountDoesNotExist()"));
heap.remove(accounts[0]);
}

Expand Down
4 changes: 2 additions & 2 deletions test-foundry/TestHeapOrdering.t.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "./helpers/CommonHeapOrdering.sol";
import "./TestCommonHeapOrdering.t.sol";
import "./helpers/ConcreteHeapOrdering.sol";

contract TestHeapOrdering is CommonHeapOrdering {
contract TestHeapOrdering is TestCommonHeapOrdering {
constructor() {
heap = new ConcreteHeapOrdering();
}
Expand Down
71 changes: 71 additions & 0 deletions test-foundry/TestHeapOrderingInvariant.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "forge-std/console.sol";

import "./helpers/ConcreteHeapOrdering.sol";

contract Heap is ConcreteHeapOrdering {
using HeapOrdering for HeapOrdering.HeapArray;

uint256 public MAX_SORTED_USERS = 16;

/// Functions to fuzz ///

function updateCorrect(address _id, uint96 _newValue) public {
uint256 oldValue = heap.getValueOf(_id);
if (oldValue != 0 || _newValue != 0)
heap.update(_id, heap.getValueOf(_id), _newValue, MAX_SORTED_USERS);
}
}

contract TestHeapInvariant is Test {
Heap public heap;

function setUp() public {
heap = new Heap();
}

struct FuzzSelector {
address addr;
bytes4[] selectors;
}

// Target specific selectors for invariant testing
function targetSelectors() public view returns (FuzzSelector[] memory) {
FuzzSelector[] memory targets = new FuzzSelector[](1);
bytes4[] memory selectors = new bytes4[](1);
selectors[0] = Heap.updateCorrect.selector;
targets[0] = FuzzSelector(address(heap), selectors);
return targets;
}

// Rule:
// For all i in [[0, size]],
// value[i] >= value[2i + 1] and value[i] >= value[2i + 2]
function invariantHeap() public {
uint256 length = heap.length();

for (uint256 i; i < length; ++i) {
assertTrue((i * 2 + 1 >= length || i * 2 + 1 >= heap.size() || heap.accountsValue(i) >= heap.accountsValue(i * 2 + 1))); // prettier-ignore
assertTrue((i * 2 + 2 >= length || i * 2 + 2 >= heap.size() || heap.accountsValue(i) >= heap.accountsValue(i * 2 + 2))); // prettier-ignore
}
}

// Rule:
// For all i in [[0, length]], indexOf(account.id[i]) == i
function invariantIndexOf() public {
uint256 length = heap.length();

for (uint256 i; i < length; ++i) {
assertTrue(heap.indexOf(heap.accountsId(i)) == i);
}
}

// Rule:
// size <= 2 * MAX_SORTED_USERS
function invariantSize() public {
assertTrue(heap.size() <= 2 * heap.MAX_SORTED_USERS());
}
}
33 changes: 0 additions & 33 deletions test-foundry/TestRandomHeapOrdering.t.sol

This file was deleted.

33 changes: 0 additions & 33 deletions test-foundry/TestRandomThreeHeapOrdering.t.sol

This file was deleted.

4 changes: 2 additions & 2 deletions test-foundry/TestThreeHeapOrdering.t.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "./helpers/CommonHeapOrdering.sol";
import "./TestCommonHeapOrdering.t.sol";
import "./helpers/ConcreteThreeHeapOrdering.sol";

contract TestThreeHeapOrdering is CommonHeapOrdering {
contract TestThreeHeapOrdering is TestCommonHeapOrdering {
constructor() {
heap = new ConcreteThreeHeapOrdering();
}
Expand Down
72 changes: 72 additions & 0 deletions test-foundry/TestThreeHeapOrderingInvariant.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "forge-std/console.sol";

import "./helpers/ConcreteThreeHeapOrdering.sol";

contract Heap is ConcreteThreeHeapOrdering {
using ThreeHeapOrdering for ThreeHeapOrdering.HeapArray;

uint256 public MAX_SORTED_USERS = 16;

/// Functions to fuzz ///

function updateCorrect(address _id, uint96 _newValue) public {
uint256 oldValue = heap.getValueOf(_id);
if (oldValue != 0 || _newValue != 0)
heap.update(_id, heap.getValueOf(_id), _newValue, MAX_SORTED_USERS);
}
}

contract TestHeapInvariant is Test {
Heap public heap;

function setUp() public {
heap = new Heap();
}

struct FuzzSelector {
address addr;
bytes4[] selectors;
}

// Target specific selectors for invariant testing
function targetSelectors() public view returns (FuzzSelector[] memory) {
FuzzSelector[] memory targets = new FuzzSelector[](1);
bytes4[] memory selectors = new bytes4[](1);
selectors[0] = Heap.updateCorrect.selector;
targets[0] = FuzzSelector(address(heap), selectors);
return targets;
}

// Rule:
// For all i in [[0, size]],
// value[i] >= value[3i + 1] and value[i] >= value[3i + 2] and value[i] >= value[3i + 3]
function invariantHeap() public {
uint256 length = heap.length();

for (uint256 i; i < length; ++i) {
assertTrue((i * 3 + 1 >= length || i * 3 + 1 >= heap.size() || heap.accountsValue(i) >= heap.accountsValue(i * 3 + 1))); // prettier-ignore
assertTrue((i * 3 + 2 >= length || i * 3 + 2 >= heap.size() || heap.accountsValue(i) >= heap.accountsValue(i * 3 + 2))); // prettier-ignore
assertTrue((i * 3 + 3 >= length || i * 3 + 3 >= heap.size() || heap.accountsValue(i) >= heap.accountsValue(i * 3 + 3))); // prettier-ignore
}
}

// Rule:
// For all i in [[0, length]], indexOf(account.id[i]) == i
function invariantIndexOf() public {
uint256 length = heap.length();

for (uint256 i; i < length; ++i) {
assertTrue(heap.indexOf(heap.accountsId(i)) == i);
}
}

// Rule:
// size <= 3 * MAX_SORTED_USERS
function invariantSize() public {
assertTrue(heap.size() <= 3 * heap.MAX_SORTED_USERS());
}
}
Loading