日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

汇编 div_Solidity汇编开发简明教程

發布時間:2024/9/19 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 汇编 div_Solidity汇编开发简明教程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在用Solidity開發以太坊智能合約時,使用匯編可以直接與EVM交互,降低 gas開銷成本,更精細的控制智能合約的行為,因此值得Solidity開發者學習 并加以利用。本文是Solidity匯編開發的簡明教程,旨在幫助你快速熟悉 如何在Solidity智能合約代碼中嵌入匯編代碼。

以太坊教程鏈接: Dapp入門 | 電商Dapp實戰 | Token實戰 | Php對接 | Java對接 | Python對接 | C#對接 | Dart對接

2、以太坊虛擬機和堆棧結構機器

以太坊虛擬機EVM有自己的指令集,該指令集中目前包含了 144個操作碼,詳情參考Geth源代碼

這些指令是Solidity抽象出來的,可以在Solidity內聯使用。例如:

contract Assembler { function do_something_cpu() public { assembly { // start writing evm assembler language } }}

EVM是一個棧虛擬機,棧這種數據結構只允許兩個操作:壓入(PUSH)或彈出(POP)數據。 最后壓入的數據位于棧頂,因此將被第一個彈出,這被稱為后進先出 (LIFO:Last In, First Out):

棧虛擬機將所有的操作數保存在棧上,關于棧虛擬機的詳細信息 可以參考stack machine 基礎

3、堆棧結構機器的操作碼

為了能夠解決實際問題,棧結構機器需要實現一些額外的指令,例如 ADD、SUBSTRACT等等。指令執行時通常會先從堆棧彈出一個或多個值作為參數, 再將執行結果壓回堆棧。這通常被稱為逆波蘭表示法(RPN:Reverse Polish Notation):

a + b // 標準表示法Infixa b add // 逆波蘭表示法RPN

4、在Solidity合約中使用內聯匯編

可以在Solidity中使用assembly{}來嵌入匯編代碼段,這被稱為內聯匯編:

assembly { // some assembly code here}

在assembly塊內的代碼開發語言被稱為Yul,為了簡化我們稱其為 匯編或EVM匯編。

另一個需要注意的問題時,匯編代碼塊之間不能通信,也就是說在 一個匯編代碼塊里定義的變量,在另一個匯編代碼塊中不可以訪問。 例如:

assembly { let x := 2} assembly { let y := x // Error}

上面的代碼編譯時會報如下錯誤:

// DeclarationError: identifier not found// let y := x// ^

下面的代碼使用內聯匯編代碼計算函數的兩個參數的和并返回結果:

function addition(uint x, uint y) public pure returns (uint) { assembly { let result := add(x, y) // x + y mstore(0x0, result) // 在內存中保存結果 return(0x0, 32) // 從內存中返回32字節 } }

讓我們重寫上面的代碼,補充一些更詳細的注釋,以便說明每個指令 在EVM內部的運行原理。

function addition(uint x, uint y) public pure returns (uint) { assembly { // 創建一個新的變量result // -> 使用add操作碼計算x+y // -> 將計算結果賦值給變量result let result := add(x, y) // x + y // 使用mstore操作碼 // -> 將result變量的值存入內存 // -> 指定內存地址 0x0 mstore(0x0, result) // 將結果存入內存 // 從內存地址0x返回32字節 return(0x0, 32) }}

5、Solidity匯編中的變量定義與賦值

在Yul中,使用let關鍵字定義變量。使用:=操作符給變量賦值:

assembly { let x := 2}

如果沒有使用:=操作符給變量賦值,那么該變量自動初始化為0值:

assembly { let x // 自動初始化為 x = 0 x := 5 // x 現在的值是5}

你可以使用復雜的表達式為變量賦值,例如:

assembly { let x := 7 let y := add(x, 3) let z := add(keccak256(0x0, 0x20), div(slength, 32)) let n }

6、Solidity匯編中let指令的運行機制

在EVM的內部,let指令執行如下任務:

  • 創建一個新的堆棧槽位
  • 為變量保留該槽位
  • 當到達代碼塊結束時自動銷毀該槽位

因此,使用let指令在匯編代碼塊中定義的變量,在該代碼塊 外部是無法訪問的。

7、Solidity匯編中的注釋

在Yul匯編中注釋的寫法和Solidity一樣,可以使用單行注釋// 或多行注釋/* */。例如:

assembly { // single line comment /* Multi line comment */}

8、Solidity匯編中的字面量

在Solidity匯編中字面量的寫法與Solidity一致。不過,字符串字面量 最多可以包含32個字符。

assembly { let a := 0x123 // 16進制 let b := 42 // 10進制 let c := "hello world" // 字符串 let d := "very long string more than 32 bytes" // 超長字符串,錯誤!}

9、Solidity匯編中的塊和作用范圍

在Solidity匯編中,變量的作用范圍遵循標準規則。一個塊的范圍使用 一對大括號標識。

在下面的示例中,y和z僅在定義所在塊范圍內有效。因此y變量的作用 范圍是scope 1,z變量的作用范圍是scope 2。

assembly { let x := 3 // x在各處可見 // Scope 1 { let y := x // ok } // 到此處會銷毀y // Scope 2 { let z := y // Error } // 到此處會銷毀z}// DeclarationError: identifier not found// let z := y// ^

作用范圍的唯一例外是函數和for循環,我們將在下面解釋。

10、在Solidity匯編中使用函數的局部變量

在Solidity匯編中,只需要使用變量名就可以訪問局部變量, 無論該變量是定義在匯編塊中,還是Solidity代碼中,不過 變量必須是函數的局部變量:

function assembly_local_var_access() public pure { uint b = 5; assembly { // defined inside an assembly block let x := add(2, 3) let y := 10 z := add(x, y) } assembly { // defined outside an assembly block let x := add(2, 3) let y := mul(x, b) }}

11、在Solidity匯編中使用for循環

先看一下Solidity中循環的使用。下面的Solidity函數代碼中 計算變量的倍數n次,其中value和n是函數的參數:

function for_loop_solidity(uint n, uint value) public pure returns(uint) { for ( uint i = 0; i < n; i++ ) { value = 2 * value; } return value;}

等效的Solidity匯編代碼如下:

function for_loop_assembly(uint n, uint value) public pure returns (uint) { assembly { for { let i := 0 } lt(i, n) { i := add(i, 1) } { value := mul(2, value) } mstore(0x0, value) return(0x0, 32) } }

類似于其他開發語言中的for循環,在Solidity匯編中,for循環也包含 3個元素:

  • 初始化:let i := 0
  • 執行條件:lt(i, n) ,必須是函數風格表達式
  • 迭代后續步驟:add(i, 1)

注意:for循環中變量的作用范圍略有不同。在初始化部分定義的變量 在循環的其他部分都有效。

12、在Solidity匯編中使用while循環

在Solidity匯編中實際上是沒有while循環關鍵字的,但是可以使用 for循環實現同樣的功能:只要留空for循環的初始化部分和迭代后續步驟即可。

assembly { let x := 0 let i := 0 for { } lt(i, 0x100) { } { // 等價于:while(i < 0x100) x := add(x, mload(i)) i := add(i, 0x20) }}

13、在Solidity匯編中使用if語句

Solidity內聯匯編支持使用if語句來設置代碼執行的條件,但是 沒有其他語言中的else部分。

assembly { if slt(x, 0) { x := sub(0, x) } // Ok if eq(value, 0) revert(0, 0) // Error, 需要大括號}

if語句強制要求代碼塊使用大括號,即使需要保護的代碼只有一行, 也需要使用大括號。這和solidity不同。

如果需要在Solidity內聯匯編中檢查多種條件,可以考慮使用 switch語句。

14、在Solidity匯編中使用switch語句

EVM匯編中也有switch語句,它將一個表達式的值于多個常量 進行對比,并選擇相應的代碼分支來執行。switch語句支持 一個默認分支default,當表達式的值不匹配任何其他分支條件時,將 執行默認分支的代碼。

assembly { let x := 0 switch calldataload(4) case 0 { x := calldataload(0x24) } default { x := calldataload(0x44) } sstore(0, div(x, 2))}

switch語句有一些限制:

  • 分支列表不需要大括號,但是分支的代碼塊需要大括號
  • 所有的分支條件值必須:1)具有相同的類型 2)具有不同的值
  • 如果分支條件已經涵蓋所有可能的值,那么不允許再出現default條件
assembly { let x := 34 switch lt(x, 30) case true { // do something } case false { // do something els } default { // 不允許 } }

15、在Solidity匯編中使用函數

也可以在Solidity內聯匯編中定義底層函數。調用這些自定義的函數 和使用內置的操作碼一樣。

下面的匯編函數用來分配指定長度的內存,并返回內存指針pos:

assembly { function allocate(length) -> pos { pos := mload(0x40) mstore(0x40, add(pos, length)) } let free_memory_pointer := allocate(64)}

匯編函數的運行機制如下:

  • 從堆棧提取參數
  • 將結果壓入堆棧

和Solidity函數不同,不需要指定匯編函數的可見性,例如public或private, 因為匯編函數僅在定義所在的匯編代碼塊內有效。

16、Solidity匯編中的操作碼

EVM操作碼可以分為以下幾類:

  • 算數和比較操作
  • 位操作
  • 密碼學操作,目前僅包含keccak256
  • 環境操作,主要指與區塊鏈相關的全局信息,例如blockhash或coinbase收款賬號
  • 存儲、內存和棧操作
  • 交易與合約調用操作
  • 停機操作
  • 日志操作

詳細的操作碼可以查看Solidity文檔。


原文鏈接:http://blog.hubwiz.com/2020/02/15/solidity-assembly-tutorial/

總結

以上是生活随笔為你收集整理的汇编 div_Solidity汇编开发简明教程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。