🐥
Solidity基礎学習7日目(Mapping)
Solidity基礎学習 - マッピング(Mapping)データ構造の理解
日付: 2025年8月27日
学習内容: Solidityのマッピング(Mapping)データ構造とその使用方法について
1. マッピングの基本概念
013_Mapping.solの概要
Solidityのマッピングデータ構造について学習します。マッピングはハッシュテーブルのような構造を持ち、効率的なデータアクセスを提供します。
重要なポイント
マッピングの基本構造
contract ExampleMapping{
// 基本的なマッピング
mapping(uint => bool) public myMapping;
// アドレスをキーとするマッピング
mapping(address => bool) public myAddressMapping;
// ネストしたマッピング
mapping(uint => mapping(uint => bool)) public uintUintBoolMapping;
}
マッピングの特徴:
- ハッシュテーブル構造: 配列やリストと異なり、インデックスではなく任意のキーを使用
- 高速アクセス: O(1)の時間複雑度でデータにアクセス可能
- 動的サイズ: 事前にサイズを定義する必要がない
- キー・値ペア: 任意の型のキーと値の組み合わせ
2. ハッシュテーブルの仕組み
ハッシュ関数の動作
キー → ハッシュ関数 → ハッシュ値(配列のインデックス)
具体例:
- キー: "apple" → ハッシュ関数 → インデックス3
- キー: "banana" → ハッシュ関数 → インデックス7
- キー: "orange" → ハッシュ関数 → インデックス2
データ格納:
hashtable[3] = "リンゴ"
hashtable[7] = "バナナ"
hashtable[2] = "オレンジ"
アクセス方法:
hashtable["apple"] = "リンゴ" // キーから直接アクセス
hashtable["banana"] = "バナナ" // キーから直接アクセス
3. 各マッピングの詳細分析
3.1 基本的なマッピング(myMapping)
実装コード
mapping(uint => bool) public myMapping;
function setValue(uint _index) public{
myMapping[_index] = true;
}
機能の説明:
-
キー:
uint
型(符号なし整数) -
値:
bool
型(真偽値) -
動作: 指定されたインデックスに対応する値を
true
に設定
使用例:
// インデックス5に値を設定
setValue(5); // myMapping[5] = true
// インデックス10に値を設定
setValue(10); // myMapping[10] = true
// 値の確認
myMapping(5); // true が返される
myMapping(10); // true が返される
myMapping(3); // false が返される(デフォルト値)
3.2 アドレスマッピング(myAddressMapping)
実装コード
mapping(address => bool) public myAddressMapping;
function setMyAddressToTrue() public{
myAddressMapping[msg.sender]=true;
}
機能の説明:
-
キー:
address
型(イーサリアムアドレス) -
値:
bool
型(真偽値) -
動作: 関数を呼び出したユーザーのアドレスを
true
に設定
使用例:
// ユーザーAが関数を呼び出し
// msg.sender = 0x1234...(ユーザーAのアドレス)
setMyAddressToTrue(); // myAddressMapping[0x1234...] = true
// ユーザーBが関数を呼び出し
// msg.sender = 0x5678...(ユーザーBのアドレス)
setMyAddressToTrue(); // myAddressMapping[0x5678...] = true
3.3 ネストしたマッピング(uintUintBoolMapping)
実装コード
mapping(uint => mapping(uint => bool)) public uintUintBoolMapping;
function setUintUintBoolMapping(uint _key1, uint _key2, bool _value) public{
uintUintBoolMapping[_key1][_key2]= _value;
}
機能の説明:
-
第1層:
uintUintBoolMapping[_key1]
→mapping(uint => bool)
型を返す -
第2層:
[uintUintBoolMapping[_key1]][_key2]
→bool
型の値を返す -
キーの対応:
_key1
は1つめのuint
、_key2
は2つめのuint
使用例:
// キー1=5、キー2=10にtrueを設定
setUintUintBoolMapping(5, 10, true);
// キー1=3、キー2=7にfalseを設定
setUintUintBoolMapping(3, 7, false);
// 値の確認
uintUintBoolMapping(5, 10); // true
uintUintBoolMapping(3, 7); // false
uintUintBoolMapping(5, 7); // false(デフォルト値)
4. 自動生成されるgetter関数
4.1 getter関数の自動生成条件
自動生成される要素
// 以下の要素にpublic修飾子が付いている場合、getter関数が自動生成される
uint public balance; // 基本型
uint[] public numbers; // 配列
mapping(uint => bool) public myMapping; // マッピング
mapping(uint => mapping(uint => bool)) public nested; // ネストしたマッピング
自動生成されない場合:
-
private
修飾子 -
internal
修飾子 - 関数内のローカル変数
4.2 生成されるgetter関数の例
基本型の場合
uint public balance;
// 自動生成される関数
function balance() public view returns(uint) {
return balance;
}
マッピングの場合
mapping(uint => bool) public myMapping;
// 自動生成される関数
function myMapping(uint _key) public view returns(bool) {
return myMapping[_key];
}
ネストしたマッピングの場合
mapping(uint => mapping(uint => bool)) public uintUintBoolMapping;
// 自動生成される関数
function uintUintBoolMapping(uint _key1, uint _key2) public view returns(bool) {
return uintUintBoolMapping[_key1][_key2];
}
5. アクセス方法の違い
5.1 外部からのアクセス
正しい使用方法
// コントラクトの外部から
uint currentBalance = contract.balance(); // 基本型
uint number = contract.numbers(5); // 配列
bool value = contract.myMapping(5); // マッピング
bool nestedValue = contract.uintUintBoolMapping(5, 10); // ネストしたマッピング
間違った使用方法
// エラー!変数に直接アクセスできない
uint currentBalance = contract.balance; // エラー
uint number = contract.numbers; // エラー
bool value = contract.myMapping; // エラー
5.2 内部からのアクセス
コントラクト内部での使用方法
contract Example {
uint public balance;
function internalAccess() public view returns(uint) {
// 内部では変数名で直接アクセス可能
return balance; // 正しい
}
function externalAccess() public view returns(uint) {
// 外部からは関数呼び出しが必要
return this.balance(); // 正しい(thisを使用)
}
}
6. 実装のポイント
6.1 セキュリティ設計
基本的なセキュリティ考慮事項
function setValue(uint _index) public {
// 適切な検証を追加
require(_index > 0, "Index must be greater than 0");
// 値の設定
myMapping[_index] = true;
}
セキュリティの考慮点:
- 入力値の検証: 適切な範囲チェック
- アクセス制御: 必要に応じて権限管理
- エラーハンドリング: 適切なエラーメッセージ
6.2 ガスコストの最適化
効率的な実装
// 読み取り専用関数はview修飾子を使用
function getValue(uint _index) public view returns(bool) {
return myMapping[_index];
}
// 状態変更は必要最小限に
function setValue(uint _index) public {
myMapping[_index] = true;
}
最適化のポイント:
-
view
修飾子: 状態変更しない関数でのガス節約 - 効率的な状態更新: 必要最小限の処理のみ実行
- 適切な関数設計: 各機能を適切に分離
7. 実用的な応用
7.1 拡張可能な機能
権限管理の追加
contract SecureMapping {
mapping(uint => bool) private myMapping;
mapping(address => bool) public authorizedUsers;
address public owner;
constructor() {
owner = msg.sender;
authorizedUsers[msg.sender] = true;
}
modifier onlyAuthorized() {
require(authorizedUsers[msg.sender], "Not authorized");
_;
}
function setValue(uint _index) public onlyAuthorized {
myMapping[_index] = true;
}
function getValue(uint _index) public view onlyAuthorized returns(bool) {
return myMapping[_index];
}
}
拡張機能:
- 権限管理: 承認されたユーザーのみが利用可能
- 所有者制御: 管理者によるユーザー管理
- セキュリティ強化: 未承認ユーザーからの操作を拒否
7.2 より柔軟なマッピング機能
複数の値を持つマッピング
struct UserInfo {
string name;
uint age;
bool isActive;
}
mapping(uint => UserInfo) public users;
function setUser(uint _id, string memory _name, uint _age) public {
users[_id] = UserInfo(_name, _age, true);
}
function getUser(uint _id) public view returns(string memory name, uint age, bool isActive) {
UserInfo memory user = users[_id];
return (user.name, user.age, user.isActive);
}
複合データの利点:
- 柔軟性: 複数の情報を1つのキーで管理
- 効率性: 関連データの一元管理
- 拡張性: 新しいフィールドの追加が容易
8. 学習の成果
8.1 習得した概念
- マッピング: ハッシュテーブル構造のデータ型
- ネストしたマッピング: 複数層のマッピング構造
- getter関数: 自動生成されるアクセス関数
- アクセス制御: 内部・外部からのアクセス方法の違い
- ハッシュ関数: キーからインデックスへの変換
8.2 実装スキル
- マッピング機能の実装能力
- ネストしたマッピングの適切な設計
- getter関数の理解と活用
- セキュリティを考慮したデータ管理
8.3 技術的な理解
- ハッシュテーブル: 効率的なデータアクセスの仕組み
- 自動生成: コンパイラによるgetter関数の生成
- アクセス制御: Solidityのカプセル化の実現方法
- ガス最適化: 効率的なマッピング操作
9. 今後の学習への応用
9.1 発展的な機能
- 複雑なネスト: 3層以上のマッピング構造
- 動的マッピング: 実行時のマッピング操作
- マッピングの削除: キー・値ペアの管理
9.2 セキュリティの向上
- リエントランシー攻撃: 再入攻撃への対策
- オーバーフロー: 数値計算の安全性確保
- アクセス制御: 適切な権限管理の実装
9.3 パフォーマンスの最適化
- ガスコスト: 効率的なマッピング操作
- メモリ管理: 適切なデータ構造の選択
- スケーラビリティ: 大量データの効率的な処理
参考:
Discussion