在 BTFS 上托管 NFT 元数据

将 NFT 图片和元数据 JSON 上传到 BTFS,并将生成的 URI 用作 tokenURI。

📘

前置阅读

BitTorrent File System(BTFS)是基于 TRON 网络与 BitTorrent 生态构建的去中心化文件存储协议。在 NFT 场景中,定义每个 Token 名称、图像及属性的元数据 JSON 文件通常托管在链下,并通过智能合约中的 tokenURI 函数引用。BTFS 可用于托管这类元数据,降低对单一 Web 服务器的依赖。

本指南将引导你将图片和元数据 JSON 上传到 BTFS,生成可用于 TRC-721 合约的 URI。

元数据托管选择:BTFS 与其他方案

选项费用持久性去中心化程度
BTFS以当前客户端和存储服务报价为准取决于存储合约、续费和副本可用性在 BTFS 网络上去中心化
IPFS + 固定服务(如 Pinata)订阅或按固定付费只要固定即可持续在 IPFS 网络上去中心化
Arweave一次性付费永久存储永久去中心化
自托管 CDN托管费用只要你维护主机即可持续中心化

BTFS 是 TRON 生态中常用的 NFT 元数据托管方案之一。

步骤 1——安装并初始化 BTFS

从官方 go-btfs 仓库安装,并从 Releases 下载适合当前系统和架构的最新稳定版本,核对页面提供的 SHA-256 摘要。

执行 btfs init 初始化本地节点时,系统会生成节点密钥,并可从同一密钥派生 TRON 格式地址和 BTTC 格式地址。初始化后应用 storage renter 配置,以启用从 BTFS 网络购买存储的功能:

btfs config profile apply storage-client

节点启动后,可运行 btfs id 查看地址。本指南的 Gas 和存储付费流程使用输出中的 BTTC 格式 BttcAddress0x...),不要将它与 TRON Base58 格式地址(T...)混用。

BTFS 初始化输出,显示节点地址

步骤 2——启动节点并准备存储费用

运行 btfs daemon。首次启动时,节点会显示本次生成的 BTTC 地址;如果提示 Gas 余额不足,保持 daemon 运行,核对终端中由本地节点输出的地址和所需余额,再从你控制的钱包通过 BTTC 网络向该地址转入足量 BTT,使其余额达到本次客户端提示的要求。BTT 用于 BTTC 链上 Gas,不用于支付存储费用。节点检测到余额后会继续部署 vault;部署完成后运行 btfs id,确认输出中的 BttcAddress 与充值地址一致,并且同时显示 VaultAddress

上传者属于 BTFS storage renter,需要用 WBTT 支付存储提供者。通过当前版本 BTFS Dashboard 的 Swap 功能或你控制的 BTTC 钱包准备 WBTT,并转入本节点的 BttcAddress;然后使用本地节点把 WBTT 存入它自己的 vault:

# 查看 vault 的 WBTT 余额;输出以 10^18 基础单位计
btfs vault balance

# 示例:将 1 WBTT(10^18 基础单位)从本节点的 BttcAddress 存入 VaultAddress
WBTT_AMOUNT_WEI=1000000000000000000
btfs vault deposit "$WBTT_AMOUNT_WEI"

等待存入交易确认后,再运行 btfs vault balance。余额应增加;随后可先上传一个小文件验证完整流程。不同版本的 Dashboard 布局可能变化,操作前应以已安装版本的 Release Notes、btfs vault --help 和界面提示为准。

⚠️

旧版教程中的 btfs wallet deposit 流程不适用于当前 BTFS 版本。不要把资金发送到截图、示例输出或第三方教程中的地址;只使用你本地节点本次启动生成并经 btfs id 核对的地址。

步骤 3——上传图片

步骤 3.1——将图片添加到本地节点

准备一个图片文件(本指南使用 coral.jpeg)。要把内容上传到 BTFS 存储提供者,应在添加时启用 Reed-Solomon 纠删码(Reed-Solomon erasure coding):

btfs add --chunker=reed-solomon coral.jpeg
printf '粘贴上一条命令返回的图片 CID:'
read -r FILE_CID
BTFS add 输出,显示生成的哈希值

第一条命令输出一个 CID;将该值粘贴到紧随其后的提示中,后续命令通过 FILE_CID 复用它。不同分片方式会为同一文件生成不同 CID,不能用旧教程或截图中的 CID 代替本次输出。

⚠️

不带 --chunker=reed-solomon 的普通 btfs add 只按默认分块方式加入本地节点。BTFS 4.1 的 storage upload 对这类 CID 会进入 copy=0 兼容路径,只创建一个待存储副本;自动续期只延长该存储合约,不会增加副本或纠删码冗余。普通模式适合本地或临时验证,不应作为长期 NFT 元数据的默认方案。

步骤 3.2——上传到 BTFS 网络

通过步骤 3.1 返回的 Reed-Solomon CID 上传文件;BTFS 会把数据分片和校验分片分配给存储提供者。对于需要长期托管的 NFT 元数据,可在 BTFS 4.1 或更高版本中启用自动续期。如果只需要固定期限,可将 --autorenew 改为 --storage-length=30 等明确天数。自动续期仍需 Vault 中有充足余额;应持续监控余额和续期状态,不能把内容寻址 URI 本身视为永久存储保证。

以下完整示例使用 jq 从本次上传响应中提取 Session ID、查询上传状态并确认自动续期。如果未安装 jq,脚本会给出提示且不会执行上传:

if [ -z "${FILE_CID:-}" ]; then
  printf '%s\n' "请先完成步骤 3.1,并在当前终端设置 FILE_CID" >&2
elif command -v jq >/dev/null 2>&1; then
  UPLOAD_RESULT=$(btfs storage upload "$FILE_CID" --autorenew) &&
    printf '%s\n' "$UPLOAD_RESULT" &&
    SESSION_ID=$(printf '%s\n' "$UPLOAD_RESULT" | jq -er '.ID') &&
    btfs storage upload status "$SESSION_ID" &&
    btfs storage upload renew info "$FILE_CID"
else
  printf '%s\n' "需要先安装 jq,然后重新运行此代码块" >&2
fi

其中 status 命令只查询一次当前状态;请使用同一个 SESSION_ID 重复执行该命令,直到所有数据分片和校验分片均显示完成。还可以运行 btfs storage upload renew list 查看本节点启用自动续期的全部文件。自动续期只延长已建立的分片存储关系;它不能替代分片完成检查,也不会自行增加冗余。

BTFS daemon 日志,显示文件存储成功

步骤 3.3——验证文件可读取

另一个从未导入该 CID 的 BTFS 节点上读取文件。先粘贴步骤 3.1 得到的 CID,再确认该节点能通过网络发现并获取内容:

printf '粘贴步骤 3.1 返回的图片 CID:'
read -r FILE_CID
btfs cat "/btfs/$FILE_CID" > downloaded-coral.jpeg

分别计算原文件和下载文件的 SHA-256 摘要,两者应一致:

Linux:

sha256sum coral.jpeg downloaded-coral.jpeg

macOS:

shasum -a 256 coral.jpeg downloaded-coral.jpeg

如果没有第二个节点,也可以通过你选择的独立 BTFS 网关按 https://<GATEWAY_HOST>/btfs/<CID> 获取并比较内容,但不要使用上传节点自身的本地网关完成这项检查。第二节点或网关取回只能证明检查当时可用,不能单独证明各分片由相互独立的提供者保存或保证长期可用;还应结合上传会话中全部分片的完成状态、续期状态和 Vault 余额判断。公共网关也可能限流、迁移或下线。

步骤 4——构建元数据 JSON

创建一个 JSON 文件(本指南使用 coral.json),遵循 TRC-721 元数据结构。将 REPLACE_WITH_IMAGE_CID 替换为步骤 3.1 实际返回的图片 CID,使 image 字段采用内容寻址的 btfs://<CID> URI,避免把元数据绑定到单一 HTTP 网关:

{
  "name": "Coral #1",
  "description": "A unique TRC-721 collectible.",
  "image": "btfs://REPLACE_WITH_IMAGE_CID",
  "attributes": [
    { "trait_type": "Color", "value": "Pink" },
    { "trait_type": "Rarity", "value": "Rare" }
  ]
}

步骤 5——上传元数据 JSON

以与图片相同的方式上传 JSON:先运行 btfs add --chunker=reed-solomon coral.json,保存返回的元数据 CID,并将其赋给步骤 3.2 使用的 FILE_CID。图片和元数据是两个独立 CID;应分别确认两次上传的全部分片均已完成。如果使用自动续期,还应分别运行 renew info 确认两者都已启用:

btfs add --chunker=reed-solomon coral.json
printf '粘贴上一条命令返回的元数据 CID:'
read -r METADATA_CID
FILE_CID=$METADATA_CID

然后在当前终端中重新运行步骤 3.2 的命令块,把元数据文件上传到 BTFS Storage。上面的赋值会让该命令块使用元数据 CID。

上传元数据 JSON 文件到 BTFS

上传完成后,在步骤 3.3 使用的独立节点或网关读取元数据。先粘贴步骤 5 得到的 CID,再验证返回的是预期 JSON:

printf '粘贴步骤 5 返回的元数据 CID:'
read -r METADATA_CID
btfs cat "/btfs/$METADATA_CID"

步骤 6——铸造时将 URI 用作 tokenURI

在已部署的 TRC-721 合约上调用 safeMint 时,将步骤 5 返回的元数据 CID 组成 btfs://<METADATA_CID>,并作为 uri 参数。铸造前确认目标钱包、市场或索引器能解析 btfs://;如果只能接受 HTTPS,应选用你能够长期维护或替换的网关转换层,而不是依赖某个示例网关域名。完整的铸造流程请参见发行 TRC-721 Token


相关资源