91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Solidity運行原理是什么

發布時間:2021-12-07 15:10:20 來源:億速云 閱讀:142 作者:iii 欄目:互聯網科技

本篇內容介紹了“Solidity運行原理是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

引  言

作為一門面向智能合約的語言,Solidity與其他經典語言既有差異也有相似之處。

一方面,服務于區塊鏈的屬性使其與其他語言存在差異。例如,合約的部署與調用均要經過區塊鏈網絡確認;執行成本需要被嚴格控制,以防止惡意代碼消耗節點資源。

另一方面,身為編程語言,Solidity的實現并未脫離經典語言,比如Solidity中包含類似棧、堆的設計,采用棧式虛擬機來進行字節碼處理。

Solidity的生命周期

與其他語言一樣,Solidity的代碼生命周期離不開編譯、部署、執行、銷毀這四個階段。下圖整理展現了Solidity程序的完整生命周期: Solidity運行原理是什么

經編譯后,Solidity文件會生成字節碼。這是一種類似jvm字節碼的代碼。部署時,字節碼與構造參數會被構建成交易,這筆交易會被打包到區塊中,經由網絡共識過程,最后在各區塊鏈節點上構建合約,并將合約地址返還用戶。

當用戶準備調用該合約上的函數時,調用請求同樣也會經歷交易、區塊、共識的過程,最終在各節點上由EVM虛擬機來執行。

下面是一個示例程序,我們通過remix探索它的生命周期。

pragma solidity ^0.4.25;

contract Demo{
    uint private _state;
    constructor(uint state){
        _state = state;
    }
    function set(uint state) public {
        _state = state;
    }
}
編譯

源代碼編譯完后,可以通過ByteCode按鈕得到它的二進制:

608060405234801561001057600080fd5b506040516020806100ed83398101806040528101908080519060200190929190

還可以得到對應的字節碼(OpCode):

PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP1 PUSH2 0xED DUP4 CODECOPY DUP2 ADD DUP1 PUSH1 0x40 MSTORE DUP2 ADD SWAP1 DUP1 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP PUSH1 0xA4 DUP1 PUSH2 0x49 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x3F JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x60FE47B1 EQ PUSH1 0x44 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x4F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x6C PUSH1 0x4 DUP1 CALLDATASIZE SUB DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0x6E JUMP JUMPDEST STOP JUMPDEST DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0x4e 0xd9 MOD DIFFICULTY 0x4c 0xc4 0xc9 0xaa 0xbd XOR EXTCODECOPY MSTORE 0xb2 0xd4 DUP7 0xdf 0xc5 0xde 0xa9 DUP1 SLT PUSH1 0xC3 CALLDATACOPY XOR 0x5d 0xad KECCAK256 0xe1 0x1f DUP2 SHL STOP 0x29

其中下述指令集為set函數對應的代碼,后面會解釋set函數如何運行。

JUMPDEST DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP JUMP STOP
部署

編譯完后,即可在remix上對代碼進行部署,構造參數傳入0x123: Solidity運行原理是什么

部署成功后,可得到一條交易回執: Solidity運行原理是什么

點開input,可以看到具體的交易輸入數據: Solidity運行原理是什么

上面這段數據中,標黃的部分正好是前文中的合約二進制;而標紫的部分,則對應了傳入的構造參數0x123。

這些都表明,合約部署以交易作為介質。結合區塊鏈交易知識,我們可以還原出整個部署過程:

  • 客戶端將部署請求(合約二進制,構造參數)作為交易的輸入數據,以此構造出一筆交易

  • 交易經過rlp編碼,然后由發送者進行私鑰簽名

  • 已簽名的交易被推送到區塊鏈上的節點

  • 區塊鏈節點驗證交易后,存入交易池

  • 輪到該節點出塊時,打包交易構建區塊,廣播給其他節點

  • 其他節點驗證區塊并取得共識。不同區塊鏈可能采用不同共識算法,FISCO BCOS中采用PBFT取得共識,這要求經歷三階段提交(pre-prepare,prepare, commit)

  • 節點執行交易,結果就是智能合約Demo被創建,狀態字段_state的存儲空間被分配,并被初始化為0x123

執行

根據是否帶有修飾符view,我們可將函數分為兩類:調用與交易。由于在編譯期就確定了調用不會引起合約狀態的變更,故對于這類函數調用,節點直接提供查詢即可,無需與其他區塊鏈節點確認。而由于交易可能引起狀態變更,故會在網絡間確認。

下面將以用戶調用了set(0x10)為假設,看看具體的運行過程。

首先,函數set沒有配置view/pure修飾符,這意味著其可能更改合約狀態。所以這個調用信息會被放入一筆交易,經由交易編碼、交易簽名、交易推送、交易池緩存、打包出塊、網絡共識等過程,最終被交由各節點的EVM執行。

在EVM中,由SSTORE字節碼將參數0xa存儲到合約字段_state中。該字節碼先從棧上拿到狀態字段_state的地址與新值0xa,隨后完成實際存儲。

下圖展示了運行過程: Solidity運行原理是什么

這里僅粗略介紹了set(0xa)是如何運行,下節將進一步展開介紹EVM的工作機制以及數據存儲機制。

銷毀

由于合約上鏈后就無法篡改,所以合約生命可持續到底層區塊鏈被徹底關停。若要手動銷毀合約,可通過字節碼selfdestruct。銷毀合約也需要進行交易確認,在此不多作贅述。

EVM原理

在前文中,我們介紹了Solidity程序的運行原理。經過交易確認后,最終由EVM執行字節碼。對EVM,上文只是一筆帶過,這一節將具體介紹其工作機制。

運行原理

EVM是棧式虛擬機,其核心特征就是所有操作數都會被存儲在棧上。下面我們將通過一段簡單的Solidity語句代碼看看其運行原理:

uint a = 1;
uint b = 2;
uint c = a + b;

這段代碼經過編譯后,得到的字節碼如下:

PUSH1 0x1
PUSH1 0x2
ADD

為了讀者更好了解其概念,這里精簡為上述3條語句,但實際的字節碼可能更復雜,且會摻雜SWAP和DUP之類的語句。

我們可以看到,在上述代碼中,包含兩個指令:PUSH1和ADD,它們的含義如下:

  • PUSH1:將數據壓入棧頂。

  • ADD:POP兩個棧頂元素,將它們相加,并壓回棧頂。

這里用半動畫的方式解釋其執行過程。下圖中,sp表示棧頂指針,pc表示程序計數器。當執行完push2 0x1后,pc和sp均往下移: Solidity運行原理是什么

類似地,執行push2 0x2后,pc和sp狀態如下: Solidity運行原理是什么

最后,當add執行完后,棧頂的兩個操作數都被彈出作為add指令的輸入,兩者的和則會被壓入棧: Solidity運行原理是什么

存儲探究

在開發過程中,我們常會遇到令人迷惑的memory修飾符;閱讀開源代碼時,也會看到各種直接針對內存進行的assembly操作。不了解存儲機制的開發者遇到這些情況就會一頭霧水,所以,這節將探究EVM的存儲原理。

在前文《智能合約編寫之Solidity的基礎特性》中我們介紹過,一段Solidity代碼,通常會涉及到局部變量、合約狀態變量。

而這些變量的存儲方式存在差別,下面代碼表明了變量與存儲方式之間的關系。

contract Demo{
    //狀態存儲
    uint private _state;

    function set(uint state) public {
        //棧存儲
        uint i = 0;
        //內存存儲
        string memory str = "aaa";
    }
}

棧用于存儲字節碼指令的操作數。在Solidity中,局部變量若是整型、定長字節數組等類型,就會隨著指令的運行入棧、出棧。

例如,在下面這條簡單的語句中,變量值1會被讀出,通過PUSH操作壓入棧頂:

uint i = 1;

對于這類變量,無法強行改變它們的存儲方式,如果在它們之前放置memory修飾符,編譯會報錯。

內存

內存類似java中的堆,它用于儲存"對象"。在Solidity編程中,如果一個局部變量屬于變長字節數組、字符串、結構體等類型,其通常會被memory修飾符修飾,以表明存儲在內存中。

本節中,我們將以字符串為例,分析內存如何存儲這些對象。

1. 對象存儲結構

下面將用assembly語句對復雜對象的存儲方式進行分析。

assembly語句用于調用字節碼操作。mload指令將被用于對這些字節碼進行調用。mload(p)表示從地址p讀取32字節的數據。開發者可將對象變量看作指針直接傳入mload。

在下面代碼中,經過mload調用,data變量保存了字符串str在內存中的前32字節。

string memory str = "aaa";
bytes32 data;
assembly{
    data := mload(str)
}

掌握mload,即可用此分析string變量是如何存儲的。下面的代碼將揭示字符串數據的存儲方式:

function strStorage() public view returns(bytes32, bytes32){
    string memory str = "你好";
    bytes32 data;
    bytes32 data2;
    assembly{
        data := mload(str)
        data2 := mload(add(str, 0x20))
    }   
    return (data, data2);
}

data變量表示str的0~31字節,data2表示str的32~63字節。運行strStorage函數的結果如下:

0: bytes32: 0x0000000000000000000000000000000000000000000000000000000000000006
1: bytes32: 0xe4bda0e5a5bd0000000000000000000000000000000000000000000000000000

可以看到,第一個數據字得到的值為6,正好是字符串"你好"經UTF-8編碼后的字節數。第二個數據字則保存的是"你好"本身的UTF-8編碼。

熟練掌握了字符串的存儲格式之后,我們就可以運用assembly修改、拷貝、拼接字符串。讀者可搜索Solidity的字符串庫,了解如何實現string的concat。

2. 內存分配方式

既然內存用于存儲對象,就必然涉及到內存分配方式。

memory的分配方式非常簡單,就是順序分配。下面我們將分配兩個對象,并查看它們的地址:

function memAlloc() public view returns(bytes32, bytes32){
    string memory str = "aaa";
    string memory str2 = "bbb";
    bytes32 p1;
    bytes32 p2;
    assembly{
        p1 := str
        p2 := str2
    }   
    return (p1, p2);
}

運行此函數后,返回結果將包含兩個數據字:

0: bytes32: 0x0000000000000000000000000000000000000000000000000000000000000080
1: bytes32: 0x00000000000000000000000000000000000000000000000000000000000000c0

這說明,第一個字符串str1的起始地址是0x80,第二個字符串str2的起始地址是0xc0,之間64字節,正好是str1本身占據的空間。此時的內存布局如下,其中一格表示32字節(一個數據字,EVM采用32字節作為一個數據字,而非4字節): Solidity運行原理是什么

  • 0x40~0x60:空閑指針,保存可用地址,本例中是0x100,說明新的對象將從0x100處分配。可以用mload(0x40)獲取到新對象的分配地址。

  • 0x80~0xc0:對象分配的起始地址。這里分配了字符串aaa

  • 0xc0~0x100:分配了字符串bbb

  • 0x100~...:因為是順序分配,新的對象將會分配到這里。

狀態存儲

顧名思義,狀態存儲用于存儲合約的狀態字段。

從模型而言,存儲由多個32字節的存儲槽構成。在前文中,我們介紹了Demo合約的set函數,里面0x0表示的是狀態變量_state的存儲槽。所有固定長度變量會依序放到這組存儲槽中。

對于mapping和數組,存儲會更復雜,其自身會占據1槽,所包含數據則會按相應規則占據其他槽,比如mapping中,數據項的存儲槽位由鍵值k、mapping自身槽位p經keccak計算得來。

從實現而言,不同的鏈可能采用不同實現,比較經典的是以太坊所采用的MPT樹。由于MPT樹性能、擴展性等問題,FISCO BCOS放棄了這一結構,而采用了分布式存儲,通過rocksdb或mysql來存儲狀態數據,使存儲的性能、可擴展性得到提高。

“Solidity運行原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宁城县| 裕民县| 喜德县| 云梦县| 乡宁县| 洞头县| 宣威市| 西华县| 甘德县| 唐河县| 彰化县| 普宁市| 通州区| 泰宁县| 富民县| 马边| 永定县| 巫溪县| 西青区| 丰台区| 金湖县| 栖霞市| 莲花县| 安庆市| 长春市| 布尔津县| 瓦房店市| 天镇县| 涞源县| 甘泉县| 巫溪县| 黄浦区| 乌兰浩特市| 靖远县| 东乌珠穆沁旗| 土默特左旗| 龙江县| 尖扎县| 柘城县| 赞皇县| 郸城县|