在研究Filecoin的邏輯之前,IPFS作為其存儲實現(xiàn)的協(xié)議,其關(guān)鍵特性需要有初步的認識。IPFS是在前人的基礎(chǔ)上,借鑒了bt下載,github工程管理,DHT,SFS等成熟的點對點系統(tǒng)之后,做出的協(xié)議上的改進和優(yōu)化。每個IPFS節(jié)點都會使用本地存儲作為全網(wǎng)存儲的一部分,所有節(jié)點都是平等的,沒有節(jié)點享有特權(quán)。節(jié)點與節(jié)點之間可以同步文件以及管理信息,他是一個獨立的,有許多子系統(tǒng)組成的系統(tǒng)。我們將把他拆分成不同的子模塊來分析,但是這并不意味著子模塊是獨立存在的,它們之間需要相互配合才能實現(xiàn)IPFS的各種能力。
1,IPFS節(jié)點標識
每一個ipfs節(jié)點都有一個唯一ID。首先通過S/Kademlia's
static crypto
puzzle方法生成一個密鑰對,然后對公鑰進行hash計算,得到一個hash值,此hash值就是該節(jié)點的唯一ID。每個新節(jié)點都會存儲節(jié)點的ID,公鑰和秘鑰。IPFS可以每次啟動都創(chuàng)建一個新節(jié)點,但是推薦使用舊的節(jié)點ID。Go語言版本的ID生成代碼:
difficulty =
n = Node{}
do {
n.PubKey, n.PrivKey = PKI.genKeyPair()
n.NodeId = hash(n.PubKey)
p = count_preceding_zero_bits(hash(n.NodeId))
} while (p < difficulty)
節(jié)點與節(jié)點建立連接之后,首先互相交換彼此的公鑰,并通過計算公鑰的hash,然后對比節(jié)點唯一ID與此hash值是否一致,作為判斷對方是否是一個有效的IPFS節(jié)點條件之一。
為了方便升級,IPFS節(jié)點在描述節(jié)點的hash值的同時,會在hash值前面增加hash生成的算法和hash值的長度,這種自描述的hash值使得節(jié)點與節(jié)點之間交互時,變得靈活和容易兼容。
2. 點對點網(wǎng)絡(luò)鏈接協(xié)議
a,IPFS支持各種傳輸層協(xié)議,他并不依賴于IP地址。
b,可靠性,如果底層網(wǎng)絡(luò)無法提供可靠性,ipfs使用utp和sctp協(xié)議來自己保證網(wǎng)絡(luò)的可靠性
c,可聯(lián)通性,ipfs支持NAT穿墻技術(shù)
d,數(shù)據(jù)完整性,ipfs可以進行檢查數(shù)據(jù)的hash以確定得到的數(shù)據(jù)是否是無損的
e,通過檢查消息發(fā)送者的公鑰,確保數(shù)據(jù)的所有權(quán)
3. 路由:文件位置定位
通過DSHT技術(shù),IPFS可以定位其他節(jié)點的網(wǎng)絡(luò)地址以及能夠提供某數(shù)據(jù)塊的網(wǎng)絡(luò)節(jié)點信息。對于小于1KB的數(shù)據(jù),IPFS直接存儲在DHT中,對于較大數(shù)據(jù),IPFS將能夠提供特定數(shù)據(jù)的節(jié)點的NodeID存儲在DHT中。不同的場景下會有不同的技術(shù)實現(xiàn),只要實現(xiàn)路由的基本接口,系統(tǒng)的其它模塊就可以正常運行,并不依賴于接口的具體實現(xiàn)。抽象的路由接口為:
type IPFSRouting interface {
FindPeer(node NodeId)
// gets a particular peer's network address
SetValue(key []bytes, value []bytes)
// stores a small metadata value in DHT
GetValue(key []bytes)
// retrieves small metadata value from DHT
ProvideValue(key Multihash)
// announces this node can serve a large value
FindValuePeers(key Multihash, min int)
// gets a number of peers serving a large value
}
4. 數(shù)據(jù)查詢與下載: BitSwap Protocol
BitSwap
Protocol的數(shù)據(jù)交換類似于BitTorrent下載,不同之處是,BitSwap
Protocol不拘泥于一個種子內(nèi)關(guān)聯(lián)的數(shù)據(jù)塊,BitSwap
更像是一個存儲市場,任何節(jié)點都可以獲取數(shù)據(jù)塊,不管數(shù)據(jù)之間是否強相關(guān)或者什么樣的文件格式.節(jié)點與節(jié)點之間可以在這個市場交易數(shù)據(jù)塊
節(jié)點之間對數(shù)據(jù)交互需求,天然的引申出token這樣的價值代幣,來提現(xiàn)數(shù)據(jù)的存儲與輸入輸出,在這個過程中,如果對端節(jié)點訪問某個數(shù)據(jù)塊,但是自己并沒有存有這樣的節(jié)點時,自己應(yīng)該發(fā)起一個有限級較低的服務(wù),來幫助自己的朋友節(jié)點來尋找他需要的數(shù)據(jù)庫并幫其緩存。
A, BitSwap Credit
該協(xié)議鼓勵節(jié)點做種子,以方便別人再使用時可以快速找到想要的數(shù)據(jù),節(jié)點默認可以隨意發(fā)送數(shù)據(jù)給其他節(jié)點,但是前提是對端沒有過度濫用這個機制,因此:首先節(jié)點統(tǒng)計他與其他節(jié)點之間的傳輸數(shù)據(jù)多少,如果對方收到數(shù)據(jù)多,分享的數(shù)據(jù)少,那么本節(jié)點可以根據(jù)這個比例來確定分享給對方節(jié)點的數(shù)據(jù)比例。如果對方節(jié)點共享的數(shù)據(jù)過少,那么本方節(jié)點可以決定停止共享數(shù)據(jù)給對方節(jié)點,過一定時間之后再共享數(shù)據(jù)。
B, BitSwap Strategy
傳輸策略有很多種,各有利弊,目前IPFS的策略正在開發(fā)中,但是有幾個總的原則需要滿足:
1,策略可以使得存儲交易的性能最優(yōu)化
2,防止揩油的節(jié)點使得全網(wǎng)的性能下降
3,對陌生的或者惡意的節(jié)點有抵抗性
4,對信任的節(jié)點具有較高的性能
目前在實際中使用的策略模型為:
這個模型可以防止女巫攻擊,保護好與以前成功發(fā)生過數(shù)據(jù)傳輸節(jié)點的關(guān)系,并且能夠快速阻止變質(zhì)的節(jié)點,直到它回復(fù)為止。
C,BitSwap Ledger
節(jié)點通過一個賬本數(shù)據(jù)結(jié)構(gòu)記錄與其它節(jié)點之間的數(shù)據(jù)交換情況,每次點對點數(shù)據(jù)鏈接建立之后,首先會檢查賬本信息,如果賬本信息不一致,則重新開始記賬,這樣可能會導(dǎo)致攻擊者故意用錯誤的賬本來摸出自己的不良記錄,但是同時他也失去了其他節(jié)點對他的信任。是否允許經(jīng)常丟棄賬本的節(jié)點接收數(shù)據(jù)取決于節(jié)點自身的安全設(shè)計,這個沒有嚴格的限制。同時節(jié)點也可以隨意丟棄關(guān)于某些節(jié)點的賬本信息,因為有些節(jié)點可能已經(jīng)永遠消失了。賬本條目數(shù)據(jù)結(jié)構(gòu):
type Ledger struct {
owner NodeId
partner NodeId
bytes_sent int
bytes_recv int
timestamp Timestamp
}
D, BitSwap Specification
// Additional state kept
type BitSwap struct {
ledgers map[NodeId]Ledger
// Ledgers known to this node, inc inactive
active map[NodeId]Peer
// currently open connections to other nodes
need_list []Multihash
// checksums of blocks this node needs
have_list []Multihash
// checksums of blocks this node has
}
type Peer struct {
nodeid NodeId
ledger Ledger
// Ledger between the node and this peer
last_seen Timestamp
// timestamp of last received message
want_list []Multihash
// checksums of all blocks wanted by peer
// includes blocks wanted by peer's peers
// Protocol interface:
interface Peer {
open (nodeid :NodeId, ledger :Ledger);
send_want_list (want_list :WantList);
send_block (block :Block) -> (complete :Bool);
close (final :Bool);
}
Peer.open(NodeId, Ledger).
與對端建立連接的時候,節(jié)點會讀取存儲在本地的賬本信息,如果沒有則新建一個賬本,然后發(fā)送open消息給對端節(jié)點。對端節(jié)點收到open請求之后,根據(jù)本節(jié)點的賬本信息決定是否接受此請求并建立連接,如果本節(jié)點不被接受,則會提示合適的錯誤信息,如果接受節(jié)點的open請求,則對端節(jié)點檢查本地賬本與本節(jié)點的賬本記錄,如果一直則直接建立連接,如果不一致則新建關(guān)于本節(jié)點的賬本數(shù)據(jù),然后建立連接。
Peer.send_want_list(WantList).
當鏈接建立之后,本節(jié)點會根據(jù)特定策略廣播want_list列表給所有對端節(jié)點,當對端節(jié)點收到want_list的廣播消息之后,會檢查本地存儲的數(shù)據(jù)塊,然后根據(jù)前面講到的策略發(fā)送數(shù)據(jù)給本節(jié)點。
Peer.send_block(Block).
發(fā)送數(shù)據(jù)動作比較直接,對端收到數(shù)據(jù)之后檢查hash值,然后返回檢查結(jié)果。如果成功接收數(shù)據(jù),對端節(jié)點把數(shù)據(jù)庫從need_list
遷移到
need_list,同時本節(jié)點和對端節(jié)點同時更新數(shù)據(jù)傳輸賬本。如果傳輸失敗,對端接收節(jié)點可以根據(jù)情況,自行決定是否繼續(xù)接收后續(xù)的數(shù)據(jù)傳輸。
Peer.close(Bool).
關(guān)閉連接的參數(shù)說明對端是主動關(guān)閉連接還是超時關(guān)閉連接,連接關(guān)閉后,雙發(fā)情況關(guān)于該鏈接的所有數(shù)據(jù),并保存好數(shù)據(jù)傳輸?shù)馁~本信息,供下次使用。
5. 數(shù)據(jù)存儲格式
DHT和BitSwap使得IPFS組件了點對點的數(shù)據(jù)塊存儲和分片系統(tǒng),在此基礎(chǔ)上IPFS創(chuàng)建了Merckle
DAG數(shù)據(jù)結(jié)構(gòu),這樣所有的數(shù)據(jù)以及數(shù)據(jù)的鏈接可以通過hash唯一標示,同時可以根據(jù)hash值檢查數(shù)據(jù)是否無損,而且相同的hash對應(yīng)相同的內(nèi)容,通過hash值可以檢查重復(fù)的數(shù)據(jù)存儲。
type IPFSLink struct {
Name string// name or alias of this link
Hash Multihash // cryptographic hash of target
Size int // total size of target
}
type IPFSObject struct {
links []IPFSLink // array of links
data []byte // opaque content data
}
以上數(shù)據(jù)結(jié)構(gòu)設(shè)計使得IPFS既可以根據(jù)內(nèi)容尋址,又可以將數(shù)據(jù)的格式完全交給用戶自定義實現(xiàn)。IPFS對象可以通過多級目錄的方式訪問,類似Unix的文件系統(tǒng)。IPFS每個節(jié)點都會存儲有自己的本地存儲,本地節(jié)點訪問其它節(jié)點數(shù)據(jù)后會緩存數(shù)據(jù)內(nèi)容,至少是暫時緩存。如果你希望某些數(shù)據(jù)對象長期保持在本地存儲,可以pin一下這個IPFS對象,這樣每次訪問次對象時都可以在本地存儲獲取。任何人都可以添加IPFS對象到全球網(wǎng)絡(luò)并針對對象加密,加密后的對象與未加密對象擁有不同的hash值,即使他們解密后擁有同樣的內(nèi)容,再IPFS的管理里面他們屬于不同的對象,IPFS會根據(jù)用戶的私鑰對加密數(shù)據(jù)解密,加密屬性具有遞歸性,父對象加密的時候,子節(jié)點也需要解密才能訪問,當然父對象和子對象可以采用不同的秘鑰。加密對象的數(shù)據(jù)結(jié)構(gòu):
type EncryptedObject struct {
Object []bytes
// raw object data encrypted
Tag []bytes
// optional tag for encryption groups
}
type SignedObject struct {
Object []bytes
// raw object data signed
Signature []bytes
// hmac signature
PublicKey []multihash
// multihash identifying key
}
6. 文件系統(tǒng)
IPFS提供了具有版本管理能力的文件系統(tǒng),IPFS把文件對象分成了4類:
A,文件對象:Blob,可尋址的二進制對象,類似操作系統(tǒng)里面文件系統(tǒng)存儲的數(shù)據(jù)塊。
{
"data": "some data here",
// blobs have no links
}
B,文件對象: list。List表示了一個有多個Blob組成的大型文件,在數(shù)據(jù)的開頭描述了該list的數(shù)據(jù)項的數(shù)據(jù)類型。
{
"data": ["blob", "list", "blob"],
// lists have an array of object types as data
"links": [
{ "hash": "XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x",
"size": 189458 },
{ "hash": "XLHBNmRQ5sJJrdMPuu48pzeyTtRo39tNDR5",
"size": 19441 },
{ "hash": "XLWVQDqxo9Km9zLyquoC9gAP8CL1gWnHZ7z",
"size": 5286 }
// lists have no names in links
]
}
C,文件對象:tree 與list相比增加了name屬性
{
"data": ["blob", "list", "blob"],
// trees have an array of object types as data
"links": [
{ "hash": "XLYkgq61DYaQ8NhkcqyU7rLcnSa7dSHQ16x",
"name": "less", "size": 189458 },
{ "hash": "XLHBNmRQ5sJJrdMPuu48pzeyTtRo39tNDR5",
"name": "script", "size": 19441 },
{ "hash": "XLWVQDqxo9Km9zLyquoC9gAP8CL1gWnHZ7z",
"name": "template", "size": 5286 }
// trees do have names
]
}
D,文件對象: commit,代表了任何對象在版本管理歷史中的一個快照
ipfs file-cat --json
{
"data": {
"type": "tree",
"date": "2014-09-20 12:44:06Z",
"message": "This is a commit message."
}, "links": [
{ "hash": "",
"name": "parent", "size": 25309 },
{ "hash": "",
"name": "object", "size": 5198 },
{ "hash": "",
"name": "author", "size": 109 }
] }
7. 命名系統(tǒng)
IPFS提供了IPNS技術(shù)使得用戶可以將不變的命名空間指向不同的數(shù)據(jù)內(nèi)容。同時IPFS支持IPNS下的域名訪問,修改本機的DNS 文件,可以將較長的hash值訪問對象改為域名訪問:
# this DNS TXT record
ipfs.benet.ai. TXT "ipfs=XLF2ipQ4jD3U ..."
# behaves as symlink
ln -s /ipns/XLF2ipQ4jD3U /ipns/fs.benet.ai
# User can get a link from
/ipns/shorten.er/foobar
# To her own namespace
/ipns/XLF2ipQ4jD3UdeX5xp1KBgeHRhemUtaA8Vm
8. IPFS的適用場景
IPFS是由很多小的系統(tǒng)組成,模塊與模塊之間分工明確,可以靈活組合使用,實現(xiàn)不同場景下的多種功能:
As a mounted global filesystem, under /ipfs and /ipns. 2. As a mounted personal sync folder that automatically
versions, publishes, and backs up any writes.
3. As an encrypted file or data sharing system.
4. As a versioned package manager for all software.
5. As the root filesystem of a Virtual Machine.
6. As the boot filesystem of a VM (under a hypervisor).
7.
As a database: applications can write directly to the Merkle DAG data
model and get all the versioning, caching, and distribution IPFS
provides.
8. As a linked (and encrypted) communications platform.
9. As an integrity checked CDN for large files (without SSL).
10. As an encrypted CDN.
11. On webpages, as a web CDN.
12. As a new Permanent Web where links do not die.