区块链 Bitcoinfile 文件存储协议 (中文版)

bibodeng · 2019年01月05日 · 最后由 bibodeng 回复于 2019年03月05日 · 21 次阅读
本帖已被设为精华帖!

比特币文件存储协议(v0.3)

1. 简介

本协议将会展示一个使用比特币现金区块链来存储文件的简洁协议,该协议允许在链上存储文件,也允许创建一个固定的URI,指向非链上文件。该协议的初衷是想打造一个接口简单又稳定且匿名的文件存储系统,这样比特币现金就能完善没有存储功能这个缺点了。

当时设计这个协议是为了方便SLP token的创世交易里面存储一个JSON文档,BFP(Bitcoin File Protocol)是一个独立的协议,已经从SLP token方案剥离出来,可以独立使用。它设计了一个 DAG(有向无环图)来链接各个交易里面的文件块及元数据。这样的协议在当前阶段是很有用的,因为现在区块和交易存储空间有限,故而要将它们切割成碎片,要的时候再重组起来,等以后区块大了,会有更大的文件需要存储。

2. 协议

本协议描述如何处理外部URI和区块内部完整文件。

2.1 BFP 存储类型

不同的BFP存储类型用来表示不同的文件结构,无论在哪一种 BFP文件类型中,OP_RETURN里面都需要一个bfp_msg_type用来标识类型,当前有三种文件结构:

  • bfp_msg_type = 0x01: 文件 (OP_RETURN中存储数据)

  • bfp_msg_type = 0x02: 文件 (P2SH中存储数据)

  • bfp_msg_type = 0x03: 文件夹

2.2 文件 (BFP 文件类型 0x01)

文件将上传到区块链上交易的OP_RETURN中,放置位置为 vout=0,也即要放在每个存储数据块的交易的第一个输出。文件块通过第二个输出(vout=1)来放置下一个文件块的指针,用来标识下一个块的位置。文件可以分享给任何一个人,只要把最后一个带有元数据的交易哈希给对方即可。下面两个例子展示如何通过该协议上传一个文件的两个部分。

bfp-fig-1

**Figure 1: 一个文件存储例子,先发一个充值交易,随后两个带数据块的交易. OP_RETURN里的数据用黄色高亮了。

最后一笔交易的哈希就是这个文件的物理位置,通过这个哈希,你就能完整的恢复出整个文件。最后一个交易包含一组元数据,包括要恢复出该文件所需的关联数据块的数量。

建议每次存储文件前,发送一笔充值交易,就像上图最前面表示的一样,这笔资金需要cover住所有后续交易的所有费用(包括矿工费和指针的花费)。这样做的好处是,能够降低资金管理的复杂程度,同时也能够减少超过正常的花费,量入为出。但是需要你先计算出每次文件存储的费用,不过这个应该还好,因为每个携带数据块的交易都只有一个输入,有一到两个输出。

2.2.1 链上文件存储

  1. 数据块顺序:数据块应该要按照交易签名的顺序来放置,先签名的交易要存储前面的数据块。这意味着第一个交易,要放置文件的第一个数据块,最后一个数据块,放在最后一个交易里。如果最后一个数据块的空间不够放,可以和元数据一起放在最后一个块里。

  2. 元数据 OP_RETURN消息: 最后一个交易应该包含一个OP_RETURN输出,位置在交易的第一个输出(vout=0),规定的格式如下:

  • OP_RETURN <lokad_id_int = 'BFP\x00'> <bfp_msg_type = 0x01> <chunk_count_int> <filename_no_extension_utf8*> <file_extension_utf8*> <file_byte_count_int*> <file_sha256_bytes*> <previous_file_version_sha256_bytes*> <file_uri_utf8*> <chunk_X_data_bytes*>

  • 如果该文件一个数据块空间即可容纳,并且能附带上元数据,那么一个交易就可以存完一整个文件

  • 如果一个文件要分成好几个数据块来存放,那么至少要使用一个如下所述的OP_RETURN来存储一个数据块

  1. 数据块交易的OP_RETURN: 对于每个非最后数据块,都应该包含一个OP_RETURN消息,使用下面的格式存储:
  • OP_RETURN <chunk_X_data_bytes>
  1. 数据块接力棒:对于非最终区块,都需要一个接力棒,来指示下一个数据块和元数据位置。 数据块交易的第二个输出(vout=1)应该输出一个UTXO,下一个数据块或者包含元数据的块就花费这笔UTXO,这样就能把数据块连接在一起了。接力棒要作为下个交易的第一个输入(vin=0),这样能够方便地查找的一个块的上下文。

2.2.2 如何下载一个链上文件

文件位置在包含元数据OP_RETURN的那笔交易里面,获得了那笔交易的哈希,就能完整恢复文件,具体包含以下几个步骤:

  1. 下载包含元数据的交易

  2. 解析第一个输出(vout=0)的OP_RETURN里面存储的元数据

  3. 如果元数据里面存储的chunk_count = 1 ,且这个块里面包含了数据块内容,那么把这个数据块内容提取出来,就完成了文件的提取

  4. 如果chunk_count = 0,表示数据并未存储在区块链上,即使 chunk_data里面有数据,也应该忽略,一个非链上存储文件可以通过chunk_count = 0 和一个非空的 file_uri_utf8 来指定,这将会在只有元数据不可变的应用中非常有用,URL指向的内容可变没有关系。

  5. 如果还有更多的数据块需要下载,你只需要溯源第一笔输入(vin=0)的交易,就可以找到上一个数据块,然后用 "数据块交易的OP_RETURN" 格式解析即可

  6. 重复第五步直到解析完指定的数据块数量

  7. 按照先签名先排序的方式组装整个文件,也即按照上面顺序得到结果后,逆序组装即可得到完整文件了。

2.2.3 如何下载非链上文件

既然提供了file_uri_utf8,那么你按照所需的方法通过URL下载该文件即可,如果你用的是IPFS,可以参考给出的IPFS附录A可以参考。

2.3 文件 (BFP 文件类型 0x02)

大一点的文件(大于100KB)要存储在链上,可以使用bfp_msg_type 0x02。这个类型和0x01类型差不多,只不过文件块被嵌入在了一个 P2SH 脚本里面,通过把数据放在交易的第一个输入(vin=0)的解锁脚本scriptSig里面,而不是放在OP_RETURN里面。每个数据块通过第二个输出(vout=1)来相互关联。该文件可以通过最后一个数据块或者元数据块交易来定位。

P2SH赎回脚本长得像下面:

OP_DROP ... OP_DROP <PubKey> OP_CHECKSIG

解锁脚本长得像下面:

<signature> <data> ... <data> <redeem script>

...的地方表示可以有很多个 OP_DROP 或者 <data>。每个data单元包含约 520 字节的数据片段,它需要把这些data片段都连接起来构成一个数据块。通过对应个数的OP_DROP,可以让解锁脚本里面压入的数据不影响P2SH脚本的功能。

2.4 文件夹 (BFP 文件类型 0x03)

一个文件夹类型的交易可以存储一个或多个文件或子文件夹的交易哈希,这个文件类型其实就是简单地提供一个交易ID列表。

2.4.1 如何创建文件夹

  1. 元数据交易的OP_RETURN消息: 一个交易在第一个输出位置(vout=0)中设置一个 元数据OP_RETURN 消息,元数据格式如下:
  • OP_RETURN <lokad_id_int = 'BFP\x00'> <bfp_msg_type = 0x02> <list_page_count> <folder_name*> <folder_description*> <txid_0_int> ... <txid_i_int*> ... <txid_n_int*>

    <txid_0_int><txid_x_int*> 代表一个交易哈希,指向另外一个BFP文件或者目录,至少要提供一个交易哈希。

  1. 列表页OP_RETURN消息: 一个BFP文件夹中,可以存放无限数量的BFP文件和子文件夹。列表页的数量要在OP_RETURN元数据中指定,并且这个数字必须大于1,表示启用列表页,列表页的格式如下:
  • OP_RETURN <txid_0_int> ... <txid_i_int*> ... <txid_n_int*>
  1. 列表页接力棒: 对于任何一个列表页交易,使用一个接力棒来连接一个文件夹元数据交易和一个列表页交易。这个接力棒输出,应该放在第二个输出位置(vout=1), 该笔UTXO,应该在下一个交易中作为第一个输入(vin=0),这样才算是一个合法的引用。列表页交易的下一个交易,可以是一个列表页交易,也可以是元数据交易。

2.4.2 如何在一个文件夹中发现文件

决定一个文件和文件夹是否包含在一个文件夹中的规则是非常简单的,一个协议实现应该解析OP_RETURN里面的元数据,和其它存储的列表页交易里面的OP_RETURN消息,这样遍历下去,就可以把所有的文件或子文件找出来了。

3. OP_RETURN 语法和格式要求

  1. 数据字段用尖括号括起字段名表示,如

  2. 非必填字段通过字段名后面带星号来表示,可以用 0x4c 0x00来表示留空,当然也可以用 0x4d 0x00 0x000x4e 0x00 0x00 0x00 0x00.

  3. 压入数据操作码没有展示在上面,对于每个数据字段都要有对应的压入数据操作码。

  • 只有操作码 0x010x4e 是合法的 (它们在OP_RETURN之后),这意味着不是所有的数据操作码都是可以的。 不允许在OP_RETURN的任何地方使用空操作码 0x00 (OP_0) 或者 1字节的文字操作码如 0x4f-0x60 (OP_1OP_16 以及 OP_1NEGATE) 。例如,在 0x01 文件类型交易中,不能使用 0x58 来压入 数字'8'在 1字节的 chunk_count_int 字段上, 即使在正常的比特币脚本里面 0x58 通常等效于 0x01 0x08 (push [0x08])。因为这个原因,有些标准的比特币脚本解释器,将所有PUSH操作码等价了,但是却不能用来解析BFP交易。

  • 比特币脚本允许多种方式将一个字节数字压入,BFP协议同样支持。例如,可以通过以下方式push一个4字节的块 (比如说 Lokad ID) : 0x04 [chunk], 0x4c 0x04 [chunk], 0x4d 0x04 0x00 [chunk], 或 0x4e 0x04 0x00 0x00 0x00 [chunk].

  1. BFP的Lokad Terab id 是 0x00504642 ,当推入栈中时,通过小端法字节排序,字面上的意思就是 BFP\x00。更多Lokad Terab 项目信息请参考 GitHub.

  2. Endianness: 所有的数据压入操作,除了Lokad协议标识,都应该可以压入脚本栈中,使用的是大端字节序。

  3. 每个变量的数据编码方式将会包含在每个变量名的最后面。

4. Bitcoinfile URI

这部分说明不是协议规则,只是一个用于提升用户体验的额外推荐。我建议使用一个bitcoinfile:前缀,搭配指向链上交易ID或者链下别的地方的ID,用于标识一个Bitcoinfile文件。未来也可以使用形如bitcoinfiles:这样的方式来表示文件夹。

例如:

bitcoinfile:<txid-of-a-file>

bitcoinfiles:<txid-of-a-folder>

使用交易ID前缀,不会影响协议规则,实现规则的代码也应该略过前缀,即使用户提供了带前缀的输入。只有交易ID才是决定BFP文件内容的关键。

5. 其它考虑

  1. 网络规则当前限制了每个块中关联交易不能超过25个,这限制了本文件协议的数据吞吐量,超过5KB的文件会受到影响。实现方案应该注意关联交易的UTXO应该在创建文件前就准备好。例如,如果用户先前已经在本区块期间创建过交易,那么一次文件上传所需的交易就应该少于25个,否则的话,超出部分不会被广播。

  2. 为每次的文件上传设置一个大小限制是一个明智的做法。

  3. 文件数据可以采用很多方式编码,我建议通过设置的扩展名字段,来表示加密类型,如果本地文件不带有内部加密机制(如 PDF)。

  4. 在某些情况下,文件记录保持一个引用前一个文件哈希(地址)的字段是非常有用的,因此,我们设计了一个名叫<previous_file_version_sha256_bytes*>的可选字段。

共收到 3 条回复
aaron67 将本帖设为了精华贴 01月05日 22:56

CSW 对这协议怎么看

bsverhh 回复

他应该要支持吧,具体没有听到他评论过,这个协议是实现链上存储的关键协议,unwritter的B协议就是基于这个的

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册