前言
當你在區塊鏈瀏覽器上查詢交易時,是否只是查看概覽和內部交易?那麼事件日誌呢?是否在不起眼的角落被你忽略了。
交易事件日誌對於用戶以及開發者來說實際上都是至關重要的。通過觸發事件不僅能將鏈上智能合約的交易通知給外界,還能讓智能合約開發者對合約進行測試、保證合約安全。
接下來就幫助大家詳細理解下關於以太坊的事件日誌以及關於它所延伸出來的一些基礎知識。
事件
一、 什麼是事件
事件是能方便地調用以太坊虛擬機日誌功能的接口。
而Solidity 事件就是EVM 的日誌功能之上的抽象。應用程序可以通過以太坊客戶端的RPC 接口訂閱和監聽這些事件,允許我們打印在區塊鏈上的信息。
所以通過Solidity 事件,我們可以做到:
-
測試智能合約中的特定變量
-
索引變量以重建存儲狀態
-
監聽事件用於改變前端狀態
-
創建子圖以更快地讀取數據
二、聲明和触發事件
我們以官方ERC20 合約代碼為例,在IERC20.sol 文件中通過event 關鍵字進行聲明。
我們可以把事件看作是一個特殊類型,上面的代碼中我們創建了一個名為Transfer 的事件,在該事件中有兩種參數類型:有索引(indexed) 和無索引。其中from 和to 參數是有索引的,而value 參數是沒有索引的。
在ERC20.sol 的_transfer 函數中通過emit 關鍵字觸發相應事件(之前的版本里並不需要使用emit)。
日誌
一、什麼是日誌
在以太坊中,日誌是用來存儲事件。當事件被調用時,會觸發參數存儲到交易的日誌中。其不能被智能合約訪問,但是可以提供關於交易和區塊中發送的信息。
我們隨意點開一條交易(0x477ed7208127bea597142622d52df46d3e4967835bd3609995581eb5aaeeec3e),查看其日誌Logs。
通過日誌我們可以將日誌分為四個部分:
1、Address: 地址。即發出事件的合約地址或者賬戶的地址。
2、Name: 名字。即觸發的事件名及其參數。
3、Topics: 主題。即事件中有索引(indexed) 的參數。
4、Data: 數據。即事件中沒有索引的參數。
二、日誌記錄中的主題
上面我們有說到主題(Topics),接下來我們詳細說下主題。
每個日誌記錄都包含「主題(topics)」和「數據(data)」。主題是32 字節(256 位),用於描述事件中發生的事情。不同的操作碼(LOG0 LOG1 LOG2 LOG3 LOG4) 用以描述需要包含在日誌記錄中的主題數。
EVM 中有5 個操作碼用於觸發事件日誌並創建日誌記錄,分別是LOG0,LOG1,LOG2,LOG3 以及LOG4,它們用於描述智能合約中的事件,例如代幣的轉移、所有權的變更等。 LOG1 即包含了一個主題,而單個日誌記錄中最多可以包含的主題就是LOG4 的四個主題。
Topics0 通常為發生事件名稱的簽名(keccak256 的哈希值),包括其參數的類型(address,uint256 等),Topics1 為第一個索引參數的值,Topics2 為第二個索引參數的值。
該主題中Topics0 的值為0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef,其事件為上一行Name 的內容。
而我們對事件Transfer(address,address,uint256) 進行keccak256 加密後得到的結果和Name 的值一樣,說明Name 的值的確為事件名稱的簽名。當然,有一個例外是沒有事件簽名的,那就是觸發「匿名事件」時。
Topics1 就是第一個索引參數的值,即form 地址的值。 Topics2 就是第二個索引參數的值,即to 地址的值。從內部調用分析也能看到的確是這樣。
主題只能包含32 個字節的數據,所以像可能超過32 個字節的內容如數組、字符串等的內容不能用作主題,如果要嘗試包含大於32 個字節的數據,則該主題必須進過hash 計算,所以超過32 個字節後最好當做數據包含在日誌記錄中。
三、日誌記錄中的數據
日誌記錄除了主題,還有一部分內容就是數據,數據就是事件的非索引參數的ABI 編碼或者hash 值,我們可以使用Dec 或Hex 查看數據data 的值。
數據和主題都有各自的優劣:
-
主題是可以搜索到的,數據不能搜索到。
-
數據比主題所需要的gas 少。
由於主題是帶有索引的參數,所以我們可以直接在日誌中進行搜索,而數據是ABI 編碼或hash 值,所以不能直接搜索。
根據黃皮書我們可以找到日誌的相關gas 成本,日誌的基礎費用是375 gas,每個主題也是375 gas,而數據字節的成本是8 gas。
我們可以通過黃皮書知道日誌的gas 費用非常便宜,一個ERC20 代幣轉賬事件的成本最多只花費1756 gas(日誌基礎的375 gas,轉賬事件3 個主題的375 * 3 =1125 gas,數據字節最大的32 字節為8 * 32 = 256 gas),而標準以太幣的轉賬需要花費21000 gas。當然了,前面說的只是日誌記錄操作自身的成本,智能合約開發中不能單純值計算日誌記錄操作的成本,但在開發中,我們可以僅在狀態變量中保存智能合約所需要使用的數據,其他的就用事件來處理,這樣能省下很多的gas 費用。
觸發事件
接下來以一個實例進行說明觸發事件,下面的代碼實現了符合ERC20 標準的代幣合約所使用的轉賬事件。
由於上面不是一個「匿名事件」,所以第一個主題將包含事件的簽名(簽名時只需要參數的類型)。
然後我們看一下該事件的參數,其中from 和_to 地址都是有索引的,value 值是沒有索引的。所以_from 和_to 地址會被當成主題,而_value 值會被當成數據。
在3.3 節中我們說到過主題能被搜索,而數據不能,所以我們能在日誌中搜索from 地址和_to 地址值的相關轉賬日誌,卻不能夠搜索到轉賬金額為_value 值的轉賬日誌。由於該事件具有3 個主題(事件的簽名,from,_to),所以該日誌記錄操作將使用LOG3 操作碼。
那如果我們想要找到數據的內容呢?這裡就需要知道操作碼在EVM 中的參數。 LOG3 雖然包含3 個主題,在EVM 中卻有5 個參數。
如果要讀取數據的內容,通過以下的方式就可以從內存中讀取事件數據了。
釣魚
一、事件在釣魚中的使用
前面介紹了那麼多日誌事件,那這些是如何和釣魚聯繫到一起的呢?攻擊者一般會通過日誌事件偽裝成交易所或者名人等給受害者轉幣(該幣無實際交易價值,是釣魚代幣),受害者看到是交易所或者名人轉來的代幣則放鬆警惕,此時攻擊者會引導受害者到有釣魚代幣的池子中,受害者看到該代幣交易價值極高,會立刻授權進行交易,而此時就陷入了攻擊者設置的圈套,攻擊者會讓受害者授權從而盜取走受害者錢包中的錢。
下圖就是之前發生的一起釣魚事件,攻擊者偽裝成幣安熱錢包給其他人轉釣魚代幣。
我們可以在BSC 瀏覽器上通過標籤找到官方地址。
通過查詢,發現Binance Hot Wallet 6 地址正是0x8894e0a0c962cb723c1976a4421c95949be2d4e3
由於瀏覽器記錄是根據事件來的,所以說topics1 的值即sender 的值就是0x8894e0a0c962cb723c1976a4421c95949be2d4e3
二、復現
下下面是BEP20 的偽代碼,以BNB Chain 主網為例進行複現,攻擊者創建一個名為「Phishing Token」的釣魚代幣。
如下圖所示,新增Binance 參數其值為0x8894E0a0c962CB723c1976a4421c95949bE2D4E3
然後,我們要修改如下圖紅色標記代碼,將emit 觸發事件中的sender 地址修改為Binance。
部署好合約(https://bscscan.com/address/0x7c08aa19b8da2c14591506d7d3c385fc702e0630)後調用transfer 函數將釣魚代幣轉發給受害者。
查看交易信息,發現這裡的from 地址並不是攻擊者的地址0x95E2Ea34dEB5C0954B91a47f459770D20568A15B,而是Binance: Hot Wallet 6 的地址0x8894E0a0c962CB723c1976a4421c95949bE2D4E3
查看Logs 日誌,Topics1 記錄的sender 地址同樣也是Binance Hot Wallet 6 地址,而 Topics2 記錄的recipient 就是受害者的地址了。
總結
細節決定成敗,不要認為事件日誌是微不足道的滄海一粟。在區塊鏈世界越是細節的地方越容易被黑客攻擊利用,往往需要更加謹慎小心。同時需要注意的是,我們也不能因為日誌所展示出來的內容掉入騙子設計好的騙局中。再次提醒大家,不要隨意點擊陌生鏈接,更不要隨意授權他人。當我們更加深入理解事件日誌的時候,才能更好的防止自己上當受騙。