第 6 章 データ伝送

6.1 双方向データフロー

DTP 実装は以下の 2 つの独立したデータフロー方向をサポート しなければならない

方向名称送信側受信側
Terminal → Fayデータ収集(Collection)SlaveMaster
Fay → Terminalデータ注入(Injection)MasterSlave

両方向は以下を満たさ なければならない

  1. 同一の LogicalFrame 形式と処理フローを使用する。
  2. 独立したシーケンス番号空間を維持する。
  3. 独立した再開状態(未確認 Fragment キャッシュ、最高確認済みシーケンス番号など)を維持する。
  4. 互いに干渉しない:一方の方向の状態変化は他方の方向に影響を与えて はならない

6.2 データ収集フロー(Terminal → Fay)

データ収集は以下のフローに従わ なければならない

6.2.1 送信側(DTP_Slave)のフロー

  1. 端末アプリケーションが提出するデータを受信する。
  2. Fragment を構築する: a. 新しい Fragment_ID(UUID v4)を生成する。 b. agreementId を現在のアクティブな Agreement の Agreement_ID に設定する。 c. originTimestamp をデータが実際に発生した時刻の UTC ミリ秒タイムスタンプに設定する。現在時刻を使用 してはならない。 d. 構造化された ContextMetadata を付加する(第 4.11 節を参照)。 e. dagDependencies を付加する(空配列で もよい)。
  3. DAG 依存関係を検証する: a. DAG Manager を呼び出して循環を形成しないことを検証する(第 6.7 節を参照)。 b. 循環を検出した場合、その Fragment を拒否し DAG_CYCLE_DETECTED エラー(4001)を返 さなければならない
  4. LogicalFrame を構築する: a. frameType = "data" を設定する。 b. Agreement_ID 圧縮規則を適用する(第 4.5 節を参照)。 c. 単調増加の sequenceNumber を割り当てる(データ収集方向)。 d. 暗号化メタデータを設定する。
  5. Payload を暗号化する:CAP で事前ネゴシエーションされた鍵を使用して Fragment の data フィールドを暗号化する。
  6. LogicalFrame をシリアライズする。
  7. Transport_Adapter を呼び出してバイナリデータを送信する。
  8. Fragment を未確認キャッシュに加える(第 8.2 節を参照)。

6.2.2 受信側(DTP_Master)のフロー

  1. Transport_Adapter から渡されたバイナリデータを受信する。
  2. LogicalFrame にデシリアライズする。失敗した場合、フレームを破棄し FRAME_DESERIALIZATION_FAILED エラー(1001)を返 さなければならない
  3. プロトコルバージョンを検証する(第 10 章を参照)。
  4. Agreement_ID を解析する(第 4.5 節の圧縮規則を適用)。未知の Agreement に関連付けられている場合、フレームを破棄し AGREEMENT_NOT_FOUND エラー(3001)を返 さなければならない
  5. Payload を復号する。失敗した場合、フレームを破棄し DECRYPTION_FAILED エラー(2001)を返 さなければならない
  6. Fragment にデシリアライズする。
  7. DAG 依存関係を検証する: a. すべての依存対象 Fragment が既に存在する場合、受け入れて accepted としてマーク しなければならない。 b. 依存対象 Fragment がまだ到着していない場合、pending としてマークしてキャッシュ しなければならない(第 6.7 節を参照)。 c. 循環を検出した場合、拒否し DAG_CYCLE_DETECTED エラー(4001)を返 さなければならない
  8. 受信状態を更新する:その Fragment の sequenceNumber がインクリメントされていれば、その方向の最高受信済みシーケンス番号として設定する。
  9. 確認応答を送信する(第 8 章を参照)。
  10. Fragment を Personal Data Heap に永続化する。

6.3 データ注入フロー(Fay → Terminal)

データ注入は以下のフローに従わ なければならない

6.3.1 送信側(DTP_Master)のフロー

  1. Personal Data Heap からクエリし、Agreement の dataRange でフィルタリングして最小化データセットを生成する。
  2. Fragment を構築する(6.2.1 のステップ 2-3 と同じ)。
  3. LogicalFrame を構築し、Payload を暗号化し、シリアライズし、送信する(6.2.1 のステップ 4-8 と同じ)。ただしデータ注入方向のシーケンス番号空間を使用する。

6.3.2 受信側(DTP_Slave)のフロー

  1. デシリアライズし、バージョンを検証し、Agreement_ID を解析し、復号し、Fragment にデシリアライズし、DAG を検証する(6.2.2 のステップ 1-7 と同じ)。
  2. 受信状態を更新し、確認応答を送信する(6.2.2 のステップ 8-9 と同じ)。
  3. Fragment を端末アプリケーションに引き渡す。

6.4 Agreement_ID の圧縮伝送

実装は第 4.5 節で定義された Agreement_ID 圧縮規則に厳密に従わ なければならない

6.4.1 圧縮の例

以下は仕様に準拠した伝送シーケンスである:

Fragment 1: agreementId = "abc-123"   (新しい Agreement、完全 ID)
Fragment 2: agreementId = null        ("abc-123" を継承)
Fragment 3: agreementId = null        ("abc-123" を継承)
Fragment 4: agreementId = "def-456"   (新しい Agreement に切替、完全 ID)
Fragment 5: agreementId = null        ("def-456" を継承)

6.4.2 圧縮の制約

実装は以下を満たさ なければならない

  1. 圧縮を行わないことを選択 してもよい(つまり全 Fragment が完全な Agreement_ID を携帯する)。
  2. 圧縮を選択する場合、4.5 節の規則に厳密に従わ なければならない
  3. 受信側は圧縮と非圧縮の両モードを同時にサポート しなければならない
  4. コンテキスト Agreement_ID はセッションの一時停止からの復旧後、null にリセット しなければならない(つまり復旧後の最初の Fragment は完全 ID を携帯 しなければならない)。

6.5 シーケンス番号管理

6.5.1 単調増加

各 Fragment の sequenceNumber は以下を満たさ なければならない

  1. 単一セッション内で単調増加する。
  2. 厳密に連続する べきである(つまり毎回 1 ずつ増加する)。
  3. 重複や後退をしては ならない

6.5.2 双方向の独立性

データ収集方向とデータ注入方向は独立したシーケンス番号空間を維持 しなければならない

データ収集方向 (collection):  seq 1, 2, 3, 4, 5, ...
データ注入方向 (injection):   seq 1, 2, 3, 4, 5, ...

実装は 2 つの方向間でシーケンス番号空間を共有 してはならない

6.5.3 再起動とセッション

シーケンス番号は以下を満たさ なければならない

  1. 新しいセッション開始時、シーケンス番号はリセット しなければならない(0 または 1 から開始 すべきである)。
  2. セッションが Suspended から復旧する際、シーケンス番号は一時停止前の値を保持 しなければならず、リセット してはならない
  3. シーケンス番号が実装定義の最大値に近づいた場合、送信側はオーバーフローを避けるために能動的に新しいセッションを作成 しなければならない

6.6 オリジナルタイムスタンプの保全

実装は伝送過程で Origin_Timestamp が変更されないことを保証 しなければならない

  1. 送信側はデータが実際に発生した時刻を Origin_Timestamp として記録 しなければならない
  2. シリアライズ、暗号化、伝送、復号、デシリアライズで Origin_Timestamp を変更 してはならない
  3. 受信側は受信した Origin_Timestamp を変更 してはならない
  4. 受信側が永続化する際、Origin_Timestamp を保持 しなければならない

実装はフレームヘッダーの外に独立した「伝送タイムスタンプ」フィールドを維持 してもよい(実装定義)が、Origin_Timestamp と混同 してはならない

6.7 DAG 依存関係の処理

6.7.1 Fragment を DAG に追加

受信側は Fragment を受信した際、DAG Manager を介して依存関係を処理 しなければならない

  1. Fragment の dagDependencies を抽出する。
  2. 各依存関係について: a. 対象の Fragment_ID が DAG に既に存在するかを確認する。 b. そのエッジを追加すると循環を形成するかを確認する。
  3. 確認結果に応じて以下の 3 つの結果のいずれかを返す:
結果意味後続のアクション
acceptedすべての依存関係が解決済み、循環なしFragment を DAG に追加 しなければならない
pending一部の依存関係が未解決(対象 Fragment 未到着)、循環なしその Fragment をキャッシュし、依存関係の解決を待つ 必要がある
rejected循環を検出その Fragment を拒否し、DAG_CYCLE_DETECTED エラー(4001)を返 さなければならない

6.7.2 遅延解決

Fragment が pending 状態の場合:

  1. 実装はその Fragment キャッシュ内で依存関係を継続的に待機 しなければならない
  2. 依存対象 Fragment が到着した際、キャッシュ内の関連するすべての pending Fragment を再評価 しなければならない
  3. 最大キャッシュ時間を設定 してもよい(実装定義)。タイムアウト後、DAG_DEPENDENCY_UNRESOLVED エラー(4002)を返して破棄 すべきである

6.7.3 循環検出アルゴリズム

実装は新しい Fragment を追加する前に循環を検出 しなければならない。検出アルゴリズムは DFS(深さ優先探索)または Tarjan アルゴリズムを使用 すべきである

具体的なアルゴリズム:新しい Fragment の各依存対象から出発して DFS を実行する。依存対象から新しい Fragment 自身に到達できる場合、循環を形成する。

6.8 複数 Agreement の交錯伝送

複数のアクティブな Agreement が共存する場合、送信側は以下を満たさ なければならない

  1. 各 Fragment のフレームヘッダーで Agreement_ID を介して正しい Agreement に関連付ける。
  2. Agreement を切り替える際(つまり次の Fragment が異なる Agreement に属する場合)、その Fragment に完全な Agreement_ID を携帯 しなければならない(つまり null 圧縮は使用できない)。
  3. Agreement の priority に従って送信順序をスケジューリング すべきである