🚀
分散型動画配信に挑む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 |
| (動画ファイルストレージ) |
+---------------------------+
流れ:
- フロントエンドで動画をアップロード
- IPFSに動画を保存し、CID(Content Identifier)を取得
- EthereumスマートコントラクトにCIDや動画タイトル等を記録
- ユーザーは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
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で動画情報取得
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);
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;
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で個別アドレスに閲覧権限を付与
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;
}
}
4. Tips & ベストプラクティス
-
IPFSゲートウェイの冗長化
複数のパブリックゲートウェイ(例:dweb.link,cloudflare-ipfs.com)を使って、動画再生障害を回避。 -
スマートコントラクトのガスコスト最適化
動画本体は絶対
自動レビュー結果 (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