本文檔包含Ceramic 協議的技術規範。有關Ceramic 網絡的詳細介紹,請參見Ceramic 概述。

索引

協議概述文檔生命週期創建文檔更新文檔查找文檔文檔標識符文檔日誌區塊鏈錨定衝突解決文檔記錄文檔類型Ceramic 文檔類型更新規則文檔更新與傳播查詢未來的改進實施情況

協議概述

Ceramic 是一種分散的協議,可以在全球公共網絡上創建可變的、防篡改的智能文檔。智能文檔是通過數字簽名、文檔更新規則和區塊鏈錨定的結合來實現的。 Ceramic 文檔是一種靈活的數據結構,可以用來表示各種各樣的東西,如自我主權身份、特定的數據或內容、媒體文檔、模式、數據訪問控制策略、可驗證的憑據、多方之間的協議等。該協議不依賴任何特定的區塊鏈系統,而是利用IPLD 將文檔更改成編碼為哈希鏈接的數據結構,稱為默克爾DAG(有向無環圖),並將這些更新錨定到區塊鏈,該區塊鍊為給定文檔的內容提供嚴格的排序。因此,Ceramic 可以被概念化為單個“文檔鏈”的網絡。驗證特定文檔的狀態,只需要用戶同步給定文檔的數據,用戶不需要像所有保持全局狀態的區塊鍊網絡(即比特幣、以太坊)那樣同步整個網絡的狀態。 Ceramic 網絡中的一個節點因此可以選擇只查詢相關文檔。這意味著沒有全局的文檔賬簿。

Ceramic 文檔由一個僅追加的記錄日誌組成,由一個分散標識符(DID) 簽名進行更新,根據一組狀態轉換規則進行驗證,然後錨定在區塊鏈上。 Doctypes 指定了管理文檔有效更新(狀態轉換)的顯式規則。這些可能是直接事件,如所有者的簽名,也可能是間接事件,如另一個文檔狀態的變化。 Doctype 允許Ceramic 節點以分散的方式管理和驗證給定文檔的狀態,而不需要集中的服務器或中間商。當對特定文檔進行有效更新時,該節點隨後將更新消息傳遞給網絡的其他部分,對該文檔感興趣的任何節點將在其節點上更新給定文檔的狀態。因此,任何節點都可以接收對文檔的查詢,並返回正確的狀態,即使它不維護文檔本身的狀態。該協議對於哪個區塊鏈用於錨定是不可知的,開發人員可以選擇將他們的文檔錨定在他們選擇的區塊鏈上。

文檔生命週期

Ceramic 網絡中的每個文檔都是獨立的,所有文檔的生命週期都可以用同樣的方式進行廣義描述。文檔可以發生三個主要動作:創建、更新和查找。

創建文檔

要創建文檔,將創建一個包含文檔初始內容的IPLD 對象。這稱為創世記錄,其CID 用作文檔標識符(docId)。根據文檔類型,可能會有關於創世記錄的有效內容的約束。

更新文檔

為了更新文檔,需要添加兩條記錄。這些更新被添加到文檔日誌中,這是一個記錄的鏈接列表。首先,創建一個包含內容更新的簽名記錄並添加到日誌中。創建簽名記錄後,該記錄的CID 將被發送到一個錨定服務,該服務將一組文檔更新批量發送到默克樹中,並將根哈希提交到區塊鏈進行最終確認。錨定服務然後返回給定CID 的錨定記錄,該記錄被添加到文檔日誌中。現已對文檔進行了完全更新,節點現在將包含docId 和錨定記錄的CID 的更新消息發佈到Ceramic pubsub topic。

查找文檔

要查找文檔,需要docId。一旦知道docId,節點可以通過向Ceramic pubsub topic 發送查找請求來查找文檔。具有該文檔狀態的其他節點將使用他們所知道的最新記錄CID 進行響應。進行查找的節點現在解析它接收的所有CID 的文檔日誌(在大多數情況下,這將只是一個CID)。如果有任何衝突,它們將通過衝突解決機制來解決。一旦解決了任何衝突,簽名記錄的所有更新都將應用於創世記錄,並找到文檔的最新狀態。

文檔標識符

在Ceramic 中,每個文檔都有一個唯一的標識符(DocID)。這是文檔的永久標識符,永遠不會更改。標識符編碼在CIP-59 中定義。當編碼為字符串時,DocID 通常會在協議處理程序的前面:

ceramic://

例如,docId 可能看起來像這樣:

ceramic://kjzl6fddub9hxf2q312a5qjt9ra3oyzb7lthsrtwhne0wu54iuvj852bw9wxfvs

文檔版本

每次將文檔錨定在具有錨記錄的區塊鏈上時,都會創建文檔的新版本。文檔的每個版本都可以通過使用以下格式來引用:

ceramic:// ?version=

文檔日誌

Ceramic 文檔由一個僅追加的日誌組成,該日誌可以簡化為單個json 對象。日誌中的每個記錄都是一個IPLD 對象,可以由其CID 引用。由於CID 是唯一的標識符,根據對象的內容,我們可以創建一個鍊錶,其中每個記錄都包含指向日誌中前一個條目的上一個指針。這使得日誌成為記錄的不可變歷史。然而,日誌可能有不同的分支。為了處理這一點,使用了通過區塊鏈錨定的衝突解決策略。日誌中的每個記錄都是特定類型的:創世紀、簽名或錨定。創世記錄是文檔的第一個記錄。簽名記錄包含對文檔的更新。錨定記錄將文檔更新錨定到區塊鏈,形成exlpicit 版本。這些記錄的結構將在下面的文檔記錄部分中描述。

區塊鏈錨定

文檔更新使用IPLD 編碼的默克樹錨定到區塊鏈。這棵默克樹的根放在區塊鏈上,樹的每一片葉子都包含文檔更新的哈希。這意味著來自許多不同文檔的大量文檔更新只能使用一個事務錨定到區塊鏈。由於默克樹是使用IPLD 編碼的,特定文檔更新的見證(默克證明)可以通過IPFS 網絡高效同步。在大多數情況下,這種錨定服務將由第三方運行,該第三方聚合來自許多用戶的許多文檔更新,但是如果用戶願意,他們當然可以選擇錨定自己的記錄。在用戶直接錨定自己的更新的情況下,他們可能不使用默克樹。

在許多情況下,單個用戶可能會更新少量文檔。他們不必發送自己的區塊鏈交易,而是可以將文檔更新發送到錨定服務。該服務接收來自多個不同用戶的錨定請求,並定期將這些請求批量發送到單個事務中。任何人都可以運行該服務,並且可以配置在Ceramic 節點中使用的錨定服務。不同的服務可能提供自定義的錨定間隔,或者錨定到不同的區塊鏈。根據給定文檔的上下文和用例,一個區塊鏈可能比另一個區塊鏈更受歡迎。

衝突解決

如果特定文檔日誌有兩個不同的分支,規範分支通過簡單地查看衝突的錨記錄來確定哪個更新最先發生。當兩個錨在同一個區塊鏈上時,使用錨的區塊高度;如果錨在不同的區塊鏈上,則使用區塊時間戳。

需要注意的是,更新可能有較早的錨記錄,但不遵循給定文檔類型的更新規則。因此,在正確應用更新規則後,將應用衝突解決方案。由於所有文檔類型都需要簽名更新,惡意行為者需要訪問文檔創建者的私鑰,才能創建更有效的衝突分支。

數據扣留攻擊

對這種衝突解決系統的一種建議攻擊是數據扣留攻擊。在這種情況下,用戶創建一個文檔,進行兩次沖突的更新,並將其中一次更新早於另一次,但只發布後來錨定的更新的數據。現在,對文檔的後續更新將在第二次發布的更新之上進行。每個觀察者都將接受這些更新為有效,因為他們沒有看到第一次更新。但是,如果用戶稍後發布了早期更新的數據,文檔將返回到此更新,對文檔進行的所有其他更新將無效。

這本質上是一種雙重支出攻擊,這是區塊鏈解決的問題。然而,由於身份只有一個所有者,即用戶,這就沒什麼問題了。在這種情況下,“雙重支出”將導致用戶丟失其身份上積累的所有歷史和關聯,他們自然不願意這樣做。同樣,對於多方簽名的Tile 文檔,由於所有各方都需要簽署所有更新,他們都需要意識到數據扣留攻擊。

就組織身份而言,這更像是一個問題,例如,如果組織的舊管理員想製造麻煩。這可以通過引入“重錨”來解決,這種錨更依賴於一些鏈上機制。例如,控制身份的智能合同或DAO。

兩個錨,一樣的區塊高度

雖然不太可能,但從技術上講,兩個文檔更新以相同的區塊高度錨定是可能的。如果發生這種情況,協議只需選擇二進制格式中CID 最小的更新記錄。

文檔記錄

記錄是Ceramic 文檔的基本構建塊。記錄是一個IPLD 對象,包含一些數據和該數據的證明。每個記錄類型都提供了一種驗證其證明的方法。證明可以有許多形式,但最常見的例子是簽名和區塊鏈錨。下面最常見的記錄類型是用IPLD 架構描述的預期數據格式定義的。

創世記錄

創世記錄是文檔的第一個記錄。該記錄的CID 用於創建文檔的持久DocId ,這是一個不可變的永久鏈接,用於識別特定文檔。下面概述的模式描述了任何文檔類型的創世記錄的一般形式。它有三個主要屬性,doctype, header, 和data。 doctype 是描述該文檔文檔類型的字符串。 header 是一個屬性,包括關於文檔的附加元數據。最後,data 屬性包含創建時文檔的內容。

type GenesisHeader struct { owners [String] schema optional String tags optional [String] unique optional String}type GenesisRecord struct { doctype String header GenesisHeader data Any}

需要由所有文檔類型定義的所需屬性。但是如何使用其中的數據取決於特定的文檔類型。

doctype - 包含給定doctype 名稱的字符串owners - 定義文檔所有者的字符串數組data - 用於生成此文檔內容的數據

上面有一些可選的屬性在Ceramic 協議中有特殊用途。

schema - 表示包含模式的文檔的Ceramic 文檔的字符串。由特定的文檔類型使用模式來驗證文檔內容tags - 可用於對給定文檔進行分類的字符串數組。 Ceramic 網絡中的錨和索引服務將使用此屬性unique - 如果存在,應包含一個隨機字符串,允許文檔獲得唯一的DocId,即使它與另一個文檔具有完全相同的內容

簽名記錄

簽名記錄允許更新文檔。它包含簽名證明(例如地址鏈接)或包裝在其中(例如使用dag-jose 的文檔類型)。簽名記錄包含指向前一條記錄的指針,作為數據對文檔的更新,以及編碼的簽名。簽名的驗證方式取決於使用的文檔類型。

type SignedHeader struct { owners optional [String] schema optional String tags optional [String]}type SignedRecord struct { id Link prev Link refs [Link] header optional SignedHeader data Any}

需要由所有簽名記錄定義的所需屬性:

id - 指向創世記錄的IPLD 鏈接prev - 到上一條記錄的IPLD 鏈接refs - IPLD 鏈接的數組(參見參考鏈接)data - 用於更新文檔內容的數據,例如補丁對象

標頭中的三個可選屬性用於特定目的:

owners - 應包括更新文檔的所有者,例如密鑰輪換schema - 應包含以更新文檔使用的模式tags - 應包括更新文檔的標籤

錨點記錄

錨點記錄只是證明prev 屬性的CID 錨定在區塊鏈上。此記錄的格式可以在下面看到。 proof 屬性包含AnchorProof 的CID。此證明元數據對象由錨定在同一區塊鏈上同一默克爾樹中的所有文檔更新共享。 path 屬性是通往包含有CID 的默克爾樹葉子的唯一路徑,該CID 也包含在prev 屬性中。

錨點記錄:

type MerkleNode struct { L Link R Link}type AnchorProof struct { chainId String blockNumber Int blockTimestamp Int txHash Link root Link}type AnchorRecord struct { id Link prev Link refs [Link] proof &AnchorProof path String

除了上面定義的id、prev 和ref 之外,我們還有以下屬性:

chainId - 表示特定區塊鏈的CAIP-2 字符串blockNumber - 包含事務的區塊數量blockTimestamp - 包含事務的區塊時間戳txHash - 區塊鏈交易的CID,例如eth-txroot - 到默克節點的IPLD鏈接path - 表示從root SignedRecord 的路徑,也由prev引用

請注意,為了方便起見,將block Number 和block Timestamp 添加到此對像中,但需要驗證這些數字。 txHash 包括含有根CID 的區塊鏈交易的CID。使用此tx 哈希,可以使用外部區塊鏈api 來驗證證明的信息。最後,根屬性包含IPLD 默克樹根的CID。

下面描述了錨點記錄的圖形表示以及它在包含其證明的默克樹中的路徑。默克根和IPLD 對像都由上面定義的默克節點表示。

要驗證特定的錨點記錄,使用以下算法:

使用證明CID 從ipfs 獲取證明元數據對像從root 獲取所有數據,並遵循路徑中的所有鏈接驗證路徑的結尾與上一個相同使用txHash 從給定區塊鏈獲取交易數據驗證block Number 和block Timestamp 是否正確

使用IPLD 存儲錨點證明和默克樹的一個很好的特性是,共享相同證明對象並在默克樹中具有相似路徑的文檔最終將協作存儲其證明的部分數據。

其他記錄類型

將來可能會有新的記錄類型由額外的CIP 定義。這些可能包括像DAO 記錄這樣的東西,理論上它可以作為錨和簽名記錄。如果定義了新的記錄類型,它需要在每個Doctype 中得到明確的支持。

參考鏈接

已簽名和錨點記錄中的refs 屬性包含組成該文檔的記錄列表。它被用作加快文檔日誌同步過程的一種方式。為了實現這一目標,refs 屬性根據它們與當前節點的對數距離指向一組以前的記錄。有關如何實現這一點,請參閱ipfs-log-source。

文檔類型

每個Ceramic 文檔都必須指定一個文檔類型(doctype)。 doctype 描述了應用於文檔更新功能的狀態轉換規則以及文檔中數據的格式。

Ceramic 文檔類型

Ceramic 目前有兩種主要的文檔類型,但如果需要,將來還可以添加更多。

Tile-用於標識符、內容、媒體、模式、元數據、訪問控制等的通用文檔。 CAIP-10 link-從區塊鏈地址到DID 的鏈接

更新規則

每個doctype 需要指定什麼構成有效的updates/state 轉換和有效的日誌記錄序列的規則。 doctype 還可以為給定文檔的內容指定所需的數據格式。例如,CAIP-10 link doctype 只允許一個DID 作為其內容。

文檔更新及傳播

給定一個docId(例如/ceramic/3id/ ),可以通過與Ceramic 網絡中的其他節點通信來檢索完整日誌。這是通過使用libp2p pubsub 在對等節點之間共享文檔日誌的更新來實現的。 Ceramic 網絡中的所有節點都加入了/ceramicpubsub 話題。當節點對文檔進行更改時,新的head(最近的記錄CID)將與docId 一起在Pub/Sub 話題上共享。對該文檔感興趣的對等體看到此消息,使用IPFS 獲取新記錄,並將日誌應用於文檔。如果節點獲得兩個或多個衝突head,則使用衝突解決機制。

更新消息格式:

查詢

節點可以向其他節點查詢特定文檔的最新head。為此,它們向Pub/Sub 話題發送 REQUEST 消息。看到此消息的其他節點將使用RESPONSE 消息進行響應。

請求消息格式:

如果一個已經下線的節點重新聯機,它必須對所有相關文檔發出請求,以確保它保持所有更新。

錨元數據消息

當節點從錨點服務請求錨點時,它將收到關於該服務打算何時將文檔更新錨點在區塊鏈上的元數據。然而,對同一文檔感興趣的其他節點將不知道這些額外的元數據信息。為了與其他節點共享這些信息, ANCHOR_META 消息用於告訴其他節點已經向特定的錨點服務發出了請求。感興趣的節點可以查詢給定的錨點服務來檢索元數據信息。

消息類型

未來改進

擁有一個所有文檔都共享的Pub/Sub 話題的主要原因是為了更容易地創建一個連接良好的網絡。這樣做的好處是,您可以從對同一文檔感興趣的節點獲得更新,即使沒有直接連接到它們。這種方法的主要缺點是可擴展性。一旦網絡增長,Pub/Sub中的文檔和消息量對許多節點來說將會非常大。為了解決這個問題,可以使用基於docId 的namespace-ing 將文檔分成多個不同的空間。具體情況還不確定。

Pub/Sub 方法的一個潛在問題是某種形式的DoS。當節點對特定文檔發出請求時,惡意行為者可能會發送許多與請求文檔不對應的head。這將導致請求節點不得不進行大量計算,以確保所有接收到的head 實際上都是不正確的。解決這個問題有幾種不同的方法。一種是使用針鋒相對的系統,在這種系統中,節點與發送許多不正確響應的節點斷開連接。如果許多用戶這樣做,這應該在惡意節點開始執行攻擊時有效地阻止它們。另一種方法是在響應中包含零知識證明,證明消息中的CID 確實對應於正確的文檔。

實用情況

目前,有一個Typecript(js-Ceramic )實現可用。如果您有興趣用另一種語言提供實現,請聯繫我們,我們將協助您開始!

Ceramic JavaScript客戶端