I wrote this function:
function removeClaim(uint256 _claimId) public returns (bool success) {
require(msg.sender == owner || msg.sender == claims[_claimId].issuer);
// Emit event and store burned signature before deleting to save gas for copy.
IdentityContractLib.Claim storage claim = claims[_claimId];
emit ClaimRemoved(_claimId, claim.topic, claim.scheme, claim.issuer, claim.signature, claim.data, claim.uri);
burnedSignatures[claim.signature] = true; // Make sure that this same claim cannot be added again.
// Delete entries of helper directories.
uint256[] storage array = topics2ClaimIds[claim.topic];
uint32 positionInArray = 0;
while(_claimId != array[positionInArray]) {
positionInArray++;
}
for(uint32 i = positionInArray; i < array.length - 1; i++) {
array[i] = array[i+1];
}
array.length = array.length - 1;
// Delete the actual directory entry.
claim.topic = 0;
claim.scheme = 0;
claim.issuer = address(0);
claim.signature = "";
claim.data = "";
claim.uri = "";
return true;
}
I'm trying to reduce the size of topics2ClaimIds[claim.topic]
which I aliased to array
. But changing array.length = array.length - 1;
to topics2ClaimIds[claim.topic].length = array.length - 1;
doesn't change the problem.
If I add a return right before array.length = array.length - 1;
, the function executes normally. However, if I add a return right after this line (or none at all letting the function execute till the end), I get this error:
Error: Returned error: VM Exception while processing transaction: invalid opcode
If I understand the documentation correctly, every storage array is a dynamically sized array:
For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array. Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion. Increasing the length adds new zero-initialised elements to the array. Reducing the length performs an implicit :ref:delete on each of the removed elements. If you try to resize a non-dynamic array that isn’t in storage, you receive a Value must be an lvalue error.
(https://solidity.readthedocs.io/en/v0.5.3/types.html)
In case this is wrong, please let me know.
The mapping I use and therefore the array in question is declared as follows in the header of the contract:
mapping (uint256 => uint256[]) topics2ClaimIds;
Btw. I realize that arrays are a terrible data structure for what I'm trying to do to begin with. If Solidity has better data structures available (like a binary heap for starters), please let me know.
require(array.length > 0);
after the for loop, I still see the same behavior: With a return right before the size reduction (but after the require), it doesn't fail. Without it, it fails. So my array has at least 1 element.require
will cause the whole transaction to revert if the array is empty. If you do not want that you can useif (array.length > 0) { array.pop(); }
for solc 0.5.xx. Using loops to iterate the whole array is something I'd not recommend because of possible excesive gas costs.array.pop()
is not an option as I need to delete a specific element that can be anywhere in the array.