雷志偉 李加福 張桂剛 趙 旭 張 勇 邢春曉
(1.清華大學(xué)行業(yè)可信區(qū)塊鏈應(yīng)用技術(shù)聯(lián)合研究中心 北京 100084)(2.中國科學(xué)院自動化研究所 北京 100190)(3.清華大學(xué)信息國家研究中心,計算機系,互聯(lián)網(wǎng)產(chǎn)業(yè)研究院 北京 100084)
在抗擊新冠肺炎疫情初期,各地各部門企業(yè)紛紛貢獻己力,自主開發(fā)疫情防控信息系統(tǒng)。一方面能加強政府部門的管理,另一方面簡化現(xiàn)實中人力物力消耗[1]。在人員進出公共場所的跟蹤管理方面,相應(yīng)有應(yīng)用于廣西地區(qū)的“掃碼抗疫情”、云南地區(qū)的“云南抗疫情”等。它們都是基于微信小程序開發(fā),其功能和使用方式大抵相同,群眾以個人身份注冊,另一個則是公共場所的工作人員以公共場所的身份進入系統(tǒng)并生成二維碼進行張貼,進出人員手動掃碼實現(xiàn)數(shù)據(jù)上傳,從而減免了手工登記流程。
區(qū)塊鏈作為一個分布式數(shù)據(jù)庫,記錄著區(qū)塊鏈從創(chuàng)世塊到當(dāng)前塊的所有交易,具有去中心化、不可更改性、匿名性和可審計性這幾個特點。鑒于微信小程序基于微信平臺擁有龐大用戶量,不需要下載安裝反復(fù)登錄與卸載,對用戶而言,大大簡化了使用程序。因此使用微信小程序展現(xiàn)基于區(qū)塊鏈的進出登記系統(tǒng)是一個不錯的選擇。與別的出入登記系統(tǒng)不同,健康鏈系統(tǒng)將由公共場所工作人員對出入人員進行掃碼,并保存當(dāng)前測量的溫度值,以加強疫情工作的防控。
微信小程序本質(zhì)上是一種由原生APP和HTML5混合開發(fā)的HyBird App技術(shù)方案。即是一種在原生App里內(nèi)置瀏覽器,采用網(wǎng)頁的形式來呈現(xiàn)功能的系統(tǒng)架構(gòu)。它既解決了原生App下載安裝和更新、應(yīng)用商店發(fā)布審核周期長的問題,又解決了純Web App安全性相對較低,數(shù)據(jù)容易泄露或者被劫持的問題。
微信小程序的系統(tǒng)架構(gòu)如圖1,它以微信APP作為宿主進程,通過云端下載動態(tài)的Web資源文件到本地并動態(tài)渲染W(wǎng)eb界面。在純Web App中,界面渲染跟JavaScript的腳本執(zhí)行在一個單線程中,這就容易導(dǎo)致一些邏輯任務(wù)搶占渲染的資源。因此微信小程序采用雙線程模型,打開一個微信小程序,相當(dāng)于進程啟動了兩個線程。其中一個線程用來渲染View視圖,另外一個App Service邏輯線程動態(tài)執(zhí)行JavaScript腳本,用來處理邏輯、數(shù)據(jù)請求和接口調(diào)用。
圖1 小程序架構(gòu)
在微信APP和Web模塊之間有一個JSBridge跨語言雙向通訊機制,這個通信層協(xié)調(diào)Web模塊的視圖線程與邏輯線程的數(shù)據(jù)和事件交互,邏輯線程把數(shù)據(jù)變化通知到視圖線程,觸發(fā)視圖頁面更新,視圖線程把觸發(fā)的事件通知到邏輯線程進行業(yè)務(wù)處理。更為重要的是,它不但負(fù)責(zé)傳遞Web模塊對系統(tǒng)權(quán)限的相關(guān)功能調(diào)用,還包括傳遞Web視圖渲染和JavaScript腳本執(zhí)行[2]。在安卓系統(tǒng)里,渲染和腳本引擎都是調(diào)用基于開源Webkit庫優(yōu)化和擴展的騰訊X5瀏覽器。在iOS系統(tǒng)里,蘋果基于Web-Kit開發(fā)了WKWebView組件來渲染視圖,并基于WebKit的C/C++實現(xiàn)和包裝實現(xiàn)了JavaScriptCore腳本執(zhí)行框架。
JS-SDK就是對JSBridge的一個包裝,它是一整套網(wǎng)頁開發(fā)工具包,開放了拍攝、錄音、語音識別、二維碼、地圖、支付、分享、卡券等幾十個API。
最后,App Service線程運行在沙箱環(huán)境中,從而方便管控與安全,比如避免JavaScript腳本隨意地跳轉(zhuǎn)網(wǎng)頁或者改變界面上的內(nèi)容[3]。
Bitcoin(比特幣)作為區(qū)塊鏈技術(shù)的起源,是目前發(fā)展最成熟的開源區(qū)塊鏈平臺之一,擁有大量的開發(fā)人員和活躍的開發(fā)社區(qū)。Ethereum(以太坊)核心是支持智能合約的EVM(以太坊虛擬機),提供了大量方便接口便于開發(fā)者進行深度應(yīng)用開發(fā)。Hyperledger Fabric(超級賬本)使用諸如單節(jié)點共識、分布式隊列共識等共識方式,并支持智能合約和外部組件擴展。但Ethereum和Fabric部署困難相對困難。EOS區(qū)塊鏈采用DPOS共識機制,生成區(qū)塊速度快、延遲低,能支持?jǐn)?shù)百萬級別用戶。但EOS作為新興的區(qū)塊鏈平臺發(fā)展還不成熟,同時開發(fā)社區(qū)及開發(fā)人員相對較少,進行開發(fā)研究較為困難[4]。
比特幣作為一種以交易為模型的數(shù)字貨幣系統(tǒng),其交易的數(shù)據(jù)結(jié)構(gòu)如圖2。交易由交易輸入和交易輸出組成,交易輸入和交易輸出可以有多個,表示一次交易可以將先前多個賬戶中的比特幣合并后轉(zhuǎn)給另外多個,每個輸入主要由上筆交易的哈希PrevTxHash、上筆交易的輸出索引Index和輸入腳本ScriptSig組成,其中ScriptSig是持有者對當(dāng)前交易的簽名。通過對某個交易的輸入可構(gòu)成多條以交易為結(jié)點的鏈表,并一直向前追溯至源頭的Coinbase交易(即挖礦所得的比特幣)。如果一筆交易的輸出沒有任何另一筆交易的輸入與之對應(yīng),則說明該輸出中的比特幣未被花費[5]。
圖2 比特幣的交易數(shù)據(jù)結(jié)構(gòu)
區(qū)塊是區(qū)塊鏈的一個數(shù)據(jù)單元,它由區(qū)塊頭和區(qū)塊體組成。比特幣的區(qū)塊數(shù)據(jù)架構(gòu)如圖3,區(qū)塊體包含所有的交易內(nèi)容,區(qū)塊頭包含版本號、時間戳、隨機值和難度值等基本信息,并包含上一個區(qū)塊的哈希值和交易的默克爾樹根哈希值。區(qū)塊的哈希值通過對區(qū)塊頭進行兩次SHA256哈希運算得到。通過指向上一個區(qū)塊的哈希值所有的區(qū)塊構(gòu)成一個鏈條,同時,區(qū)塊的哈希值和對區(qū)塊內(nèi)的所有交易計算得到的默克爾樹根哈希值都可以保證數(shù)據(jù)的不可篡改和完整[6]。
圖3 比特幣的區(qū)塊數(shù)據(jù)架構(gòu)
Nginx是一個跨平臺的Web服務(wù)器,相比Apache、Tomcat和Jetty,它具備高擴展、熱部署、單機支持10萬以上的并發(fā)連接、低內(nèi)存消耗和高可靠的特性。
Nginx提供了異步的、非阻塞的Web服務(wù),它的系統(tǒng)架構(gòu)如圖4,它主要由一個Master主進程、多個Worker工作進程、一個可選的Cachemanager緩存索引管理進程以及一個可選的Cacheloader緩存索引進程組成。
Master主進程用來啟動和管理Worker工作進程,使用管道機制與工作進程通信,并且通過信號機制與外界通信來實現(xiàn)重啟、退出、停止等系統(tǒng)服務(wù)。Worker進程是提供Web服務(wù)的主體,它收到Web請求通過代理轉(zhuǎn)發(fā)給后端服務(wù)器,由后端服務(wù)器進行數(shù)據(jù)處理和頁面組織后返回數(shù)據(jù)。其中FastCGI是一個可伸縮地、高速地在Nginx服務(wù)器和動態(tài)腳本語言間通信的應(yīng)用層通信協(xié)議,PHP作為一種服務(wù)端、跨平臺的HTML嵌入式的腳本語言,它的PHP-FPM模塊實現(xiàn)了FastCGI協(xié)議并負(fù)責(zé)處理PHP動態(tài)請求,從而分擔(dān)了Nginx前端的壓力,使Nginx專注處理靜態(tài)請求[7]。
Cachemanager進程和Cacheloader進程主要是將歷史應(yīng)答數(shù)據(jù)進行本地緩存,從而提高請求的響應(yīng)效率,降低網(wǎng)絡(luò)壓力。Cacheloader進程在Nginx服務(wù)啟動一段之后(默認(rèn)是1min)由主進程生成,在緩存元數(shù)據(jù)重建完成后就自動退出。Cachemanager進程存在于主進程的整個生命期,負(fù)責(zé)管理緩存索引,支持工作進程對緩存數(shù)據(jù)的快速查詢。
多個Worker進程之間是對等的,它們同等競爭來自客戶端的請求,通過互斥鎖機制保證最終由一個Worker進程進行處理,從而做到資源的負(fù)載均衡[8]。
系統(tǒng)架構(gòu)如圖5,系統(tǒng)將搭建SDK接口服務(wù)器作為微信程序和區(qū)塊鏈的通訊中介。微信程序通過Https協(xié)議與SDK接口進行數(shù)據(jù)交互,SDK接口與區(qū)塊鏈系統(tǒng)通過JSON-RPC協(xié)議進行數(shù)據(jù)交互。SDK接口程序由進出人員的個人數(shù)據(jù)接口、公共場所的數(shù)據(jù)接口和進出人員記錄的數(shù)據(jù)接口等等組成。系統(tǒng)配備傳統(tǒng)數(shù)據(jù)庫存儲數(shù)據(jù)以避免區(qū)塊鏈數(shù)據(jù)查詢效率低下問題。
由于區(qū)塊鏈平臺的共識機制,數(shù)據(jù)上鏈有時間延遲,接口程序通過將采用定時查詢機制來得到數(shù)據(jù)上鏈等其他數(shù)據(jù)操作結(jié)果。SDK接口與區(qū)塊鏈系統(tǒng)的數(shù)據(jù)流程如圖6。
在增強數(shù)據(jù)的隱私和安全方面,Https協(xié)議在Http協(xié)議的基礎(chǔ)上加了一層SSL/TLS加密層,SSL/TLS通過將對稱密碼、公鑰密碼、單向散列函數(shù)、消息認(rèn)證碼,偽隨機數(shù)生成器和數(shù)字簽名等技術(shù)相結(jié)合來實現(xiàn)安全通信。此外,SSL/TLS還可以通過切換密碼套件來使用強度更高的密碼算法[9]。
圖6 SDK接口與區(qū)塊鏈系統(tǒng)的數(shù)據(jù)流程
鑒于數(shù)據(jù)的隱私和安全需求,比特幣系統(tǒng)將采用私有鏈的方式布置節(jié)點。
MySQL作為一個關(guān)系型數(shù)據(jù)庫管理系統(tǒng),其體積小、速度快、源碼開放,且對PHP有很好的支持。因此數(shù)據(jù)庫基于MySQL開發(fā),主要包含三個表:個人信息表、公共場所表和進出記錄表。
表1 個人信息表
表2 公共場所表
代碼主要通過微信開發(fā)者工具開發(fā),開發(fā)者可以完成API和頁面的開發(fā)調(diào)試、代碼查看和編輯、小程序預(yù)覽和發(fā)布等等功能。微信小程序1個Page頁面對應(yīng)本系統(tǒng)1個模塊,1個page頁面主要由4個文件構(gòu)成,js腳本文件即是頁面邏輯,wxml是頁面結(jié)構(gòu)展示,wxss是純前端的頁面樣式表,用于輔助wxml展示,json則是頁面配置文件[10]。
表3 進出記錄表
小程序在使用HTTPS發(fā)起網(wǎng)絡(luò)請求時只可以跟指定的域名與進行網(wǎng)絡(luò)通信,因此需要在微信公眾平臺賬號里對應(yīng)的項目設(shè)置里面設(shè)置一個通訊域名,并將域名完成備案。
比特幣加入了OP_RETURN腳本命令,它后面可以緊跟一定容量的數(shù)據(jù),專門用于存儲和交易邏輯無關(guān)的數(shù)據(jù)。這個交易輸出不會加入UTXO集合,從而避免了UTXO數(shù)據(jù)庫的大小不斷“膨脹”[11]。另外,由于比特幣基于LibEvent開源庫實現(xiàn)了一個JSON-RPC的遠程調(diào)用Server端,系統(tǒng)主要增加兩個自定義的RPC API接口。一個是創(chuàng)建包含OPReturn數(shù)據(jù)的交易,一個是查詢交易的OPReturn數(shù)據(jù)。
SDK接口主要是基于PHP語言通過libcurl擴展庫實現(xiàn)了基于Http的JSON-RPC客戶端[12]。其主要函數(shù)代碼如下。
//初始化一個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);
//設(shè)置選項
curl_setopt_array($curl,$options);
//執(zhí)行并獲取結(jié)果
$this->raw_response=curl_exec($curl);
//釋放連接
curl_close($curl);
系統(tǒng)主要包括首頁還有其他模塊。圖7是個人注冊頁面,圖8是公共場所掃碼頁面,圖9是個人進出記錄頁面,圖10是公共場所進出記錄頁面。
圖7 個人注冊頁面
圖8 公共場所掃碼頁面
圖9 個人進出記錄
圖10 公共場所進出記錄
添加OPReturn數(shù)據(jù)到交易的API函數(shù)主要包含創(chuàng)建交易、交易簽名和發(fā)送交易三個過程,此處展示創(chuàng)建交易的部分代碼。
static void createCustomTransaction(const CTxDestination&dest,const CAmount&amount,const std::string&customdata,CMutableTransaction&rawTx){
//創(chuàng)建輸入
uint32_t nSeq=td::numeric_limits
CTxIn in(outpoint,CScript(),nSeq);
rawTx.vin.push_back(in);
//創(chuàng)建OPReturn輸出
std::vector
for(size_t i=0;i data.push_back(customdata[i]); CTxOut out(0,CScript()< rawTx.vout.push_back(out); //創(chuàng)建一筆輸出 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++圖形界面庫,具備跨平臺、易擴展、面向?qū)ο蠛烷_發(fā)文檔豐富的特點[13]。圖11是系統(tǒng)基于Windows平臺的比特幣版本,它基于Visual Studio上編譯Bitcoin-qt,其過程包括安裝Qt軟件、安裝vcpkg并調(diào)用相關(guān)命令下載軟件庫,安裝Visual Studio Qt插件等其它設(shè)置[14]。 圖11 基于Windows平臺的比特幣程序 系統(tǒng)對界面進行了功能擴展,能方便地查看交易的輸入和輸出內(nèi)容。圖12作為示例,其數(shù)據(jù)內(nèi)容 為“南園食堂 雷志偉2020/05/12 14:54:54 36.4”,從而完成了對用戶在某個地點進出的信息登記,并保存了溫度值。另外,通過遍歷區(qū)塊的所有交易,可以得到所有人員的進出記錄,從而達到數(shù)據(jù)的不可篡改和溯源。 圖12 交易的OPReturn數(shù)據(jù)查看 通過更改相關(guān)參數(shù)和優(yōu)化,交易性能在單節(jié)點的情況下能達到100個TPS以上。比如將生成塊的時間由每10min改成10s,將調(diào)整難度時間由2周改成80s,將交易內(nèi)存池擴大,并擴大交易最大容量大小等[15]。 在測試性能時,需要提前創(chuàng)建大量未花費的交易,并記錄交易哈希值以備使用。創(chuàng)建交易的思路是將某個交易按固定數(shù)量生成多個輸出,然后循環(huán)以上次的交易輸出作為新的交易輸入得到更多的交易。以1個CoinBase交易為例,其輸入為50個比特幣,將其按數(shù)量輸出為80份。再對這80個交易輸出進行操作,對每個輸出作為新的交易輸入又得到100個交易輸出,以同樣的步驟再次劃分為100倍。最終得到800000個數(shù)量約為6000聰比特幣的交易輸出。以下為示例代碼: void createDivisionTransaction(const COutPoint& outpoint,const CAmount&amount,int nCount,CMutableTransaction&rawTx){ … //創(chuàng)建多個交易輸出 for(int i=0;i CAmount nAmount=(amount-10000)*1.0f/nCount; CTxOut out1(nAmount,scriptPubKey); rawTx.vout.push_back(out1); 系統(tǒng)也對數(shù)據(jù)的存儲優(yōu)化進行了嘗試。在比特幣的文件存儲系統(tǒng)中,blocks目錄下blk開頭的后綴為dat的文件表示當(dāng)前的區(qū)塊數(shù)據(jù),rev開頭的后綴為dat的文件是區(qū)塊回滾數(shù)據(jù)。chainstate目錄主要存儲UTXO(Unspent Transaction Output)相關(guān)的數(shù)據(jù),以Leveldb數(shù)據(jù)庫存儲,indexes目錄存儲的同樣是基于Leveldb的交易索引數(shù)據(jù)。 當(dāng)收到新的區(qū)塊或者區(qū)塊回滾時,系統(tǒng)代碼以CChainState::ConnectTip和DisconnectTip為相應(yīng)的入口。 數(shù)據(jù)分片在區(qū)塊鏈的數(shù)據(jù)副本基礎(chǔ)上進行,將相關(guān)數(shù)據(jù)分為100個區(qū),具體分區(qū)根據(jù)交易在區(qū)塊中的順序而來,序號1、101、1001等序號除以100其模為1的交易組成第1個分區(qū),依次類推。表4列出了第9個、19個...99個分區(qū)的數(shù)據(jù)。系統(tǒng)測試時將8,000,000個長度為100個字符的數(shù)據(jù)存儲在區(qū)塊鏈上,區(qū)塊高度為21105,總區(qū)塊大小為2825M,總的UTXO數(shù)據(jù)大小為588M,交易索引總大小為389M。A表示交易的順序,B是每個分區(qū)下面的區(qū)塊數(shù)據(jù)大小,D是分區(qū)后交易索引大小,F(xiàn)是分區(qū)后UTXO數(shù)據(jù)大小。C、E、G表示當(dāng)前分區(qū)所占比例。 表4 數(shù)據(jù)分片存儲表 由表可知,數(shù)據(jù)呈現(xiàn)正確態(tài)勢,由于區(qū)塊中交易個數(shù)不等,相對來說,交易序號越少,分區(qū)中的交易也相對集中,數(shù)據(jù)量也越大。 通過本文基于區(qū)塊鏈體系架構(gòu)建立的健康鏈系統(tǒng),有效地解決了現(xiàn)實生活中的人員進出登記流程的繁瑣問題。但同時系統(tǒng)屬于疫情防控和復(fù)工復(fù)產(chǎn)特殊形勢下迅速推出的一項舉措,由于時間緊迫,尚有很多問題和工作需要進行下一步的思考。在設(shè)計數(shù)據(jù)的加密模塊方面、區(qū)塊鏈的性能方面、通信模塊的安全方面比如對SSL/TLS的FREAK攻擊、SSL3.0的POODLE攻擊和利用證書的時間差進行攻擊等都需要考慮和防范。最后,底層區(qū)塊鏈平臺可以考慮基于以太坊來實現(xiàn)并可以開發(fā)智能合約來做到疫情報警。4.4 區(qū)塊鏈性能優(yōu)化
5 結(jié)語