Solidity

嘗試調整儲存陣列大小時出現無效的操作碼異常

  • January 21, 2020

我寫了這個函式:

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;
}

我正在嘗試減小topics2ClaimIds[claim.topic]我別名為array. 但是array.length = array.length - 1;改成topics2ClaimIds[claim.topic].length = array.length - 1;不會改變問題。

如果我在之前添加一個 return array.length = array.length - 1;,該函式將正常執行。但是,如果我在這一行之後添加一個 return (或者根本不讓函式執行到最後),我會收到這個錯誤:

Error: Returned error: VM Exception while processing transaction: invalid opcode

如果我正確理解文件,每個儲存陣列都是一個動態大小的陣列:

對於動態大小的數組(僅可用於儲存),可以分配此成員來調整數組的大小。訪問目前長度之外的元素不會自動調整數組大小,而是會導致斷言失敗。增加長度會將新的零初始化元素添加到數組中。減少長度會對每個刪除的元素執行隱式 :ref:delete。如果您嘗試調整不在儲存中的非動態數組的大小,則會收到 Value must be an lvalue 錯誤。

( https://solidity.readthedocs.io/en/v0.5.3/types.html )

如果這是錯誤的,請告訴我。

我使用的映射,因此有問題的數組在契約的標題中聲明如下:

mapping (uint256 => uint256[]) topics2ClaimIds;

順便提一句。我意識到數組對於我開始嘗試做的事情來說是一種糟糕的資料結構。如果 Solidity 有更好的資料結構可用(比如初學者的二進制堆),請告訴我。

我有點迷失了,部分是因為我們看不到整個事情,部分是因為資料結構導致了複雜的程式碼。你躲開了那個。

順便提一句。我意識到數組對於我開始嘗試做的事情來說是一種糟糕的資料結構。如果 Solidity 有更好的資料結構可用(比如初學者的二進制堆),請告訴我。

Solidity 沒有這樣的內置結構,但可以創建它們。

從數組(集​​合)中刪除似乎引起了頭痛。查看此技術的描述:https ://medium.com/robhitchens/solidity-crud-part-2-ed8d8b4f74ec

正如部落格中提到的,該技術仍然有效,但原始程式碼有點過時了。它可以作為一個庫來解除安裝集合管理問題。https://medium.com/robhitchens/solidity-crud-epilogue-e563e794fde

實際回購:https ://github.com/rob-Hitchens/UnorderedKeySet

我發現只執行我對您的意圖的解釋而不是四處尋找程式碼中的錯誤更省時。

pragma solidity 0.5.16;

import "./HitchensUnorderedKeySet.sol";

contract Claims {

   using HitchensUnorderedKeySetLib for HitchensUnorderedKeySetLib.Set;

   struct Claim {
       uint scheme;
       address issuer;
       bytes signature;
       bytes data;
       bytes url;
   }    

   struct Topic {
       HitchensUnorderedKeySetLib.Set claimIdSet;
   }

   HitchensUnorderedKeySetLib.Set topicIdSet;

   mapping(bytes32 => Topic) topics; // can't be public because the compiler doesn't know who to handle the Set type
   mapping(bytes32 => Claim) public claims;
   mapping(bytes32 => bool) public usedSignatureHashes;

   function newTopic(bytes32 topicId) public {
       topicIdSet.insert(topicId);
   }

   function removeTopic(bytes32 topicId) public {
       Topic storage t = topics[topicId];
       require(t.claimIdSet.count() == 0, "Cannot delete topic with claims. Remove the claims first.");
       delete topics[topicId];
       topicIdSet.remove(topicId);
   }

   function newClaim(bytes32 topicId, bytes32 claimId, uint scheme, address issuer, bytes memory signature, bytes memory data, bytes memory url) public {
       require(topicIdSet.exists(topicId), "Topic ID not found.");
       require(!usedSignatureHashes[keccak256(signature)], "Claim signature was used before.");
       usedSignatureHashes[keccak256(signature)] = true;
       Topic storage t = topics[topicId];
       t.claimIdSet.insert(claimId); // this will not allow a duplicate
       Claim storage c = claims[claimId];
       c.scheme = scheme;
       c.issuer = issuer;
       c.signature = signature;
       c.data = data;
       c.url = url;
   }

   function removeClaim(bytes32 topicId, bytes32 claimId) public {
       Topic storage t = topics[topicId];
       t.claimIdSet.remove(claimId); // will revert if claim ID does not exist in topic
       delete claims[claimId];
   }

   // inspectors
   function topicClaimCount(bytes32 topicId) public view returns(uint) {
       return topics[topicId].claimIdSet.count();
   }

   function topicClaimIdAtIndex(bytes32 topicId, uint index) public view returns(bytes32) {
       return topics[topicId].claimIdSet.keyAtIndex(index); // must exist
   }

   // convenience function for Remix
   function arbitraryKey() public view returns(bytes32 key) {
       key = keccak256(abi.encodePacked(block.number));
   }

}

誠然,它只是一個塗鴉,所以沒有保證,但表面測試似乎證實了它正在做這項工作。

希望能幫助到你。

引用自:https://ethereum.stackexchange.com/questions/79169