利用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) $
- 安装必要的库 :
(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
- 检查并修复可能的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 ../../../
- 创建智能合约项目目录 :
(videos-venv) $ mkdir videos-sharing-smart-contract
(videos-venv) $ cd videos-sharing-smart-contract
(videos-venv) $ mkdir contracts tests
- 下载Populus配置文件 :
(videos-venv) $ wget https://raw.githubusercontent.com/ethereum/populus/master/populus/assets/defaults.v9.config.json -O project.json
- 修改配置文件 :
{
"compilation": {
"backend": {
"class": "populus.compilation.backends.VyperBackend"
},
"contract_source_dirs": [
"./contracts"
],
"import_remappings": []
}
}
- 编写智能合约代码(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 独立性等。同时,通过合理的架构设计和代码实现,我们可以实现视频的上传、播放、点赞等核心功能。在实际开发过程中,需要注意智能合约的安全性和性能优化,以及前端应用的用户体验。
希望本文能为开发者提供一个全面的指导,帮助他们构建出更加稳定、高效和功能丰富的去中心化视频共享应用。
超级会员免费看
28

被折叠的 条评论
为什么被折叠?



