Contract-Development

新的臨時結構導致狀態變數被覆蓋

  • April 17, 2022

我正在執行一個實驗,我試圖創建以下行為:

  1. 服務所有者將 Experiment1 部署到區塊鏈並記錄為所有者。
  2. 所有者然後添加使用者地址、使用者定義的 uint 和使用者定義的字節 32。
  3. 資訊被推送到 arrA 數組,記錄的索引映射到使用者的地址並被推送到 arrAIdices 數組。

問題出在 addToArrA 函式中:似乎狀態變數被覆蓋了,我不明白如何防止這種情況發生。特別是,所有者被_user覆蓋,並且arrA的長度設置為_AdataA。完全不知道如何解決這個問題。

pragma solidity ^0.4.17;

contract Experiment1 {
   address public owner;

   modifier onlyOwner {require(msg.sender == owner); _;}

   struct structA {
       address user;
       uint AdataA;
       bool AdataB;
       bytes32 AdataC;

       structB[] arrB;
   }

   struct structB {
       uint BdataA;
       uint BdataB;
   }

   structA[] public arrA;

   // Solidity cannot return structs, and can only return arrays of addresses, bools, or uints
   mapping (address => uint256[]) private arrAIndices;

   // CurrencyHedge: Contract creator.
   function Experiment1() {
       owner = msg.sender;
   }

   // addToStructAArr: add an element to structAArr
   function addToArrA(address _user, uint _AdataA, bytes32 _AdataC) public onlyOwner {

       structA tempStructA;

       // Populate the information in the new structA.  This method must be used,
       // since struct arrays cannot be passed as parameters.
       // Refer to https://ethereum.stackexchange.com/questions/3525/how-to-initialize-empty-struct-array-in-new-struct
       tempStructA.user = _user;
       tempStructA.AdataA = _AdataA;
       tempStructA.AdataB = false;
       tempStructA.AdataC = _AdataC;

       // Add the structA to the global list, and associate the index of the hedge with the beneficiary
       arrA.push(tempStructA);
       arrAIndices[_user].push(arrA.length - 1);
   }

   // getArrALength: Check on length of arrA
   function getArrALength() public onlyOwner returns (uint) {
       uint length = arrA.length;
       return length;
   }

   // getArrAIndices: Retrieve a list of indices of the allHedges array associated with a particular beneficiary's
   // Refer to https://ethereum.stackexchange.com/questions/3589/how-can-i-return-an-array-of-struct-from-a-function
   function getArrAIndices(address _user) public onlyOwner returns (uint256[]) {
       return arrAIndices[_user];
   }
}

據我所知,Solidity 似乎不喜歡嵌套在結構內的結構數組,而且 Solidity 似乎預設將“tempStructA”用於儲存而不是記憶體。解決方法是改用映射,並使用 ID 號(在本例中為數組索引號)將資訊連結在一起。我不會再次發布整個契約,而是會逐步進行更改,並在必要時引用程式碼。

請記住,在這種情況下,契約所有者和 ‘user’ / ‘_user’ 是不同的。

1)‘結構B

$$ $$arrB’ 已從 structA 中刪除,而是進行了以下映射:

// Each address is associated with an array of indices corresponding to particular structA elements in arrA
mapping (address => uint256[]) private arrAIndices;

// A reverse mapping is made to make it easy to verify that a given index is associated with a given address
mapping (uint => address) private arrAToUser;

// Each index of arrA is mapped to a structB[] array
mapping (uint => structB[]) private arrB;
  1. 然後將“addToArrA”函式修改為:
function addToArrA(address _user, uint _AdataA, bytes32 _AdataC) public onlyOwner {

   structA memory newA = structA(_user, _AdataA, false, _AdataC);

   // Add the structA to the global list, and associate the index of the hedge with the beneficiary
   arrA.push(newA);
   uint newIndex = arrA.length - 1;
   arrAIndices[_user].push(newIndex);
   arrAToUser[newIndex] = _user;
}

3)如果我們想添加一些structB並將它們與特定的structA關聯,我們可以添加這個函式:

function addToArrB(address _user, uint256 _index, uint _BdataA, uint64 _BdataB) public onlyOwner {
   require(arrAToUser[_index] == _user);

   structB memory newB = structB(_BdataA, _BdataB);
   arrB[_index].push(newB);
}

問題是:

structA tempStructA;

被(隱式)聲明為未初始化的儲存指針。它未初始化,因為沒有右側表達式,例如= structAs[index];. 這是storage因為memory沒有明確聲明structA memory tempStructA;

因此,我們structA從儲存槽 0 開始,這意味著變數的佈局大致如下:

       address user; // slot 0, overwrites owner
       uint AdataA;  // slot 1, overwrites arrA.length;
       bool AdataB;  
       bytes32 AdataC;
       structB[] arrB;

這是未修復程式碼的多個問題的 Remix 警告。

在此處輸入圖像描述

嘗試將memory結構推入storage. 這將使資料結構受到質疑。您可以使用 Solidity CRUD 模式解決這個問題。https://medium.com/robhitchens/solidity-crud-part-1-824ffa69509a

或者,發瘋並使用庫實現,在這裡:https ://github.com/rob-Hitchens/UnorderedKeySet

pragma solidity 0.5.1;

import "./HitchensUnorderedKeySet.sol";
import "./HitchensUnorderedAddressSet.sol";

contract Experiment1 {

   using HitchensUnorderedAddressSetLib for HitchensUnorderedAddressSetLib.Set;
   using HitchensUnorderedKeySetLib for HitchensUnorderedKeySetLib.Set;

   address public owner;

   modifier onlyOwner {require(msg.sender == owner); _;}
   modifier onlyValidUser(address user) {
       require(addresses.exists(user), "usernot found.");
       _;
   }

   struct StructA {
       uint aDataA;
       bool aDataB;
       bytes32 aDataC;
       HitchensUnorderedKeySetLib.Set bIds;
       mapping(bytes32 => StructB) structBs;
   }

   HitchensUnorderedAddressSetLib.Set addresses;
   mapping(address => StructA) structAs;

   struct StructB {
       uint bDataA;
       uint bDataB;
   }

   event LogNewA(address sender, address A, uint aDataA, bool aDataB, bytes32 aDataC);
   event LogNewB(address sender, address A, bytes32 bId, uint bDataA, uint bDataB);

   // CurrencyHedge: Contract creator.
   constructor() public {
       owner = msg.sender;
   }

   // addToStructAArr: add an element to structAArr
   function addToArrA(address user, uint aDataA, bool aDataB, bytes32 aDataC) public onlyOwner {
       addresses.insert(user);
       StructA storage a = structAs[user];
       a.aDataA = aDataA;
       a.aDataB = aDataB;
       a.aDataC = aDataC;
       emit LogNewA(msg.sender, user, aDataA, aDataB, aDataC);
   }
   function addToArrB(address user, uint bDataA, uint bDataB) public onlyOwner onlyValidUser(user) {
       bytes32 bKey = keccak256(abi.encodePacked(user, bDataA, bDataB, now)); // possibly inadequate attempt at unique ID
       StructA storage a = structAs[user];
       a.bIds.insert(bKey);
       StructB storage b = a.structBs[bKey];
       b.bDataA = bDataA;
       b.bDataB = bDataB;
       emit LogNewB(msg.sender, user, bKey, bDataA, bDataB);
   }
   function getArrAbyAddress(address user) public view onlyValidUser(user) returns(uint aDataA, bool aDataB, bytes32 aDataC, uint bElementCount) {
       StructA storage a = structAs[user];
       return(a.aDataA, a.aDataB, a.aDataC, bCountByAddress(user));
   }
   function bCountByAddress(address user) public view onlyValidUser(user) returns(uint count) {
       return structAs[user].bIds.count();
   }
   function getArrALength() public view returns (uint) {
       return addresses.count();
   }
   function getAddressAtIndex(uint index) public view returns(address) {
       return addresses.keyAtIndex(index);
   }
   function getBIdAtIndex(address user, uint index) public view onlyValidUser(user) returns(bytes32) {
       StructA storage a = structAs[user];
       return a.bIds.keyAtIndex(index);
   }
   function getB(address user, bytes32 BId) public view onlyValidUser(user) returns(uint bdataA, uint bDataB) {
       StructA storage a = structAs[user];
       require(a.bIds.exists(BId));
       StructB storage b = a.structBs[BId];
       return(b.bDataA, b.bDataB);
   }
}

有機會:

  1. 通過刪除 A 和 B 實例的可能性進行擴展。
  2. 用指向相關 A 實例的指針列舉所有 B 結構。
  3. 優化儲氣成本。
  4. 縮小契約規模。

這裡的重點是可讀性和解決這個人為的實驗契約的原始問題。

希望能幫助到你。

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