以太幣交易所 以太幣交易所
Ctrl+D 以太幣交易所
ads
首頁 > FIL幣 > Info

EVM 深入探討 Part 2_BSP

Author:

Time:1900/1/1 0:00:00

點擊閱讀:EVM 深入探討 Part 1

導語

在第 1 部分中,我們探討了 EVM 如何通過被調用的合約函數知道需要運行哪個字節碼,其中我們了解了調用棧、calldata、函數簽名和 EVM 操作碼指令。

在第 2 部分中,我們將開啟內存之旅,全面了解合約的內存以及它在 EVM 上的工作方式。

此系列我們將引介翻譯 noxx 的文章(https://noxx.substack.com/)深入探討 EVM 的基礎知識。

我們依然使用第 1 部分中在 remix 上為大家演示的示例代碼。

第 1 部分中我們根據合約編譯后生成的字節碼研究了與功能選擇相關的部分。在本文中,我們將注意力放在字節碼的前 5 個字節。

這 5 個字節表示初始化 “空閑內存指針” 操作。要完全理解這些字節碼的作用,首先需要理解管理支配合約內存的數據結構。

合約內存是一個簡單的字節數組,其中數據存儲可以使用 32 字節(256 位)或 1 字節(8 位)的數據塊存儲數據,但是讀取時每次只能讀取固定大小的 32 字節(256 位)的數據塊。下面的圖片說明了此結構以及合約內存的讀/寫功能。

這個功能是由操作內存的 3 個操作碼決定的。

MSTORE (x, y):從內存位置 “x” 開始存儲一個 32 字節(256 位)的 “y” 值。

MLOAD (x):從內存位置 “x” 開始將 32 字節(256 位)加載到調用棧上。

MSTORE8 (x, y):在內存位置 “x” 存儲一個 1 字節(8 位)的值 “y”(32 字節棧值的最低有效字節)。

ConsenSys zkEVM項目Linea推出忠誠度活動以鼓勵早期用戶使用:5月2日消息,據官方消息,ConsenSys 旗下 zkEVM 項目 Linea 聯合 Galxe 推出忠誠度活動以鼓勵早期用戶使用網絡。[2023/5/3 14:39:19]

你可以將內存位置簡單地看作是開始寫入/讀取數據的數組索引。如果想寫入/讀取超過 1 個字節的數據,只需繼續從下一個數組索引寫入或讀取。

EVM Playground 有助于鞏固我們這 3 個操作碼的運行原理、作用以及內存位置的理解。單擊 Run 和右上角的箭頭進行調試來查看堆棧和內存是如何更改的。(操作碼上方有注釋來描述每個部分的作用)

可能會注意到一些奇怪的現象,我只添加了 1 個字節,為什么多了這么多零呢?

3、內存擴展

當合約寫入內存時,需要為寫入的字節數支付 Gas,也就是擴大內存的開銷。如果我們正在寫入一個以前沒有寫入過的內存區域,那么第一次使用它會產生額外的內存擴展開銷。

寫入之前未觸及的內存空間時,內存以 32 字節(256 位)為增量擴展。前 724 個字節,內存擴展呈線性增長,之后呈二次方增長。(由以太坊黃皮書公式 326 擴大內存的 Gas 開銷得出,公式為:

,擴展內存時為每個額外的字的開銷。其中 a 是合約調用中寫入的最大內存位置,以 32 字節字為單位。用 1024 字節內存為例,那么 a = 32 。)

在位置 32 處寫入 1 個字節之前,我們的內存是 32 個字節。此時我們開始往未觸及的內存空間寫入內容,結果,內存增加了 32 個字節,增加到 64 個字節。內存中所有位置的都初始被定義為 0,這也是為什么我們會看到 2200000000000000000000000000000000000000000000000000000000000000 被添加到內存中的原因。

Dymension正在為Cosmos生態系統開發與EVM兼容的Layer 2 rollup:4月27日消息,區塊鏈開發公司 Dymension 宣布正在為 Cosmos 生態系統開發與 EVM 兼容的 Layer 2 rollup。該 rollup 仍處于測試階段,旨在簡化以太坊應用程序在 Cosmos 上的部署。[2023/4/27 14:31:33]

4、內存是一個字節數組

調試過程中,我們可能注意到的第二件事發生在我們從內存位置 33 (0x21) 運行 MLOAD 時。我們將以下值返回到調用棧。

3300000000000000000000000000000000000000000000000000000000000000

內存讀取可以從一個非 32 字節元素開始。

內存是一個字節數組,這意味著可以從任何內存位置開始讀取(和寫入)。我們不限于 32 的倍數。內存是線性的,可以在字節級別進行尋址。內存只能在函數中新建。它可以是新實例化的復雜類型,如數組/結構(例如,通過 新建一個 int[...])或從存儲引用的變量中復制。

現在我們對數據結構已有了一定的了解了,接下來讓我們來看空閑內存指針。

5、空閑內存指針

空閑內存指針只是一個指向空閑內存開始位置的指針。它確保智能合約可以跟蹤到哪些內存位置已寫入,哪些未寫入。這可以防止合約覆蓋已分配給另一個變量的某些內存。當一個變量被寫入內存時,合約將首先引用空閑內存指針來確定數據應該存儲在哪里。然后,它通過記錄要寫入新位置的數據量來更新空閑內存指針。這兩個值的簡單相加將產生新的空閑內存開始的位置。

空閑內存指針的位置 + 數據的字節大小 = 新空閑內存指針的位置

6、字節碼

就像我們之前所提到的,空閑內存指針是通過這 5 個操作碼在運行時字節碼的定義的。

這些操作碼聲明空閑內存指針位于內存中字節 0x40(十進制中的 64)處,值為 0x80(十進制中的 128)。

dForce宣布其全套DeFi產品即將上線Evmos:3月2日消息,DeFi 協議 dForce 宣布其全套 DeFi 產品即將上線 Cosmos EVM Hub Evmos,包括借貸協議、去中心化 Stablecoin USX、跨鏈橋 dForce Bridge 等。[2022/3/2 13:31:58]

Solidity 的內存布局保留了 4 個 32 字節的插槽:

0x00 - 0x3f (64 bytes):暫存空間,可用于語句之間,即內聯匯編和哈希散列方法。

0x40 - 0x5f (32 bytes):空閑內存指針,當前分配的內存大小,空閑內存的起始位置,初始化為 0x80。

0x60 - 0x7f (32 bytes):插槽 0,用作動態內存數組的初始值,永遠不應寫入。

我們可以看到,0x40 是空閑內存指針的預定義位置。而值 0x80 只是在 4 個 32 字節保留值插槽之后可寫入的第一個內存字節。

7、合約中的內存

為了鞏固我們到目前為止所學到的知識,接下來將看看內存和空閑內存指針是如何在 Solidity 代碼中更新的。

我們創建 MemoryLane 合約來進行演示。合約的 memoryLane() 定義了兩個長度分別為 5 和 2 的數組,并將 uint256 類型的 1 賦值給 b。

要查看合約代碼在 EVM 中執行的詳細信息可以將其復制到 Remix IDE 中編譯并部署合約。調用  memoryLane() 后進入 DeBug 模式來逐步執行操作碼(以上操作可以參考:

https://remix-ide.readthedocs.io/en/latest/tutorial_debug.html)。

將簡化版操作碼提取到 EVM Playground 中,可通過這個鏈接查看具體的操作碼及注釋信息(https://noxx.substack.com/p/evm-deep-dives-the-path-to-shadowy-d6b#:~:text=version%20into%20an-,EVM%20Playground,-and%20will%20run)。

ZKSwap開發負責人 Alex Lee:構建支持通用 EVM 的 rollup 擴容解決方案 ZKSwap在路上:據官方消息,2021年04月12日晚,由Gate.io主辦的直播專訪節目《酒局幣赴》邀請到ZKSwap開發負責人 Alex Lee直播分享近期最新發展。直播期間Alex與Gate.io合伙人酒兒就面對市場競爭格局產生變化后,ZKS將如何把握機遇與挑戰進行了探討與交流。

Alex 表示,目前,Layer2賽道已經是一片繁榮了,技術上不斷創新,各種產品也層出不窮。ZKSwap推出的 Zkspeed 擴容方案兼顧了 ZK-Rollup、Validium 和 Optimistic rollup 方案的特點。即實現所有與 Layer1 交互的交易數據全部上鏈(ZK-Rollup),把單純 Layer2 的交易數據存放在鏈下(Validium),交易 hash 數據上鏈,同時 ZKSpeed 也會提供一個完全上鏈的版本,這樣可以實現更高的安全性,并提供零知識證明保證狀態轉換的有效性。雖然目前 ZKSwap 的方案還不兼容 EVM,但ZKSwap 團隊的愿景正是構建一個支持通用 EVM 的 rollup 擴容解決方案,使得其他應用無需重新編寫智能合約就能實現快速遷移,目前 ZKSwap 團隊已經投入研究,并取得了一些進展。[2021/4/12 20:12:00]

這里將操作碼分成 6 個不同的部分依次解讀,刪除了 JUMP 以及與內存操作無關的操作碼同時將注釋添加了進去方便查看當前在執行什么操作。

1)空閑內存指針初始化(EVM Playground 操作碼代碼 1-15 行)

首先,0x80(十進制為 128)先入棧,這是由 Solidity 內存布局規定的值,當前內存中沒有任何東西。

最后,我們調用 MSTORE,它將第一項從棧 0x40 彈出以確定在內存中寫入的位置,并將第二個值 0x80 作為寫入的內容。這樣留下了一個空棧,但已經填充了一部分到內存中。內存由十六進制字符表示,其中每個字符代表 4 位。例如:在內存中有 192 個十六進制字符,這意味著我們有 96 個字節(1 字節 = 8 位 = 2 個十六進制字符)。如果我們回顧 Solidity 的內存布局會發現,前 64 個字節將被分配為暫存空間,接下來的 32 個字節將用于空閑內存指針。

聲音 | 以太坊基金會安全負責人:不認為EIP 615提案會帶來更好的EVM:據CoinDesk消息,在本周五(4月12日)的雙周會議上,以太坊基金會安全負責人Martin Hoste Swende提到,提議的EIP 615將需要至少兩個硬分叉才能完全執行,并且在執行后一個硬分叉之前,對EVM實際代碼計算“產生的積極速度影響”將不會明顯。 Swende表示,“這是我對EIP的主要擔憂,它需要做很多工作,但我不認為它會帶來一個更好的EVM。對于外部工具來說,這可能更好,比如你正在對智能合同的安全屬性進行逆向分析。” 據悉,EIP 615是被考慮納入伊斯坦布爾硬分叉的五個提案之一,旨在對以太坊虛擬機(EVM)進行改進。EIP 615的作者Greg Colvin、Brooklyn Zelenka、Pawel Bylic和Christina Reitwiessner寫道,“EVM的設計使得低Gas成本、高性能的執行變得困難,我們提議通過加強安全保障和提高EVM的性能極限來解決這些問題。”[2019/4/13]

2)內存分配變量 “a” 和空閑內存指針更新(EVM Playground 第 16-34 行)

接下來的部分,我們將跳到每個部分的結束狀態,并簡潔概述。

首先,為變量 “a”(bytes32)分配下一個內存,并更新空閑內存指針。編譯器將通過數組大小和默認數組元素大小確定需要多少空間。Solidity 中內存數組中的元素都是占據 32 字節的倍數(這同樣適用于 bytes1[],但 bytes 和 string 不適用)。當前需要分配的內存為 5 * 32 字節,表示為 160 或 0xa0(16 進制的 160)。我們可以看到它被壓入棧中并添加到當前空閑內存指針 0x80(十進制中的 128)來獲取新的空閑內存指針值。這將返回 0x120(十進制的 288 = 128 + 160),我們可以看到它已被寫入空閑內存指針位置。調用棧將變量 “a” 的內存位置保存在棧 0x80 上,以便以后可以在需要時引用它。0xffff 代表一個 JUMP(無條件跳轉) 位置,可以忽略,因為它與內存操作無關。

3)內存初始化變量 “a”(EVM Playground 第 35-95 行)

已經分配好了內存并且更新了空閑內存指針,接下來需要為變量 “a” 初始化內存空間。由于該變量只是被聲明并沒有被賦值,它將被初始化為零值。

EVM 通過使用了 CALLDATACOPY(復制消息數據)操作碼來進行操作,其中存在 3 個變量。

memoryOffset/destOffset(將數據復制到的內存位置) 

calldataOffset/offset(需要復制的 calldata 中的字節偏移量)

size/length(要復制的字節大小)

表達式:

memory[destOffset:destOffset+length] = msg.data[offset:offset+length]

在這個例子中,memoryOffset(destOffset) 是變量 “a”(0x80)的內存位置。calldataOffset(offset) 是實際 calldata 的大小,因為并不需要復制任何 calldata,所以初始化內存為零。最后,傳入的變量為 0xa0(十進制的 160)。

這是可以看到我們的內存已經擴展到 288 字節(這包括插槽 0),并且調用棧再次保存了變量的內存位置和以及棧上的 JUMP 地址。

這與變量 “a” 的內存分配和空閑內存指針更新相同,只是這次是針對 “bytes32 memory b”。內存指針更新為 0x160(十進制為 352),等于先前的空閑內存指針 288 加上新變量的大小 64(以 bytes 64 為單位)。空閑內存指針已在內存中更新為 0x160,那么現在在棧上就擁有變量 “b”(0x120)的內存位置。

與變量 “a” 的內存初始化相同。現在內存已增加到 352 字節,棧內仍然保存 2 個變量的內存位置。

最后,我們開始為數組 “b” 索引 0 賦值。代碼指出 b 的值應該為 1。該值被壓入棧 0x01。接下來發生向左移位,但是移位的輸入為 0,這意味著我們的值不會改變。接下來,要寫入 0x00 的數組索引位置被壓入堆棧,并檢查該值是否小于數組 0x02 的長度。如果不是,則執行跳轉到處理此錯誤狀態的字節碼的不同部分。MUL(乘法)和 ADD(加法) 操作碼用于確定需要將值寫入內存中的哪個位置以使其對應于正確的數組索引。

0x20 (10 進制為 32) * 0x00 (10 進制為 0) = 0x00

需要記住,內存數組是 32 字節的元素,因此該值表示數組索引的起始位置。鑒于我們正在寫入索引 0,沒有偏移量,也就是從 0x00 開始寫入。

0x00 + 0x120 = 0x120 (10 進制為 288)

ADD 用于將此偏移值添加到變量 “b” 的內存位置。偏移量為 0,直接將數據寫入分配的內存位置。最后, MSTORE 將值 0x01 存儲到這個內存位置 0x120。

下圖顯示了函數執行結束時的系統狀態。所有棧項都已彈出。請注意,實際上在 remix 中還有一些項目留在堆棧上,一個 JUMP 位置和函數簽名,但是它們與內存操作無關,因此在 EVM playground 中被省略了。

內存已更新為包含 b = 1 賦值,在我們內存的倒數第三行,0 值變成了 1。可以驗證該值位于正確的內存位置,b 應占用位置 0x120 - 0x13f(bytes 289 - 320)。

我們現在對合約內存的工作原理有了一定程度的了解。在后續需要編寫代碼時,將為我們提供很好理解與幫助。當你跳過一些合同操作碼,看到某些內存位置不斷彈出 (0x40) ,現在就知道他們的確切含義了。

在本系列下一篇文章中,我們將在 EVM 深入探討系列第 3 部分深入探討合約存儲的工作原理,了解存儲插槽包裝(slot packing),揭開存儲插槽的神秘面紗。

慢霧科技

個人專欄

閱讀更多

金色財經Maxwell

元宇宙之心

老雅痞

Odaily

去中心化金融社區

鴕鳥區塊鏈

PANews

Mindao

吳說區塊鏈

Foresight News

金色薦讀

Tags:BSPNBSSETFFSBSPAY價格nbs幣官網SetherCrypto Puffs

FIL幣
虧損仍占主導 比特幣將持續探底_比特幣

文章來源:https://insights.glassnode.com原文作者:CryptoVizArt, Glassnode比特幣在經歷了幾個月的極低波動后,本周已經回升到超2萬美元的水平.

1900/1/1 0:00:00
區塊鏈技術發展趨勢與銀行業探索實踐_區塊鏈

在數字化轉型的大背景下,全球主要國家都在加快布局區塊鏈技術,搶占新一輪創新變革的高地。 一、區塊鏈技術概述 技術特征 區塊鏈技術是密碼學、共識算法、P2P通信、智能合約等多種技術的集成創新,打造.

1900/1/1 0:00:00
zkSync 2.0 主網上線在即 有哪些重要信息和項目值得關注?_ZKS

文章來源:wagame.eth 編譯:DeFi 之道 zksync 2.0 主網將在 3 周內到來.

1900/1/1 0:00:00
對話OpenBlox:一張NFT門票解鎖一站式NFT游戲平臺_BLO

基于EVM(以太坊虛擬機)的一站式NFT游戲平臺OpenBlox即將推出其首個鏈游RogueBlox.

1900/1/1 0:00:00
晚間必讀 | 加密市場四季度有哪些值得關注的敘事_區塊鏈

金色財經編輯部每晚為讀者精挑了當天最值得精讀的5篇文章,希望您每一天都能獲得新的知識財富。世界杯、球迷代幣、Berachain、Cosmos 2.0、去中心化穩定幣、DeFi和NFT的結合、新風.

1900/1/1 0:00:00
晚間必讀 | 虛擬社會、區塊鏈和元宇宙_區塊鏈

金色財經編輯部每晚為讀者精挑了當天最值得精讀的5篇文章,希望您每一天都能獲得新的知識財富。在對元世界的描述中,我們認為至關重要的是 “ 開放的元宇宙世界是去中心化的,允許用戶.

1900/1/1 0:00:00
ads