BTFS 基础
架构
btfs 是 BTTC 的一项应用,是一个分布式协议。注意:btfs 和 btt 是两个完全不同的概念,需要分清楚,btfs 是一项应用,btt 是代币,在 btfs 内可以通过存储/空投获得 btt 收益。除此之外,btt 在很多其他场合也会出现,目前着重在 btfs 应用。
btfs 内部主要分为两个角色,五个合约。
两个角色:
- renters:租户,有存储数据的需求
- host:主机,可以存储数据
五个合约:
- Vault contract:租户需要存储数据时,需要通过保险库合约向主机发送特定金额的支票来支付费用;主机可以通过这个合约,兑现支票到自己钱包
- Staking contract:质押合约。主机必须质押 btt,当租户需要上传文件时,通过这个合约确定找谁存储
- Proof-of-storage contract:存储证明合约。主机托管文件后,需要定期生成文件存储证明并提交到存储证明合约,如果错过提交期间,会被处罚
- Airdrop contract:空投合约。主机将根据文件大小和存储时长从 btfs 收到 btt 空投。
- Price contract:价格合约,更新全网存储价格。
这个架构图中缺失了两个合约,更关注整个存储过程。租户通过价格合约获得当前存储价格,然后质押合约会给出主机推荐,然后租户向保险库合约进行质押,同时向主机节点发送需要保存的文件和支票,主机完成存储后,(会向存储证明合约发送存储证明)并向保险库合约兑换支票。这样就实现了两个不信任的角色之间的交易。
部署方案
为了实现单机多节点,同时对性能需求进行限制,选择使用 docker 单机部署的方案。(后期可根据需求上 k8s 方案)。
镜像获取
# 方法一(简便)
docker pull ghcr.io/bittorrent/go-btfs:latest
# 方法二(复杂,但精准)
git clone https://github.com/bittorrent/go-btfs
cd go-btfs
docker image build -t btfs_docker .
配置 volume
主要是三个目的:数据转移,明确定义明确使用,复用一些数据。目前还不明确 btfs 那些数据可以复用,暂时先不复用。
docker volume create btfs-vol-0
配置网络
暂且略过。看后期是否有需要进行更细化的网络管理。
启动节点
$ docker container run -v btfs-vol-0:/data/btfs -p 40000:4001 -p 50000:5001 --detach --name btfs0 ghcr.io/bittorrent/go-btfs:btfs-v2.1.2
# 日志显示需要发送启动资金
$ docker logs btfs0
...
the address of Bttc format is: 0x2d000000000000000000000000000000
the address of Tron format is: TE86000000000000000000000000000000
cannot continue until there is sufficient (100 Suggested) BTT (for Gas) available on 0x2d000000000000000000000000000000
发送启动资金以及修改配置
# 通过下方指令获得 bttc format
(host) docker logs btfs0 |grep 'the address of Bttc format is' |awk '{print $7}'
# 然后通过metamask,或者 btfs bttc send-btt-to <address> <amount>
# 此时假定已经发送完启动资金,然后查看日志
(host) docker logs btfs0
self vault: 0xbb0000000000000000000000000000000000000
Swarm listening on /ip4/127.0.0.1/tcp/4001
Swarm listening on /ip4/172.17.0.50/tcp/4001
Swarm listening on /p2p-circuit
Swarm announcing /ip4//tcp/42877
Swarm announcing /ip4/127.0.0.1/tcp/4001
Swarm announcing /ip4/172.17.0.50/tcp/4001
API server listening on /ip4/0.0.0.0/tcp/5001
Dashboard: http://0.0.0.0:5001/dashboard
Gateway (readonly) server listening on /ip4/0.0.0.0/tcp/8080
Remote API server listening on /ip4/127.0.0.1/tcp/5101
Daemon is ready
# 此时还需要修改配置,才可以访问
# 下方的 cat 指令只是用来检测配置变动前后情况,方便排查,实际部署的时候并不需要
(host) docker exec -it btfs0 /bin/sh
(container) cat /data/btfs/config |head -15
(container) btfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["*"]'
(container) btfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST"]'
(container) cat /data/btfs/config |head -15
(container) cat /data/btfs/config | grep -E "Analytics|StorageHostEnabled"
(container) btfs config --json Experimental.Analytics true
(container) btfs config --json Experimental.StorageHostEnabled true
(container) cat /data/btfs/config | grep -E "Analytics|StorageHostEnabled"
# 退出容器,重启容器
(container) exit
(host) docker restart btfs1
web 访问
打开任意的 btfs 服务,设置里面的 API 修改为上述实例的地址,即可打开管理界面。初次评分为 0,预计需要24小时,需要保持节点在线。
部署时可能出现的问题
Q: Error: lock /data/btfs/repo.lock: someone else has the lock A: 指令等待一会再执行。
Q:重启容器后,出现:Error: Could not connect to blockchain rpc, please check your network connection.Post "https://rpc.bittorrentchain.io/": dial tcp: lookup rpc.bittorrentchain.io on 223.5.5.5:53: write udp 172.17.0.50:52328->223.5.5.5:53: write: invalid argument
A:等几秒钟,重启该容器
关键信息获取
- 链ID、节点钱包地址、保险库地址、钱包私钥:
btfs cheque chaininfo
- 节点ID:
btfs id | grep ID
- 查看助记词:
cat /data/btfs/config |grep Mnemonic
- 查看收到的合约信息:
btfs cheque stats
host 分数
只有这个分数达到 8 分以上,才可以接合约,接空投,那么我们需要关注这个分数是怎么出来的。
首先这个分数是有五个数据整合算出来的:
- uptime_score:在线时长得分,权重60%,满分10分,权重满分6分
- age_score:主机年龄,权重20%,满分10分,权重满分2分
- version_score:版本分,权重10%,满分10分,权重满分1分
- download_speed_score:下载速度得分,权重5%,满分10分,权重满分0.5分
- upload_speed_score:上传速度得分,权重5%,满分10分,权重满分0.5分
可以看出最高分为10分,8分是官方定义的标准线。以上数据可以通过 btfs storage stats info
或者 curl 127.0.0.1:5001/api/v1/storage/stats/info?l=false
进行查询。
那么下一个问题就是这个得分是如何得到的?
代码内(不包括依赖库),这个分数是直接从本地的 datastore 获得数据进行展示的,而这个存储数据其实是链上的数据。一个很重要的数据结构 StorageStat_HostStats
,这一块进入到了 go-btfs-common,这是一个基础项目,主要使用 javascript/go 实现 protobuf 进行数据的设置、传递。在这些地方没有显式的代码说明分数是如何计算的,这里直接用了 rpc 的方法进行数据传输,难以追踪。
一次存储尝试
在帮别人存储前,先尝试下当个租客。在本地存储一个 116MB 的 go1.15.6.linux-amd64.tar.gz
文件,然后金库充值足够的金额,进行 upload,下面是相关日志。
Current host stats will be synced
Current host settings will be synced
your btt balance is 142233300000000000000, will swap 100000000000000000000 btt to wbtt
copy get, shardHashes:[QmbC3uTGKhWCEThJi8DH8UiEfMwkUAuBuLjqWjsfFix11V] fileSize:120980347, shardSize:120980347, copy:0 err:<nil>
size:0.11267172824591398 GB, price:125000000000000000000 , storageLength:30, TotalPay:422518980000000000000
check, balance=100000000000000000000, realAmount=422518980000000000000
check, err: insufficient token balance
your btt balance is 511873600000000000000, will swap 400000000000000000000 btt to wbtt
copy get, shardHashes:[QmbC3uTGKhWCEThJi8DH8UiEfMwkUAuBuLjqWjsfFix11V] fileSize:120980347, shardSize:120980347, copy:0 err:<nil>
size:0.11267172824591398 GB, price:125000000000000000000 , storageLength:30, TotalPay:422518980000000000000
check, balance=500000000000000000000, realAmount=422518980000000000000
size:0.11267172824591398 GB, price:125000000000000000000 , storageLength:30, TotalPay:422518980000000000000
[2022-04-28T14:47:41Z] session: bdd9c4ce-ac0c-44bd-8878-9194c2f0825d entered state: submit, msg: Hosts found! Checking chequebook balance, and visiting guard.
check, balance=500000000000000000000, realAmount=422518980000000000000
[2022-04-28T14:47:42Z] session: bdd9c4ce-ac0c-44bd-8878-9194c2f0825d entered state: guard, msg: Preparing meta-data and challenge questions.
[2022-04-28T14:47:42Z] session: bdd9c4ce-ac0c-44bd-8878-9194c2f0825d entered state: guard:file-meta-signed, msg: Preparing meta-data and challenge questions.
[2022-04-28T14:48:11Z] session: bdd9c4ce-ac0c-44bd-8878-9194c2f0825d entered state: guard:questions-signed, msg: Preparing meta-data and challenge questions.
[2022-04-28T14:51:34Z] session: bdd9c4ce-ac0c-44bd-8878-9194c2f0825d entered state: error, msg: failed to send challenge questions to guard: [GuardClient: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp: lookup guard.btfs.io on 8.8.8.8:53: read udp 172.17.0.45:50680->8.8.8.8:53: i/o timeout"]
日志显示挑战问题无法发送到守卫,这里是对外网的问题。
仅仅是寻找 host,内存直接干掉 500 MB。寻找到 host 后的数据传输、签支票、验证存储证明的资源消耗无法验证。
第二次存储测试
第一次测试因为网络的原因无法完成,按照同样的步骤重新进行测试。
# 客户端操作
## 以下显示内容为原始内容格式化后的结果
/ # btfs storage upload QmbC3uTGKhWCEThJi8DH8UiEfMwkUAuBuLjqWjsfFix11V
{"ID":"22f6bb3b-60c3-45c0-96df-096da767c672"}
/ # btfs storage upload 22f6bb3b-60c3-45c0-96df-096da767c672 status
{
"Status": "submit",
"Message": "Hosts found! Checking chequebook balance, and visiting guard.",
"AdditionalInfo": "",
"FileHash": "QmbC3uTGKhWCEThJi8DH8UiEfMwkUAuBuLjqWjsfFix11V",
"Shards": {
"22f6bb3b-60c3-45c0-96df-096da767c672:QmbC3uTGKhWCEThJi8DH8UiEfMwkUAuBuLjqWjsfFix11V:0": {
"ContractID": "22f6bb3b-60c3-45c0-96df-096da767c672,675c4139-0498-4f69-b758-cdc08278bc9c",
"Price": 125000000,
"Host": "16Uiu2HAmL4WidZhrnrvUxmBG2Cp7U9Do4gypY2gs8twXML7WP2Xh",
"Status": "contract",
"Message": "",
"AdditionalInfo": ""
}
}
}
/ # btfs storage upload 22f6bb3b-60c3-45c0-96df-096da767c672 status
{
"Status": "error",
"Message": "GuardClient: rpc error: code = Unknown desc = Persistence manager: such file hash already exist for such renter",
"AdditionalInfo": "",
"FileHash": "QmbC3uTGKhWCEThJi8DH8UiEfMwkUAuBuLjqWjsfFix11V",
"Shards": {
"22f6bb3b-60c3-45c0-96df-096da767c672:QmbC3uTGKhWCEThJi8DH8UiEfMwkUAuBuLjqWjsfFix11V:0": {
"ContractID": "22f6bb3b-60c3-45c0-96df-096da767c672,675c4139-0498-4f69-b758-cdc08278bc9c",
"Price": 125000000,
"Host": "16Uiu2HAmL4WidZhrnrvUxmBG2Cp7U9Do4gypY2gs8twXML7WP2Xh",
"Status": "contract",
"Message": "",
"AdditionalInfo": ""
}
}
}
# 日志
copy get, shardHashes:[QmbC3uTGKhWCEThJi8DH8UiEfMwkUAuBuLjqWjsfFix11V] fileSize:120980347, shardSize:120980347, copy:0 err:<nil>
size:0.11267172824591398 GB, price:125000000000000000000 , storageLength:30, TotalPay:422518980000000000000
check, balance=500000000000000000000, realAmount=422518980000000000000
size:0.11267172824591398 GB, price:125000000000000000000 , storageLength:30, TotalPay:422518980000000000000
[2022-05-05T11:52:06Z] session: 22f6bb3b-60c3-45c0-96df-096da767c672 entered state: submit, msg: Hosts found! Checking chequebook balance, and visiting guard.
check, balance=500000000000000000000, realAmount=422518980000000000000
[2022-05-05T11:52:07Z] session: 22f6bb3b-60c3-45c0-96df-096da767c672 entered state: guard, msg: Preparing meta-data and challenge questions.
[2022-05-05T11:52:07Z] session: 22f6bb3b-60c3-45c0-96df-096da767c672 entered state: guard:file-meta-signed, msg: Preparing meta-data and challenge questions.
[2022-05-05T11:52:08Z] session: 22f6bb3b-60c3-45c0-96df-096da767c672 entered state: error, msg: GuardClient: rpc error: code = Unknown desc = Persistence manager: such file hash already exist for such renter
大概的意思就是找到了存储方,然后检查余额,都没问题后准备元文件,发现这个文件的hash在网络上已经存在,不予保存。结局是失败的,但是知道了同样的文件在网络上只可以存在一份。
第三次存储测试
第二次失败的原因是我使用的文件比较容易获取(go1.16的安装包),我们尝试换个网络上不存在的文件进行第三次测试。
# 客户端操作
/ # btfs add /tmp/A
added QmbpRkc1o3QY75noqRn2DQBT8QSASxiZwfoEmcR8xr2195 A
372.15 KiB / 372.15 KiB [====================================] 100.00%/ #
/ # btfs storage upload QmbpRkc1o3QY75noqRn2DQBT8QSASxiZwfoEmcR8xr2195
{"ID":"178e2f6e-7641-4f5e-9b5a-5d595d8f8230"}
/ # btfs storage upload 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 status
{
"Status": "complete",
"Message": "Payment successful! File storage successful!",
"AdditionalInfo": "",
"FileHash": "QmbpRkc1o3QY75noqRn2DQBT8QSASxiZwfoEmcR8xr2195",
"Shards": {
"178e2f6e-7641-4f5e-9b5a-5d595d8f8230:QmbpRkc1o3QY75noqRn2DQBT8QSASxiZwfoEmcR8xr2195:0": {
"ContractID": "178e2f6e-7641-4f5e-9b5a-5d595d8f8230,15542555-f163-4937-99c4-4b271e5d6c6b",
"Price": 125000000,
"Host": "16Uiu2HAkxGXhEBe3Y5YnwvGDk3oRChQQFDS2kC6tkRkfdxPFsUMJ",
"Status": "contract",
"Message": "",
"AdditionalInfo": "UPLOADED"
}
}
}
# 日志
copy get, shardHashes:[QmbpRkc1o3QY75noqRn2DQBT8QSASxiZwfoEmcR8xr2195] fileSize:381216, shardSize:381216, copy:0 err:<nil>
size:0.00035503506660461426 GB, price:125000000000000000000 , storageLength:30, TotalPay:1331381000000000000
check, balance=500000000000000000000, realAmount=1331381000000000000
size:0.00035503506660461426 GB, price:125000000000000000000 , storageLength:30, TotalPay:1331381000000000000
[2022-05-05T11:58:17Z] session: 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 entered state: submit, msg: Hosts found! Checking chequebook balance, and visiting guard.
check, balance=500000000000000000000, realAmount=1331381000000000000
[2022-05-05T11:58:18Z] session: 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 entered state: guard, msg: Preparing meta-data and challenge questions.
[2022-05-05T11:58:18Z] session: 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 entered state: guard:file-meta-signed, msg: Preparing meta-data and challenge questions.
[2022-05-05T11:58:20Z] session: 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 entered state: guard:questions-signed, msg: Preparing meta-data and challenge questions.
[2022-05-05T11:58:21Z] session: 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 entered state: wait-upload, msg: Confirming file shard storage by hosts.
[2022-05-05T11:58:21Z] session: 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 entered state: wait-upload:req-signed, msg: Confirming file shard storage by hosts.
[2022-05-05T11:58:54Z] session: 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 entered state: pay, msg: uploaded, doing the cheque payment.
send cheque: paying... host:16Uiu2HAkxGXhEBe3Y5YnwvGDk3oRChQQFDS2kC6tkRkfdxPFsUMJ, amount:1331381000000000000, contractId:178e2f6e-7641-4f5e-9b5a-5d595d8f8230,15542555-f163-4937-99c4-4b271e5d6c6b.
payInCheque done
[2022-05-05T11:58:54Z] session: 178e2f6e-7641-4f5e-9b5a-5d595d8f8230 entered state: complete, msg: Payment successful! File storage successful!
接入合约(待更新)
上述的部署,需要等待一段时间后才可以接入合约,这里先用已经配置好的 btfs2 进行合约接入。
# 创建 vault contract:0x9AF4bEc1A30BeC47756Ecef4cf43B91592121bC9
# 创建 vault contract:0x763d7858287B9a33F4bE5bb3df0241dACc59BCc7
# 0x363d3d373d3d3d363d735cf1f8c1c89e6fd24863918571917b5109d6a1705af43d82803e903d91602b57fd5bf3
# Palkeoramix decompiler.
def _fallback() payable: # default function
delegate 0x5cf1f8c1c89e6fd24863918571917b5109d6a170 with:
funct call.data[return_data.size len 4]
gas gas_remaining wei
args call.data[return_data.size + 4 len calldata.size - 4]
if not delegate.return_code:
revert with ext_call.return_data[return_data.size len return_data.size]
return ext_call.return_data[return_data.size len return_data.size]
运行问题
arp 异常
# tail -10 /var/log/syslog
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.050754] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.051954] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.052283] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.074320] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.074871] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.139645] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.146649] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.146961] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.147239] neighbour: arp_cache: neighbor table overflow!
Apr 28 19:16:57 nl-5288-V5 kernel: [365687.147509] neighbour: arp_cache: neighbor table overflow!
上述内核错误信息,一直在出现,尝试查询本地的 arp 数量和配置信息,发现并没有超过内核设置。
$ arp -an|wc -l
101
$ sysctl -a | grep net.ipv4.neigh.default.gc_thresh
net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh2 = 2048
net.ipv4.neigh.default.gc_thresh3 = 4096
在 5.1 的内核上确实存在 bug,可以参考:查找kernel 5.1 出现 bug: neighbor table overflow!的过程,并且在 5.2 版本就已经修复了。
而我们的环境是 Linux nl-5288-V5 5.13.0-40-generic #45~20.04.1-Ubuntu SMP Mon Apr 4 09:38:31 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
。
目前无法判断该问题对机器本身有什么影响。(指的是机器运行,以及网络通信)
2022-04-24 13:37 机器异常卡死问题
# 判断机器重启时间
$ date
Thu 28 Apr 2022 07:44:56 PM CST
$ uptime
19:45:18 up 4 days, 6:03, 5 users, load average: 6.29, 19.05, 19.78
# 机器启动时间大概在 2022-04-24 13:41 分,查看相关 syslog
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.260386] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.327924] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.448076] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.448323] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.448564] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.448792] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.448994] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.449193] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:37:47 nl-5288-V5 kernel: [12709346.449391] neighbour: arp_cache: neighbor table overflow!
Apr 24 13:42:21 nl-5288-V5 systemd-modules-load[481]: Inserted module 'lp'
Apr 24 13:42:21 nl-5288-V5 systemd-modules-load[481]: Inserted module 'ppdev'
Apr 24 13:42:21 nl-5288-V5 systemd-modules-load[481]: Inserted module 'parport_pc'
Apr 24 13:42:21 nl-5288-V5 systemd-modules-load[481]: Inserted module 'msr'
# 在收到告警后,通知机房的人进行现场重启。从上述日志中,难以分析卡死的原因。
$ last -x |grep reboot
reboot system boot 5.13.0-40-generi Sun Apr 24 13:42 still running
reboot system boot 5.4.0-42-generic Sun Nov 28 11:20 still running
# 查看重启信息,也没有什么头绪,该问题暂不可解
内存消耗异常
从2022-04-27号开始关注内存数据,监控上的内存数据是一直在降低的。数据不够完整,这里指提出一些可能性:
- page cache 越来越多
- 程序单体运行过程需要越来越多的内存
这两个其实可以归类为一个问题,就是单机节点过多,每个的数据读取都需要占据内存空间,也需要系统的 page cache。
启动连接失败
一般的报错信息如下:
Error: Could not connect to blockchain rpc, please check your network connection.Post "https://rpc.bt.io/": dial tcp: lookup rpc.bt.io on 223.5.5.5:53: write udp 172.17.0.45:60094->223.5.5.5:53: write: invalid argument
本质上可能还是国内网络的波动,这种情况没有确认的修复办法,只能等待一会重试。可以尝试调整外部网络环境。(注:这个问题的出现和公网映射没有关系)
2022.4.29 日会议记录,需要做的事
- 存储整个流程,能否走内部网络?
- 开个机器,打通数据转发,等待两个月。7402+512G+1T nvme