重新審視Solidity事件的用法【智能合約實戰】

在開發區塊鏈應用時,如果把智能合約當成數據庫使用就錯了 —— 不幸的是, 我剛剛意識到這一點,意識到在狀態變量中只應該保存Solidity智能合約需要使用的數據, 而其他數據都應該利用智能合約的事件機制轉儲到外部處理。在我們開發的去中心化 金融平臺上,就是利用事件實現了金融交易的歷史追溯,因此相信這篇文章對你 開發以太坊區塊鏈應用會有幫助。

學習以太坊DApp開發: Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart

開發區塊鏈解決方案需要以不同的思維考慮數據、控制和隱私問題。其中有些 技術細節容易掌握,例如數字貨幣基本上可以理解為一個賬戶餘額表再加上一些操作 餘額的方法。但有些技術細節則更復雜一些,例如你對自己構建的去中心化 解決方案實際上並沒有太多的控制。

有一個問題一直讓我困惑,那就是在公鏈上數據到底意味著什麼?如何平衡 數據的公開透明與隱私之間的關係?

我的結論是,在以太坊區塊鏈上,只把Solidity智能合約要使用的數據保存在狀態變量裡, 任何你需要存檔的數據,都應當利用事件轉儲出來。

在這篇文章中,我們將涉及以太坊的底層架構、區塊鏈的開發準則以及一些Solidity代碼, 學習如何跟蹤報告區塊鏈的狀態變化,如何簡化你的智能合約,以及如何正確地 公開數據。本文涉及的智能合約事件緩存源代碼,可以從Github下載。

跟蹤數據的正確姿勢

在去中心化金融平臺上,我們需要提供完整的交易歷史。

像MiFID II這樣的規定要求金融平臺必須在監管機構要求時提供用戶完整的金融交易歷史, 這不僅包含那些代幣轉賬交易,還包括任何引發區塊鏈狀態變化的用戶動作。

最初的研究讓我產生了這個思路:每一個狀態變化都記錄在區塊鏈上,因此一定 有辦法通過遍歷區塊來提取交易歷史。這個解決方案聽起來比將交易相關的數據保存 在智能合約裡要優雅一些,因為經驗告訴我智能合約的功能越少越好。

基於交易歷史構造的數據庫可以隨時隨地按需重建,因為原始的數據始終都在 區塊鏈上,這樣智能合約就不需要保存那些以存檔為目的的數據。於是我請一個好朋友 Bernardo幫我搜羅一下可以構造交易歷史數據庫的工具。

我一直都沒有想到用Solidity事件來解決這個問題,直到有一天Bernardo跟我說:如果把 區塊鏈觸發的事件記錄下來,會不會解決你的問題?

的確這樣,這一下子打開了我的思路。

使用智能合約的事件

在此之前我一直沒有太思考以太坊的事件機制。我瞭解在你的合約代碼中,當區塊鏈 狀態變化時,你可能會觸發一個事件而不是返回一個值。我也學過如何在前端代碼中 捕捉這些事件,不給對於以太坊Solidity合約中事件的具體運作,我實際上並不太清楚。

當你從智能合約中觸發事件時,這個事件就以半結構化的格式記錄在區塊鏈上,其中 有些字段是保持不變的,而你為事件定義的參數值將出現在附加字段中。事件是以太坊 中成本最低的操作之一,一次觸發大約消耗4000 gas。

一開始我們使用了TheGraph,它實現了我們期望的功能: 捕捉區塊鏈事件並提供一個查詢事件的GraphQL接口。不過對於我們的應用而言,利用 theGraph最終會讓整個解決方案太複雜,但是TheGraph的這些查詢讓我意識到區塊鏈上 的事件實際上變成了一個只寫的數據庫。有些字段在所有事件中都存在,你也可以定義 自己的字段。

改進的智能合約開發模式

理解Soldity合約的事件聽起來很簡單,但是這對於我們如何實現解決方案有著深遠的影響。第一個 變化就是採納了這個開發準則:

所有的狀態變化必須觸發一個事件

通過確保所有的狀態變化都產生事件,我們就不需要擔心交易的可追溯性問題。所有的 狀態變化都以事件的形式轉儲,我們只需要在前端按需提取信息即可。

但是數據的隱私問題怎麼辦?實際上,數據從交易中始終是可以恢復出來的,我們只不過 讓其利用更簡單而已。

記住,公鏈上的所有數據都是公開的

如果你之前讀過我的其他文章,你可能會注意到我總是盡力找到解決問題的 最簡單的辦法。直到不久以前我還對下面這個簡單的文檔登記代碼很滿意:

<code>pragma solidity ^

0.5

.10

;contract DocumentRegistry { event Registered(uint256 hash); mapping (

uint256

=>

uint256) documents;

function

register

(

uint256 hash

)

public

{ documents[hash] = msg.sender; emit Registered(hash); }

function

verify

(

uint256 hash

)

public

view

returns

(

uint256

)

{

return

documents[hash]; } }/<code>

很簡單,但是其實還可以更簡單高效:

<code>

pragma

solidity

^0.5.10

;

contract

DocumentRegistry {

event

Registered(uint256 hash, address sender);

function

register(uint256 hash) public {

emit

Registered(hash, msg.sender); } }/<code>

注意著第二個合約實際上什麼也沒有存下來,也沒有提供一個函數來 提取數據。但是它的確實現了同樣的功能。

我們認為事件是轉瞬即逝的,而區塊鏈上的東西都是永久性的,包括 事件。如果你希望瞭解一個文檔是否真實,只需要計算其哈希,然後再 緩存上查詢即可。

唯一的差別在於你需要維護一個外部基礎設施來跟蹤事件並在鏈下模仿 區塊鏈的狀態。這不僅相對簡單,而且在性能方面可能是最好的方案。

在這個starter-kit中我們 實現了一個簡單的事件緩存,你可以嘗試一下。

教程總結

對事件的重新審視帶來的變化是深遠的,突然之間在需要保存在智能合約 中的信息與需要臨時性觸發的信息之間,出現了一個清晰的分割。

僅在狀態變量中保存智能合約需要使用的數據,否則就利用事件處理。

我過去說在任何區塊鏈解決方案中只有10%的代碼是智能合約。現在實現 可追溯性需要的更少了。新的開發準則讓我們有了更簡潔的代碼。

原文鏈接:http://blog.hubwiz.com/2020/04/15/contract-is-not-database/


分享到:


相關文章: