上一篇文章中记录到了IPFS的BitSwap协议,今天接着往下看关于Object的部分。

Merkle DAG

Merkle DAG是IPFS核心概念之一,在Git的数据结构上进行了改造。理解Merkle DAG之前先来了解下Merkle Tree

简单说,Merkle Tree是一种特殊的树结构,其叶子节点的值为包含记录的哈希值,而非叶子节点的值为子节点哈希值合并后的哈希结果,一图胜千言:

ipfs3-1

Merkle Tree最常见的一个应用就是数据验证领域了,这种数据结构可以大量减少验证所需传输的数据量,在Top Hash已知的情况下想验证L3中的数据是否被篡改过,只需要向一个可信任的中心请求获取Hash0Hash1-1即可。

正由于比特币中将Merkle Tree的根哈希值包含在了区块头中,所以各种轻钱包(SPV)才得以出现。

ipfs3-2

而Merkle DAG则是一种 有向无环图 结构,而且和Merkle Tree有个很重要的区别就是Merkle DAG中准许非叶节点存储数据。IPFS中数据结构定义如下:

1
2
3
4
5
6
7
8
9
10
type IPFSLink struct {
Name string
Hash Multihash
Size int
}

type IPFSObject struct{
links []IPFSLink
data []byte
}

首先我们有这样一个目录结构:

1
2
3
4
5
6
ipfs
├── imgs
│   └── 1.jpg
├── test.txt
└── video
└── paxos和分布式系统.mp4

然后执行add命令添加:

1
2
3
4
5
6
7
8
ipfs add -r ipfs
added QmTBGNDfodpscKje46wyTmzsPsJ6REWebionWK9dVc2Cms ipfs/imgs/1.jpg
added QmRmfJ8RYrULTZkjt9H11kkPBswrzVQXf36QAF3XoBG6XZ ipfs/test.txt
added Qme5oV4uKLA2BDVjFtqiCfFqv74LZXTqG1oiuu5gshqvk7 ipfs/video/paxos和分布式系统.mp4
added QmQEwN1jbjPPAnfjnbXjCi92yKu7gZbZnmK2niotz4d8oK ipfs/imgs
added QmVYaDuyoS4otNXnQ7WRbBP4ZZuLi7PLhyz2LV9vLWVwWb ipfs/video
added QmPkbxXPM2hY97NR8Tf25KZxysWn4FZrsuPwD8ztYM32j4 ipfs
53.15 MiB / 53.16 MiB [=============================================================================================================================================================] 99.98%

这里插一句,在浏览器中访问

1
2
3
4
5
http://localhost:8080/ipfs/QmTBGNDfodpscKje46wyTmzsPsJ6REWebionWK9dVc2Cms

http://localhost:8080/ipfs/QmQEwN1jbjPPAnfjnbXjCi92yKu7gZbZnmK2niotz4d8oK/1.jpg

http://localhost:8080/ipfs/QmPkbxXPM2hY97NR8Tf25KZxysWn4FZrsuPwD8ztYM32j4/imgs/1.jpg

这3种访问路径都可以最终得到1.jpg这个图片。

可以使用ls命令查看文件切分情况:

1
2
3
4
ipfs ls -v Qme5oV4uKLA2BDVjFtqiCfFqv74LZXTqG1oiuu5gshqvk7
Hash Size Name
QmexC2a5eSkVuoErKhAjxpMt6DJoQeS57T3yU1cxCpb3Tu 45623854
QmR2GQi1DhvGzZ1Ra1fgpwKBTd9ubt9aPUSCvqMZPHB3cp 10103195

可以看出,mp4文件被切分成了2部分。

再使用object get命令查看ipfs这个文件夹的DAG:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ipfs object get QmPkbxXPM2hY97NR8Tf25KZxysWn4FZrsuPwD8ztYM32j4

{
"Links": [
{
"Name": "imgs",
"Hash": "QmQEwN1jbjPPAnfjnbXjCi92yKu7gZbZnmK2niotz4d8oK",
"Size": 18418
},
{
"Name": "test.txt",
"Hash": "QmRmfJ8RYrULTZkjt9H11kkPBswrzVQXf36QAF3XoBG6XZ",
"Size": 18
},
{
"Name": "video",
"Hash": "QmVYaDuyoS4otNXnQ7WRbBP4ZZuLi7PLhyz2LV9vLWVwWb",
"Size": 55727234
}
],
"Data": "\u0008\u0001"
}

也和上面给的数据结构定义一致。

还有更重要的一点,IPFS准许使用者使用block相关命令直接操作Merkle DAG中的数据,比如:

1
2
3
4
5
6
echo "hi roy" | ipfs block put
QmUHW9uK8aKYHNSSJwS1AnnBt1dV6wbKmDWDr8tmRk9pXo

ipfs block get QmUHW9uK8aKYHNSSJwS1AnnBt1dV6wbKmDWDr8tmRk9pXo

hi roy

有这个功能可玩性就很高了,白皮书中给出了几种结构:

  • 键值对存储(key-value stores)
  • 关系型数据库(traditional relatioinal databases)
  • 三元组存储(Linked Data triple stores)
  • 文档发布系统(Linked document publishing systems)
  • 通信平台(Linked communications platforms)
  • 加密货币区块链(cryptocurrency blockchains)

再插一句,很多小伙伴不知道如何向已经存在的文件夹中添加新文件,比如想向ipfs目录添加文件test2.txt:

1
2
3
4
5
6
7
8
9
ipfs add test2.txt
added QmUARAWmSHC3aiWhYFcGfQGEyfAKdUPWqFHxemY3iDJDRM test2.txt
11 B / 11 B [=======================================================================================================================================================================] 100.00%

ipfs ls -v QmPkbxXPM2hY97NR8Tf25KZxysWn4FZrsuPwD8ztYM32j4
Hash Size Name
QmQEwN1jbjPPAnfjnbXjCi92yKu7gZbZnmK2niotz4d8oK 18418 imgs/
QmRmfJ8RYrULTZkjt9H11kkPBswrzVQXf36QAF3XoBG6XZ 18 test.txt
QmVYaDuyoS4otNXnQ7WRbBP4ZZuLi7PLhyz2LV9vLWVwWb 55727234 video/

可以发现文件并没有添加进文件夹中,而仅仅是获得了一个自己的哈希值。可以使用下面的命令:

1
2
3
ipfs object patch add-link QmPkbxXPM2hY97NR8Tf25KZxysWn4FZrsuPwD8ztYM32j4 test21.txt QmUARAWmSHC3aiWhYFcGfQGEyfAKdUPWqFHxemY3iDJDRM

QmZzY6fgW2ndjoAXpa2gMSaJ3c8h6hzq1TFusdF33u2jvy

这里会返回一个新的哈希值,查看这个发现文件已经被添加进去了:

1
2
3
4
5
6
ipfs ls -v QmZzY6fgW2ndjoAXpa2gMSaJ3c8h6hzq1TFusdF33u2jvy
Hash Size Name
QmQEwN1jbjPPAnfjnbXjCi92yKu7gZbZnmK2niotz4d8oK 18418 imgs/
QmRmfJ8RYrULTZkjt9H11kkPBswrzVQXf36QAF3XoBG6XZ 18 test.txt
QmUARAWmSHC3aiWhYFcGfQGEyfAKdUPWqFHxemY3iDJDRM 19 test21.txt
QmVYaDuyoS4otNXnQ7WRbBP4ZZuLi7PLhyz2LV9vLWVwWb 55727234 video/

注意存储后的文件名是ipfs object patch add-link中指定的值,而不是原文件名。再多说一句,这个命令需要的参数仅仅是哈希值,换言之如果你知道其他人的link地址也可以加到自己的文件下。

另外,最新的文件系统已经变成IPLD了,是Merkle DAG的一个变种。

IPNS

由上面的例子也可以看出来,在IPFS中每次新增、修改某个文件都会导致哈希值的变化,这对于实际应用中是十分不便的。为了处理这个问题,IPFS中引入了一个可变命名空间的概念——IPNS(InterPlanetary Name Space),结合之前说的 路由系统 实现了”不可变的内容”和”可变的引用”的组合(可以结合编程语言中指针的概念):

1
routing.setValue(NodeId,<ns-object-hash>)

之前说过每个节点的NodeID来源与其公钥的哈希值,那么给每个节点分配一个可变命名空间/ipns/<NodeID>,用户将私钥签名过的数据发布到这个路径上,别的用户下载这个数据时可以检查签名是否和公钥、NodeID匹配来验证真实性。