IPFS初窥3
在上一篇文章中记录到了IPFS的BitSwap
协议,今天接着往下看关于Object
的部分。
Merkle DAG
Merkle DAG是IPFS核心概念之一,在Git的数据结构上进行了改造。理解Merkle DAG之前先来了解下Merkle Tree。
简单说,Merkle Tree是一种特殊的树结构,其叶子节点的值为包含记录的哈希值,而非叶子节点的值为子节点哈希值合并后的哈希结果,一图胜千言:
Merkle Tree最常见的一个应用就是数据验证领域了,这种数据结构可以大量减少验证所需传输的数据量,在Top Hash
已知的情况下想验证L3中的数据是否被篡改过,只需要向一个可信任的中心请求获取Hash0
和Hash1-1
即可。
正由于比特币中将Merkle Tree的根哈希值包含在了区块头中,所以各种轻钱包(SPV)才得以出现。
而Merkle DAG则是一种 有向无环图 结构,而且和Merkle Tree有个很重要的区别就是Merkle DAG中准许非叶节点存储数据。IPFS中数据结构定义如下:
type IPFSLink struct {
Name string
Hash Multihash
Size int
}
type IPFSObject struct{
links []IPFSLink
data []byte
}
首先我们有这样一个目录结构:
ipfs
├── imgs
│ └── 1.jpg
├── test.txt
└── video
└── paxos和分布式系统.mp4
然后执行add
命令添加:
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%
这里插一句,在浏览器中访问
http://localhost:8080/ipfs/QmTBGNDfodpscKje46wyTmzsPsJ6REWebionWK9dVc2Cms
http://localhost:8080/ipfs/QmQEwN1jbjPPAnfjnbXjCi92yKu7gZbZnmK2niotz4d8oK/1.jpg
http://localhost:8080/ipfs/QmPkbxXPM2hY97NR8Tf25KZxysWn4FZrsuPwD8ztYM32j4/imgs/1.jpg
这3种访问路径都可以最终得到1.jpg
这个图片。
可以使用ls
命令查看文件切分情况:
ipfs ls -v Qme5oV4uKLA2BDVjFtqiCfFqv74LZXTqG1oiuu5gshqvk7
Hash Size Name
QmexC2a5eSkVuoErKhAjxpMt6DJoQeS57T3yU1cxCpb3Tu 45623854
QmR2GQi1DhvGzZ1Ra1fgpwKBTd9ubt9aPUSCvqMZPHB3cp 10103195
可以看出,mp4文件被切分成了2部分。
再使用object get
命令查看ipfs
这个文件夹的DAG:
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中的数据,比如:
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
:
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/
可以发现文件并没有添加进文件夹中,而仅仅是获得了一个自己的哈希值。可以使用下面的命令:
ipfs object patch add-link QmPkbxXPM2hY97NR8Tf25KZxysWn4FZrsuPwD8ztYM32j4 test21.txt QmUARAWmSHC3aiWhYFcGfQGEyfAKdUPWqFHxemY3iDJDRM
QmZzY6fgW2ndjoAXpa2gMSaJ3c8h6hzq1TFusdF33u2jvy
这里会返回一个新的哈希值,查看这个发现文件已经被添加进去了:
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),结合之前说的 路由系统 实现了"不可变的内容"和"可变的引用"的组合(可以结合编程语言中指针的概念):
routing.setValue(NodeId,<ns-object-hash>)
之前说过每个节点的NodeID来源与其公钥的哈希值,那么给每个节点分配一个可变命名空间/ipns/<NodeID>
,用户将私钥签名过的数据发布到这个路径上,别的用户下载这个数据时可以检查签名是否和公钥、NodeID匹配来验证真实性。