🚀

IPFSとEthereumで構築する改ざん耐性動画配信技術

に公開

1. はじめに

急速に進化するWeb3時代において、動画配信プラットフォームの「改ざん耐性」「著作権保護」「分散化」は、従来のサーバー集約型モデルでは実現が難しい課題です。本記事では、Ethereumスマートコントラクトと**IPFS(InterPlanetary File System)**を組み合わせることで、これらの課題を解決する分散型動画ストリーミング基盤の具体的な構築方法を詳説します。

このアプローチにより、

  • 動画データの改ざんや消失リスクを根本的に軽減
  • 著作権情報や利用ルールの自動執行
  • 中央管理者不要の透明性・耐障害性の高いシステム
    が実現可能となります。

本記事の実装ガイドでは、「IPFSで動画ファイルを分散保存し、Ethereum上でメタデータと著作権管理をスマートコントラクトで自動化」する具体的な手順を、中級~上級開発者がすぐに活用できる完全なコード例と共に解説します。さらに、実運用で生じる課題への対処法、ベストプラクティス、他技術との比較まで踏み込み、単なる理論に留まらず、実務で役立つノウハウを提供します。

参考資料:


2. 基礎解説:アーキテクチャと技術スタック

2.1 主要な概念

  • IPFS:
    コンテンツアドレス型の分散ファイルシステム。ファイルはネットワーク全体に分散して保存され、ハッシュ値(CID)で一意に参照されるため、改ざん耐性が高い。

  • Ethereumスマートコントラクト:
    不特定多数で共有される分散台帳上に、プログラム可能な管理・取引ルール(著作権、アクセス権)を記述可能。誰でも検証でき、改ざん不可。

2.2 アーキテクチャ図

+----------------+           +---------------------+        +----------------------+
|   動画アップ主   |  (1)      |       IPFS Network   | (2)   |   Ethereum Blockchain |
+----------------+ ----->    +---------------------+ <----> +----------------------+
     | (3)                         | (4)                          | (5)
     V                             V                              V
動画ファイルをIPFSへ        CID(ハッシュ)取得          CID/著作権情報を
アップロード                 & 分散保存                   スマートコントラクト登録

(1): 動画ファイルのアップロード
(2): IPFSネットワークが動画ファイルを分散保存
(3): IPFSが返すCID(コンテンツID)を取得
(4): CID・著作権メタデータをEthereumスマートコントラクトへ登録
(5): スマートコントラクトにより改ざん耐性のある管理/配信を実現

2.3 技術スタック

  • バックエンド: Node.js, ethers.js, ipfs-http-client
  • スマートコントラクト: Solidity (>=0.8.0)
  • フロントエンド: React, web3.js or ethers.js
  • 開発環境: Hardhat (スマートコントラクト開発), Ganache (ローカルEthereumノード)
  • インフラ: Infura (Ethereum RPC/API), Pinata or web3.storage (IPFSピン止めサービス)

参考資料:


3. 実践的実装ガイド

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

動作確認済みバージョン例:

  • Node.js: v18.x
  • Solidity: ^0.8.21
  • ipfs-http-client: ^62.0.0
  • ethers: ^6.7.0
  • Hardhat: ^2.18.0
# 1. プロジェクト初期化
mkdir ipfs-eth-video-platform && cd $_
npm init -y

# 2. 依存パッケージインストール
npm install --save ethers hardhat ipfs-http-client dotenv

# 3. Hardhatプロジェクトセットアップ
npx hardhat
# (→ "Create a basic sample project" を選択)

# 4. .env 作成(Ethereumノード/Infura/Pinata等のAPIキー管理用)
cp .env.example .env

参考資料:


3.2 基本的な機能の実装

3.2.1 動画ファイルのIPFSアップロード

uploadToIPFS.js

const { create } = require('ipfs-http-client');
require('dotenv').config();

const ipfs = create({
  url: process.env.IPFS_API_URL || 'https://ipfs.infura.io:5001/api/v0',
});

async function uploadVideo(filePath) {
  const fs = require('fs');
  const file = fs.readFileSync(filePath);
  const { cid } = await ipfs.add({ content: file });
  console.log('アップロード完了 CID:', cid.toString());
  return cid.toString();
}

// 使い方
// uploadVideo('./sample.mp4');
module.exports = uploadVideo;
  • .envIPFS_API_URLやAPIキーを設定
  • 返却されるcidが動画ファイルの「改ざん耐性付きアドレス」となる

3.2.2 著作権管理用スマートコントラクト(Solidity)

contracts/VideoCopyright.sol

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

contract VideoCopyright {
    struct Video {
        address owner;
        string ipfsHash;
        string title;
        uint256 uploadTime;
        string copyrightNote;
    }

    mapping(string => Video) public videos; // ipfsHash => Video

    event VideoRegistered(address indexed owner, string ipfsHash, string title);

    function registerVideo(
        string memory _ipfsHash,
        string memory _title,
        string memory _copyrightNote
    ) external {
        require(bytes(videos[_ipfsHash].ipfsHash).length == 0, "既に登録済み");
        videos[_ipfsHash] = Video({
            owner: msg.sender,
            ipfsHash: _ipfsHash,
            title: _title,
            uploadTime: block.timestamp,
            copyrightNote: _copyrightNote
        });
        emit VideoRegistered(msg.sender, _ipfsHash, _title);
    }

    function getVideo(string memory _ipfsHash) external view returns (Video memory) {
        require(bytes(videos[_ipfsHash].ipfsHash).length != 0, "未登録");
        return videos[_ipfsHash];
    }
}

3.2.3 スマートコントラクトのデプロイ&連携

deploy.js

const hre = require("hardhat");

async function main() {
  const VideoCopyright = await hre.ethers.getContractFactory("VideoCopyright");
  const videoCopyright = await VideoCopyright.deploy();
  await videoCopyright.deployed();
  console.log("VideoCopyrightコントラクト:", videoCopyright.address);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
  • npx hardhat run scripts/deploy.js --network <network名> でデプロイ
  • デプロイ済みアドレスを.envに保存

3.2.4 スマートコントラクト登録スクリプト

registerVideo.js

const { ethers } = require("hardhat");
const uploadVideo = require("./uploadToIPFS");
require('dotenv').config();

async function main() {
  const [deployer] = await ethers.getSigners();
  const contractAddress = process.env.CONTRACT_ADDRESS;
  const VideoCopyright = await ethers.getContractFactory("VideoCopyright");
  const videoCopyright = VideoCopyright.attach(contractAddress);

  // 1. 動画ファイルをIPFSへアップロード
  const ipfsHash = await uploadVideo('./sample.mp4');

  // 2. スマートコントラクトに登録
  const tx = await videoCopyright.registerVideo(
    ipfsHash,
    "サンプル動画タイトル",
    "著作権: 2024 Example User"
  );
  await tx.wait();
  console.log("スマートコントラクト登録完了:", ipfsHash);
}

main();

参考資料:


3.3 応用的な機能の実装

3.3.1 視聴権限制御(NFTベースのアクセスコントロール)

スマートコントラクトにERC721(簡易NFT)を組み合わせ、特定ユーザーのみに視聴権限を付与可能。

contracts/VideoAccessNFT.sol

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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract VideoAccessNFT is ERC721 {
    uint256 private _tokenId;
    mapping(uint256 => string) private _videoHash;

    constructor() ERC721("VideoAccessNFT", "VANFT") {}

    function mint(address to, string memory ipfsHash) external returns (uint256) {
        _tokenId++;
        _mint(to, _tokenId);
        _videoHash[_tokenId] = ipfsHash;
        return _tokenId;
    }

    function videoHashOf(uint256 tokenId) external view returns (string memory) {
        require(_exists(tokenId), "不存在");
        return _videoHash[tokenId];
    }
}

OpenZeppelinの@openzeppelin/contractsnpm install @openzeppelin/contractsで事前インストール

3.3.2 フロントエンドでの動画再生(React + IPFS)

VideoPlayer.jsx

import React, { useState } from "react";

function VideoPlayer({ ipfsHash }) {
  const src = `https://ipfs.io/ipfs/${ipfsHash}`;
  return (
    <video controls width="600">
      <source src={src} type="video/mp4" />
      お使いのブラウザはvideoタグに対応していません
    </video>
  );
}

// 使い方例: <VideoPlayer ipfsHash="Qm..." />
export default VideoPlayer;

参考資料:


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

  1. IPFSへのアップロード時はPinningサービス必須
    → Pinataやweb3.storageでピン止めし、動画データの永続性を担保する
    Pinata, web3.storage

  2. スマートコントラクトの関数は適切にview/pure/external指定
    → 読み取り専用関数はviewにすることでガス消費を削減

  3. 動画ファイルは事前に圧縮・最適化
    → IPFSネットワーク負荷とストレージコストを抑制。ffmpeg等でエンコード推奨
    ffmpeg

  4. メタデータの一貫性管理
    → スマートコントラクトへの登録とIPFS上のメタデータ(JSON)を同期して管理

  5. フロントエンド/バックエンド間の署名認証強化
    → EIP-712署名やMetaMask連携でなりすまし・不正アップロード防止
    EIP-712


5. 詳細な考察

メリ


自動レビュー結果 (2025-08-13 00:52)

  • 記事品質: 4.3/5.0
  • トピック多様性: 5.0/5.0
  • コードサンプル: 4.2/5.0
  • 実用性・応用性: 3.0/5.0
  • 総合評価: 4.2/5.0 (良好)
  • 改善提案: より実践的な応用例や実装パターンを追加することを検討してください

Discussion