27、利用IPFS实现去中心化视频共享应用

利用IPFS实现去中心化视频共享应用

在当今数字化时代,去中心化应用正展现出巨大的潜力。本文将详细介绍如何利用IPFS(星际文件系统)构建一个去中心化的视频共享应用,包括其经济模式、架构设计、代码实现以及测试等方面。

1. 应用的特点与优势
  • 构建加密经济 :在智能合约中可以开展经济活动,如买卖、竞拍等。还能构建代币,用户用代币点赞视频,这激励用户上传更优质的视频,因为视频所有者可将点赞所得代币变现。
  • 保证API独立性 :去中心化应用能防止API受到干扰或骚扰,避免类似Twitter API那样的限制问题,增强了API的民主性。
2. 应用的主要功能
  • 视频操作 :用户可以查看视频列表、播放视频和上传视频,与YouTube的基本功能类似。
  • 付费点赞 :用户只能使用代币来点赞视频。
3. 视频共享智能合约架构

智能合约是应用的核心,它需要完成以下任务:
- 跟踪用户上传的视频。
- 利用代币及其标准操作(ERC20)。
- 提供用户使用代币点赞视频的方式。
- 将点赞所用的代币转移给视频所有者。

为了减少出现漏洞的风险,智能合约应尽量简洁。在设计智能合约的数据结构时,我们需要考虑如何跟踪用户的视频:
- 视频存储 :使用映射变量,以地址数据类型为键,另一个映射(mapping z)为值。mapping z以整数为键,包含IPFS路径(bytes[50])和视频标题(bytes[20])的结构体为值。通过整数跟踪器记录每个用户的视频数量。
- 点赞记录 :使用两个映射来记录点赞信息。一个映射(likes_videos)以bytes[100]为键,布尔值为值,用于判断用户是否已点赞某个视频;另一个映射(aggregate_likes)以bytes[100]为键,整数为值,用于记录视频的总点赞数。

以下是智能合约数据结构的mermaid流程图:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef data fill:#FFEBEB,stroke:#E68994,stroke-width:2px;

    A[用户地址]:::data --> B[用户视频映射]:::process
    B --> C[视频索引]:::data
    C --> D[视频结构体]:::process
    D --> E[IPFS路径: bytes[50]]:::data
    D --> F[视频标题: bytes[20]]:::data

    G[视频信息组合: bytes[100]]:::data --> H[点赞记录映射]:::process
    H --> I[是否点赞: bool]:::data

    J[视频信息组合: bytes[100]]:::data --> K[总点赞数映射]:::process
    K --> L[总点赞数: uint256]:::data
4. 视频共享Web应用架构

我们将开发一个Python Web应用作为智能合约的前端,这需要一个合适的服务器,如Gunicorn。但在用户上传视频时,由于需要访问私钥,可能会引发用户对私钥安全的担忧。为了解决这个问题,可以采取以下措施:
- 将Python Web应用的源代码发布到GitHub或GitLab上,让用户下载、安装并在本地运行,用户可以审核代码以确保安全。
- 更优的方案是将源代码存储在IPFS上,用户从IPFS下载,确保代码不被篡改,只需审核一次。

然而,Python Web应用存在一些局限性,如移动用户无法执行。JavaScript则具有优势,可以创建动态的静态网站,并使用React.js、Angular.js等框架开发复杂的Web应用,可在桌面和移动设备上运行。

Web应用的主要功能包括:
- 播放视频
- 上传视频
- 点赞视频
- 列出多个用户的近期视频
- 列出特定用户的视频

以下是Web应用功能的列表说明:
| 功能 | 描述 | 参数 |
| ---- | ---- | ---- |
| 播放视频 | 接受视频上传者的地址和视频索引作为参数,若视频不在本地存储,则从IPFS下载并播放 | 视频上传者地址、视频索引 |
| 上传视频 | 接受账户地址、加密私钥密码、视频文件和视频标题作为参数,先将视频文件存储在IPFS上,成功后将视频信息存储在区块链上 | 账户地址、私钥密码、视频文件、视频标题 |
| 点赞视频 | 接受点赞者地址、加密私钥密码、视频上传者地址和视频索引作为参数,确保用户未重复点赞后,将信息存储在区块链上,并转移代币 | 点赞者地址、私钥密码、视频上传者地址、视频索引 |
| 列出近期视频 | 通过在区块链上存储视频信息时创建的事件来查找多个用户的近期视频 | 无 |
| 列出特定用户视频 | 接受用户地址作为参数,从智能合约中获取该用户的视频列表 | 用户地址 |

5. 编写视频共享智能合约

以下是编写智能合约的具体步骤:
1. 设置虚拟环境

$ virtualenv -p python3.6 videos-venv
$ source videos-venv/bin/activate
(videos-venv) $
  1. 安装必要的库
(videos-venv) $ pip install eth-abi==1.2.2
(videos-venv) $ pip install eth-typing==1.1.0
(videos-venv) $ pip install py-evm==0.2.0a33
(videos-venv) $ pip install web3==4.7.2
(videos-venv) $ pip install -e git+https://github.com/ethereum/populus#egg=populus
(videos-venv) $ pip install vyper
  1. 检查并修复可能的Bug
(videos-venv) $ cd videos-venv/src/populus
(videos-venv) $ grep -R "compile(" populus/compilation/backends/vyper.py

如果发现最新版本的Vyper(0.1.0b6)会导致Populus出现问题,可以自行打补丁:

(videos-venv) $ wget https://patch-diff.githubusercontent.com/raw/ethereum/populus/pull/484.patch
(videos-venv) $ git apply 484.patch
(videos-venv) $ cd ../../../
  1. 创建智能合约项目目录
(videos-venv) $ mkdir videos-sharing-smart-contract
(videos-venv) $ cd videos-sharing-smart-contract
(videos-venv) $ mkdir contracts tests
  1. 下载Populus配置文件
(videos-venv) $ wget https://raw.githubusercontent.com/ethereum/populus/master/populus/assets/defaults.v9.config.json -O project.json
  1. 修改配置文件
{
  "compilation": {
    "backend": {
      "class": "populus.compilation.backends.VyperBackend"
    },
    "contract_source_dirs": [
      "./contracts"
    ],
    "import_remappings": []
  }
}
  1. 编写智能合约代码(VideosSharing.vy)
struct Video:
    path: bytes[50]
    title: bytes[20]

Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
UploadVideo: event({_user: indexed(address), _index: uint256})
LikeVideo: event({_video_liker: indexed(address), _video_uploader: indexed(address), _index: uint256})

user_videos_index: map(address, uint256)
name: public(bytes[20])
symbol: public(bytes[3])
totalSupply: public(uint256)
decimals: public(uint256)
balances: map(address, uint256)
allowed: map(address, map(address, uint256))
all_videos: map(address, map(uint256, Video))
likes_videos: map(bytes[100], bool)
aggregate_likes: map(bytes[100], uint256)

@public
def __init__():
    _initialSupply: uint256 = 500
    _decimals: uint256 = 3
    self.totalSupply = _initialSupply * 10 ** _decimals
    self.balances[msg.sender] = self.totalSupply
    self.name = 'Video Sharing Coin'
    self.symbol = 'VID'
    self.decimals = _decimals
    log.Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply)

@public
@constant
def allowance(_owner: address, _spender: address) -> uint256:
    return self.allowed[_owner][_spender]

@private
def _transfer(_source: address, _to: address, _amount: uint256) -> bool:
    assert self.balances[_source] >= _amount
    self.balances[_source] -= _amount
    self.balances[_to] += _amount
    log.Transfer(_source, _to, _amount)
    return True

@public
def transfer(_to: address, _amount: uint256) -> bool:
    return self._transfer(msg.sender, _to, _amount)

@public
def upload_video(_video_path: bytes[50], _video_title: bytes[20]) -> bool:
    _index: uint256 = self.user_videos_index[msg.sender]
    self.all_videos[msg.sender][_index] = Video({ path: _video_path, title: _video_title })
    self.user_videos_index[msg.sender] += 1
    log.UploadVideo(msg.sender, _index)
    return True

@public
@constant
def latest_videos_index(_user: address) -> uint256:
    return self.user_videos_index[_user]

@public
@constant
def videos_path(_user: address, _index: uint256) -> bytes[50]:
    return self.all_videos[_user][_index].path

@public
@constant
def videos_title(_user: address, _index: uint256) -> bytes[20]:
    return self.all_videos[_user][_index].title

@public
def like_video(_user: address, _index: uint256) -> bool:
    _msg_sender_str: bytes32 = convert(msg.sender, bytes32)
    _user_str: bytes32 = convert(_user, bytes32)
    _index_str: bytes32 = convert(_index, bytes32)
    _key: bytes[100] = concat(_msg_sender_str, _user_str, _index_str)
    _likes_key: bytes[100] = concat(_user_str, _index_str)
    assert _index < self.user_videos_index[_user]
    assert self.likes_videos[_key] == False
    self.likes_videos[_key] = True
    self.aggregate_likes[_likes_key] += 1
    self._transfer(msg.sender, _user, 1)
    log.LikeVideo(msg.sender, _user, _index)
    return True

@public
@constant
def video_has_been_liked(_user_like: address, _user_video: address, _index: uint256) -> bool:
    _user_like_str: bytes32 = convert(_user_like, bytes32)
    _user_video_str: bytes32 = convert(_user_video, bytes32)
    _index_str: bytes32 = convert(_index, bytes32)
    _key: bytes[100] = concat(_user_like_str, _user_video_str, _index_str)
    return self.likes_videos[_key]

@public
@constant
def video_aggregate_likes(_user_video: address, _index: uint256) -> uint256:
    _user_video_str: bytes32 = convert(_user_video, bytes32)
    _index_str: bytes32 = convert(_index, bytes32)
    _key: bytes[100] = concat(_user_video_str, _index_str)
    return self.aggregate_likes[_key]
6. 编写测试脚本

为了确保智能合约的正确性,我们需要编写测试脚本(test_video_sharing.py):

import pytest
import eth_tester

def upload_video(video_sharing, chain, account, video_path, video_title):
    txn_hash = video_sharing.functions.upload_video(video_path, video_title).transact({'from': account})
    chain.wait.for_receipt(txn_hash)

def transfer_coins(video_sharing, chain, source, destination, amount):
    txn_hash = video_sharing.functions.transfer(destination, amount).transact({'from': source})
    chain.wait.for_receipt(txn_hash)

def like_video(video_sharing, chain, video_liker, video_uploader, index):
    txn_hash = video_sharing.functions.like_video(video_uploader, index).transact({'from': video_liker})
    chain.wait.for_receipt(txn_hash)

def test_upload_video(web3, chain):
    video_sharing, _ = chain.provider.get_or_deploy_contract('VideosSharing')
    t = eth_tester.EthereumTester()
    video_uploader = t.get_accounts()[1]
    index = video_sharing.functions.latest_videos_index(video_uploader).call()
    assert index == 0
    upload_video(video_sharing, chain, video_uploader, b'video-ipfs-path', b"video title")
    index = video_sharing.functions.latest_videos_index(video_uploader).call()
    assert index == 1
    video_path = video_sharing.functions.videos_path(video_uploader, 0).call()
    assert video_path == b'video-ipfs-path'
    video_title = video_sharing.functions.videos_title(video_uploader, 0).call()
    assert video_title == b"video title"
    upload_video(video_sharing, chain, video_uploader, b'video-ipfs-path2', b"video title2")
    index = video_sharing.functions.latest_videos_index(video_uploader).call()
    assert index == 2
    video_path = video_sharing.functions.videos_path(video_uploader, 1).call()
    assert video_path == b'video-ipfs-path2'
    video_title = video_sharing.functions.videos_title(video_uploader, 1).call()
    assert video_title == b"video title2"

def test_like_video(web3, chain):
    video_sharing, _ = chain.provider.get_or_deploy_contract('VideosSharing')
    t = eth_tester.EthereumTester()
    manager = t.get_accounts()[0]
    video_uploader = t.get_accounts()[1]
    video_liker = t.get_accounts()[2]
    video_liker2 = t.get_accounts()[3]
    transfer_coins(video_sharing, chain, manager, video_liker, 100)
    transfer_coins(video_sharing, chain, manager, video_liker2, 100)
    transfer_coins(video_sharing, chain, manager, video_uploader, 50)
    upload_video(video_sharing, chain, video_uploader, b'video-ipfs-path', b"video title")
    balance_before = video_sharing.functions.balances(video_liker).call()
    like_video(video_sharing, chain, video_liker, video_uploader, 0)
    balance_after = video_sharing.functions.balances(video_liker).call()
    assert balance_after == balance_before - 1
    aggregate_likes = video_sharing.functions.video_aggregate_likes(video_uploader, 0).call()
    assert aggregate_likes == 1
    with pytest.raises(eth_tester.exceptions.TransactionFailed):
        like_video(video_sharing, chain, video_liker, video_uploader, 0)

通过以上步骤,我们可以构建一个完整的去中心化视频共享应用。在实际开发中,还需要考虑性能优化、安全性等方面的问题,以确保应用的稳定运行。

利用IPFS实现去中心化视频共享应用

7. 测试脚本详细分析

测试脚本是确保智能合约正确性和稳定性的关键部分。下面我们将详细分析测试脚本中的各个函数和测试用例。

7.1 辅助函数
  • upload_video :该函数用于上传视频。它接受视频共享合约实例、区块链实例、账户地址、视频路径和视频标题作为参数。通过调用智能合约的 upload_video 函数进行交易,并等待交易收据。
def upload_video(video_sharing, chain, account, video_path, video_title):
    txn_hash = video_sharing.functions.upload_video(video_path, video_title).transact({'from': account})
    chain.wait.for_receipt(txn_hash)
  • transfer_coins :用于在不同账户之间转移代币。接受视频共享合约实例、区块链实例、源账户地址、目标账户地址和转移金额作为参数。调用智能合约的 transfer 函数进行交易,并等待收据。
def transfer_coins(video_sharing, chain, source, destination, amount):
    txn_hash = video_sharing.functions.transfer(destination, amount).transact({'from': source})
    chain.wait.for_receipt(txn_hash)
  • like_video :该函数用于模拟用户点赞视频。接受视频共享合约实例、区块链实例、点赞者地址、视频上传者地址和视频索引作为参数。调用智能合约的 like_video 函数进行交易,并等待收据。
def like_video(video_sharing, chain, video_liker, video_uploader, index):
    txn_hash = video_sharing.functions.like_video(video_uploader, index).transact({'from': video_liker})
    chain.wait.for_receipt(txn_hash)
7.2 测试用例
  • test_upload_video :此测试用例用于验证视频上传功能。首先获取视频共享合约实例,然后选择一个账户作为视频上传者。在上传视频之前,检查该账户的最新视频索引是否为 0。接着上传一个视频,再次检查最新视频索引是否变为 1,并验证视频的路径和标题是否正确。最后再上传一个视频,检查索引是否变为 2 以及新视频的路径和标题是否正确。
def test_upload_video(web3, chain):
    video_sharing, _ = chain.provider.get_or_deploy_contract('VideosSharing')
    t = eth_tester.EthereumTester()
    video_uploader = t.get_accounts()[1]
    index = video_sharing.functions.latest_videos_index(video_uploader).call()
    assert index == 0
    upload_video(video_sharing, chain, video_uploader, b'video-ipfs-path', b"video title")
    index = video_sharing.functions.latest_videos_index(video_uploader).call()
    assert index == 1
    video_path = video_sharing.functions.videos_path(video_uploader, 0).call()
    assert video_path == b'video-ipfs-path'
    video_title = video_sharing.functions.videos_title(video_uploader, 0).call()
    assert video_title == b"video title"
    upload_video(video_sharing, chain, video_uploader, b'video-ipfs-path2', b"video title2")
    index = video_sharing.functions.latest_videos_index(video_uploader).call()
    assert index == 2
    video_path = video_sharing.functions.videos_path(video_uploader, 1).call()
    assert video_path == b'video-ipfs-path2'
    video_title = video_sharing.functions.videos_title(video_uploader, 1).call()
    assert video_title == b"video title2"
  • test_like_video :该测试用例用于验证视频点赞功能。首先获取视频共享合约实例,选择不同的账户作为管理员、视频上传者和点赞者。从管理员账户向其他账户转移代币,然后上传一个视频。在点赞视频之前,检查账户的代币余额、是否已点赞以及视频的总点赞数。点赞视频后,检查点赞者的代币余额是否减少,视频的总点赞数是否增加。最后尝试重复点赞,预期会抛出交易失败的异常。
def test_like_video(web3, chain):
    video_sharing, _ = chain.provider.get_or_deploy_contract('VideosSharing')
    t = eth_tester.EthereumTester()
    manager = t.get_accounts()[0]
    video_uploader = t.get_accounts()[1]
    video_liker = t.get_accounts()[2]
    video_liker2 = t.get_accounts()[3]
    transfer_coins(video_sharing, chain, manager, video_liker, 100)
    transfer_coins(video_sharing, chain, manager, video_liker2, 100)
    transfer_coins(video_sharing, chain, manager, video_uploader, 50)
    upload_video(video_sharing, chain, video_uploader, b'video-ipfs-path', b"video title")
    balance_before = video_sharing.functions.balances(video_liker).call()
    like_video(video_sharing, chain, video_liker, video_uploader, 0)
    balance_after = video_sharing.functions.balances(video_liker).call()
    assert balance_after == balance_before - 1
    aggregate_likes = video_sharing.functions.video_aggregate_likes(video_uploader, 0).call()
    assert aggregate_likes == 1
    with pytest.raises(eth_tester.exceptions.TransactionFailed):
        like_video(video_sharing, chain, video_liker, video_uploader, 0)
8. 部署和运行应用

在编写好智能合约和测试脚本后,接下来需要将应用部署并运行起来。以下是部署和运行应用的详细步骤:

8.1 智能合约部署
  • 编译合约 :使用之前配置好的 Populus 环境编译智能合约。在项目根目录下执行以下命令:
(videos-venv) $ populus compile
  • 部署合约 :在本地测试网络或真实的区块链网络上部署合约。使用 Populus 的 deploy 命令:
(videos-venv) $ populus deploy --chain testrpc VideosSharing
8.2 前端应用部署
  • 配置服务器 :由于我们开发的是 Python 前端应用,需要配置一个合适的服务器。可以使用 Gunicorn 作为 Web 服务器,在服务器上安装 Gunicorn:
(videos-venv) $ pip install gunicorn
  • 启动服务器 :在项目根目录下启动 Gunicorn 服务器:
(videos-venv) $ gunicorn -w 4 -b 0.0.0.0:8000 app:app

这里的 app:app 需要根据实际的 Python 应用文件和应用实例名称进行调整。

8.3 用户使用流程

用户可以通过浏览器访问部署好的前端应用。以下是用户使用应用的简单流程:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A[访问应用]:::process --> B{选择操作}:::decision
    B -->|播放视频| C[输入视频信息]:::process
    C --> D[从 IPFS 下载视频并播放]:::process
    B -->|上传视频| E[输入账户信息和视频文件]:::process
    E --> F[将视频存储到 IPFS]:::process
    F --> G[将视频信息存储到区块链]:::process
    B -->|点赞视频| H[输入点赞者信息和视频信息]:::process
    H --> I[检查是否已点赞]:::decision
    I -->|未点赞| J[转移代币并记录点赞信息]:::process
    I -->|已点赞| K[提示不能重复点赞]:::process
9. 应用的优化和扩展

为了提高应用的性能和功能,我们可以对应用进行优化和扩展。以下是一些建议:

9.1 性能优化
  • 缓存机制 :在前端应用中添加缓存机制,减少对 IPFS 和区块链的频繁访问。例如,缓存已播放的视频和已获取的视频信息。
  • 批量操作 :在智能合约中实现批量上传视频和批量转移代币的功能,减少交易次数,提高效率。
9.2 功能扩展
  • 评论和分享功能 :添加评论和分享功能,让用户可以对视频进行评论和分享到其他平台。
  • 视频分类和搜索 :实现视频分类和搜索功能,方便用户快速找到自己感兴趣的视频。
10. 总结

通过本文的介绍,我们详细了解了如何利用 IPFS 构建一个去中心化的视频共享应用。从应用的特点和优势、智能合约和 Web 应用的架构设计,到代码的编写和测试,再到应用的部署和运行,以及后续的优化和扩展,每个环节都进行了深入的探讨。

去中心化视频共享应用具有诸多优势,如构建加密经济、保证 API 独立性等。同时,通过合理的架构设计和代码实现,我们可以实现视频的上传、播放、点赞等核心功能。在实际开发过程中,需要注意智能合约的安全性和性能优化,以及前端应用的用户体验。

希望本文能为开发者提供一个全面的指导,帮助他们构建出更加稳定、高效和功能丰富的去中心化视频共享应用。

学生社团系统-学生社团“一站式”运营管理平台-学生社团管理系统-基于SSM的学生社团管理系统-springboot学生社团管理系统.zip-Java学生社团管理系统开发实战-源码 更多学生社团系统: SpringBoot+Vue学生社团“一站式”运营管理平台源码(活动管理+成员考核+经费审批) Java学生社团管理系统开发实战:SSM升级SpringBoot(招新报名+场地预约+数据看板) 基于SpringSecurity的社团管理APP(移动端签到+权限分级+消息推送) 企业级社团数字化平台解决方案(SpringBoot+Redis缓存+Elasticsearch活动搜索) 微信小程序社团服务系统开发(活动直播+社团文化墙+成员互动社区) SpringBoot社团核心源码(多角色支持+工作流引擎+API接口开放) AI赋能社团管理:智能匹配兴趣标签+活动热度预测+成员贡献度分析(附代码) 响应式社团管理平台开发(PC/移动端适配+暗黑模式+无障碍访问) 完整学生社团系统源码下载(SpringBoot3+Vue3+MySQL8+Docker部署) 高校垂直领域社团平台:百团大战系统+社团星级评定+跨校活动联盟 适用对象:本代码学习资料适用于计算机、电子信息工程、数学等专业正在做毕设的学生,需要项目实战练习的学习者,也适用于课程设计、期末大作业。 技术栈:前端是vue,后端是springboot,项目代码都经过严格调试,代码没有任何bug! 核心管理:社团注册、成员管理、权限分级 活动运营:活动发布、报名签到、场地预约 资源服务:经费申请、物资管理、文档共享 数据分析:成员活跃度、活动效果评估、社团影响力排名
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值