智慧合約是慈善基金公示的核心,負責儲存所有捐贈記錄、支出記錄,並提供對捐贈記錄、支出記錄的查詢以及基金會資金總額的查詢,也就是說最核心的業務邏輯都透過智慧合約實現,資料完全儲存在鏈上。
如上圖所示的公益基金公示DApp,智慧合約執行在超級鏈開放網路上,並對外提供資料查詢和寫入介面。資料寫入介面主要包括新增支出、新增捐款、資料初始化等,資料查詢介面主要包括查詢所有支出和捐款明細,按照捐款人查詢等。
同時,寫入介面需要進行許可權控制,只有基金會的管理賬戶才有許可權寫入資料,保證鏈上資料不被隨意更改,而基金會每次對資料的修改都會記錄在開放網路的賬本上,因此保證了資料的公開透明。
而建立在智慧合約之上的使用者互動層則非常簡單,透過開放網路SDK,可以實現對智慧合約訪問的RESTful API,並透過Web或移動程式的方式展現。
Q4:那麼下面應該是實際開發智慧合約了,超哥跟大家詳細介紹下怎麼開發這個公益基金公示智慧合約吧。
嗯,又到了令人開心的編碼環節。智慧合約開發首先是設計好合約介面和引數。前面我們已經明確了合約需求,因此在介面設計上也比較清楚,寫入介面主要包含下面三個,實現資料初始化、新增捐贈記錄、新增支出記錄功能:
讀介面主要包括下面4個,分別提供基金會當前概覽資料,按照捐款人查詢,查詢所有捐款明細,查詢所有撥款支出明細等:
確定所有介面和引數後,我們開始設計資料結構。目前C++合約支援KV儲存和Table儲存模型,在本例中,我們選用KV儲存儲存資料。為了方便遍歷查詢,我們給“所有捐款明細”、“所有支出明細”、“捐款人捐款列表”指定不同的Key字首,方便透過合約SDK中的迭代器進行遍歷查詢。以所有捐款明細為例,資料組織方式如下圖所示:
這裡的“AllDonate_”是所有捐款明細列表的Key字首,後面的“0000000001”是捐款記錄的自增ID編號。而Value則是一個半結構化資料,包括捐款者的使用者ID、捐款數額、時間、其他備註資訊等。
對於按照捐款人維度查詢的介面,則組織Key結構上稍微複雜一點,大致上以“Key字首+捐款人ID+捐款記錄ID”拼成一個key,本例中Key字首是“UserDonate_”,這樣在確定要查詢的捐款人ID後,拼接為“UserDonate_UserID”的key字首進行遍歷。
同時,考慮到要進行基金會執行概覽的統計,因此需要幾個全域性變數記錄總得金額資料,分別是:TotalDonates表示總捐款金額,TotalCosts表示總撥付支出金額,Balance表示基金會目前賬面餘額。為了維護自增ID,也需要記錄總捐款記錄數和總支出記錄數兩個統計值。
資料介面確定後,就進入了具體的編碼實現階段了,實際程式碼編寫不詳細介紹了,這個公益基金公示智慧合約樣例我們開源在了
https://github.com/xuperchain/xuperchain/blob/v3.6/core/contractsdk/cpp/example/charity_demo.cc,大家可以檢視完整的合約程式碼瞭解細節資訊。
Q5:那麼如何使用迭代器查詢呢,能舉一個查詢介面的具體例子嗎?
好的,我們以按照捐款人查詢所有捐款記錄為例介紹下迭代器如何使用。
上面是迭代器查詢的程式碼片段,可以看到使用迭代器主要分為幾個步驟:
1. 首先要確定迭代器遍歷的Key字首和終止位置,並建立迭代器物件iter。本例中Key字首就是“UserDonate_UserID%”,而“~”字元這是ASCII表中最後一個可見字元,所以作為遍歷終止位置。
2. 然後,開始進入迴圈迭代,“iter->next()”函式返回迭代器是否還有下一行符合條件的結果,如果沒有則迴圈結束。
3. 如果迭代器找到下一行結果,則透過“iter->get()”獲取到一個key-value對,Key中除去字首部分就是捐款記錄ID,value中這是捐款記錄詳情。
透過上述步驟可以遍歷出捐款人的所有捐款記錄。需要注意的是,迭代器預設的一次迴圈返回的最大結果是100行,因此如果記錄數比較多的情況下,最好是每次傳入一個起始ID進行分頁查詢。在我們的示例程式碼中,queryDonates和queryCosts兩個介面都是傳入一個查詢起始ID和每頁查詢數量做分頁查詢,大家可以參照這兩個介面瞭解具體實現。
Q6:之前的智慧合約總體功能圖中,還有一個許可權管理的部分,能詳細介紹下嗎?
許可權管理主要是對於寫介面進行許可權限制,因為捐款資料和支出資料不是任何人都能隨便寫入的,而是基金會的管理者才有許可權將捐款資訊和支出資訊寫入智慧合約對外公示,因此需要對寫入者的身份進行驗證。在超級鏈開放網路中,我們有兩種方式可以控制哪些人有許可權訪問相應的合約介面:
1. 一種是在智慧合約的介面實現程式碼中,獲取到合約上下文引數中的initiator,這個initiator是本次合約呼叫的發起人,開發者可以判斷呼叫者是否是某個具有管理許可權的address。而具有管理許可權的address則可以在合約部署時透過initialize函式引數寫入智慧合約。
2. 另一種是使用超級鏈本身的ACL許可權控制,這種方式支援合約介面級別的ACL許可權配置,可以指定那些address具有訪問某個介面的許可權,並且ACL配置可以隨時修改。相對而言,這種方式設定許可權更為靈活。
而在本例中,我們採用了第一種方法設定管理許可權,在初始化函式中透過“admin”引數寫入一個管理員賬戶address,後面的寫介面對會對比initiator是否是admin賬號。這種硬編碼的方式相對而言更具公信力,但不夠靈活。
Q7:上面我們已經完成了DApp中智慧合約的設計和開發,那麼如何部署和呼叫呢?
部署的話,智慧合約首先需要本地編譯成wasm位元組碼,然後可以透過Go SDK進行部署,也可以透過xchain.baidu.com的開放網路平臺上傳位元組碼部署。
首先介紹下使用Go SDK部署合約,上一期我們介紹了透過Go SDK如何匯入一個加密的賬戶私鑰,我們進一步介紹下如何部署一個智慧合約。在Go SDK中,部署合約已經封裝成了很方便的介面,具體的程式碼實現可以參照SDK的樣例程式碼:
https://github.com/xuperchain/xuper-sdk-go/blob/master/example/main.go#L124
透過平臺圖形化介面進行部署更為簡單方便,可以參考開放網路文件
https://xuperos.readthedocs.io/zh_CN/latest/operation_manuals.html#create
這裡也跟大家預告一個好訊息,超級鏈開放網路很快會支援開發者線上編輯合約程式碼、編譯並直接部署,整體易用性進一步提升。
合約呼叫目前可以透過Go SDK實現,具體的樣例程式碼可以參考上一期的開放網路快速上手教程:
https://xchain.baidu.com/n/news/03b09bfde5edadcb20e9e67145af4b8b