Btfs 基础

2022/04/27

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:价格合约,更新全网存储价格。

btfs 架构图

这个架构图中缺失了两个合约,更关注整个存储过程。租户通过价格合约获得当前存储价格,然后质押合约会给出主机推荐,然后租户向保险库合约进行质押,同时向主机节点发送需要保存的文件和支票,主机完成存储后,(会向存储证明合约发送存储证明)并向保险库合约兑换支票。这样就实现了两个不信任的角色之间的交易。

部署方案

为了实现单机多节点,同时对性能需求进行限制,选择使用 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 日会议记录,需要做的事

  1. 存储整个流程,能否走内部网络?
  2. 开个机器,打通数据转发,等待两个月。7402+512G+1T nvme

Search

    Table of Contents