Arrays

如何遍歷地址數組並確保 msg.sender 與儲存的地址匹配

  • July 9, 2021

我正在編寫下面的智能合約來創建一個數字系統資料庫。大多數契約都是很好的程式碼並且正在執行,但我似乎無法讓“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 =&gt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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”來繼續函式。

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