新的臨時結構導致狀態變數被覆蓋
我正在執行一個實驗,我試圖創建以下行為:
- 服務所有者將 Experiment1 部署到區塊鏈並記錄為所有者。
- 所有者然後添加使用者地址、使用者定義的 uint 和使用者定義的字節 32。
- 資訊被推送到 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;
- 然後將“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); } }
有機會:
- 通過刪除 A 和 B 實例的可能性進行擴展。
- 用指向相關 A 實例的指針列舉所有 B 結構。
- 優化儲氣成本。
- 縮小契約規模。
這裡的重點是可讀性和解決這個人為的實驗契約的原始問題。
希望能幫助到你。