はじめに
『DApps開発入門』という本や色々記事を書いているかるでねです。
今回は、ブロックチェーン上のデータをデータベースのように整理・管理できる仕組みを定義し、効率的で拡張性の高いオンチェーンデータ管理を実現する仕組みを提案しているERC7813についてまとめていきます!
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。
概要
ERC7813は、ブロックチェーン上のデータを柔軟に保存・管理するための「オンチェーンストレージパターン」を提案しています。 データを「テーブル」と呼ばれる構造体で整理し、各テーブルは「キー(key)」と「値(value)」のスキーマ(構造の定義)を持つ「レコード」の集合として構成されます。 これは、一般的なデータベースにおけるテーブルの仕組みと似ています。
この仕組みでは、以下のような要素が定義されています。
-
統一されたコントラクトインターフェース データへのアクセスを行うために、統一的なスマートコントラクトのインターフェースが提供されます。 これにより、異なる開発者やサービスが同じ形式でデータを操作できるようになります。
-
効率的なバイナリエンコーディング形式 静的データ型(サイズが固定のデータ)と動的データ型(サイズが可変のデータ)の両方を対象に、コンパクトな形式でデータをエンコード(符号化)します。 これにより、保存やイベント発火にかかるガスコスト(取引手数料)を抑えることができます。
-
標準化されたイベントによる状態管理 スマートコントラクトの状態が変更されると、標準化された形式のイベントが発行されます。 このイベントを通じて、オフチェーン(ブロックチェーンの外)で動作するインデクサー(データの追跡・整理を行う仕組み)が自動的に状態を同期し、スキーマに基づいたデータの複製を行うことができます。
-
動的なテーブル登録機能 新しいテーブルを、実行時に動的に登録することが可能です。 これは「スキーマメタデータ」を保存する特別なテーブルを通じて行われ、既存のコントラクトや統合システムを壊すことなく、システム全体を拡張できます。
この仕組みにより、ブロックチェーン上のデータ構造をより柔軟に管理し、開発者が安全かつ効率的にデータの拡張や更新を行うことができるようになります。
動機
これまでのスマートコントラクト開発では、オンチェーンのデータ管理に統一された基準が存在しませんでした。 その結果、以下のような課題が発生していました。
- データの構造が固定的で、柔軟な拡張や変更が難しい
- コントラクトのロジックとオフチェーンサービスが密結合しており、更新時に互換性が崩れる
- データ構造を変更すると、既存のシステムや連携部分に不具合が起きやすい
ERC7813が導入するストレージパターンは、こうした問題を解決するために設計されています。
メリット
| 項目 | 説明 |
|---|---|
| 自動インデックス化 | ステート変更時に発行される標準イベントを通じて、オフチェーンのサービスが自動的にデータを追跡・同期できます。これにより、スキーマ情報に基づいたAPIが容易に構築できます。 |
| カスタム読み取り関数の削除 | 特定のデータ取得関数をコントラクトごとに実装する必要がなくなり、統一されたインターフェースを通してデータを参照できます。これにより、コントラクトと外部サービス間の結合度を下げ、開発効率が向上します。 |
| 簡単なアップグレード | 「非構造化ストレージ(unstructured storage)」を採用することで、ストレージレイアウトを固定せずにロジックのみを更新可能です。従来の固定構造に伴うリスクを避けられます。 |
| 柔軟なデータ拡張 | 新しいテーブルを実行時に追加登録できるため、既存のシステムやデータ利用者との互換性を保ちながら機能を拡張できます。 |
| ガスコストの削減 | 効率的なデータパッキング(データの圧縮)により、ストレージ操作やイベント発火時のコストを軽減できます。 |
仕様
定義
Store
// Store: A smart contract that implements the interface proposed by this ERC
// and organizes data in Tables. It emits events for each data operation.この「Store」は、ERCで定義されたインターフェースを実装するスマートコントラクトです。
データを「テーブル」単位で整理し、各データ操作ごとにイベントを発行します。
Storeはデータの読み書き・削除といった操作を一元的に扱う中心的な存在です。
オンチェーン上に存在し、操作に応じてイベントを発行することで、オフチェーンのシステム(インデクサーなど)がそのステートを追跡・同期できるようになります。
Table
// Table: A storage structure that holds Records sharing the same Schema.「Table」は、同一のスキーマ(データ構造)を持つ複数のレコードを格納するためのストレージ構造です。
テーブルには「オンチェーンテーブル」と「オフチェーンテーブル」の2種類があります。
オンチェーンテーブルはブロックチェーン上にステートを保持し、イベントも発行します。
オフチェーンテーブルはデータ自体はオンチェーンに保存せず、イベントのみを発行します。
| 種類 | 説明 |
|---|---|
| オンチェーンテーブル | 状態をブロックチェーン上に保存し、インデクサーのためにイベントを発行します。 |
| オフチェーンテーブル | 状態をオンチェーンに保持せず、イベントのみを発行します。 |
Record
// Record: A piece of data stored in a Table, addressed by one or more keys.「Record」は、テーブルに格納される1つのデータ単位です。
レコードは1つ以上のキーによって一意に特定されます。
各レコードは、キー(識別子)と値(データ本体)から構成されます。
ResourceId
type ResourceId is bytes32;「ResourceId」は各テーブルを一意に識別するための32バイト値です。
この値は最初の2バイトがテーブルの種類を示し、残りの30バイトがユニークな識別子として使われます。
「0x7462("tb")」はオンチェーンテーブル、「0x6f74("ot")」はオフチェーンテーブルを意味します。
| バイト範囲 | 内容 |
|---|---|
0-1 |
テーブルの種類を示す識別子 |
2-31 |
テーブル固有のユニークID |
Schema
type Schema is bytes32;「Schema」はテーブル内のレコード構造(キーと値の型)を定義するデータ型です。
テーブルには「キーのスキーマ」と「値のスキーマ」の2種類があります。
キーのスキーマは固定長のデータ型のみで構成され、値のスキーマは固定長・可変長の両方を含むことができます。
| バイト範囲 | 内容 | 制約 |
|---|---|---|
0-1 |
静的フィールドの総バイト長 | - |
2 |
静的フィールド数 | (28 - 動的フィールド数) 以下 |
3 |
動的フィールド数 | キースキーマは0、値スキーマは最大5 |
4-31 |
各フィールドの型を1バイトで指定 | 動的型は静的型の後に並ぶ必要あり |
SchemaType
enum SchemaType {
// uint系: 0x00〜0x1F
// int系: 0x20〜0x3F
// bytes系: 0x40〜0x5F
// bool: 0x60
// address: 0x61
// 可変長配列やbytes/string型も含む
}「SchemaType」は、スキーマで定義される各フィールドの型を1バイトで表現する値です。
データ型を小さな整数値にマッピングすることで、効率的なバイナリ表現を可能にします。
| 値の範囲 | データ型 |
|---|---|
0x00〜0x1F |
uint8〜uint256 |
0x20〜0x3F |
int8〜int256 |
0x40〜0x5F |
bytes1〜bytes32 |
0x60 |
bool |
0x61 |
address |
0x62〜0x81 |
uint8[]〜uint256[] |
0xC4 |
bytes |
0xC5 |
string |
FieldLayout
type FieldLayout is bytes32;「FieldLayout」は、各静的フィールドのバイト長など、スキーマの具体的な構成情報をエンコードします。
オンチェーン上での処理を最適化するために、各フィールドの長さをあらかじめ計算し保持します。
| バイト範囲 | 内容 | 制約 |
|---|---|---|
0-1 |
静的フィールド全体の長さ | - |
2 |
静的フィールド数 | (28 - 動的フィールド数) 以下 |
3 |
動的フィールド数 | キースキーマは0、値スキーマは最大5 |
4-31 |
各静的フィールドの長さ | - |
EncodedLengths
type EncodedLengths is bytes32;「EncodedLengths」は、各動的フィールドの長さをまとめて符号化した値です。
動的データのデコードに必要な情報を保持します。
これにより、各動的フィールドの長さを個別に保存する必要がなくなり、効率が向上します。
| バイト範囲 | 型 | 内容 |
|---|---|---|
0x00〜0x06 |
uint56 | 動的データ全体の長さ |
0x07〜0x0B |
uint40 | 1つ目の動的フィールドの長さ |
0x0C〜0x10 |
uint40 | 2つ目の動的フィールドの長さ |
| ... | ... | 最大5つまで対応 |
データエンコーディング
レコードデータは、Storeメソッドやイベントで返却・発行される時に以下のルールで符号化されます。
- 静的フィールドはパディング(余分なゼロ埋め)を行わずに連結されます。
- 動的フィールドは長さを含めず、すべてを1つの
EncodedLengthsにまとめます。 - 最大フィールド数は28個で、そのうち動的フィールドは最大5個です。
エンコーディング例
(uint256 id, address owner, string description, uint8[] scores)擬似コード例は以下になります。
bytes memory staticData = abi.encodePacked(id, owner);
bytes memory packedScores = packElementsWithoutPadding(scores);
bytes memory dynamicData = abi.encodePacked(description, packedScores);
EncodedLengths encodedLengths = dynamicData.length;
encodedLengths |= (description.length << (56));
encodedLengths |= (encodedData.length << (56 + 8 * 5));
// Full record
(staticData, encodedLengths, dynamicData)Store インターフェース
interface IStore {
function getRecord(ResourceId tableId, bytes32[] calldata keyTuple)
external view returns (bytes memory staticData, EncodedLengths encodedLengths, bytes memory dynamicData);
function getField(ResourceId tableId, bytes32[] calldata keyTuple, uint8 fieldIndex)
external view returns (bytes memory data);
function getFieldLength(ResourceId tableId, bytes32[] memory keyTuple, uint8 fieldIndex)
external view returns (uint256);
}getRecord
指定されたテーブルIDとキーに対応するレコード全体を取得する関数。 静的データ・動的データ・動的データの長さ情報をすべて返します。
引数
tableId- 対象テーブルの
ResourceId。
- 対象テーブルの
keyTuple- レコードを特定するキー配列。
戻り値
staticData- 静的データのバイナリ。
encodedLengths- 動的データの長さ情報。
dynamicData- 動的データ本体。
getField
指定されたフィールドのデータを単独で取得する関数。 個別のフィールドデータをエンコード済みで返します。
引数
tableId- テーブルの
ResourceId。
- テーブルの
keyTuple- 対象レコードのキー。
fieldIndex- 取得したいフィールドの番号。
戻り値
data- エンコード済みフィールドデータ。
getFieldLength
指定されたフィールドのバイト長を取得する関数。 動的・静的フィールドに関わらず、対象データの長さを返します。
引数
tableId- テーブルの識別子。
keyTuple- 対象レコードのキー。
fieldIndex- 対象フィールドの番号。
戻り値
uint256- バイト長。
ストア操作とイベント
Store_SetRecord
event Store_SetRecord(
ResourceId indexed tableId,
bytes32[] keyTuple,
bytes staticData,
EncodedLengths encodedLengths,
bytes dynamicData
);レコード全体が設定(上書き)されたときに発行されるイベント。 レコードが新規または既存に関係なく、完全に書き換えられた際に発火します。
パラメータ
tableId- 対象テーブルのID。
keyTuple- レコードのキー配列。
staticData- 静的データの符号化結果。
encodedLengths- 動的データの長さ情報。
dynamicData- 動的データの符号化結果。
Store_SpliceStaticData
event Store_SpliceStaticData(
ResourceId indexed tableId,
bytes32[] keyTuple,
uint48 start,
bytes data
);静的データの一部が上書きされたときに発行されるイベント。 静的データの特定位置を上書きする操作が行われた場合に発行されます。
パラメータ
tableId- 対象テーブルのID。
keyTuple- レコードキー。
start- 上書きを開始するバイト位置。
data- 上書き内容。
Store_SpliceDynamicData
event Store_SpliceDynamicData(
ResourceId indexed tableId,
bytes32[] keyTuple,
uint8 dynamicFieldIndex,
uint48 start,
uint40 deleteCount,
EncodedLengths encodedLengths,
bytes data
);動的データの一部が変更されたときに発行されるイベント。 動的データの追加・削除・置換が行われたときに発行されます。
パラメータ
tableId- 対象テーブルのID。
keyTuple- レコードのキー。
dynamicFieldIndex- 動的フィールドの番号。
start- 操作の開始位置。
deleteCount- 削除するバイト数。
encodedLengths- 新しい動的フィールドの長さ情報。
data- 挿入するデータ。
Store_DeleteRecord
event Store_DeleteRecord(ResourceId indexed tableId, bytes32[] keyTuple);レコードが削除されたときに発行されるイベント。 指定されたキーのレコードがテーブルから削除された時に発行されます。
パラメータ
tableId- テーブルのID。
keyTuple- 削除対象のキー。
特殊テーブル:Tables テーブル
このテーブルは、すべてのテーブルの情報を管理する特別なオンチェーンテーブルです。 新しいテーブルを動的に登録するために使用されます。
| キー | 説明 |
|---|---|
tableId |
対象テーブルのResourceId。 |
| 値 | 説明 |
|---|---|
fieldLayout |
各静的データ型のバイト長を符号化したFieldLayout。 |
keySchema |
キーのデータ型を示すスキーマ。 |
valueSchema |
値のデータ型を示すスキーマ。 |
abiEncodedKeyNames |
キー名をABI形式でエンコードしたバイト列。 |
abiEncodedFieldNames |
フィールド名をABI形式でエンコードしたバイト列。 |
このテーブルに保存されたレコードは不変です。
登録時に一度だけStore_SetRecordイベントが発行され、その後は変更されません。
登録時に使用されるtableIdの例は以下です。
ResourceId tableId = ResourceId.wrap(
0x746273746f72650000000000000000005461626c657300000000000000000000
);この仕組みにより、オフチェーンのインデクサーは全テーブルの構造を解釈でき、 構造化されたデータベースのようにブロックチェーンデータを扱うことが可能になります。
補足
Spliceイベント
Store には、レコード全体を上書きするStore_SetRecordイベントがありますが、これだけでは効率面に課題があります。
このため、ERC7813では「部分的な更新」を可能にする2種類のスプライスイベント — Store_SpliceStaticDataとStore_SpliceDynamicData — を導入しています。
部分更新が必要な理由
もし一部のデータしか変更していないにもかかわらずStore_SetRecordイベントを発行すると、全レコードを読み出して再エンコードし、再度発行する必要があります。
これはガスコスト(手数料)と処理量の両面で非効率です。
特に大きな可変長データ(例:長文の文字列や配列)を持つレコードでは、このコストが急増します。
スプライスイベントを使えば、変更箇所だけを最小限のデータで更新できます。 これにより、以下のような効果が得られます。
| 項目 | 効果 |
|---|---|
| 更新コストの削減 | レコード全体を再構築せず、変更箇所だけを処理するためガスコストが低下します。 |
| スケーラビリティの向上 | 大きなデータ構造を扱う場合でもコストが増えにくく、スケールしやすい設計になります。 |
| オフチェーン同期の効率化 | インデクサーが差分だけを処理すればよいため、状態同期が高速になります。 |
つまり、スプライスイベントは、オンチェーンストレージの「効率的な差分更新」を可能にするための仕組みです。
動的型配列の禁止
ERCでは、string[]やbytes[]のような「動的型の配列」をスキーマで定義することを明示的に禁止しています。
これは意図的な制約であり、システムの単純化と効率性を維持するために重要です。
制約の意図
動的型の配列は「ネストされた可変長データ構造」を生むため、ストレージの実装やエンコーディング処理が複雑化します。 これにより、ストレージ操作のガスコストが増加し、データ復元(デコード)も重くなります。
この問題を避けるため、仕様では「フラットなデータ構造」を推奨しています。 つまり、すべてのレコードは一次元的なデータ構造で表現し、複雑な配列構造は別のテーブルを用いて管理します。
代替モデルの例
もしstring[]のような可変配列を保存したい場合は、以下のようなテーブル構成を使用します。
| カラム名 | 型 | 説明 |
|---|---|---|
index |
uint256 |
配列要素のインデックス |
data |
bytes |
実際の要素データ |
このように「1要素=1レコード」として保存すれば、配列の操作をテーブル操作として安全に実現できます。 これにより、設計が単純化され、ガス効率も大幅に向上します。
FieldLayoutによる最適化(FieldLayout Optimization)
FieldLayoutは、テーブル内の各静的フィールド(固定長データ)のバイト長を事前に計算して保持する構造です。
これをスキーマ(Tablesテーブル)内に含めることで、実行時の効率を大幅に向上させることができます。
最適化の仕組み
通常、スマートコントラクトは各フィールドの長さやオフセットを動的に計算します。 しかし、この処理は計算量が多く、特に複雑なスキーマではガス消費が増えます。
FieldLayoutを導入すると、各フィールドのバイト長をあらかじめ記録しておけるため、実行時に再計算する必要がありません。
これにより、ストレージ操作のコストが減り、データの読み書きが効率化されます。
主な利点
| 項目 | 説明 |
|---|---|
| 実行コストの削減 | 長さやオフセットを都度計算する必要がなくなるため、ガス使用量を削減できます。 |
| 読み取り処理の高速化 | オフチェーン側もTablesテーブルからFieldLayout情報を取得し、データを素早くデコードできます。 |
| 実装の一貫性 | すべてのテーブルで統一的な方法でスキーマ情報を扱えるようになります。 |
つまり、FieldLayoutは「スキーマ情報のキャッシュ」として働き、オンチェーン・オフチェーン両方で処理の最適化を実現する仕組みです。
特別なTablesテーブル
最後に、ERC7813では「Tablesテーブル」という特別なオンチェーンテーブルが定義されています。
これはすべてのテーブルのスキーマ情報を登録・管理するメタデータテーブルとして機能します。
意義と役割
このテーブルを設けることで、オフチェーンのインデクサーがテーブル構造(スキーマ)を自動的に把握できるようになります。 スキーマを知らない状態で単なるバイナリデータを処理するよりも、構造を理解した形でデータを扱えるようになるため、 SQLのようなクエリ機能や高度なデータ分析が可能になります。
主なメリット
| 項目 | 説明 |
|---|---|
| スキーマ認識型インデクサーの実現 | 各テーブルのスキーマ情報を基に、高度なデータクエリをサポートするAPIを構築できます。 |
| 実装の簡素化 | 既存の Store イベントを再利用してテーブル登録を行うため、新しいイベントを追加する必要がありません。 |
| 一貫性と拡張性 | すべてのテーブルが同一の Store インターフェース上で管理されるため、システム全体の整合性が保たれます。 |
イメージ図
Tables テーブル
┌─────────────────────┐
│ tableId │→ 各テーブルの識別子
│ fieldLayout │→ 静的フィールドの長さ情報
│ keySchema │→ キーの型情報
│ valueSchema │→ 値の型情報
│ abiEncodedKeyNames │→ キー名(文字列配列)
│ abiEncodedFieldNames│→ フィールド名(文字列配列)
└─────────────────────┘
↓ 登録イベント(Store_SetRecord)
他のすべてのテーブルがここで登録されるこのように、Tablesテーブルはブロックチェーン上での「スキーマ管理の中枢」として機能し、オフチェーンサービスがデータ構造を正確に理解できるようにします。
参考実装
Storeイベントインデックス
以下の例は、単純なインメモリインデクサーが Store イベントを利用して Store 状態をオフチェーンで複製する方法を示しています。
このインデクサーは生のエンコード済みデータに対して動作します。
この例ではTypeScriptを使用していますが、他の言語でも容易に再現できます。
type Hex = `0x${string}`;
type Record = {
staticData: Hex;
encodedLengths: Hex;
dynamicData: Hex;
};
const store = new Map<string, Record>();
// Create a key string from a table ID and key tuple to use in our store Map above
function storeKey(tableId: Hex, keyTuple: Hex[]): string {
return `${tableId}:${keyTuple.join(",")}`;
}
// Like `Array.splice`, but for strings of bytes
function bytesSplice(
data: Hex,
start: number,
deleteCount = 0,
newData: Hex = "0x"
): Hex {
const dataNibbles = data.replace(/^0x/, "").split("");
const newDataNibbles = newData.replace(/^0x/, "").split("");
return `0x${dataNibbles
.splice(start, deleteCount * 2)
.concat(newDataNibbles)
.join("")}`;
}
function bytesLength(data: Hex): number {
return data.replace(/^0x/, "").length / 2;
}
function processStoreEvent(log: StoreEvent) {
if (log.eventName === "Store_SetRecord") {
const key = storeKey(log.args.tableId, log.args.keyTuple);
// Overwrite all of the Record's fields
store.set(key, {
staticData: log.args.staticData,
encodedLengths: log.args.encodedLengths,
dynamicData: log.args.dynamicData,
});
} else if (log.eventName === "Store_SpliceStaticData") {
const key = storeKey(log.args.tableId, log.args.keyTuple);
const record = store.get(key) ?? {
staticData: "0x",
encodedLengths: "0x",
dynamicData: "0x",
};
// Splice the static field data of the Record
store.set(key, {
staticData: bytesSplice(
record.staticData,
log.args.start,
bytesLength(log.args.data),
log.args.data
),
encodedLengths: record.encodedLengths,
dynamicData: record.dynamicData,
});
} else if (log.eventName === "Store_SpliceDynamicData") {
const key = storeKey(log.args.tableId, log.args.keyTuple);
const record = store.get(key) ?? {
staticData: "0x",
encodedLengths: "0x",
dynamicData: "0x",
};
// Splice the dynamic field data of the Record
store.set(key, {
staticData: record.staticData,
encodedLengths: log.args.encodedLengths,
dynamicData: bytesSplice(
record.dynamicData,
log.args.start,
log.args.deleteCount,
log.args.data
),
});
} else if (log.eventName === "Store_DeleteRecord") {
const key = storeKey(log.args.tableId, log.args.keyTuple);
// Delete the whole Record
store.delete(key);
}
}セキュリティ
アクセス制御
ERC7813が定義しているのは、Store からデータを読み取るための関数のみです。
具体的には次の3つです。
| 関数名 | 役割 |
|---|---|
getRecord |
指定されたテーブルIDとキーに基づき、レコード全体を取得する。 |
getField |
指定されたフィールドのみを取得する。 |
getFieldLength |
指定されたフィールドの長さを取得する。 |
これらは読み取り専用の関数です。
一方で、「データを書き込む」、「変更する」、「削除する」といった操作に関する関数は、この標準では定義されていません。
そのため、Store の実装を行う開発者は、それぞれのユースケースに合わせて適切なアクセス制御(Access Control)を設ける必要があります。
アクセス制御が必要な理由
ブロックチェーン上では、すべてのコントラクトやアカウントが公開されており、関数が「公開状態(public/external)」であれば、誰でも呼び出すことができます。 そのため、もしデータ更新のメソッドを無制限に呼び出せるようにしてしまうと、悪意のある第三者がストレージの内容を自由に書き換えるリスクがあります。
実装上の対策例
| 対策 | 説明 |
|---|---|
| ロールベースアクセス制御 | OwnableやAccessControlを利用して、管理者(owner)または特定のロールを持つアドレスのみが書き込みできるようにする。 |
| ホワイトリスト方式 | 許可された特定のコントラクトまたはアカウントのみが Store への書き込み操作を行えるよう制限する。 |
| 認可付き関数呼び出し | トランザクション発行時に署名検証を行い、署名者が正当な権限を持つか確認する。 |
開発者は、自身のアプリケーションがどのようなデータを管理するのか、誰がどのようにそのデータを変更できるのかを明確に定義し、それに基づいたアクセス制御を設計する必要があります。
オンチェーンデータの公開性
もう1つ重要なセキュリティの特徴は、Store に保存されたデータは**完全に公開されているという点です。
通常のスマートコントラクトでは、storage変数は「内部状態」として扱われ、他のコントラクトから直接読み取ることはできません。
もし他のコントラクトに読み取らせたい場合は、開発者が明示的に「読み取り関数」を用意する必要があります。
しかし、ERC7813で定義された Store はそれとは異なります。
Store 内にあるデータは、以下の読み取り関数を通じて、あらゆるコントラクトからアクセス可能です。
| 関数名 | 概要 |
|---|---|
getRecord |
テーブルIDとキーを指定すると、レコード全体を取得できます。 |
getField |
特定のフィールドを単独で取得できます。 |
getFieldLength |
フィールドのバイト長を取得できます。 |
この仕組みにより、オンチェーン上の他のスマートコントラクトも、Store に保存されたデータを直接読み取ることができます。
これは透明性と相互運用性を高める利点がありますが、一方で「秘匿すべき情報を保存してはいけない」という明確な制約も生まれます。
公開データであることの影響
- 他コントラクトからの読み取り
他のスマートコントラクトが
getRecordなどを呼び出すことで、データを参照できます。 これにより、複数のアプリケーション間でデータを共有することが容易になります。 - オフチェーンからのアクセス
誰でもブロックチェーンをスキャンすることで、
Store内のすべてのデータを復元できます。 そのため、個人情報や機密データを格納してはいけません。 - 暗号化が必要なケース どうしても機密データを扱いたい場合は、あらかじめ暗号化した状態で保存し、復号はオフチェーンで行うように設計することが推奨されます。
注意点のまとめ
| 注意点 | 説明 |
|---|---|
| すべてのデータは公開状態 | Store内に保存されたデータは、オンチェーン・オフチェーン問わず誰でも閲覧可能です。 |
| センシティブ情報の格納禁止 | 個人情報、秘密鍵、パスワード、内部ロジックに関するデータなどを直接保存してはいけません。 |
| 暗号化保存を検討 | 機密性を求める場合は、暗号化後のデータのみを保存する方式を取る必要があります。 |
引用
alvarius (@alvrs), dk1a (@dk1a), frolic (@frolic), ludens (@ludns), vdrg (@vdrg), yonada yonada@proton.me, "ERC-7813: Store, Table-Based Introspectable Storage [DRAFT]," Ethereum Improvement Proposals, no. 7813, November 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7813.
最後に
今回は「ブロックチェーン上のデータをデータベースのように整理・管理できる仕組みを定義し、効率的で拡張性の高いオンチェーンデータ管理を実現する仕組みを提案しているERC7813」についてまとめてきました! いかがだったでしょうか?
質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!
他の媒体でも情報発信しているのでぜひ他も見ていってください!
