with revert checks

pull/348/head
agroce 5 years ago
parent 0f135d18c1
commit 2f613c0e96
  1. 3
      examples/solidity/addressarrayutils/AddressArrayUtils.sol
  2. 348
      examples/solidity/addressarrayutils/AddressArrayUtils_withHasDuplicateBug.sol
  3. 54
      examples/solidity/addressarrayutils/JustHasDuplicate.sol
  4. 2
      examples/solidity/addressarrayutils/TestAddressArrayUtils.sol
  5. 590
      examples/solidity/addressarrayutils/TestAddressArrayUtilsRevert.sol

@ -1,4 +1,5 @@
pragma solidity ^0.4.25;
pragma solidity 0.4.24;
library AddressArrayUtils {

@ -0,0 +1,348 @@
pragma solidity 0.4.24;
library AddressArrayUtils {
/**
* Finds the index of the first occurrence of the given element.
* @param A The input array to search
* @param a The value to find
* @return Returns (index and isIn) for the first occurrence starting from index 0
*/
function indexOf(address[] memory A, address a) internal pure returns (uint256, bool) {
uint256 length = A.length;
for (uint256 i = 0; i < length; i++) {
if (A[i] == a) {
return (i, true);
}
}
return (0, false);
}
/**
* Returns true if the value is present in the list. Uses indexOf internally.
* @param A The input array to search
* @param a The value to find
* @return Returns isIn for the first occurrence starting from index 0
*/
function contains(address[] memory A, address a) internal pure returns (bool) {
(, bool isIn) = indexOf(A, a);
return isIn;
}
/// @return Returns index and isIn for the first occurrence starting from
/// end
function indexOfFromEnd(address[] A, address a) internal pure returns (uint256, bool) {
uint256 length = A.length;
for (uint256 i = length; i > 0; i--) {
if (A[i - 1] == a) {
return (i, true);
}
}
return (0, false);
}
/**
* Returns the combination of the two arrays
* @param A The first array
* @param B The second array
* @return Returns A extended by B
*/
function extend(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
uint256 aLength = A.length;
uint256 bLength = B.length;
address[] memory newAddresses = new address[](aLength + bLength);
for (uint256 i = 0; i < aLength; i++) {
newAddresses[i] = A[i];
}
for (i = 0; i < bLength; i++) {
newAddresses[aLength + i] = B[i];
}
return newAddresses;
}
/**
* Returns the array with a appended to A.
* @param A The first array
* @param a The value to append
* @return Returns A appended by a
*/
function append(address[] memory A, address a) internal pure returns (address[] memory) {
address[] memory newAddresses = new address[](A.length + 1);
for (uint256 i = 0; i < A.length; i++) {
newAddresses[i] = A[i];
}
newAddresses[A.length] = a;
return newAddresses;
}
/**
* Returns the combination of two storage arrays.
* @param A The first array
* @param B The second array
* @return Returns A appended by a
*/
function sExtend(address[] storage A, address[] storage B) internal {
uint256 length = B.length;
for (uint256 i = 0; i < length; i++) {
A.push(B[i]);
}
}
/**
* Returns the intersection of two arrays. Arrays are treated as collections, so duplicates are kept.
* @param A The first array
* @param B The second array
* @return The intersection of the two arrays
*/
function intersect(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
uint256 length = A.length;
bool[] memory includeMap = new bool[](length);
uint256 newLength = 0;
for (uint256 i = 0; i < length; i++) {
if (contains(B, A[i])) {
includeMap[i] = true;
newLength++;
}
}
address[] memory newAddresses = new address[](newLength);
uint256 j = 0;
for (i = 0; i < length; i++) {
if (includeMap[i]) {
newAddresses[j] = A[i];
j++;
}
}
return newAddresses;
}
/**
* Returns the union of the two arrays. Order is not guaranteed.
* @param A The first array
* @param B The second array
* @return The union of the two arrays
*/
function union(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
address[] memory leftDifference = difference(A, B);
address[] memory rightDifference = difference(B, A);
address[] memory intersection = intersect(A, B);
return extend(leftDifference, extend(intersection, rightDifference));
}
/**
* Alternate implementation
* Assumes there are no duplicates
*/
function unionB(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
bool[] memory includeMap = new bool[](A.length + B.length);
uint256 i = 0;
uint256 count = 0;
for (i = 0; i < A.length; i++) {
includeMap[i] = true;
count++;
}
for (i = 0; i < B.length; i++) {
if (!contains(A, B[i])) {
includeMap[A.length + i] = true;
count++;
}
}
address[] memory newAddresses = new address[](count);
uint256 j = 0;
for (i = 0; i < A.length; i++) {
if (includeMap[i]) {
newAddresses[j] = A[i];
j++;
}
}
for (i = 0; i < B.length; i++) {
if (includeMap[A.length + i]) {
newAddresses[j] = B[i];
j++;
}
}
return newAddresses;
}
/**
* Computes the difference of two arrays. Assumes there are no duplicates.
* @param A The first array
* @param B The second array
* @return The difference of the two arrays
*/
function difference(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
uint256 length = A.length;
bool[] memory includeMap = new bool[](length);
uint256 count = 0;
// First count the new length because can't push for in-memory arrays
for (uint256 i = 0; i < length; i++) {
address e = A[i];
if (!contains(B, e)) {
includeMap[i] = true;
count++;
}
}
address[] memory newAddresses = new address[](count);
uint256 j = 0;
for (i = 0; i < length; i++) {
if (includeMap[i]) {
newAddresses[j] = A[i];
j++;
}
}
return newAddresses;
}
/**
* @dev Reverses storage array in place
*/
function sReverse(address[] storage A) internal {
address t;
uint256 length = A.length;
for (uint256 i = 0; i < length / 2; i++) {
t = A[i];
A[i] = A[A.length - i - 1];
A[A.length - i - 1] = t;
}
}
/**
* Removes specified index from array
* Resulting ordering is not guaranteed
* @return Returns the new array and the removed entry
*/
function pop(address[] memory A, uint256 index)
internal
pure
returns (address[] memory, address)
{
uint256 length = A.length;
address[] memory newAddresses = new address[](length - 1);
for (uint256 i = 0; i < index; i++) {
newAddresses[i] = A[i];
}
for (i = index + 1; i < length; i++) {
newAddresses[i - 1] = A[i];
}
return (newAddresses, A[index]);
}
/**
* @return Returns the new array
*/
function remove(address[] memory A, address a)
internal
pure
returns (address[] memory)
{
(uint256 index, bool isIn) = indexOf(A, a);
if (!isIn) {
revert();
} else {
(address[] memory _A,) = pop(A, index);
return _A;
}
}
function sPop(address[] storage A, uint256 index) internal returns (address) {
uint256 length = A.length;
if (index >= length) {
revert("Error: index out of bounds");
}
address entry = A[index];
for (uint256 i = index; i < length - 1; i++) {
A[i] = A[i + 1];
}
A.length--;
return entry;
}
/**
* Deletes address at index and fills the spot with the last address.
* Order is not preserved.
* @return Returns the removed entry
*/
function sPopCheap(address[] storage A, uint256 index) internal returns (address) {
uint256 length = A.length;
if (index >= length) {
revert("Error: index out of bounds");
}
address entry = A[index];
if (index != length - 1) {
A[index] = A[length - 1];
delete A[length - 1];
}
A.length--;
return entry;
}
/**
* Deletes address at index. Works by swapping it with the last address, then deleting.
* Order is not preserved
* @param A Storage array to remove from
*/
function sRemoveCheap(address[] storage A, address a) internal {
(uint256 index, bool isIn) = indexOf(A, a);
if (!isIn) {
revert("Error: entry not found");
} else {
sPopCheap(A, index);
return;
}
}
/**
* Returns whether or not there's a duplicate. Runs in O(n^2).
* @param A Array to search
* @return Returns true if duplicate, false otherwise
*/
function hasDuplicate(address[] memory A) internal pure returns (bool) {
//if (A.length == 0) {
// return false;
//}
for (uint256 i = 0; i < A.length - 1; i++) {
for (uint256 j = i + 1; j < A.length; j++) {
if (A[i] == A[j]) {
return true;
}
}
}
return false;
}
/**
* Returns whether the two arrays are equal.
* @param A The first array
* @param B The second array
* @return True is the arrays are equal, false if not.
*/
function isEqual(address[] memory A, address[] memory B) internal pure returns (bool) {
if (A.length != B.length) {
return false;
}
for (uint256 i = 0; i < A.length; i++) {
if (A[i] != B[i]) {
return false;
}
}
return true;
}
/**
* Returns the elements indexed at indexArray.
* @param A The array to index
* @param indexArray The array to use to index
* @return Returns array containing elements indexed at indexArray
*/
function argGet(address[] memory A, uint256[] memory indexArray)
internal
pure
returns (address[] memory)
{
address[] memory array = new address[](indexArray.length);
for (uint256 i = 0; i < indexArray.length; i++) {
array[i] = A[indexArray[i]];
}
return array;
}
}

@ -0,0 +1,54 @@
pragma solidity 0.4.24;
import "AddressArrayUtils_withHasDuplicateBug.sol";
contract TEST {
address [] addrs1;
address [] addrs2;
address a;
bool everSet = false;
function push_it_1() public {
if (everSet) {
addrs1.push(a);
}
}
function push_it_2() public {
if (everSet) {
addrs2.push(a);
}
}
function push_it_both() public {
if (everSet) {
addrs1.push(a);
addrs2.push(1);
}
}
function set_addr(address newa) public {
everSet = true;
a = newa;
}
function echidna_hasDuplicate() public view returns (bool) {
if (!everSet) {
return true;
}
bool hasDup = false;
uint i1;
uint i2;
bool b;
for (uint i = 0; i < addrs1.length; i++) {
(i1, b) = AddressArrayUtils.indexOf(addrs1, addrs1[i]);
(i2, b) = AddressArrayUtils.indexOfFromEnd(addrs1, addrs1[i]);
if (i1 != (i2-1)) {
hasDup = true;
}
}
return hasDup == AddressArrayUtils.hasDuplicate(addrs1);
}
}

@ -1,4 +1,4 @@
pragma solidity ^0.4.25;
pragma solidity 0.4.24;
import "AddressArrayUtils.sol";

@ -0,0 +1,590 @@
pragma solidity 0.4.24;
import "AddressArrayUtils.sol";
contract TEST {
address [] addrs1;
address [] addrs2;
address a;
bool everSet = false;
function push_it_1() public {
if (everSet) {
addrs1.push(a);
}
}
function push_it_2() public {
if (everSet) {
addrs2.push(a);
}
}
function push_it_both() public {
if (everSet) {
addrs1.push(a);
addrs2.push(1);
}
}
function set_addr(address newa) public {
everSet = true;
a = newa;
}
function echidna_find() public view returns (bool) {
if (!everSet) {
return true;
}
uint256 i;
uint256 j;
bool b;
(i, b) = AddressArrayUtils.indexOf(addrs1, a);
if (b) {
if (addrs1[i] != a) {
return false;
}
for (j = 0; j < i; j++) {
if (addrs1[j] == a) {
return false;
}
}
if (!AddressArrayUtils.contains(addrs1, a)) {
return false;
}
(i, b) = AddressArrayUtils.indexOfFromEnd(addrs1, a);
if (!b) {
return false;
}
if (addrs1[i-1] != a) {
return false;
}
for (j = (addrs1.length-1); j >= i; j--) {
if (addrs1[j] == a) {
return false;
}
}
} else {
for (j = 0; j < addrs1.length; j++) {
if (addrs1[j] == a) {
return false;
}
}
if (AddressArrayUtils.contains(addrs1, a)) {
return false;
}
(i, b) = AddressArrayUtils.indexOfFromEnd(addrs1, a);
if (b) {
return false;
}
}
return true;
}
function echidna_append() public view returns (bool) {
if (!everSet) {
return true;
}
address [] memory addrs1PlusA = AddressArrayUtils.append(addrs1, a);
uint256 i;
uint256 j;
bool b1;
bool b2;
if (AddressArrayUtils.isEqual(addrs1, addrs1PlusA)) {
return false;
}
(i, b1) = AddressArrayUtils.indexOfFromEnd(addrs1PlusA, a);
if (!b1) {
return false;
}
if (i != (addrs1PlusA.length)) {
return false;
}
if (addrs1PlusA[i-1] != a) {
return false;
}
(j, b2) = AddressArrayUtils.indexOf(addrs1PlusA, a);
if (!b2) {
return false;
}
if (addrs1PlusA[j] != a) {
return false;
}
if (AddressArrayUtils.contains(addrs1, a)) {
if (j >= (i-1)) {
return false;
}
if (!AddressArrayUtils.hasDuplicate(addrs1PlusA)) {
return false;
}
} else {
if (j != (i-1)) {
return false;
}
}
return true;
}
function echidna_extend() public view returns (bool) {
if (!everSet) {
return true;
}
address [] memory addrs1PlusAddrs2 = AddressArrayUtils.extend(addrs1, addrs2);
uint256 i;
for (i = 0; i < addrs1.length; i++) {
if (!AddressArrayUtils.contains(addrs1PlusAddrs2, addrs1[i])) {
return false;
}
}
for (i = 0; i < addrs2.length; i++) {
if (!AddressArrayUtils.contains(addrs1PlusAddrs2, addrs2[i])) {
return false;
}
}
if (!AddressArrayUtils.contains(addrs1, a) && !AddressArrayUtils.contains(addrs2, a)) {
if (AddressArrayUtils.contains(addrs1PlusAddrs2, a)) {
return false;
}
}
return true;
}
function echidna_sExtend() public returns (bool) {
if (!everSet) {
return true;
}
bool notInEither = !AddressArrayUtils.contains(addrs1, a) && !AddressArrayUtils.contains(addrs2, a);
uint256 addrs1Length = addrs1.length;
uint256 addrs2Length = addrs2.length;
AddressArrayUtils.sExtend(addrs1, addrs2);
uint256 i;
for (i = 0; i < addrs2.length; i++) {
if (!AddressArrayUtils.contains(addrs1, addrs2[i])) {
return false;
}
}
if (addrs1.length != (addrs1Length + addrs2Length)) {
return false;
}
if (notInEither && AddressArrayUtils.contains(addrs1, a)) {
return false;
}
return true;
}
function echidna_hasDuplicate() public view returns (bool) {
if (!everSet) {
return true;
}
bool hasDup = false;
uint i1;
uint i2;
bool b;
for (uint i = 0; i < addrs1.length; i++) {
(i1, b) = AddressArrayUtils.indexOf(addrs1, addrs1[i]);
(i2, b) = AddressArrayUtils.indexOfFromEnd(addrs1, addrs1[i]);
if (i1 != (i2-1)) {
hasDup = true;
}
}
return hasDup == AddressArrayUtils.hasDuplicate(addrs1);
}
function echidna_isEqual() public view returns (bool) {
if (!everSet) {
return true;
}
uint256 length1 = addrs1.length;
uint256 length2 = addrs2.length;
bool areEqual = AddressArrayUtils.isEqual(addrs1, addrs2);
if (length1 != length2) {
return !areEqual;
}
for (uint i = 0; i < length1; i++) {
if (addrs1[i] != addrs2[i]) {
return !areEqual;
}
}
return areEqual;
}
function echidna_sReverse() public returns (bool) {
if (!everSet) {
return true;
}
uint256 length = addrs1.length;
address a0;
address a1;
if (length > 0) {
a0 = addrs1[0];
a1 = addrs1[length-1];
}
AddressArrayUtils.sReverse(addrs1);
if (addrs1.length != length) {
return false;
}
if (length > 0) {
if (a0 != addrs1[length-1]) {
return false;
}
if (a1 != addrs1[0]) {
return false;
}
}
return true;
}
function ourEqual(address[] memory A, address[] memory B) internal pure returns (bool) {
uint256 i;
uint256 j;
bool found;
for (i = 0; i < A.length; i++) {
found = false;
for (j = 0; j < B.length; j++) {
if (A[i] == B[j]) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
for (i = 0; i < B.length; i++) {
found = false;
for (j = 0; j < A.length; j++) {
if (A[j] == B[i]) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
function echidna_diff() public view returns (bool) {
if (!everSet) {
return true;
}
address [] memory empty = new address[](0);
address [] memory diff1 = AddressArrayUtils.difference(addrs1, addrs2);
address [] memory diff2 = AddressArrayUtils.difference(addrs2, addrs1);
if (!ourEqual(addrs1, addrs2) == ourEqual(diff1, diff2)) {
return false;
}
if (!ourEqual(AddressArrayUtils.difference(addrs1, addrs1), empty)) {
return false;
}
return true;
}
function echidna_union() public view returns (bool) {
if (!everSet) {
return true;
}
address [] memory empty = new address[](0);
address [] memory union1 = AddressArrayUtils.union(addrs1, addrs2);
address [] memory union2 = AddressArrayUtils.union(addrs2, addrs1);
address [] memory unionB1 = AddressArrayUtils.unionB(addrs1, addrs2);
address [] memory unionB2 = AddressArrayUtils.unionB(addrs2, addrs1);
if (!ourEqual(union1, union2)) {
return false;
}
if (!AddressArrayUtils.hasDuplicate(addrs1) && !AddressArrayUtils.hasDuplicate(addrs2)) {
if (!ourEqual(unionB1, unionB2)) {
return false;
}
}
if (!ourEqual(AddressArrayUtils.union(addrs1, empty), addrs1)) {
return false;
}
return true;
}
function echidna_intersect() public view returns (bool) {
if (!everSet) {
return true;
}
address [] memory empty = new address[](0);
address [] memory intersect1 = AddressArrayUtils.intersect(addrs1, addrs2);
address [] memory intersect2 = AddressArrayUtils.intersect(addrs2, addrs1);
if (!ourEqual(intersect1, intersect2)) {
return false;
}
if (!ourEqual(AddressArrayUtils.intersect(addrs1, empty), empty)) {
return false;
}
return true;
}
function echidna_remove() public view returns (bool) {
if (!everSet) {
return true;
}
if (!AddressArrayUtils.contains(addrs1, a)) {
return true;
}
uint256 acount = 0;
uint256 i;
for (i = 0; i < addrs1.length; i++) {
if (addrs1[i] == a) {
acount++;
}
}
address [] memory removed = AddressArrayUtils.remove(addrs1, a);
if (removed.length != (addrs1.length-1)) {
return false;
}
uint256 acountNew = 0;
for (i = 0; i < removed.length; i++) {
if (removed[i] == a) {
acountNew++;
}
}
if (acountNew != (acount-1)) {
return false;
}
if (!AddressArrayUtils.hasDuplicate(addrs1)) {
if (AddressArrayUtils.contains(removed, a)) {
return false;
}
}
return true;
}
function echidna_revert_remove() public view returns (bool) {
if (!everSet) {
revert();
}
if (AddressArrayUtils.contains(addrs1, a)) {
revert();
}
AddressArrayUtils.remove(addrs1, a);
}
function echidna_pop() public view returns (bool) {
if (!everSet) {
return true;
}
if (!AddressArrayUtils.contains(addrs1, a)) {
return true;
}
uint256 aIndex;
bool aFound;
(aIndex, aFound) = AddressArrayUtils.indexOf(addrs1, a);
uint256 acount = 0;
uint256 i;
for (i = 0; i < addrs1.length; i++) {
if (addrs1[i] == a) {
acount++;
}
}
address [] memory removed;
address apop;
(removed, apop) = AddressArrayUtils.pop(addrs1, aIndex);
if (apop != a) {
return false;
}
if (removed.length != (addrs1.length-1)) {
return false;
}
uint256 acountNew = 0;
for (i = 0; i < removed.length; i++) {
if (removed[i] == a) {
acountNew++;
}
}
if (acountNew != (acount-1)) {
return false;
}
if (!AddressArrayUtils.hasDuplicate(addrs1)) {
if (AddressArrayUtils.contains(removed, a)) {
return false;
}
}
return true;
}
function echidna_sRemoveCheap() public returns (bool) {
if (!everSet) {
return true;
}
if (!AddressArrayUtils.contains(addrs1, a)) {
return true;
}
uint256 acount = 0;
uint256 i;
for (i = 0; i < addrs1.length; i++) {
if (addrs1[i] == a) {
acount++;
}
}
uint256 oldLength = addrs1.length;
bool anyDuplicates = AddressArrayUtils.hasDuplicate(addrs1);
AddressArrayUtils.sRemoveCheap(addrs1, a);
if (addrs1.length != (oldLength-1)) {
return false;
}
uint256 acountNew = 0;
for (i = 0; i < addrs1.length; i++) {
if (addrs1[i] == a) {
acountNew++;
}
}
if (acountNew != (acount-1)) {
return false;
}
if (!anyDuplicates) {
if (AddressArrayUtils.contains(addrs1, a)) {
return false;
}
}
return true;
}
function echidna_revert_sRemoveCheap() public returns (bool) {
if (!everSet) {
revert();
}
if (AddressArrayUtils.contains(addrs1, a)) {
revert();
}
AddressArrayUtils.sRemoveCheap(addrs1, a);
}
function echidna_sPop() public returns (bool) {
if (!everSet) {
return true;
}
if (!AddressArrayUtils.contains(addrs1, a)) {
return true;
}
uint256 aIndex;
bool aFound;
(aIndex, aFound) = AddressArrayUtils.indexOf(addrs1, a);
uint256 acount = 0;
uint256 i;
for (i = 0; i < addrs1.length; i++) {
if (addrs1[i] == a) {
acount++;
}
}
uint256 oldLength = addrs1.length;
bool anyDuplicates = AddressArrayUtils.hasDuplicate(addrs1);
address apop = AddressArrayUtils.sPop(addrs1, aIndex);
if (apop != a) {
return false;
}
if (addrs1.length != (oldLength-1)) {
return false;
}
uint256 acountNew = 0;
for (i = 0; i < addrs1.length; i++) {
if (addrs1[i] == a) {
acountNew++;
}
}
if (acountNew != (acount-1)) {
return false;
}
if (!anyDuplicates) {
if (AddressArrayUtils.contains(addrs1, a)) {
return false;
}
}
return true;
}
function echidna_revert_sPop() public returns (bool) {
if (!everSet) {
revert();
}
uint256 index = addrs1.length;
AddressArrayUtils.sPop(addrs1, index);
}
function echidna_sPopCheap() public returns (bool) {
if (!everSet) {
return true;
}
if (!AddressArrayUtils.contains(addrs1, a)) {
return true;
}
uint256 aIndex;
bool aFound;
(aIndex, aFound) = AddressArrayUtils.indexOf(addrs1, a);
uint256 acount = 0;
uint256 i;
for (i = 0; i < addrs1.length; i++) {
if (addrs1[i] == a) {
acount++;
}
}
uint256 oldLength = addrs1.length;
bool anyDuplicates = AddressArrayUtils.hasDuplicate(addrs1);
address apop = AddressArrayUtils.sPopCheap(addrs1, aIndex);
if (apop != a) {
return false;
}
if (addrs1.length != (oldLength-1)) {
return false;
}
uint256 acountNew = 0;
for (i = 0; i < addrs1.length; i++) {
if (addrs1[i] == a) {
acountNew++;
}
}
if (acountNew != (acount-1)) {
return false;
}
if (!anyDuplicates) {
if (AddressArrayUtils.contains(addrs1, a)) {
return false;
}
}
return true;
}
function echidna_revert_sPopCheap() public returns (bool) {
if (!everSet) {
revert();
}
uint256 index = addrs1.length;
AddressArrayUtils.sPopCheap(addrs1, index);
}
function echidna_argGet() public view returns (bool) {
if (!everSet) {
return true;
}
if (addrs1.length < 1) {
return true;
}
bool found;
uint256 index;
uint256 i;
uint256[] memory indexArray = new uint256[](addrs2.length);
for (i = 0; i < indexArray.length; i++) {
(index, found) = AddressArrayUtils.indexOf(addrs1, addrs2[i]);
if (found) {
indexArray[i] = index;
} else {
indexArray[i] = 0;
}
}
address[] memory argGetResult = AddressArrayUtils.argGet(addrs1, indexArray);
for (i = 0; i < argGetResult.length; i++) {
if (argGetResult[i] != addrs1[indexArray[i]]) {
return false;
}
}
return true;
}
}
Loading…
Cancel
Save