雷志偉 李加福 張桂剛 趙 旭 張 勇 邢春曉
(1.清華大學行業可信區塊鏈應用技術聯合研究中心 北京 100084)(2.中國科學院自動化研究所 北京 100190)(3.清華大學信息國家研究中心,計算機系,互聯網產業研究院 北京 100084)
在抗擊新冠肺炎疫情初期,各地各部門企業紛紛貢獻己力,自主開發疫情防控信息系統。一方面能加強政府部門的管理,另一方面簡化現實中人力物力消耗[1]。在人員進出公共場所的跟蹤管理方面,相應有應用于廣西地區的“掃碼抗疫情”、云南地區的“云南抗疫情”等。它們都是基于微信小程序開發,其功能和使用方式大抵相同,群眾以個人身份注冊,另一個則是公共場所的工作人員以公共場所的身份進入系統并生成二維碼進行張貼,進出人員手動掃碼實現數據上傳,從而減免了手工登記流程。
區塊鏈作為一個分布式數據庫,記錄著區塊鏈從創世塊到當前塊的所有交易,具有去中心化、不可更改性、匿名性和可審計性這幾個特點。鑒于微信小程序基于微信平臺擁有龐大用戶量,不需要下載安裝反復登錄與卸載,對用戶而言,大大簡化了使用程序。因此使用微信小程序展現基于區塊鏈的進出登記系統是一個不錯的選擇。與別的出入登記系統不同,健康鏈系統將由公共場所工作人員對出入人員進行掃碼,并保存當前測量的溫度值,以加強疫情工作的防控。
微信小程序本質上是一種由原生APP和HTML5混合開發的HyBird App技術方案。即是一種在原生App里內置瀏覽器,采用網頁的形式來呈現功能的系統架構。它既解決了原生App下載安裝和更新、應用商店發布審核周期長的問題,又解決了純Web App安全性相對較低,數據容易泄露或者被劫持的問題。
微信小程序的系統架構如圖1,它以微信APP作為宿主進程,通過云端下載動態的Web資源文件到本地并動態渲染Web界面。在純Web App中,界面渲染跟JavaScript的腳本執行在一個單線程中,這就容易導致一些邏輯任務搶占渲染的資源。因此微信小程序采用雙線程模型,打開一個微信小程序,相當于進程啟動了兩個線程。其中一個線程用來渲染View視圖,另外一個App Service邏輯線程動態執行JavaScript腳本,用來處理邏輯、數據請求和接口調用。

圖1 小程序架構
在微信APP和Web模塊之間有一個JSBridge跨語言雙向通訊機制,這個通信層協調Web模塊的視圖線程與邏輯線程的數據和事件交互,邏輯線程把數據變化通知到視圖線程,觸發視圖頁面更新,視圖線程把觸發的事件通知到邏輯線程進行業務處理。更為重要的是,它不但負責傳遞Web模塊對系統權限的相關功能調用,還包括傳遞Web視圖渲染和JavaScript腳本執行[2]。在安卓系統里,渲染和腳本引擎都是調用基于開源Webkit庫優化和擴展的騰訊X5瀏覽器。在iOS系統里,蘋果基于Web-Kit開發了WKWebView組件來渲染視圖,并基于WebKit的C/C++實現和包裝實現了JavaScriptCore腳本執行框架。
JS-SDK就是對JSBridge的一個包裝,它是一整套網頁開發工具包,開放了拍攝、錄音、語音識別、二維碼、地圖、支付、分享、卡券等幾十個API。
最后,App Service線程運行在沙箱環境中,從而方便管控與安全,比如避免JavaScript腳本隨意地跳轉網頁或者改變界面上的內容[3]。
Bitcoin(比特幣)作為區塊鏈技術的起源,是目前發展最成熟的開源區塊鏈平臺之一,擁有大量的開發人員和活躍的開發社區。Ethereum(以太坊)核心是支持智能合約的EVM(以太坊虛擬機),提供了大量方便接口便于開發者進行深度應用開發。Hyperledger Fabric(超級賬本)使用諸如單節點共識、分布式隊列共識等共識方式,并支持智能合約和外部組件擴展。但Ethereum和Fabric部署困難相對困難。EOS區塊鏈采用DPOS共識機制,生成區塊速度快、延遲低,能支持數百萬級別用戶。但EOS作為新興的區塊鏈平臺發展還不成熟,同時開發社區及開發人員相對較少,進行開發研究較為困難[4]。
比特幣作為一種以交易為模型的數字貨幣系統,其交易的數據結構如圖2。交易由交易輸入和交易輸出組成,交易輸入和交易輸出可以有多個,表示一次交易可以將先前多個賬戶中的比特幣合并后轉給另外多個,每個輸入主要由上筆交易的哈希PrevTxHash、上筆交易的輸出索引Index和輸入腳本ScriptSig組成,其中ScriptSig是持有者對當前交易的簽名。通過對某個交易的輸入可構成多條以交易為結點的鏈表,并一直向前追溯至源頭的Coinbase交易(即挖礦所得的比特幣)。如果一筆交易的輸出沒有任何另一筆交易的輸入與之對應,則說明該輸出中的比特幣未被花費[5]。

圖2 比特幣的交易數據結構
區塊是區塊鏈的一個數據單元,它由區塊頭和區塊體組成。比特幣的區塊數據架構如圖3,區塊體包含所有的交易內容,區塊頭包含版本號、時間戳、隨機值和難度值等基本信息,并包含上一個區塊的哈希值和交易的默克爾樹根哈希值。區塊的哈希值通過對區塊頭進行兩次SHA256哈希運算得到。通過指向上一個區塊的哈希值所有的區塊構成一個鏈條,同時,區塊的哈希值和對區塊內的所有交易計算得到的默克爾樹根哈希值都可以保證數據的不可篡改和完整[6]。

圖3 比特幣的區塊數據架構
Nginx是一個跨平臺的Web服務器,相比Apache、Tomcat和Jetty,它具備高擴展、熱部署、單機支持10萬以上的并發連接、低內存消耗和高可靠的特性。
Nginx提供了異步的、非阻塞的Web服務,它的系統架構如圖4,它主要由一個Master主進程、多個Worker工作進程、一個可選的Cachemanager緩存索引管理進程以及一個可選的Cacheloader緩存索引進程組成。
Master主進程用來啟動和管理Worker工作進程,使用管道機制與工作進程通信,并且通過信號機制與外界通信來實現重啟、退出、停止等系統服務。Worker進程是提供Web服務的主體,它收到Web請求通過代理轉發給后端服務器,由后端服務器進行數據處理和頁面組織后返回數據。其中FastCGI是一個可伸縮地、高速地在Nginx服務器和動態腳本語言間通信的應用層通信協議,PHP作為一種服務端、跨平臺的HTML嵌入式的腳本語言,它的PHP-FPM模塊實現了FastCGI協議并負責處理PHP動態請求,從而分擔了Nginx前端的壓力,使Nginx專注處理靜態請求[7]。
Cachemanager進程和Cacheloader進程主要是將歷史應答數據進行本地緩存,從而提高請求的響應效率,降低網絡壓力。Cacheloader進程在Nginx服務啟動一段之后(默認是1min)由主進程生成,在緩存元數據重建完成后就自動退出。Cachemanager進程存在于主進程的整個生命期,負責管理緩存索引,支持工作進程對緩存數據的快速查詢。
多個Worker進程之間是對等的,它們同等競爭來自客戶端的請求,通過互斥鎖機制保證最終由一個Worker進程進行處理,從而做到資源的負載均衡[8]。
系統架構如圖5,系統將搭建SDK接口服務器作為微信程序和區塊鏈的通訊中介。微信程序通過Https協議與SDK接口進行數據交互,SDK接口與區塊鏈系統通過JSON-RPC協議進行數據交互。SDK接口程序由進出人員的個人數據接口、公共場所的數據接口和進出人員記錄的數據接口等等組成。系統配備傳統數據庫存儲數據以避免區塊鏈數據查詢效率低下問題。
由于區塊鏈平臺的共識機制,數據上鏈有時間延遲,接口程序通過將采用定時查詢機制來得到數據上鏈等其他數據操作結果。SDK接口與區塊鏈系統的數據流程如圖6。
在增強數據的隱私和安全方面,Https協議在Http協議的基礎上加了一層SSL/TLS加密層,SSL/TLS通過將對稱密碼、公鑰密碼、單向散列函數、消息認證碼,偽隨機數生成器和數字簽名等技術相結合來實現安全通信。此外,SSL/TLS還可以通過切換密碼套件來使用強度更高的密碼算法[9]。

圖6 SDK接口與區塊鏈系統的數據流程
鑒于數據的隱私和安全需求,比特幣系統將采用私有鏈的方式布置節點。
MySQL作為一個關系型數據庫管理系統,其體積小、速度快、源碼開放,且對PHP有很好的支持。因此數據庫基于MySQL開發,主要包含三個表:個人信息表、公共場所表和進出記錄表。

表1 個人信息表

表2 公共場所表
代碼主要通過微信開發者工具開發,開發者可以完成API和頁面的開發調試、代碼查看和編輯、小程序預覽和發布等等功能。微信小程序1個Page頁面對應本系統1個模塊,1個page頁面主要由4個文件構成,js腳本文件即是頁面邏輯,wxml是頁面結構展示,wxss是純前端的頁面樣式表,用于輔助wxml展示,json則是頁面配置文件[10]。

表3 進出記錄表
小程序在使用HTTPS發起網絡請求時只可以跟指定的域名與進行網絡通信,因此需要在微信公眾平臺賬號里對應的項目設置里面設置一個通訊域名,并將域名完成備案。
比特幣加入了OP_RETURN腳本命令,它后面可以緊跟一定容量的數據,專門用于存儲和交易邏輯無關的數據。這個交易輸出不會加入UTXO集合,從而避免了UTXO數據庫的大小不斷“膨脹”[11]。另外,由于比特幣基于LibEvent開源庫實現了一個JSON-RPC的遠程調用Server端,系統主要增加兩個自定義的RPC API接口。一個是創建包含OPReturn數據的交易,一個是查詢交易的OPReturn數據。
SDK接口主要是基于PHP語言通過libcurl擴展庫實現了基于Http的JSON-RPC客戶端[12]。其主要函數代碼如下。
//初始化一個CURL會話
$curl=curl_init("{$this->proto}://{$this->host}:{$this->port}/{$this->url}"
$options=array(CURLOPT_HTTPAUTH =>CURLAUTH_BASIC,
CURLOPT_USERPWD=>$this->username.':'. $this->pwd,
CURLOPT_RETURNTRANSFER=>true,
CURLOPT_FOLLOWLOCATION=>true,
CURLOPT_MAXREDIRS=>10,
CURLOPT_HTTPHEADER=>array('Content-type:application/json'),
CURLOPT_POST=>true,
CURLOPT_POSTFIELDS=>$request);
//設置選項
curl_setopt_array($curl,$options);
//執行并獲取結果
$this->raw_response=curl_exec($curl);
//釋放連接
curl_close($curl);
系統主要包括首頁還有其他模塊。圖7是個人注冊頁面,圖8是公共場所掃碼頁面,圖9是個人進出記錄頁面,圖10是公共場所進出記錄頁面。

圖7 個人注冊頁面

圖8 公共場所掃碼頁面

圖9 個人進出記錄

圖10 公共場所進出記錄
添加OPReturn數據到交易的API函數主要包含創建交易、交易簽名和發送交易三個過程,此處展示創建交易的部分代碼。
static void createCustomTransaction(const CTxDestination&dest,const CAmount&amount,const std::string&customdata,CMutableTransaction&rawTx){
//創建輸入
uint32_t nSeq=td::numeric_limits
CTxIn in(outpoint,CScript(),nSeq);
rawTx.vin.push_back(in);
//創建OPReturn輸出
std::vector
for(size_t i=0;i data.push_back(customdata[i]); CTxOut out(0,CScript()< rawTx.vout.push_back(out); //創建一筆輸出 CScript script PubKey=GetScriptForDestination(dest); CAmount nAmount=amount-3000;//3000 fee is enough CTxOut out1(nAmount,script PubKey); rawTx.vout.push_back(out1); Bitcoin-qt是一個基于Qt的GUI程序。Qt作為一個C++圖形界面庫,具備跨平臺、易擴展、面向對象和開發文檔豐富的特點[13]。圖11是系統基于Windows平臺的比特幣版本,它基于Visual Studio上編譯Bitcoin-qt,其過程包括安裝Qt軟件、安裝vcpkg并調用相關命令下載軟件庫,安裝Visual Studio Qt插件等其它設置[14]。 圖11 基于Windows平臺的比特幣程序 系統對界面進行了功能擴展,能方便地查看交易的輸入和輸出內容。圖12作為示例,其數據內容 為“南園食堂 雷志偉2020/05/12 14:54:54 36.4”,從而完成了對用戶在某個地點進出的信息登記,并保存了溫度值。另外,通過遍歷區塊的所有交易,可以得到所有人員的進出記錄,從而達到數據的不可篡改和溯源。 圖12 交易的OPReturn數據查看 通過更改相關參數和優化,交易性能在單節點的情況下能達到100個TPS以上。比如將生成塊的時間由每10min改成10s,將調整難度時間由2周改成80s,將交易內存池擴大,并擴大交易最大容量大小等[15]。 在測試性能時,需要提前創建大量未花費的交易,并記錄交易哈希值以備使用。創建交易的思路是將某個交易按固定數量生成多個輸出,然后循環以上次的交易輸出作為新的交易輸入得到更多的交易。以1個CoinBase交易為例,其輸入為50個比特幣,將其按數量輸出為80份。再對這80個交易輸出進行操作,對每個輸出作為新的交易輸入又得到100個交易輸出,以同樣的步驟再次劃分為100倍。最終得到800000個數量約為6000聰比特幣的交易輸出。以下為示例代碼: void createDivisionTransaction(const COutPoint& outpoint,const CAmount&amount,int nCount,CMutableTransaction&rawTx){ … //創建多個交易輸出 for(int i=0;i CAmount nAmount=(amount-10000)*1.0f/nCount; CTxOut out1(nAmount,scriptPubKey); rawTx.vout.push_back(out1); 系統也對數據的存儲優化進行了嘗試。在比特幣的文件存儲系統中,blocks目錄下blk開頭的后綴為dat的文件表示當前的區塊數據,rev開頭的后綴為dat的文件是區塊回滾數據。chainstate目錄主要存儲UTXO(Unspent Transaction Output)相關的數據,以Leveldb數據庫存儲,indexes目錄存儲的同樣是基于Leveldb的交易索引數據。 當收到新的區塊或者區塊回滾時,系統代碼以CChainState::ConnectTip和DisconnectTip為相應的入口。 數據分片在區塊鏈的數據副本基礎上進行,將相關數據分為100個區,具體分區根據交易在區塊中的順序而來,序號1、101、1001等序號除以100其模為1的交易組成第1個分區,依次類推。表4列出了第9個、19個...99個分區的數據。系統測試時將8,000,000個長度為100個字符的數據存儲在區塊鏈上,區塊高度為21105,總區塊大小為2825M,總的UTXO數據大小為588M,交易索引總大小為389M。A表示交易的順序,B是每個分區下面的區塊數據大小,D是分區后交易索引大小,F是分區后UTXO數據大小。C、E、G表示當前分區所占比例。 表4 數據分片存儲表 由表可知,數據呈現正確態勢,由于區塊中交易個數不等,相對來說,交易序號越少,分區中的交易也相對集中,數據量也越大。 通過本文基于區塊鏈體系架構建立的健康鏈系統,有效地解決了現實生活中的人員進出登記流程的繁瑣問題。但同時系統屬于疫情防控和復工復產特殊形勢下迅速推出的一項舉措,由于時間緊迫,尚有很多問題和工作需要進行下一步的思考。在設計數據的加密模塊方面、區塊鏈的性能方面、通信模塊的安全方面比如對SSL/TLS的FREAK攻擊、SSL3.0的POODLE攻擊和利用證書的時間差進行攻擊等都需要考慮和防范。最后,底層區塊鏈平臺可以考慮基于以太坊來實現并可以開發智能合約來做到疫情報警。

4.4 區塊鏈性能優化


5 結語