當前位置:
首頁 > 最新 > bitsharesjs庫詳解一:ChainStore

bitsharesjs庫詳解一:ChainStore

bitshares開發入門:開源代碼總覽介紹了比特股開源代碼的總體情況,其中,bitsharesjs位於UI層之下,bitsharesjs-ws之上。本文嘗試開一個系列之頭:這個系列全部解析 bitsharesjs 代碼。

bitsharesjs 庫有三個主要模塊,ECC, Chain和Serializer。ECC是關於橢圓曲線密碼學的一些貼近錢包操作的庫,Chain是關於鏈上數據獲取和交易發起的,Serializer是Chain的工具支持,一般無需直接使用。 本文闡述Chain中的一個類: ChainStore。ChainStore的功能是鏈上數據的獲取和緩存。本文提到的代碼,如無特別說明,均以bitsharesjs的根目錄為相對目錄的起點。

環境準備

安裝Nodejs到本地,建議安裝當前的LTS版本,本文寫作時,為 6.10.3 (如果已經安裝請跳過這一步)

克隆代碼到本地 ( 命令行下執行:git clonehttps://github.com/bitshares/bitsharesjs.git)

進入 bitsharesjs目錄, npm install

從測試代碼說起

測試代碼文件: test/chain/ChainStore.js

測試方法,命令行鍵入

npm run test:chain

注意這個測試會測試 test/chain目錄下的所有測試文件, ChainStore只是一個。如果沒有本地重錢包,你會發現ChainStore會測試失敗。下文教你如何修改代碼來做測試。

背景說明:測試使用的是mocha BDD測試框架,並且(整個項目)使用了 babel轉碼。

第3行

import{FetchChain,ChainStore}from"../../lib";

導入了ChainStore。

第9-15行

before(function(){/* use wss://bitshares.openledger.info/ws if no

local node is available */returnApis.instance("ws://127.0.0.1:8090",true)

.init_promise.then(function(result){coreAsset=result[].network.core_asset;ChainStore.init();});});

所有測試用例運行之前需要做初始化:先連接上全節點,測試代碼使用的是本地節點,第10行注釋說得明白,如果沒有本地節點,那麼就使用公網節點,例如openleger的。國內測試,建議改成帝國的: wss://bit.btsabc.org/ws 。 另外第13行有個 bug ,需要在前面加上 return,否則默認 return undefined,整個函數就會resolve掉,可能導致ChainStore沒有初始化完成就執行測試用例,會出錯的。修改後的代碼應該是這個樣子:

before(function(){/* use wss://bitshares.openledger.info/ws if no

local node is available */returnApis.instance("wss://bit.btsabc.org/ws",true)

.init_promise.then(function(result){coreAsset=result[].network.core_asset;returnChainStore.init();});});

這樣就可以測試了。但是,讀者會發現,測試用例不見得全部pass。這裡面有另一個BUG,下文詳解。

init函數

當底層Api(bitsharesjs-ws提供的Apis)初始化OK時,必須調用ChainStore的init函數初始化,正如第13行所做的那樣。

首先, ChainStore這個變數,容易混淆,這個是從 lib/chain/src/ChainStore.js這個文件導入的,而這個文件定義了一個ChainStore類,但本身導出的確實ChainStore類的一個全局Singleton

letchain_store=newChainStore();

1352行生成了ChainStore類的一個實例。

exportdefaultchain_store;

1407行導出這個實例。

因此測試代碼導入的ChainStore,是ChainStore.js文件中定義的ChainStore類的一個全局實例。這句話很繞口,多讀幾遍。

回到init函數,該函數返回一個promise,resolve的時候初始化成功。其他函數必須在init函數返回resolve之後調用。正因為這個特點,才有了上文所述第13行的少return的BUG。

4個測試用例的所調用的兩個函數

4個測試用例實際上主要調用了ChainStore(Singleton)的兩個函數:

getAsset

subscribe

a表示空間,兩個取值:1表示協議對象,這些對象會在websocket和p2p網路上傳輸;2表示實現對象,用於節點本地存儲,可認為是共識數據的衍生數據。

b表示類別,協議對象和實現對象都有十多類不同數據。

c表示實例,不同類型數據的實例編號。

例如

2.1.0 表示動態全局相關數據,抓取的實例請看原文

1.3.x 表示各種類型的資產

1.3.0 核心資產BTS

1.3.113 錨定資產bitCNY

1.2.x 表示各個賬號

1.2.0 理事會多重簽名賬號

1.2.121 理事會成員巨蟹的賬號 bitcrab

1.2.12376 理事會成員abit的賬號 abit

1.7.x 表示用戶提交的限價單

1.8.x 表示call order(我還真沒搞清楚是什麼意思,請留言)

1.11.x 表示用戶相關的活動歷史,提交限價單,取消限價單,轉賬給別人,收到轉賬等等

常用對象列表可參看大部分的對象類型。

好,回到getObject函數,這個函數總是立即返回的,返回值有三種情況:

返回Map 類型的對象,表示緩存中有了這個對象

返回null,表示沒有這個Object(id無效)

返回undefined,表示正在查詢API節點,需要以後重新調用

getAsset是getObject的封裝,因此返回值同樣遵守這個約定。由於getObject立即返回,而調用的時候如果返回undefined,怎麼等呢?用 subscribe函數。 subscribe函數是通用的事件監聽函數, 當 websocket連接之後,任何從API節點的事件,都會觸發所有的監聽者(subscriber)。

這個設計本身是否足夠好?我認為不夠好,因為subscribe會導致大量的無效監聽,而getObject和subscribe的聯合使用,從理論上說不一定能達到預期的效果: 因為監聽者無法區分事件本身,而JS的非同步特性會導致不確定性。從測試代碼來說,4個測試用例並行執行,和webSocket的事件觸發次序的不確定性,會導致subscribe裡面的getAsset函數不一定得到想要的結果。如果改寫其中的一個測試用例,設成it.only (忽略其他的測試用例),目前我的測試結果是總可以通過的,但從理論上,我仍然不相信這種單個測試用例的測試方法:萬一監聽到一個不相關的事件呢?從這個意義上來說,測試代碼還不好寫得正確,現有測試代碼怎麼改成邏輯自洽的還很難。

另外,就ChainStore來說,測試代碼的覆蓋也完全不夠,下面看看例子代碼。

例子代碼

例子代碼在這裡: examples/chainstore.js

運行

npm run example:chainStore

可以發現一直列印ChainStore的全局動態對象 2.1.0的當前值。

例子代碼比測試代碼簡單,用到的函數是getObject,運行例子代碼會對上文提到的無效監聽設計有直觀的認識。

例子代碼的修改

運行修改的例子代碼會不停的輸出巨蟹的賬號相關信息。關於操作歷史,最重要的是什麼操作?op的數據結構是二元組,第一個數表示操作類型,第二個對象表示具體的數據。而操作類型可以在 lib/chain/src/ChainTypes.js 裡面找到,代碼我就不貼了。

總結

關於ChainStore的代碼解讀就這些了,這個過程我總結下來:

ChainStore的介面設計不算特別合理。怎麼樣才更好呢?是一個值得思考的問題。

業務邏輯和代碼需要結合起來,比如a.b.c對象的意義,操作類型的意義。

ChainStore測試和例子的質量不高,大體可判斷,bitsharesjs總體的代碼質量有待改進,如果對質量要求高,可以考慮直接使用錢包和節點的 JSON RPC API。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 跟我一起學區塊鏈開發 的精彩文章:

TAG:跟我一起學區塊鏈開發 |