🚀

分散型動画配信に挑むEthereumとIPFS連携設計

に公開

分散型動画配信に挑むEthereumとIPFS連携設計 🚀

1. はじめに

中央集権型プラットフォームが抱える検閲リスク、サービス停止、データ所有権の問題が注目される中、分散型動画ストリーミングは新しいユースケースとして台頭しています。本記事では「EthereumスマートコントラクトとIPFSを活用した分散型動画ストリーミングプラットフォームの設計と実装」をテーマに、実務開発者が直面する設計・実装課題を解決するための具体的な方法論を解説します。

本記事の特徴:

  • EthereumとIPFSの連携による動画配信基盤の設計パターン
  • Solidityによるスマートコントラクト実装例
  • Node.js/Reactによる動画アップロード・再生UIのサンプル
  • スケーラビリティ、セキュリティ、コスト効率を考慮したベストプラクティス
  • 他の分散ストレージ技術や従来型CDNとの徹底比較

この記事を読むことで得られること:

  • 分散型動画配信の本質課題と、その実装現場での突破口
  • Ethereum+IPFS連携の具体的な実装手順(コード付き)
  • スマートコントラクトを活用した動画管理の最適解
  • 今後の分散型ストリーミングサービス設計への応用方法

参考資料:


2. 分散型動画配信の基礎技術とアーキテクチャ

2.1 主要概念

  • Ethereum:分散型アプリケーションを構築するためのスマートコントラクト・プラットフォーム。動画メタデータや所有権、アクセス権の管理に活用。
  • IPFS (InterPlanetary File System):P2P型の分散ファイルストレージ。動画本体の保存に最適。
  • Frontend (React):ユーザーが動画をアップロード・視聴するためのインターフェース。

2.2 アーキテクチャ図

+---------------------------+      +-------------------+
|    User (Web UI)          | <--> |  Ethereum         |
|  (動画アップ/再生)          |      |  (スマートコントラクト) |
+---------------------------+      +-------------------+
            |                                 |
            v                                 |
+---------------------------+                 |
|        Backend            |-----------------+
| (Node.js/React)           |
+---------------------------+
            |
            v
+---------------------------+
|         IPFS              |
| (動画ファイルストレージ)    |
+---------------------------+

流れ:

  1. フロントエンドで動画をアップロード
  2. IPFSに動画を保存し、CID(Content Identifier)を取得
  3. EthereumスマートコントラクトにCIDや動画タイトル等を記録
  4. ユーザーはEthereumから動画リストを取得し、IPFSから動画を再生

参考資料:


3. 実践的実装ガイド

3.1 環境準備とセットアップ

バージョン指定例:

  • Node.js: v18.17.0
  • Solidity: ^0.8.20
  • Truffle: v5.9.0
  • IPFS HTTP API: v59.0.0
  • React: 18.2.0

必要パッケージのインストール

# Node.jsプロジェクト初期化
npm init -y

# Truffle, Ganache, IPFS HTTP client, React, web3
npm install truffle@5.9.0 ganache@7.8.1 web3@4.3.0 ipfs-http-client@59.0.0 react@18.2.0

IPFSノードの起動

npm install -g ipfs
ipfs daemon

参考: IPFSインストール手順

ローカルEthereumノードの起動(Ganache)

npx ganache --port 8545

参考: Ganache Quickstart


3.2 基本的な機能の実装

3.2.1 スマートコントラクトの作成(Solidity)

contracts/VideoRegistry.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VideoRegistry {
    struct Video {
        address uploader;
        string title;
        string ipfsCid;
        uint timestamp;
    }

    Video[] public videos;

    event VideoUploaded(address indexed uploader, string title, string ipfsCid, uint timestamp);

    function uploadVideo(string calldata title, string calldata ipfsCid) external {
        videos.push(Video(msg.sender, title, ipfsCid, block.timestamp));
        emit VideoUploaded(msg.sender, title, ipfsCid, block.timestamp);
    }

    function getVideoCount() external view returns (uint) {
        return videos.length;
    }

    function getVideo(uint index) external view returns (address, string memory, string memory, uint) {
        require(index < videos.length, "Index out of bounds");
        Video storage v = videos[index];
        return (v.uploader, v.title, v.ipfsCid, v.timestamp);
    }
}

ポイント:

  • 動画ごとにuploader, title, ipfsCid, timestampを記録
  • uploadVideoで新規動画登録、getVideoで動画情報取得

参考: Solidity Structs
Solidity Event機能

3.2.2 IPFSへの動画アップロード(Node.js)

upload.js

const { create } = require('ipfs-http-client');
const fs = require('fs');

const ipfs = create({ url: 'http://localhost:5001' }); // ローカルIPFSノード

async function uploadVideo(filePath) {
  const file = fs.readFileSync(filePath);
  const result = await ipfs.add({ path: filePath, content: file });
  console.log('IPFS CID:', result.cid.toString());
  return result.cid.toString(); // 例: Qm...
}

// 使用例
uploadVideo('./sample.mp4').catch(console.error);

参考: ipfs-http-clientドキュメント
IPFSファイルAPI

3.2.3 フロントエンドからの動画一覧取得・再生(React)

src/App.js

import React, { useEffect, useState } from 'react';
import Web3 from 'web3';

const CONTRACT_ADDRESS = '0xYourContractAddress';
const ABI = [ /* 上記Solidity ABIをここに貼り付け */ ];

const IPFS_GATEWAY = 'https://ipfs.io/ipfs/';

function App() {
  const [videos, setVideos] = useState([]);
  const web3 = new Web3('http://localhost:8545');
  const contract = new web3.eth.Contract(ABI, CONTRACT_ADDRESS);

  useEffect(() => {
    async function fetchVideos() {
      const count = await contract.methods.getVideoCount().call();
      const arr = [];
      for (let i = 0; i < count; i++) {
        const video = await contract.methods.getVideo(i).call();
        arr.push(video);
      }
      setVideos(arr);
    }
    fetchVideos();
  }, []);

  return (
    <div>
      <h1>分散型動画ストリーミング</h1>
      {videos.map((v, i) => (
        <div key={i}>
          <h3>{v[1]}</h3>
          <video controls width="480" src={`${IPFS_GATEWAY}${v[2]}`}></video>
          <p>Uploaded: {new Date(parseInt(v[3]) * 1000).toLocaleString()}</p>
        </div>
      ))}
    </div>
  );
}

export default App;

参考: React Hooks入門
Web3.js公式


3.3 応用的な機能の実装

3.3.1 アクセス制御付き動画配信

課題: 誰でも閲覧できてしまうため、限定公開や有料配信を実現するにはスマートコントラクト側で権限制御を組み込む必要があります。

contracts/VideoRegistry.sol(アクセス制御拡張)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VideoRegistryACL {
    struct Video {
        address uploader;
        string title;
        string ipfsCid;
        uint timestamp;
        bool isPrivate;
    }

    Video[] public videos;
    mapping(uint => mapping(address => bool)) public accessGranted;

    event VideoUploaded(address indexed uploader, string title, string ipfsCid, uint timestamp, bool isPrivate);

    function uploadVideo(string calldata title, string calldata ipfsCid, bool isPrivate) external {
        videos.push(Video(msg.sender, title, ipfsCid, block.timestamp, isPrivate));
        emit VideoUploaded(msg.sender, title, ipfsCid, block.timestamp, isPrivate);
    }

    function grantAccess(uint videoId, address user) external {
        require(videoId < videos.length, "Invalid videoId");
        require(msg.sender == videos[videoId].uploader, "Only uploader can grant access");
        accessGranted[videoId][user] = true;
    }

    function getVideo(uint index) external view returns (address, string memory, string memory, uint, bool) {
        require(index < videos.length, "Index out of bounds");
        Video storage v = videos[index];
        if (v.isPrivate) {
            require(accessGranted[index][msg.sender] || msg.sender == v.uploader, "No access");
        }
        return (v.uploader, v.title, v.ipfsCid, v.timestamp, v.isPrivate);
    }
}

ポイント:

  • isPrivateによる公開/非公開フラグ
  • grantAccessで個別アドレスに閲覧権限を付与

参考: Solidityのmapping型
スマートコントラクトのアクセスポリシー

3.3.2 チャレンジ:「動画の断片化+複数IPFSノードへの分散」

  • 動画ファイルをチャンク化して複数のIPFSノードに分散保存し、耐障害性・スケーラビリティを高める手法の調査・実装例
  • チャンクごとにCIDを管理し、スマートコントラクトで配列として保存

contracts/VideoRegistryChunks.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VideoRegistryChunks {
    struct Video {
        address uploader;
        string title;
        string[] chunkCids;
        uint timestamp;
    }

    Video[] public videos;

    function uploadVideo(string calldata title, string[] calldata chunkCids) external {
        videos.push(Video(msg.sender, title, chunkCids, block.timestamp));
    }

    function getChunkCids(uint index) external view returns (string[] memory) {
        require(index < videos.length, "Index out of bounds");
        return videos[index].chunkCids;
    }
}

参考: IPFS Chunking
Solidity動的配列の扱い


4. Tips & ベストプラクティス

  1. IPFSゲートウェイの冗長化
    複数のパブリックゲートウェイ(例: dweb.link, cloudflare-ipfs.com)を使って、動画再生障害を回避。

    参考: IPFS Gateway Status

  2. スマートコントラクトのガスコスト最適化
    動画本体は絶対


自動レビュー結果 (2025-08-01 01:01)

  • 記事品質: 4.6/5.0
  • トピック多様性: 5.0/5.0
  • コードサンプル: 5.0/5.0
  • 実用性・応用性: 4.4/5.0
  • 総合評価: 4.8/5.0 (優秀)

Discussion