如何遍歷地址數組並確保 msg.sender 與儲存的地址匹配
我正在編寫下面的智能合約來創建一個數字系統資料庫。大多數契約都是很好的程式碼並且正在執行,但我似乎無法讓“AddAttendancePoints”功能正常工作。
該函式的主體應執行以下操作:
- 啟動一個循環,遍歷特定“寄存器”中保存的地址數組。(“寄存器”是每次使用者想要一個新寄存器時創建的資料結構)
- 檢查發送消息的地址是否儲存在該“寄存器”數組中。<- “這是我的程式碼有錯誤的地方,問題是,當您在數組中儲存 2 個或更多地址時,事務會恢復。程式碼不允許第二個或第三個地址添加出勤點,即使他們有得救了。”
- 在循環結束時,如果“msg.sender”不匹配“
$$ i $$" 當循環通過“Register’s”地址數組時。<- 由於某種原因,第一次呼叫“AddAttendancePoints”後的每個事務都會返回這行程式碼,即使 msg.sender 保存在“Register’s”數組中也是如此。
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; contract Register { uint256 public totalRegisters; struct completeRegister { address[] attendees; uint256 attendancePoints; uint256 sessionNumber; uint256 startTime; uint256 finishTime; string title; bool active; } mapping (uint256 => completeRegister) RegisterToNumber; modifier RegisterActive(uint256 _registerNumber) { require (RegisterToNumber[_registerNumber].active == true); _; } function NewRegister (address[] memory _attendees, string memory _title) public { RegisterToNumber[totalRegisters] = completeRegister(_attendees, 0, totalRegisters, block.timestamp, 0, _title, true); totalRegisters = totalRegisters + 1; } function FinaliseRegister (uint256 _registerNumber) public RegisterActive(_registerNumber) returns (bool) { RegisterToNumber[_registerNumber].finishTime = block.timestamp; RegisterToNumber[_registerNumber].active = false; return true; } function AddAttendee(address _attendee, uint256 _registerNumber) public RegisterActive(_registerNumber) { RegisterToNumber[_registerNumber].attendees.push(_attendee); } function AddAttendancePoints(uint256 _registerNumber) public RegisterActive(_registerNumber) { for (uint256 i = 0; i < RegisterToNumber[_registerNumber].attendees.length; i++) { if (RegisterToNumber[_registerNumber].attendees[i] == msg.sender) { RegisterToNumber[_registerNumber].attendancePoints = RegisterToNumber[_registerNumber].attendancePoints + 1; } else { revert(); } } } function GetSession (uint256 _registerNumber) public view returns (address[] memory, uint256, uint256, uint256, uint256, string memory, bool) { return (RegisterToNumber[_registerNumber].attendees, RegisterToNumber[_registerNumber].attendancePoints, RegisterToNumber[_registerNumber].sessionNumber, RegisterToNumber[_registerNumber].startTime, RegisterToNumber[_registerNumber].finishTime, RegisterToNumber[_registerNumber].title, RegisterToNumber[_registerNumber].active); } }
函式“AddAttendancePoints”是智能合約中唯一“不”工作的函式。如果你在 remix 中部署並玩弄程式碼,你會看到下面的函式不允許腳本按照 body 中寫的邏輯執行。
function AddAttendancePoints(uint256 _registerNumber) public RegisterActive(_registerNumber) { for (uint256 i = 0; i < RegisterToNumber[_registerNumber].attendees.length; i++) { if (RegisterToNumber[_registerNumber].attendees[i] == msg.sender) { RegisterToNumber[_registerNumber].attendancePoints = RegisterToNumber[_registerNumber].attendancePoints + 1; } else { revert(); } } }
當 msg.sender 儲存在“Register’s”數組中時,程式碼應該允許事務執行,第一個地址沒問題並且工作正常,但是,當您將第二個和第三個地址添加到數組並嘗試呼叫“AddAttendancePoints “交易恢復。
我需要知道是否有任何其他方式來編寫程式碼,以便智能合約可以執行上述邏輯並檢查地址是否與 msg.sender 匹配。我在想,一旦您執行了循環,它也許可以滿足要求,但是我們使用下面顯示的程式碼得到了相同的結果。
- 我沒有使用“if”,而是使用了“require”並得到了相同的結果。
function AddAttendancePoints(uint256 _registerNumber) public RegisterActive(_registerNumber) { for (uint256 i = 0; i < RegisterToNumber[_registerNumber].attendees.length; i++) { require (RegisterToNumber[_registerNumber].attendees[i] == msg.sender); RegisterToNumber[_registerNumber].attendancePoints = RegisterToNumber[_registerNumber].attendancePoints + 1; } }
為了從這一點向前推進,當數組有超過 1 個條目時,智能合約需要能夠將“保存的”地址與“msg.sender”匹配。請記住,數組儲存在結構中,訪問數組可能是問題所在。
任何幫助都會很棒!
如果可能,您不應該迭代任意長度的數組。我建議改用映射。
無論如何,該功能會恢復,因為您說當 msg.sender 不等於另一位與會者時恢復,因此與多人的會議將失敗。
當 msg.sender 不是與會者時,您可能想要恢復。
function AddAttendancePoints(uint256 _registerNumber) public RegisterActive(_registerNumber) { for (uint256 i = 0; i < RegisterToNumber[_registerNumber].attendees.length; i++) { if (RegisterToNumber[_registerNumber].attendees[i] == msg.sender) { RegisterToNumber[_registerNumber].attendancePoints = RegisterToNumber[_registerNumber].attendancePoints + 1; return; // stop looking } } require(false, "msg.sender isn't an attendee"); }
我已經在上面給出的幫助下解決了這個問題。謝謝伊斯梅爾!
通過刪除“else”和“revert”並在循環末尾添加“return”,我現在可以在 Register 處於活動狀態時向其添加其他點,該程式碼僅允許保存在寄存器的數組以將點添加到儲存中。
程式碼寫成:
function AddAttendancePoints(uint256 _registerNumber) public RegisterActive(_registerNumber) returns (bool success) { for (uint256 i = 0; i < RegisterToNumber[_registerNumber].attendees.length; i++) { if (RegisterToNumber[_registerNumber].attendees[i] == msg.sender) { RegisterToNumber[_registerNumber].attendancePoints = RegisterToNumber[_registerNumber].attendancePoints + 1; return success; } } }
你可以看到我已經刪除了 2 行程式碼,並且還使用返回函式關閉了循環。
經驗教訓:在遍歷任意長度時使用“return”關閉循環,因此 EVM 會注意到循環中的停止,並且如果在呼叫時值與另一個值不匹配,則不要添加“else”來繼續函式。