【區塊鏈安全 | 第十九篇】類型之映射類型

文章目錄

  • 映射類型
      • 可迭代映射

在這里插入圖片描述

映射類型

映射類型使用語法 mapping(KeyType KeyName? => ValueType ValueName?),映射類型的變量聲明使用語法 mapping(KeyType KeyName? => ValueType ValueName?) VariableName

KeyType 可以是任何內置值類型、bytesstring 或任何合約類型或枚舉類型。其他用戶定義的復雜類型,如映射、結構體或數組類型是不允許的。

ValueType 可以是任何類型,包括映射、數組和結構體。

KeyNameValueName 是可選的(因此 mapping(KeyType => ValueType) 也是有效的),它們可以是任何有效的標識符,但不能是類型。

你可以將映射看作哈希表,它在內部初始化,每個可能的鍵都會映射到一個值,該值的字節表示是全零,即類型的默認值。相似之處僅限于此,鍵數據不會存儲在映射中,只有它的 keccak256 哈希值用于查找值。

由于這個原因,映射沒有長度或鍵值是否已設置的概念,因此無法在沒有額外信息的情況下刪除映射。

映射只能有存儲數據位置,因此只能作為狀態變量、作為函數中的存儲引用類型,或者作為庫函數的參數。它們不能作為合約函數的公共參數或返回參數。這些限制也適用于包含映射的數組和結構體。

你可以將映射類型的狀態變量標記為公共的,Solidity 會為你自動創建一個 getter。KeyType 變為 getter 的一個參數,并使用 KeyName(如果指定的話)。如果 ValueType 是值類型或結構體,getter 返回與該類型匹配的 ValueType(如果指定了 ValueName)。如果 ValueType 是數組或映射,則 getter 會有一個參數對應每個 KeyType,并遞歸處理。

在下面的示例中,MappingExample 合約定義了一個公共的 balances 映射,鍵類型為 address,值類型為 uint,將以太坊地址映射到無符號整數值。由于 uint 是值類型,getter 返回一個與該類型匹配的值,在 MappingUser 合約中,你可以看到它返回指定地址的值。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;// 定義一個包含地址和余額的映射的合約
contract MappingExample {// 聲明一個公共的映射,address => uint,記錄每個地址的余額mapping(address => uint) public balances;// 更新函數:允許發送者更新他們的余額function update(uint newBalance) public {balances[msg.sender] = newBalance;  // 將調用者的余額更新為 newBalance}
}// 定義一個合約用于與 MappingExample 合約進行交互
contract MappingUser {// 一個函數,用來調用 MappingExample 合約的 update 方法,并返回當前合約地址的余額function f() public returns (uint) {// 創建 MappingExample 合約的實例MappingExample m = new MappingExample();// 調用 update 函數,將余額設置為 100m.update(100);// 返回當前合約地址的余額return m.balances(address(this));  // 返回 MappingExample 合約中當前合約地址的余額}
}

下面的示例是一個簡化版的 ERC20 代幣。_allowances 是一個映射類型,嵌套在另一個映射類型內部。我們為映射提供了可選的 KeyNameValueName。這不會影響合約的功能或字節碼,它僅僅是在 ABI 中為映射的 getter 輸入和輸出設置了名稱字段。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;// 定義一個包含映射的智能合約
contract MappingExampleWithNames {// 定義一個 public 映射,映射地址(address)到余額(uint)。映射的鍵為 `user`,值為 `balance`。// 這個映射會自動生成一個 getter 函數,可以根據地址(address)查詢對應的余額。mapping(address user => uint balance) public balances;// 更新余額的函數,接受一個 `newBalance` 參數。// 這個函數會將調用者(msg.sender)的余額更新為 `newBalance`。function update(uint newBalance) public {// 使用調用者的地址(msg.sender)作為鍵,更新其對應的余額值。balances[msg.sender] = newBalance;}
}

在下面的示例中,_allowances 映射用于記錄某人被授權從你的賬戶中提取的金額:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;contract MappingExample {// 定義一個私有映射 `_balances`,將每個地址映射到一個無符號整數(余額)。mapping(address => uint256) private _balances;// 定義一個私有映射 `_allowances`,它是一個二層映射,記錄每個地址(owner)允許另一個地址(spender)提取的金額。mapping(address => mapping(address => uint256)) private _allowances;// 定義事件,當轉賬發生時觸發。event Transfer(address indexed from, address indexed to, uint256 value);// 定義事件,當批準時觸發,表明某個地址被授權從另一個地址提取一定金額。event Approval(address indexed owner, address indexed spender, uint256 value);// 查詢某個地址被授權的提取金額function allowance(address owner, address spender) public view returns (uint256) {return _allowances[owner][spender];}// 從一個賬戶向另一個賬戶轉賬,同時檢查授權金額是否足夠function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {// 確保 sender 允許 msg.sender(調用者)提取足夠的金額require(_allowances[sender][msg.sender] >= amount, "ERC20: Allowance not high enough.");// 減少授權金額_allowances[sender][msg.sender] -= amount;// 執行轉賬_transfer(sender, recipient, amount);return true;}// 批準另一個地址(spender)從調用者的賬戶中提取指定金額(amount)function approve(address spender, uint256 amount) public returns (bool) {// 確保 spender 地址不是零地址require(spender != address(0), "ERC20: approve to the zero address");// 設置授權金額_allowances[msg.sender][spender] = amount;// 觸發批準事件emit Approval(msg.sender, spender, amount);return true;}// 內部轉賬函數,用于更新賬戶余額,并觸發轉賬事件function _transfer(address sender, address recipient, uint256 amount) internal {// 確保 sender 和 recipient 不是零地址require(sender != address(0), "ERC20: transfer from the zero address");require(recipient != address(0), "ERC20: transfer to the zero address");// 確保 sender 有足夠的余額require(_balances[sender] >= amount, "ERC20: Not enough funds.");// 執行轉賬:從 sender 減去金額,給 recipient 增加金額_balances[sender] -= amount;_balances[recipient] += amount;// 觸發轉賬事件emit Transfer(sender, recipient, amount);}
}

可迭代映射

你不能直接迭代映射,即不能枚舉它們的鍵。不過,你可以在其基礎上實現一個數據結構并對其進行迭代。例如,下面的代碼實現了一個 IterableMapping 庫,User 合約將數據添加到該庫中,并且 sum 函數會迭代這個數據結構以求和所有值。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.8;// 定義 IndexValue 結構體,用于存儲每個鍵對應的索引和數值
struct IndexValue { uint keyIndex; // 鍵的索引位置uint value;    // 鍵對應的值
}// 定義 KeyFlag 結構體,用于標記鍵是否被刪除
struct KeyFlag { uint key;      // 鍵的值bool deleted;  // 是否已刪除標志
}// 定義 itmap 結構體,包含了一個映射和一個存儲鍵的數組,以及當前大小
struct itmap {mapping(uint => IndexValue) data; // 存儲鍵值對的映射KeyFlag[] keys;                   // 存儲鍵的數組uint size;                         // 數據大小
}// 定義一個類型 Iterator,實質上是 uint 類型,用于遍歷
type Iterator is uint;// 定義 IterableMapping 庫,提供操作 itmap 類型數據的函數
library IterableMapping {// 插入數據到 itmap 中,如果已存在則更新數據function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {uint keyIndex = self.data[key].keyIndex; // 獲取當前鍵的索引self.data[key].value = value;            // 更新該鍵對應的值if (keyIndex > 0) {return true; // 如果鍵已經存在,返回 true,表示數據已替換} else {// 如果鍵不存在,分配一個新的索引keyIndex = self.keys.length;self.keys.push(); // 在數組末尾添加一個新元素self.data[key].keyIndex = keyIndex + 1; // 設置新鍵的索引self.keys[keyIndex].key = key; // 將鍵添加到鍵數組中self.size++; // 增加數據大小return false; // 返回 false,表示插入了新的鍵值對}}// 從 itmap 中刪除指定的鍵function remove(itmap storage self, uint key) internal returns (bool success) {uint keyIndex = self.data[key].keyIndex; // 獲取該鍵的索引if (keyIndex == 0) {return false; // 如果鍵不存在,返回 false}delete self.data[key]; // 刪除數據映射中的鍵值對self.keys[keyIndex - 1].deleted = true; // 將對應的 KeyFlag 標記為已刪除self.size--; // 減小數據大小return true; // 返回 true,表示刪除成功}// 檢查 itmap 中是否包含指定的鍵function contains(itmap storage self, uint key) internal view returns (bool) {return self.data[key].keyIndex > 0; // 如果該鍵存在,返回 true}// 初始化遍歷,返回一個迭代器function iterateStart(itmap storage self) internal view returns (Iterator) {return iteratorSkipDeleted(self, 0); // 跳過已刪除的項,返回起始迭代器}// 檢查當前迭代器是否有效function iterateValid(itmap storage self, Iterator iterator) internal view returns (bool) {return Iterator.unwrap(iterator) < self.keys.length; // 如果迭代器位置小于鍵數組長度,則有效}// 獲取下一個迭代器,跳過已刪除的項function iterateNext(itmap storage self, Iterator iterator) internal view returns (Iterator) {return iteratorSkipDeleted(self, Iterator.unwrap(iterator) + 1); // 跳到下一個有效項}// 獲取當前迭代器對應的鍵和值function iterateGet(itmap storage self, Iterator iterator) internal view returns (uint key, uint value) {uint keyIndex = Iterator.unwrap(iterator); // 獲取迭代器的索引key = self.keys[keyIndex].key;             // 獲取鍵value = self.data[key].value;              // 獲取值}// 跳過已刪除的項,返回有效的迭代器位置function iteratorSkipDeleted(itmap storage self, uint keyIndex) private view returns (Iterator) {while (keyIndex < self.keys.length && self.keys[keyIndex].deleted) // 如果該項被標記為刪除,則跳過keyIndex++;return Iterator.wrap(keyIndex); // 返回跳過已刪除項后的迭代器}
}// User 合約使用 IterableMapping 庫進行數據操作
contract User {itmap data; // 聲明一個 itmap 類型的變量來保存數據// 使用 IterableMapping 庫來操作 itmap 類型的數據using IterableMapping for itmap;// 插入數據到 itmap 中function insert(uint k, uint v) public returns (uint size) {// 調用 IterableMapping 庫的 insert 函數插入數據data.insert(k, v);// 返回當前數據的大小return data.size;}// 計算所有存儲數據的總和function sum() public view returns (uint s) {// 遍歷 itmap 中的所有數據并計算總和for (Iterator i = data.iterateStart(); // 初始化迭代器data.iterateValid(i); // 檢查迭代器是否有效i = data.iterateNext(i) // 獲取下一個有效項) {(, uint value) = data.iterateGet(i); // 獲取當前項的值s += value; // 將當前值累加到總和}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/74486.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74486.shtml
英文地址,請注明出處:http://en.pswp.cn/web/74486.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

動態循環表單+動態判斷表單類型+動態判斷表單是否必填方法

頁面效果&#xff1a; 接口請求到的數據格式&#xff1a; list: [{demandType: "設備輔助功能要求",demandSettingList: [{id: "1907384788664963074",name: "測試表單",fieldType: 0,contentValue: "",vaildStatus: 0, // 0 非必填&a…

藍橋杯DFS算法題(java)

最大連通 鏈接&#xff1a; https://www.lanqiao.cn/problems/2410/learning/ 問題描述 小藍有一個 30 行 60 列的數字矩陣&#xff0c;矩陣中的每個數都是 0 或 1 。 1100100000111111101010010010011010101110110110111010011111100100000000010100011011000000100101100011…

解鎖物種分布模擬新技能:MaxEnt 模型與 R 語言的奇妙融合

技術點目錄 第二章、常用數據檢索與R語言自動化下載及可視化方法第三章、R語言數據清洗與特征變量篩選第四章、基于ArcGIS、R數據處理與進階第五章、基于Maxent的物種分布建模與預測第六章、基于R語言的模型參數優化第七章、物種分布模型結果分析與論文寫作 —————————…

三軸云臺之相機技術篇

一、結構設計 三軸云臺通常由空間上三個互相垂直的框架構成&#xff0c;包括內框&#xff08;俯仰框&#xff09;、中框&#xff08;方位框&#xff09;和外框&#xff08;橫滾框&#xff09;。這些框架分別負責控制相機的俯仰運動、方位運動和橫滾運動&#xff0c;從而實現對目…

全文 - MLIR Toy Tutorial Chapter 3 :高層次上語言特定的分析和變換

使用 C 風格的模式匹配和重寫來優化轉置運算 使用 DRR 優化 reshape 運算 創建一種貼近輸入語言的語義表示的方言&#xff0c;可以在 MLIR 中分析、變換和優化&#xff0c;這些過程中需要用到高級語言的信息&#xff0c;而且通常是在語言的 AST 上執行的這些過程。…

js逆向入門圖靈爬蟲練習平臺 第四題學習

(base64解碼&#xff09;地址:aHR0cHM6Ly9zdHUudHVsaW5ncHl0b24uY24vcHJvYmxlbS1kZXRhaWwvNC8 先找到請求接口帶有加密參數&#xff1a; 全局搜索Sign,找到參數生成位置 看到這就一目了然塞&#xff0c;知道參數是怎么構造生成的&#xff0c;不知道這段 JavaScript 代碼沒關系…

【Flask開發】嘿馬文學web完整flask項目第2篇:2.用戶認證,Json Web Token(JWT)【附代碼文檔】

教程總體簡介&#xff1a;2. 目標 1.1產品與開發 1.2環境配置 1.3 運行方式 1.4目錄說明 1.5數據庫設計 2.用戶認證 Json Web Token(JWT) 3.書架 4.1分類列表 5.搜索 5.3搜索-精準&高匹配&推薦 6.小說 6.4推薦-同類熱門推薦 7.瀏覽記錄 8.1配置-閱讀偏好 8.配置 9.1項目…

[dp5_多狀態dp] 按摩師 | 打家劫舍 II | 刪除并獲得點數 | 粉刷房子

目錄 1.面試題 17.16. 按摩師 題解 2.打家劫舍 II 題解 3.刪除并獲得點數 題解 4.粉刷房子 題解 一定要有這樣的能力&#xff0c;碰到一個新題的時候&#xff0c;可以往之前做過的題方向靠&#xff01; 打家劫舍問題模型: 不能選擇相鄰的兩個數&#xff0c;并且要最終…

基于javaweb的SSM羽毛球會員俱樂部系統場館課程運動設計與實現(源碼+文檔+部署講解)

技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、小程序、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;免費功能設計、開題報告、任務書、中期檢查PPT、系統功能實現、代碼編寫、論文編寫和輔導、論文…

windows下git bash安裝SDKMan報錯Looking for unzip...Not found

需要在jdk8和jdk17兩個版本切換。最簡單的是通過手動切換&#xff0c;但切換過程太繁瑣&#xff0c;修改環境變量&#xff0c;達到切換目的。于是嘗試其它解決方案&#xff0c;最終確實使用sdkman工具。 確保安裝了git Git - Downloading Package 記住安裝的路徑&#xff0c;…

rnn的音頻降噪背后技術原理

rnniose: 這個演示展示了 RNNoise 項目&#xff0c;說明了如何將深度學習應用于噪聲抑制。其核心理念是將經典的信號處理方法與深度學習結合&#xff0c;打造一個小巧、快速的實時噪聲抑制算法。它不需要昂貴的 GPU —— 在樹莓派上就能輕松運行。 相比傳統的噪聲抑制系統&…

劍指Offer(數據結構與算法面試題精講)C++版——day3

劍指Offer&#xff08;數據結構與算法面試題精講&#xff09;C版——day3 題目一&#xff1a;數組中和為0的3個數字題目二&#xff1a;和大于或等于k的最短子數組題目三&#xff1a;乘積小于k的子數組 題目一&#xff1a;數組中和為0的3個數字 前面我們提到&#xff0c;在一個排…

全新UI好看404頁面源碼

源碼介紹 全新UI好看404頁面源碼,源碼由HTMLCSSJS組成&#xff0c;記事本打開源碼文件可以進行內容文字之類的修改&#xff0c;雙擊html文件可以本地運行 效果預覽 源碼獲取 全新UI好看404頁面源碼

遞歸典例---漢諾塔

https://ybt.ssoier.cn/problem_show.php?pid1205 #include<bits/stdc.h> #define endl \n #define pii pair<int,int>using namespace std; using ll long long;void move(int n,char a,char b,char c) // n 個盤子&#xff0c;通過 b&#xff0c;從 a 移動到 …

php的高速緩存

部署方法 在我們安裝的nginx中默認不支持memc和srcache功能&#xff0c;需要借助第三方模塊來讓nginx支持此功能。 tar zxf srcache-nginx-module-0.33.tar.gz tar zxf memc-nginx-module-0.20.tar.gz 下載這倆個模塊&#xff0c;然后編譯安裝的時候加進去 編譯安裝完成之后…

視頻設備軌跡回放平臺EasyCVR打造視頻智能融合新平臺,驅動智慧機場邁向數字新時代

一、行業背景? 隨著 5G、AI、物聯網、大數據等前沿技術的不斷更新換代&#xff0c;交通行業進入數字化轉型的高速發展時期。航空業作為交通領域的重要部分&#xff0c;數字化進程從追求速度往注重質量的轉變。但機場在數字化轉型中面臨許多嚴峻挑戰&#xff0c;如現有運營模式…

【論文閱讀】Anchor Graph Network for Incomplete Multiview Clustering

摘要 近年來&#xff0c;不完全多視圖聚類&#xff08;IMVC&#xff09;受到廣泛關注。然而&#xff0c;現有研究仍然存在以下幾個不足之處&#xff1a;1) 部分方法忽略了樣本對在全局結構分布中的關聯性&#xff1b;2) 許多方法計算成本較高&#xff0c;因此無法應用于大規模…

15. 遠程服務器運行jemter的GUI方式

1. 問題 在 linux 服務器或遠程服務器上&#xff0c;安裝 Jmeter&#xff0c;打不開 Jmeter 的 GUI 界面。 環境&#xff1a; linux 服務器mac 電腦 需求&#xff1a;在遠程服務器中&#xff0c;啟動 jmeter&#xff08;./bin/jmeter &&#xff09;后&#xff0c;在 ma…

Ansible:playbook的高級用法

文章目錄 1. handlers與notify2. tags組件3. playbook中使用變量3.1使用 setup 模塊中變量3.2在playbook 命令行中定義變量3.3在playbook文件中定義變量3.4使用變量文件3.5主機清單文件中定義變量主機變量組&#xff08;公共&#xff09;變量 1. handlers與notify Handlers&am…

什么是msvcp140.dll?msvcp140.dll丟失的解決方法又有哪些?

msvcp140.dll 是 Microsoft Visual C Redistributable 的核心動態鏈接庫文件&#xff0c;許多軟件和游戲依賴它來運行。當系統提示“msvcp140.dll丟失”時&#xff0c;意味著該文件無法被正確加載&#xff0c;導致程序崩潰或無法啟動。本文將提供最全面的 msvcp140.dll丟失的解…