聊聊接入Arbitrum的正確姿勢

本文首發于公眾號:Keegan小鋼


前言

我們知道,目前最主流的 Ethereum Layer2 方案中,主要有 Optimistic RollupZK Rollup 兩大類。而 Optimistic Rollup 的實現方案中,則是 Optimism 和 Arbitrum 最受關注。而我們最近接入了 Arbitrum,測試了好一段時間了,期間還踩到了一些很重要的坑,會影響安全性和可用性的,所以我覺得有必要分享下我們的這些經驗,以便后續想接入 Arbitrum 的項目團隊避免重復踩坑。

第一步

我原本以為,ArbitrumKovanRinkeby 等 Layer1 的測試網一樣,是可以將智能合約無縫切換的,即運行在 Kovan、Rinkeby 和 Ethereum Mainnet 的智能合約無需任何修改,就可以直接部署到 Arbitrum。但事實證明,我的這個認知是大錯特錯的。Arbitrum 跟 Layer1 的差異性原來非常關鍵,如果不特殊處理,有些場景甚至都會變得不可用,而且安全性也會大大降低,具體細節后文會再細說。

因此,接入 Arbitrum 的第一步工作,我的建議是一定要接入 Arbitrum Testnet 進行測試。如果 Arbitrum Testnet 上還缺少什么東西的話,比如沒有 UniswapV2 或者 SushiSwap,那可以自己部署一套 UniswapV2 或 SushiSwap 的合約上去。

而要在 Arbitrum Testnet 上進行測試,就需要領取 Arbitrum Testnet 上的測試幣用來支付 Gas,即 Arbitrum Testnet 上的 ETH。但是,因為 Arbitrum Testnet 本身并沒有可領取 ETH 的 Faucet 水龍頭,所以需要先在 Layer1 的測試網領取測試幣,再通過 Arbitrum Bridge 將測試幣轉到 Arbitrum Testnet 上。

Arbitrum Testnet 所使用的 Layer1 測試網絡是 Rinkeby,所以就需要先領取 Rinkeby 網絡的測試幣。說到這,其實 Arbitrum 一開始使用的測試網絡是 Kovan 的,但后來不知道為何遷移到了 Rinkeby。而事實上,Kovan 網絡比 Rinkeby 網絡要穩定很多。就說近一兩個月內,Rinkeby 就已經出現了不止一次長時間不出塊的問題,每次都長達好幾個小時。我們都知道,區塊鏈不出塊,那就什么都做不了了,無法交易,無法測試,只能干等網絡恢復。這也可以算是接入 Arbitrum 要知道的第一個坑了。

Rinkeby 網絡的水龍頭,我知道的有三個:

  1. https://faucet.rinkeby.io/
  2. https://faucet.paradigm.xyz/
  3. https://faucets.chain.link/rinkeby

第一個水龍頭可以領取到最多幣,一次最多可以領取到 18.75 ETH。但我最近幾次嘗試領取都失敗了,說是已經沒幣可領了。

第二個水龍頭每次可以領取到好幾種幣,包括 1 ETH, 1 wETH, 500 DAI, and 5 NFTs。不過,對推特賬號有要求,要求至少有 1 條推文、15 個 followers、注冊 1 個月以上。我自己的推特賬號目前也才只有 5 個 followers,不滿足條件。

第三個水龍頭是 Chainlink 提供的,雖然每次只能領取 0.1 ETH,但好在沒有推特的要求,也沒有時間限制,所以可以連續多次領取。這也是我最常用的水龍頭。

從 Layer1 的水龍頭領取到 ETH 之后,就可以通過 Arbitrum 橋將 ETH 轉到 Layer2 的 Arbitrum Testnet 了。Arbitrum 橋的地址為:

  • https://bridge.arbitrum.io/

不過,使用 Arbitrum 橋之前,還要先在 MetaMask 錢包中添加 Arbitrum Testnet 的信息,包括 RPC URL、Chain ID、區塊瀏覽器等。Arbitrum Testnet 的信息可配置如下:

  • Network Name: Arbitrum Testnet
  • New RPC URL: https://rinkeby.arbitrum.io/rpc
  • Chain ID: 421611
  • Currency Symbol: ETH
  • Block Explorer URL: https://testnet.arbiscan.io/

通過 Arbitrum 橋就可以將 Token 在 Layer1 和 Layer2 之間轉移。不過,需要了解,從 L1 轉入 L2 大概需要 10 分鐘的時間才確認到賬,而從 L2 轉回 L1 卻需要長達一周左右的時間。轉賬確認時間比較久,這也是 Optimistic Rollup 的一個弊端。

Untitled.png

block.number 的坑

熟悉 Solidity 的同學們都知道,在智能合約中可以通過調用 block.number 獲取當前的區塊高度。

智能合約部署在 Ethereum 主網,就獲取到主網的區塊高度;部署在 Kovan 測試網,就獲取到 Kovan 網絡的區塊高度;部署在 Rinkeby 測試網,就獲取到 Rinkeby 網絡的區塊高度。因此,直覺上會認為 block.number 獲取到的就是當前網絡的區塊高度。

但在 Arbitrum 中發現,原來并非如此。在 Arbitrum 中運行的智能合約,block.number 讀取的并非當前 Arbitrum 網絡的區塊高度,而是 Layer1 的區塊高度。而且,讀取 Layer1 的區塊高度還不是連續的,會隔幾個區塊才讀取一次。

比如,在 Arbitrum Testnet 中,block.number 實際讀取到的是 Rinkeby 網絡的區塊高度;在 Arbitrum Mainnet 中,則讀取到的是 Ethereum Mainnet 的區塊高度。而且,假設 block.number 當前讀取到的區塊高度為 9992886,那下一次讀取到有變化的區塊高度不是 9992887,而是 9992890。經過測試,在 Arbitrum Testnet 中會隔 4 個 Layer1 的區塊才更新一次,這個間隔可能會跨越 Layer2 的 10 幾到 30 幾個區塊。

這是一個大坑啊,還是反直覺的,我至今也不明白為什么不直接讀取當前 Layer2 網絡的區塊高度?因為 Layer2 的合約,是無法直接讀取 Layer1 的合約的,那么廣泛使用的 block.number 返回 Layer1 的非連續的區塊高度有什么用呢?我也想不到在什么樣的場景下,Layer2 的智能合約需要去讀取 Layer1 的區塊高度?

這種情況下,很多使用 block.number 作為條件判斷或計算的 Dapp,都會大大降低可用性和安全性。

Compound 為例子,CToken 合約中有下面這段代碼,用來累加計算最新產生的利息的:

function accrueInterest() public returns (uint) {/* Remember the initial block number */uint currentBlockNumber = getBlockNumber();uint accrualBlockNumberPrior = accrualBlockNumber;/* Short-circuit accumulating 0 interest */if (accrualBlockNumberPrior == currentBlockNumber) {return uint(Error.NO_ERROR);}......
}

因為 Compound 的利息是按區塊計算的,所以只要發生了存取借還,每個區塊都會計算一次利息并累加更新。以上代碼就是獲取當前區塊和上一次更新的區塊,如果是同個區塊則不再計算了。這在 Layer1 上是沒有任何問題的,但在 Arbitrum 上,就會導致連續幾十個區塊都不會計算利息,這期間就給黑客提供很多想象空間了,可用性和安全性都大大降低。

再說說我目前負責的 DEX 的一個場景,為了防范閃電貸攻擊,我們限制了同個賬戶不能在同個區塊內同時開平倉,所以,開倉和平倉函數,都會有這樣一個判斷:

require(traderLatestOperation[trader] != block.number, "ONE_BLOCK_TWICE_OPERATION"
);

traderLatestOperation[trader] 會保存 trader 上一次開倉或平倉的時間。原本的這段邏輯只會限制在同個區塊內不能多次操作,但如今卻變成了用戶將在幾十個區塊內都無法操作,這大大降低了可用性,自然不是我們想要的結果。

那如何解決這個問題呢?咨詢了 Arbitrum 的團隊之后,終于有了解決方案。原來 Arbitrum 中有自己封裝了一個合約叫 ArbSys,合約地址為 0x0000000000000000000000000000000000000064,其中有個 arbBlockNumber() 函數可以讀取到 Arbitrum 網絡本身的當前區塊高度。

ArbSys(100).arbBlockNumber() // returns Arbitrum block number

因此,只要將使用 block.number 的地方,替換成調用 ArbSys(100).arbBlockNumber() 就可以解決問題了。

雖然問題解決了,但這樣的話,對于需要部署到多鏈的 Dapp 來說,就需要根據不同的鏈進行兼容適配了,無法做到一套代碼完全通用。

不過,block.number 的坑其實還不是最大的,我們遇到最大的坑其實在于 block.timestamp。

block.timestamp 的坑

和 block.number 一樣,在 Arbitrum 讀取的 block.timestamp 也不是當前網絡的區塊時間。那是否和 block.number 一樣,是取自 Layer1 的區塊時間呢?其實也不是,咨詢過 Arbitrum 的技術人員,說是比 Layer1 的區塊時間要稍微早一些。而且,也因為 Arbitrum 并不會從 Layer1 連續讀取每個區塊,所以,timestamp 的更新也是同樣有著高時延。經過測試,Arbitrum Testnet 的 block.timestamp 更新時延為 1 分鐘。

那么,是否和 block.number 一樣,Arbitrum 自身提供了合約函數可以讀取當前網絡的當前區塊時間呢?結果是沒有,Arbitrum 提供的 ArbSys 合約只提供了方法查詢 Layer2 的區塊高度和 chainid,但卻沒有提供方法查詢 Layer2 的當前區塊時間。連解決方案都沒有提供,所以才說這是最大的坑。我也是沒想明白,既然都提供了查詢 Layer2 的區塊高度,為何就不提供查詢區塊時間呢?是技術上有難度嗎?

因為沒有方法可獲取到 Arbitrum 當前網絡的區塊時間,就會導致很多依賴于 block.timestamp 的 Dapp 面臨可用性和安全性降低的可能。其中包括 Uniswap TWAP 價格預言機,包括 UniswapV2 的,也包括 UniswapV3 的。

我們知道,TWAP 價格的計算,數據來源于 UniswapV2Pair 合約或 UniswapV3Pool 合約所保存的累計價格或累計 Tick 值。而在合約實現中,累計值只會在 block.timestamp 不一樣時才會更新, UniswapV2Pair 就是在以下函數中更新累計值 price0CumulativeLastprice1CumulativeLast

// update reserves and, on the first call per block, price accumulators
function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');uint32 blockTimestamp = uint32(block.timestamp % 2**32);uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desiredif (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {// * never overflows, and + overflow is desiredprice0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;}reserve0 = uint112(balance0);reserve1 = uint112(balance1);blockTimestampLast = blockTimestamp;emit Sync(reserve0, reserve1);
}

因此,在 Arbitrum Testnet 中,累計值至少 1 分鐘才會更新一次,Arbitrum 主網中沒精確測試過,但應該是差不多的。因為 Arbitrum 的出塊時間大概為 2~6 秒,所以累計值可能長達 30 個 Arbitrum 區塊才會更新一次。如此嚴重的高時延,那計算出來的 TWAP 的準確性自然也大幅降低了。

同為 Optimistic RollupOptimism 其實也存在同樣的問題,所以在 Uniswap 的官方文檔中還有下面這段說明:

Untitled1.png

不過,Optimism 的時延只有 20 多秒,沒有 Arbitrum 的這么高時延。另外,也不知道 Optimism 有沒有提供方法查詢 Layer2 的區塊時間,我目前沒找到。

總而言之,這種情況下,對于想要接入 Arbitrum 的項目來說,當需要使用到 block.timestamp 作為判斷條件時,沒有太優雅的解決方案,我只能提供一些思路。

首先,思考下是否可以不用區塊時間而改用區塊高度,那就可以用 ArbSys(100).arbBlockNumber() 方案解決問題。

其次,如果業務上的時間周期比較長,比如 30 分鐘、幾小時甚至幾天,那延后 1 分鐘還是可以接受的。比如,假設讀取的是 1 小時內的 TWAP 價格,那 1 分鐘的時延倒是影響沒那么大。

最后,若實在必須要求低時延,那也許只能等未來 Arbitrum 在這方面有所優化了。

總結

目前,在 Arbitrum 上主要遇到的問題就是這些了,block.number 和 block.timestamp 是最大的兩個坑,其他問題都是小問題。其他項目在接入 Arbitrum 之前,可以先考慮好對應問題的解決方案。也希望 Arbitrum 能盡快優化自身,以能達到所有 Dapp 的智能合約真的能夠無需修改地從 Layer1 無縫遷移到 Layer2。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44749.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44749.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44749.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java套紅:指定位置合并文檔-NiceXWPFDocument

需求&#xff1a;做個公文系統&#xff0c;需要將正文文檔在某個節點點擊套紅按鈕&#xff0c;實現文檔套紅 試了很多方法&#xff0c;大多數網上能查到但是實際代碼不能找到關鍵方法&#xff0c;可能是跟包的版本有關系&#xff0c;下面記錄能用的這個。 一&#xff1a;添加依…

redis-cli 連接Redis

Redis-cli介紹 redis-cli 是原生 Redis 自帶的命令行工具&#xff0c;您可以在云主機或本地設備上通過 redis-cli 連接 Redis 數據庫&#xff0c;進行數據管理。 redis-cli 客戶端的使用方法&#xff0c;請參考官方文檔。 連接命令 redis-cli -h <redis_instance_address…

一個極簡的 Vue 示例

https://andi.cn/page/621516.html

【qt】客戶端連接到服務器

獲取到IP地址和端口號. 通過connectToHost() 來進行連接. 對于客戶端來講,只需要socket即可. 客戶端連接服務端只需要使用套接字(Socket)來進行通信。客戶端通過創建一個套接字來連接服務端&#xff0c;然后可以通過套接字發送和接收數據。套接字提供了一種簡單而靈活的方式來…

接口安全配置

問題點&#xff1a; 有員工在工位在某個接口下鏈接一個集線器&#xff0c;從而擴展上網接口&#xff0c;這種行為在某些公司是被禁止的&#xff0c;那么網絡管理員如何控制呢&#xff1f;可以配置接口安全來限制鏈接的數量&#xff0c;切被加入安全的mac地址不會老化&#xff…

JS實現:統計字符出現頻率/計算文字在文本中的出現次數

要實現這個功能&#xff0c;JavaScript 一個非常強大的方法&#xff0c;那就是reduce() reduce() 它用于將數組的所有元素減少到一個單一的值。這個值可以是任何類型&#xff0c;包括但不限于數字、字符串、對象或數組。 reduce() 方法接收一個回調函數作為參數&#xff0c;這個…

win10 docker-compose搭建ELK日志收集

elk的威名大家都知道&#xff0c;以前前司有專門的人維護&#xff0c;現在換了環境&#xff0c;實在不想上服務器看&#xff0c;所以就摸索下自己搭建&#xff0c;由于現場服務器是需要類似向日葵那樣連接&#xff0c;我還是把日志弄回來&#xff0c;自己本地filebeat上傳到es中…

ESP32和ESP8266的WIFI的136個問題與答案

ESP32和ESP8266的WIFI的136個問題與答案 ESP32和ESP8266 WIFI相關問題與答案&#xff0c;具有一定的參考價值。ESP32-S3模塊 1. ESP32 和 ESP8266 是否支持中文 SSID&#xff1f; ESP32 和 ESP8266 均支持中文 SSID&#xff0c;但需要使用相應的庫和設置。需要注意的是&#…

自定義函數---隨機數系列函數

大家有沒有發現平常在寫隨機數的時候&#xff0c;需要引入很多的頭文件&#xff0c;然后還需要用一些復雜的函數&#xff0c;大家可能不太習慣&#xff0c;于是我就制作了一個頭文件 // random_number.h #ifndef RANDOM_NUMBER_H // 預處理指令&#xff0c;防止頭文件被重復包含…

六款領先的電腦監控軟件系統(哪些電腦軟件可以監控電腦)

在當今信息時代&#xff0c;企業對數據安全和員工生產力的關注度越來越高。電腦監控軟件系統成為企業管理的重要工具&#xff0c;幫助企業確保信息安全、提高工作效率。本文將介紹幾款領先的電腦監控軟件系統&#xff0c;以便企業選擇最適合的解決方案。固信電腦監控軟件 可免費…

如何在SQLServer中更改端口

在SQL Server中更改端口通常涉及SQL Server配置管理器的使用&#xff0c;以下是一個詳細的步驟指南&#xff1a; 一、打開SQL Server配置管理器 通過開始菜單&#xff1a;點擊“開始”菜單&#xff0c;搜索“SQL Server配置管理器”并打開它。通過運行窗口&#xff1a;按Win …

log4j2的日志框架(詳細,springboot和異步日志的實現)

目錄 log4j2的介紹 Log4j2的性能 SpringBoot中的使用Log4j2 log4j2的進階--異步日志 AsyncAppender方式 AsyncLogger方式 log4j2的介紹 Apache Log4j 2是對Log4j的升級版&#xff0c;參考了logback的一些優秀的設計&#xff0c;并且修復了一些問題&#xff0c;因此帶 來…

Go-知識測試-測試參數

Go-知識測試-測試參數 1. -args2. -json3. -o4. -bench5. -benchtime6. -cpu7. -count8. -failfast9. -list10. -parallel11. -run12. -timeout13. -v14 -benchmem 1. -args 指示go test 把-args 后面的參數帶到測試中去。具體的測試函數會根據此參數來控制測試流程。 -args后…

主機安全-進程、命令攻擊與檢測

目錄 概述反彈shell原理nc/dev/xxx反彈shell下載不落地反彈Shell各種語言反彈shell linux提權sudosuid提權mysql提權 Dnslog參考 概述 本文更新通過在主機&#xff08;不含容器&#xff09;上直接執行命令或啟動進程來攻擊的場景。檢測方面以字節跳動的開源HIDS elkeid舉例。每…

磁感應強度檢測模塊使用教程

目錄 一、磁感應強度檢測模塊(AT 協議版本、Modbus 協議版本)1、參數2、報警引腳 二、AT版本1、接線說明2、AT 指令 三、Modbus 版本1、接線說明2、Modbus 指令格式3、Modbus 指令 一、磁感應強度檢測模塊(AT 協議版本、Modbus 協議版本) 圖1 正面 圖2 背面 AT 協議版本和 Modb…

Letter Exchange

這道題目看官方題解就好了&#xff0c;這個轉換圖論挺顯然的 證明一下為什么最后一定是 顯然練完貶值后圖只能長成這個樣子 在消掉長度為\(2\)的環后&#xff0c;如果說圖沒邊了&#xff0c; 那么顯然就不用交換了&#xff0c;否則的話我們任取一條邊 那么對于\(2\)號點來說&am…

韋東山嵌入式linux系列-驅動進化之路:總線設備驅動模型

1 驅動編寫的 3 種方法 以 LED 驅動為例 1.1 傳統寫法 使用哪個引腳&#xff0c;怎么操作引腳&#xff0c;都寫死在代碼中。 最簡單&#xff0c;不考慮擴展性&#xff0c;可以快速實現功能。 修改引腳時&#xff0c;需要重新編譯。 應用程序調用open等函數最簡單的方法是驅動…

(深度估計學習)Depth Anything V2 復現

Depth Anything V2 復現 一、配置環境二、準備數據1. 權重文件2. 訓練數據 三、Test四、Train 代碼&#xff1a;https://github.com/DepthAnything/Depth-Anything-V2 一、配置環境 在本機電腦win跑之后依舊爆顯存&#xff0c;放到服務器跑&#xff1a;Ubuntu22.04&#xff0c…

使用Zabbix進行服務監控:構建高效穩定的IT服務管理平臺

使用Zabbix進行服務監控&#xff1a;構建高效穩定的IT服務管理平臺 在當今的數字化時代&#xff0c;IT服務管理&#xff08;ITSM&#xff09;對于確保企業IT系統的穩定性和性能至關重要。服務監控是ITSM的重要組成部分&#xff0c;可以幫助企業實時了解IT系統的運行狀況&#…

微調Qwen2大語言模型加入領域知識

目錄 試用Qwen2做推理安裝LLaMA-Factory使用自有數據集微調Qwen2驗證微調效果 試用Qwen2做推理 參考&#xff1a;https://qwen.readthedocs.io/en/latest/getting_started/quickstart.html from transformers import AutoModelForCausalLM, AutoTokenizer device "cuda…