近日,DAS 創始人 TimYang (楊敏)在 Nervos CKB 上開發了 DAS
去中心化帳戶服務。藉着此次的產品開發,TimYang 將經過《從 DAS 開始瞭解 CKB
應用開發》系列文章,向你們闡述他的設計思路和開發歷程,讓你們瞭解如何在世界上第一個基於 UTXO 架構的公鏈 CKB 上構建產品級應用。數據庫在第一篇文章中,Tim 將向你們介紹他們在設計 DAS 時面臨的第一個大問題 ——如何保證 DAS 帳戶的惟一性。歡迎閱讀及體驗。架構
DAS(Decentralized Account Services),是基於 CKB 構建的去中心化帳戶服務。DAS 項目自己,旨在爲新世界提供一套兼具抗審查性、惟一性、可識別性的帳戶體系。在 DAS 的第一階段,它看起來像是以太坊的 ENS,而且具有一些比 ENS 更爲優秀的特性。但 DAS 要作的,不只僅是更好的 ENS,而是試圖爲加密世界的「去中心化帳戶/身份」這塊拼圖,帶來新的定義。併發
DAS 不是一個概念性產品,它目前已經運行在 CKB 測試網上,並預計於近期上線主網。能夠經過 https://da.services 體驗測試版本。學習
DAS 是基於 CKB 開發的區塊鏈應用。在諸多公鏈中,爲何咱們要選擇基於 CKB 來進行開發呢?緣由有二:區塊鏈
CKB 是少有的在 UTXO 模型之上構建智能合約環境,並主張「鏈下計算,鏈上驗證」的公鏈平臺。這些主張和設計通過了充分的考量,很是具備前瞻性,但同時也帶來了全新的去中心化應用開發範式。習慣了中心化應用開發和以太坊智能合約開發的開發者,在剛開始接觸 CKB 開發時,會充滿不適應。加之目前尚沒有標杆應用出現,這讓開發者們對於 CKB 到底能作什麼,是否是真的值得花精力去學習 CKB,充滿疑問。測試
《從 DAS 開始瞭解 CKB 應用開發》系列文章的目的,也正在於此。咱們將咱們在 DAS 實踐過程當中的問題、思考,以及解決方案整理成一系列文章,讓你們瞭解咱們是如何基於 CKB 構建產品級應用的。但願藉此給更多開發者帶來啓發,瞭解 CKB 能作什麼,應該怎麼作。ui
須要說明的是:加密
在面對一個問題時,咱們採用的思路和解決方案,不必定是最優解,甚至大機率不是。但這些知足咱們場景的思路和解決方案,若能給你們帶來啓發,目的便已達到。
這一系列的文章,都假定讀者已經充分理解 Cell 模型和「鏈下計算,鏈上驗證」模型。
咱們將在第一篇文章中,探討 DAS 面臨的第一個棘手問題:spa
每一個 DAS 帳戶都須要一個 Cell 來存儲其數據,Cell 是經過不一樣交易來建立的,這意味着 DAS
系統的全局狀態數據是分散存儲在各個角落的。同時每一個 DAS 帳戶又必須具備惟一性。那麼,當一個 DAS
帳戶註冊行爲發生時,咱們如何判斷該帳戶是否已經存在呢?
咱們把這個問題通常化:對於分散存儲的數據集,在插入數據時,如何保證每條數據的惟一性?設計
對於習慣了中心化應用開發和以太坊智能合約開發的開發者而言,要保證註冊的帳戶不重複,這一件幾乎不用思考的事情,你能夠把全部的數據都放入合約的存儲空間,因爲這些數據是集中存儲的,因此在插入數據以前,你只須要先檢索一下數據是否存在便可。
但鑑於 CKB 的 Cell 模型,數據分散存儲在用戶本身的空間中,咱們沒法在鏈上去檢索全部數據。畢竟咱們不可能在一筆交易的輸入中,放下全部已經存在的 Cell。即使能放下,鏈上腳本也沒法知曉這筆交易在構造時,交易發起人是否真的將全部須要 Cell 都放到了輸入中。
咱們將列舉全部咱們曾考慮過的保證惟一性的方案。之因此把最終沒采用的方案都拿出來分析,是但願你們能夠經過觀察咱們走過的「彎路」,開始適應 CKB 的開發範式,避免之後本身走「彎路」。
在討論方案以前,咱們應先明確咱們的設計原則。正是這些原則,最終決定了咱們採用什麼樣的方案。這些原則,優先級從高到低依次爲:
若是你只關心最終方案,能夠直接跳轉到「方案六」開始閱讀。
這是最符合直覺的一種方案,畢竟以太坊的智能合約就能夠這麼幹。建立一個 GlobalStatusCell,在 GlobalStatusCell 的 data 中存放全部已註冊的帳戶。當新的註冊發生時,在交易中把這個 GlobalStatusCell 做爲輸入,修改後的GlobalStatusCell 做爲輸出。type 腳本檢查新註冊的帳戶是否已經存在,若是存在就返回非 0,交易失敗;若是不存在,那就檢查輸出的 GlobalStatusCell 中是否包含了新帳戶,而後返回 0,交易成功,註冊完成。
這種思路不可行的緣由在於:
事實上咱們會發現,「Cell 競爭問題」是在 CKB 上開發應用時,要時刻警戒的問題。它對用戶體驗的影響多是致命的。
既然一個 GlobalStatusCell 放全部帳戶會致使競爭,那咱們把帳戶分散到多個帳戶呢?好比,對帳戶名作 hash,將全部 hash 值前 3 位相同的已註冊帳戶放到同一個 SubStatusCell 裏。當一個新的註冊產生時,必須將對應的 SubStatusCell 消費,以修改其內部數據。
這個方案仍存在一些問題:
依然存在必定的 Cell 競爭,若是按 hash 前3位來建立 SubStatusCell,須要提早建立 4096 個 SubStatusCell,假定在一個週期內有 50 個併發的註冊請求,按照「抽屜原理」,仍有 26% 的機率出現 Cell 競爭。儘管 50 的併發請求稍顯苛刻,在早期可能根本達不到,但應該認識到:
再次強調:在 CKB 上開發應用時,應該時刻關注你的應用會佔用多少 CKB 存儲空間,由於總的狀態空間是極其有限的。
全部的註冊都要經過 DAS 官方的服務進行,DAS 官方斷定可註冊後,用官方私鑰簽名一筆交易,向用戶發放 DAS 帳戶 Cell。這個方案在實現上很是簡單,但問題也很明顯:
好比,找 7 個「能夠信任」的組織做爲超級節點,管理各自的私鑰。超級節點們運行超級節點服務程序,將全部已註冊的帳戶存儲在本身的中心化數據庫中,當一個註冊請求產生時(指用戶構造一個包含註冊信息的 Cell),各個超級節點將判斷其是否已經註冊過。若是未註冊過,那就用私鑰簽名一筆交易,釋放一個代表「本超級節點認爲這個帳戶能夠註冊」的 Cell,當有 4 個以上的超級節點都釋放了這樣的 Cell 時,其中一個節點就會匯聚全部的這些 Cell,做爲依據去建立 DAS 帳戶。
這種思路,看似能夠很好的解決方案三中的一些問題,但卻引入了更多的問題:
既然要實現註冊時去重這麼複雜,那乾脆註冊時就不去重了。任何人在任什麼時候候均可以「註冊」任何帳戶,而後在用戶要查詢一個帳戶的解析記錄時,由解析程序去找出那個最先「註冊」的帳戶,將其做爲合法的帳戶返回給用戶。
這種獨特的思路,存在的問題主要是如何保證客戶端運行「合理」的解析程序:
若是不能保證你們始終運行相同的最新的解析程序,整個系統勢必會在應用層面上不一致。由此會引起各類形式的欺詐,最終你們會對這個系統失去信心。
最後,咱們來介紹 DAS 最終所採用的方案 —— 有序鏈表。
咱們將咱們要解決的問題,作更通常化的表述:
對於分散存儲的數據集,在插入數據時,如何保證每條數據的惟一性?
答案是,使用邏輯上的有序鏈表。感謝 @guiqing 的啓發。
每一個已註冊的 DAS 帳戶,都有一個 Cell 用來存儲其相關的信息,稱爲 AccountCell。咱們要求全部的 AccountCell 按某種順序排序,好比按帳戶名作字典序升序。當要註冊一個新的 DAS 帳戶時,其 AccountCell 必須插入到合適的位置,以保證不破壞這種順序。
AccountCell 的簡化結構以下:
注意:account_id 取值爲帳戶名,僅僅是爲了表述方便,實際上 DAS 使用的是其帳戶名 hash 的前 10 位。
咱們假定鏈上已經有 a.bit,b.bit,如今一個用戶要註冊 d.bit,註冊前鏈表結構以下:
註冊後的鏈表結構以下:
隨後,有一個用戶要註冊 c.bit,那麼註冊後的鏈表結構以下:
從上面咱們能夠看到,當須要註冊一個新帳戶時,須要對鏈表中處於其前方的AccountCell 的 next_account_id 字段進行修改。這也意味着,須要構造一筆交易,能消費掉其前方的 Cell,並建立相對應的新 Cell。對於應該修改哪一個 Cell,也即新的 DAS 帳戶應該插入在鏈表的哪一個位置,這些都是由用戶使用的註冊程序根據鏈上的狀態,自動幫用戶完成的(看,鏈下計算)。
那若是註冊程序不當心(或者用戶惡意的)構造一筆交易,試圖建立重複帳戶,或者將帳戶插入錯誤的位置,會怎麼樣呢。這時候咱們的 type 腳本就起做用了,會致使這類交易失敗不被打包進區塊(看,鏈上驗證)。
Cell 的 type 腳本會在 Cell 做爲輸入和輸出時都運行。咱們的 type 腳本就能夠作一些判斷,好比:
因此上述的這些判斷結果若是都爲真,且整個交易結構也知足其餘一些必要的條件,那麼 type 腳本就會返回 0 ,意味着這是一筆合法的交易,當這筆交易歸入區塊以後,帳戶也就註冊完整了,DAS 系統的狀態完成了更新。而對於不知足這些條件的交易,根本就是不合法的交易,也就不會註冊成功。
能夠看到,這個方案知足咱們前面設定的 4 個設計原則。
判斷個數據重複性而已,在 CKB 上就要這麼複雜嗎?
咱們要理解,之因此「複雜」,其背後的本質緣由是 UTXO 模型,是 UTXO 模型致使了數據的分散存儲。
那爲何 CKB 要採用 UTXO 模型,ETH 的帳戶模型不就很好嗎?
UTXO 模型和帳戶模型各有優劣,UTXO 模型的部分優點在於:
咱們更應理解,感覺上的「複雜」,更多的來自於咱們對新範式的不適應。
應把鏈上驗證看做一種協議
能夠看到,type 腳本的約束,更像是一種協議。他規定了一筆交易應該有什麼樣的輸入和輸出,但誰來建立交易,以什麼方式建立交易,並非協議所關心的問題。
方案六也有 Cell 競爭問題呀?
是的,若是多個新的註冊的帳戶都應該直接插入到某個 AccountCell 的後面,那就會面臨 Cell 競爭的問題。因此,咱們將在下一篇文章中介紹,如何經過一種咱們稱做 「Keeper」的機制,在方案六的基礎上,完全解決 Cell 競爭問題。
最後,如咱們在開頭提到的那樣:
在面對一個問題時,咱們採用的思路和解決方案,不必定是最優解,甚至大機率不是。但這些知足咱們場景的思路和解決方案,若能給你們帶來啓發,目的便已達到。
未完待續……
在下一篇文章中,Tim 將爲咱們介紹一種稱爲「Keeper」的機制,來處理 Cell 競爭問題。歡迎你們前往 https://talk.nervos.org/t/das-ckb-das/5669 催更。
如若您有更多關於 DAS 產品的使用心得,以及在 CKB 上開發的看法,歡迎前往 Nervos Talk 論壇討論:
https://talk.nervos.org/