# 分散ストレージ ###### tags: `Yellow paper` `分散ストレージ` # 概要 SRUネットワークで必要なデータは、分散ストレージを利用し保存する。 # 分散ストレージとは 分散ストレージとは、複数のサーバがネットワークを経由してデータを分散し保存・共有しつつ処理することができる分散システムのことである。クライアントからは、データを透過的に利用することができる。 従来のデータストレージでは、セキュリティやパフォーマンスを保ったままのストレージのスケーリングが難しく、増え続けるデータに対し迅速に対応することに課題があった。 一方分散ストレージでは、データをサーバ間で分散して保持することができ、データが過大になった場合でも、シームレスにスケーリングすることができる。また単一障害点を回避する用途としても有用であり、高可用性と信頼性を必要とするシステムに向いている。 ![](https://i.imgur.com/QdlT3YO.png) # 分散ストレージの使用背景と動機 SRUネットワークの高可用性と信頼性がネットワークを使用するサービスの全体に影響するため、SRUネットワークは、一時もシステムが停止することなく正常に稼働する必要がある。SRUネットワーク上で増え続けるデータ需要に対し、パフォーマンスとセキュリティを保ったままスケーリングすることが求められる。 また、一度データが消失するとユーザの資産やサービスの資産の消失に繋がるため、安全かつ完全なデータの提供を担保する必要があるため分散ストレージをデータの保存場所として使用する。 # 分散ストレージに保存するデータ種別 分散ストレージ上で保存するデータに関して、以下の4種類がある。それぞれのデータの特徴と保存期間は以下の通りである。 ## Proof zkのproofデータを扱う。分散ストレージにアップロード後、1日間保存する。保存から1日経過後にデータを削除する。 `Proof` | field | type | description | | -------- | -------- | -------- | | zkProof | `ZkProof` | zkVerify用のProof | | publicInput | `PublicInput` | zkVerify用のPublicInput | `ZkProof` ゼロ知識証明用のProofのインターフェイスに沿う。 `PublicInput`   | filed | type | description | | -------- | -------- | -------- | | currentUserMerkleRoot | bytes32 | 反映前のユーザのマークルルート | | currentUserStateProof | bytes32[] | ユーザのrootHashより上についてのsibiling | | currentUserStateRoot | bytes32 | 反映後のユーザのマークルルート | example. ``` { proof: { zkProof: { x... }, publicInput: { currentUserMerkleRoot: '0xe51feeeefd14f6876839129c74b73fc1f79701d2e3b65f099e875b9e64cf59f4', currentUserStateProof: [ '0xe51feeeefd14f6876839129c74b73fc1f79701d2e3b65f099e875b9e64cf59f4', '0xe51feeeefd14f6876839129c74b73fc1f79701d2e3b65f099e875b9e64cf59f4', ... ], currentUserStateProof: '0xe51feeeefd14f6876839129c74b73fc1f79701d2e3b65f099e875b9e64cf59f4' } } } ``` ## Commit ブロック内のトランザクションによるストレージの差分情報を扱う。分散ストレージにアップロード後、7日間保存する。保存から7日経過後にデータを削除する。 ## OnetimeUserState AssetStorageとして保持されている。SRUネットワーク上のユーザ資産を扱う。分散ストレージにアップロード後、永続保存する。このOnetimeUserStateを扱うユーザがマージを行うと消える。 ``` [ ('Contract Address + その contract 内で宣言された変数のindex', diff), ('Contract Address + その contract 内で宣言された変数のindex', diff) ] ``` ## BackupUserState AssetStorageに保存されている。SRUネットワーク上のユーザ資産を扱う。通常の利用用途はないが、ユーザ自身がローカルのウォレットにバックアップしているEOAUserStateを失った場合備え、バックアップとして暗号化し分散ストレージ上に保存しておく。分散ストレージにアップロード後、EOAUserStateが更新がされるまで永続保存する。各ユーザが自身のEOAUserStateを更新する際に、分散ストレージ上のBackupUserStateも同様に更新する。 EOAUserStateはLeafにkeyをOwnerAddress、valueをContractAddressとそのコントラクトでの変数の定義位置を連結したハッシュ化したものを持つツリー構造であり、ツリーをそのまま分散ストレージに保存する。また外部分散ストレージへのコンテンツ識別子(CID)をローカルのデータベースに保存する。 オペレータのローカルのデータベースに保存するUserAddressと、バックアップ用として保存した外部分散ストレージのCIDのKey、Valueストア | Key | Value | | -------- | -------- | | UserAddress(ユーザのアドレス) | CID(コンテンツ識別子) | ex. ``` key: 0x65172bcb948583b197ca608d33f96d5ce35cb1def0a9c73bcf42aaf1dd972b55 value: QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR ``` ## GlobalState CommonlySharedStorageに保存されている。SRUネットワーク上にデプロイされたコントラクトの状態を扱う。分散ストレージにアップロード後、永続的に保存する。GlobalUserStateはLeafにkeyをContractAddressとそのコントラクトでの変数の定義位置を連結したハッシュ化し、valueにbytes32のデータを持つツリー構造であり、ツリーをそのまま分散ストレージに保存する。また外部分散ストレージへのコンテンツ識別子(CID)をローカルのデータベースに保存する。 オペレータのローカルのデータベースに保存するBlockNumberとGlobalStateのKey, Valueストア | Key | Value | | -------- | -------- | | BlockNumber | GlobalState | ``` key: 27017 value: GlobalState ``` オペレータのローカルのデータベースに保存するBlockNumberと、バックアップ用として保存した外部分散ストレージのCIDのKey、Valueストア | Key | Value | | -------- | -------- | | BlockNumber | CID(コンテンツ識別子) | ex. ``` key: 27017 value: QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR ``` # バックアップ用の外部分散ストレージへの保存とコンテンツ識別子 外部分散ストレージのGateway用に構築したノードを通して外部分散ストレージへのアップロードおよびダウンロードを行う。各オペレータは、外部分散ストレージへの保存したデータへのコンテンツ識別子(CID)をローカルのデータベースに保存する。基本的には永続的に保存するデータのみ、バックアップとして外部の分散ストレージ上に保存する。 # Verkle Treeによる分散ストレージの作成 オペレーター同士のストレージデータの同期時に、それぞれのオペレータのストレージに保存されている最新のBlockNumberを送信し合う。お互いどちらも持っている最新のBlockNumberのVerkle Treeで検証を開始する。以降Verkle Treeはどちらも持っている最新のものを指す。 Verkle Tree内からお互いにランダムに選択したリーフの値に対する、Proofを要求する。オペレータは、それぞれProofを受け取り検証を行う。お互いのProofの検証が正しい場合は、同期を行う予定のVerkleTreeのデータの検証を行い、差分以外のデータが正しいことを検証する。差分以外のリーフがあるTreeをランダムに抽出しVerkleTreeのProof検証を行う。検証が正しい場合は、データの同期を行う。これによって、全てのオペレータノードが正しい同一のデータを持つことを担保した分散ストレージが作成できる。 ```mermaid sequenceDiagram participant O1 as Operator1 participant O2 as Operator2 autonumber O1 ->> O2 : checkCurrentBlockNumber() O2 ->> O1 : Response: BlockNUmber O1 ->> O2 : requestProof(leafData) O2 ->> O1 : Response: Proof O1 ->> O1 : verify(proof) O2 ->> O1 : requestProof(leafNumber) O1 ->> O2 : Response: Proof O2 ->> O2 : verify(proof) O1 ->> O2 : Sync(Array<leafData>) O2 ->> O1 : Sync(Array<leafData>) ``` # 各種データ管理サービスクラスについて それぞれのデータ種別ごとに、サービスクラスを作成する。それぞれのデータを管理するプログラムのことを便宜上サービスクラスと呼ぶ。SRUネットワークのbootstrap時にサービスクラスを初期化およびセットアップする。もしsnapshotから起動する場合は、セットアップ時にsnapshotのデータを読み込ませるためにsnapshotのデータを各サービスクラスに渡すこととする。 **errorオブジェクトのインターフェイス** | Field | Value | | -------- | -------- | | message | エラーメッセージ | | code | 発生したエラーに対応するエラーコード | | data | それぞれのエラーごとにエラー詳細を記載した任意のオブジェクトを入れる | ## Proof service class Proofを管理するサービスクラスである。 | 項目 | 値 | | -------- | -------- | | 保存期間 | 1日 | ### データ構造 ローカルDBに保存している、blockNumberと対応するTxProofのKey, Valueデータ | Key | Value | | -------- | -------- | | BlockNumber | TxProof | ブロックナンバーと対応するTxProofのMapデータ `TxProof` ``` Array<transactionHash, Proof> ``` > 備考として、CommitデータごとにTransactionに対応するProof一覧を取得する。 ### getProof(blockNumber: Int): `Array<TxProof>` blockNumberを元に、TxProofをストレージから返却する。blockNumberに対応するTxProofがない場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** オペレータがTransactionをRollup時に、分散ストレージからblockNumberを元にProofを取得する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : getProof(blockNumber) D ->> O : Response: Array<TxProof> ``` ### saveProof(blockNumber: Int, proofs: `Array<TxProof>`): `Array<TxProof>` 対応するblockNumberとTxProofを渡しデータをストレージに保存する。ストレージの最新blockNumberがincrementされるようなIntのリクエストデータのみ受け付ける。レスポンスとして受け付けたTxProofを返却する。エラーが発生した場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** オペレータがオフチェーンコミット時に、ブロックに含まれるTransactionHashと対応するProofを分散ストレージに送信し保存する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : saveProof(blockNumber, Array<TxProof>) D ->> O : Response: Array<TxProof> ``` ### onDelete(blockNumber: Int) publisherからのイベントをサブスクライブし、blockNumberを元にProofをローカルDBから削除する。エラーが発生した場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** DeletePublisherが、Proofを分散ストレージに保存してから1日後にProof Service Classに削除イベントをPublishする。 **シーケンス図** ```mermaid sequenceDiagram participant O1 as Operator(DeletePublisher service class) participant O2 as Operator(Proof service class) autonumber O1 ->> O2 : emit("deleteProof", blockNumber) O2 ->> O2 : onDelete(blockNumber) ``` ## Commit service class Commitを管理するサービスクラスである。 | 項目 | 値 | | -------- | -------- | | 保存期間 | 7日 | ### データ構造 ローカルDBに保存しているblockNumberと対応するCommitのKey、Valueデータ | Key | Value | | -------- | -------- | | BlockNumber | Commit | ### getCommits(prevBlockNumber: Int): `Array<Commit>` prevBlockNumberから最新のblockNumber間での間の全てのCommitをストレージから返却する。 **ユースケース** オペレータがTransactionをRollup時に、分散ストレージからCommitを取得する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : getCommits(prevBlockNumber) D ->> O : Response: Array<Commit> ``` ### getLatestCommit(): Commit 最新のCommitを返却する。初期化後に最新のCommitがまだ保存されていない場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** オペレータがオフチェーンコミット時に、最新のCommitを分散ストレージから取得する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : getLatestCommit() D ->> O : Response: Commit ``` ### saveCommit(blockNumber: Int, commit: Commit): Commit 対応するblockNumberとCommitを渡しデータをストレージに保存する。ストレージ上の最新blockNumberがincrementされるようなIntのリクエストデータのみ受け付ける。レスポンスとして受け付けたCommitを返却する。エラーが発生した場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** オペレータがブロックを作成時に、分散ストレージにCommitを送信し保存する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : saveProof(blockNumber, Commit) D ->> O : Response: Commit ``` ### onDelete(blockNumber: Int) publisherからのイベントをサブスクライブし、blockNumberを元にCommitをローカルDBから削除する。エラーが発生した場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** DeletePublisherが、Commitを分散ストレージに保存してから7日後にCommit Service Classに削除イベントをPublishする。 **シーケンス図** ```mermaid sequenceDiagram participant O1 as Distribute Storage(DeletePublisher service class) participant O2 as Distribute Storage(Commit service class) autonumber O1 ->> O2 : emit("deleteCommit", blockNumber) O2 ->> O2 : onDelete(blockNumber) ``` ## OnetimeUserState service class OnetimeUserStateを管理するサービスクラスである。 | 項目 | 値 | | -------- | -------- | | 保存期間 | 永続 | ### データ構造 ローカルDBに保存している、blockNumberと対応するOnetimeUserStateのKey, Valueデータ | Key | Value | | -------- | -------- | | onetimeAddress | OnetimeUserState | ### getOnetimeUserState(onetimeAddress: String): OnetimeUserState onetimeAddressを元に、対応するOnetimeUserStateをストレージから返却する。blockNumberに対応するデータがない場合は、エラータイプおよびエラーメッセージを返却する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : getOnetimeUserState(onetimeAddress) D ->> O : Response: OnetimeUserState ``` ### saveOnetimeUserState(onetimeAddress: String, onetimeUserState: OnetimeUserState): OnetimeUserState 対応するblockNumberとOneTimeUserStateを渡しデータをストレージに保存する。ストレージの最新blockNumberがincrementされるようなIntのリクエストデータのみ受け付ける。レスポンスとして受け付けたにOneTimeUserStateを返却する。エラーが発生した場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** オペレータがオフチェーンコミット時に、userStateが変更になった場合にonetimeAddressごとに対応するOnetimeUserStateを分散ストレージに送信し保存する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : saveOnetimeUserState(onetimeAddress, OnetimeUserState) D ->> O : Response: OnetimeUserState ``` ## BackupUserState service class BackupUserStateを管理するサービスクラスである。 | 項目 | 値 | | -------- | -------- | | 保存期間 | 永続 | ### データ構造 ローカルDBに保存しているuserAddressと対応するBackupUserStateのKey、Valueデータ | Key | Value | | -------- | -------- | | userAddress | BackupUserState | ### getBackupUserState(userAddress: String): BackupUserState userAddressを元に、対応するBackupUserStateをストレージから返却する。userAddressに対応するデータがない場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** ユーザが自身のローカルWalletからuserStateを失ってしまった場合に、分散ストレージからBackupされている自身のUserStateを取得する。 **シーケンス図** ```mermaid sequenceDiagram actor U as User participant O as Operator participant D as Distribute Storage autonumber U ->> O : getBackupUserState(userAddress) O ->> D : fetchBackupUserState(userAddress) D ->> O : Response: BackupUserState O ->> U : Response: BackupUserState ``` ### saveBackupUserState(userAddress: String, backupUserState: BackupUserState): BackupUserState 対応するuserAddressとBackupUserStateを渡しデータをストレージに保存する。既にuserAddressと対応するBackupUserStateが存在する場合は、既存のBackupUserStateに上書きして保存をする。レスポンスとして受け付けたBackupUserStateを返却する。エラーが発生した場合は、エラータイプおよびエラーメッセージを返却する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : saveBackupUserState(userAddress, BackupUserState) D ->> O : Response: BackupUserState ``` ## GlobalState service class GlobalStateを管理するサービスクラスである。 | 項目 | 値 | | -------- | -------- | | 保存期間 | 永続 | ### データ構造 ローカルDBに保存している、blockNumberと対応するGlobalUserStateのKey, Valueデータ | Key | Value | | -------- | -------- | | blockNumber | GlobalUserState | ### getGlobalState(blockNumber: Int): GlobalState blockNumberを元に、GlobalStateを返却する。オペレータは、blockNumberに紐づくGlobalStateを分散ストレージから取得する。blockNumberに対応するGlobalStateがない場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** > TODO **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : getGlobalState(blockNumber) D ->> O : Response: GlobalState ``` ### getLatestGlobalState(): GlobalState 最新のGlobalStateを返却する。オペレータは、最新のblockNumberに紐づくGlobalStateを分散ストレージから取得する。最新のGlobalStateがまだ保存されていない場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** ユーザがTransctionを投げる時に、オペレーターはStateDiffを計算するために分散ストレージからGlobalStateを取得する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : getLatestGlobalState() D ->> O : Response: GlobalState ``` ### saveGlobalState(blockNumber: Int, globalState: GlobalState): GlobalState 対応するblockNumberとGlobalStateを渡しデータをストレージに保存する。ストレージの最新blockNumberがincrementされるようなIntのリクエストデータのみ受け付ける。レスポンスとして受け付けたGlobalStateを返却する。エラーが発生した場合は、エラータイプおよびエラーメッセージを返却する。 **ユースケース** オペレータがオフチェーンコミット時に、globalStateが変更になった場合にblockNumberに対応するGlobalStateを分散ストレージに送信し保存する。 **シーケンス図** ```mermaid sequenceDiagram participant O as Operator participant D as Distribute Storage autonumber O ->> D : saveGlobalStateCID(blockNumber, GlobalState) D ->> O : Response: GlobalState ``` ## DeletePublisher service class 各データ管理用のサービスクラスのデータ保存時に、deleteする時間と対象データのblockNumberをkeyにしたデータをTimeHashMapTableに刻む。 常にループしているHandler関数がTimeHashMapTableに刻まれたデータでdelete時間を過ぎているものがあれば、対象サービスクラスにdeleteイベントを流す。イベントpush後にTimeHashMapTableから行ったイベントのデータを取り除く。 **Proof削除用のTimeHashMapTableデータ** ``` deleteProofHashTable: Map<timestamp, blockNumber> ex. { timestamp: 20017 } ``` **シーケンス図** ```mermaid sequenceDiagram participant O1 as Operator(DeletePublisher service class) participant O2 as Operator(Proof service class) autonumber O1 ->> O2 : emit("deleteProof", blockNumber) O2 ->> O2 : onDelete(blockNumber) ``` **Commit削除用のTimeHashMapTableデータ** ``` deleteCommitHashTable: Map<timestamp, blockNumber> ex. { timestamp: 20017 } ``` **シーケンス図** ```mermaid sequenceDiagram participant O1 as Operator(DeletePublisher service class) participant O2 as Operator(Commit service class) autonumber O1 ->> O2 : emit("deleteCommit", blockNumber) O2 ->> O2 : onDelete(blockNumber) ``` # パフォーマンス **パフォーマンス** | 項目 | 値 | | -------- | -------- | | GETリクエスト | 5000 | | POSTリクエスト | 1000 | | 平均応答速度(GET) | 250millisecond | 分散ストレージへのGETリクエストへの応答速度は平均250millisecondとする。 リクエストの処理に関して、1秒あたり1000のPOST処理に対応させること。また、1秒あたり5000のGET処理に対応すさせること。分散のPOST処理に関しては、秒数あたりのリクエストが多い場合は、スループットの増加に伴い応答の平均速度に落ち着くように複数のリクエストをまとめたバルク処理を行う。 # サイズの制限 **サイズ制限** | 項目 | 値 | | -------- | -------- | | アップロードMaxサイズ | 20GB | 分散ストレージのサイズに制限はないが、アップロードできるデータの制限を20GBとする。大きいデータの場合は、分割してアップロードすることとする。 # セキュリティとデータ取得権限 **ローカルDBへのアクセス権** | 項目 | 値 | | -------- | -------- | | データの取得 | オペレータ | | データの追加 | オペレータ | | データの削除 | 内部のHandler処理のみ | 目的に応じて各種データにアクセスできる権限を制限する。ローカルDBからのデータの取得に関しては、オペレータのみ取得することができる。データの追加もオペレータのみ行うことができる。またデータの削除に関しては、外部から処理を呼び出すことはできず、内部の削除handlerでのみ実行することができる。ユーザのデータ取得の際には、オペレータ経由でデータを受け取る。