已知區塊鏈的連接方式為 Block-0(Genesis Block) --> Block-1 --> Block-2 ... Block-N 之間接連的 hash 下去,而智能合約從建立到運作的過程為:
-
撰寫智能合約
-
編譯智能合約後程式碼被轉為 bytes code
-
這些 bytes code 會轉為交易(transaction),並且被打包到區塊中
-
此智能合約被打包到該區塊之後,整個區塊會對內容進行 hash(因此我們無法改變這筆交易,也無法改變已經佈署的合約程式碼,因為這樣做會把整個區塊鏈的 hash 破壞掉)
-
由第四點可知我們無法真正地刪掉智能合約,但能夠透過「狀態(state)改變」來編輯智能合約或其他資料的狀態
智能合約的生命週期(Smart Contract Life-Cycle)
-
開始
-
編譯
-
送出交易
-
佈署
-
sendTransaction({from:..., to:empty. data:"0x123"});
-
智能合約會擁有並等價於它自己的地址
-
-
-
運作
-
互動
-
sendTransaction({from: smartConractAddress, . data:"0x123", value: 100000000});
-
-
與其進行交易或函數互動(也是交易的一種)
-
-
暫停或摧毀
-
Selfdestruct(...)
-
呼叫
selfdestruct(beneficiaryAddress)
-
此函數被呼叫並執行之後的區塊,這個智能合約將不復使用
-
-
從狀態層面刪除智能合約
-
編譯到佈署
編譯
當我們把 Solidity 的程式碼編譯之後會產生兩個檔案。
-
ABI (application binary interface)
-
bytecode
佈署
EVM 只認識 bytecode,不認識 .sol
檔,就像是我們的電腦並不認識 .cpp
檔和 .py
檔中的程式碼,只認識由 0/1 組成的機器語言。
當我們佈署智能合約到以太坊網路時需要什麼:
-
bytecode
-
一個有足夠 Ether 的 Ethereum address
-
一個可以用來簽核交易的錢包
-
一個可以用來創建交易或執行動作的工具
從 Low-Level 的角度看函式操作
一、互動
-
我們可以透過 "data" field 來 Low Level 送出交易
-
而客戶端可以使用 ABI Array 來決定如何進行互動
-
其中 ABI Array 包含了所有合約中可供使用的函式、參數、回傳值等內容
二、函式簽章(Function Signature)
-
當我們要和「不需要」任何參數的 "Getter" Function 互動時
-
function myUint()
-
取函式簽章進行 keccak256 hash後的前四個 bytes
-
bytes4(keccak256("myUint()"));
-
-
-
當我們要和「需要」參數的 "Getter" Function 互動時
-
function someFunction(uint _myUint1, address _someAddr)
-
取函式簽章進行 keccak256 hash 後的前四個 bytes
-
bytes4(keccak256("someFunction(uint256,address)"))
-
-
三、除錯(Debugging)
-
Step by step
-
是以交易為底的,通常在交易發生後,而非發生時
-
呈現每段操作碼(Opcodes)、Stack、Memory 的 Gas Costs
-
Debugger(Active Modules)
-
PUSH4
: push 4 bytes 到堆疊區記憶體-
PUSH4
492bfa18
<-->myString()
-
-
EQ
這個操作碼相當於「等於」
-
ABI
ABI(Application Binary Interface)
如果我們理解 API 就很容易了解 ABI。簡單來說 API(應用程式介面)就是程式跟程式之間互動的接口,此接口包含了提供外界存取所需要的 functions、variables 等。
ABI 也是程式之間互動的接口,但是程式是編譯後的 binary code。ABI 描述的是如何 decode/encode 程式之間傳遞的 binary 信息,從另一個角度看,它就只是一個告訴客戶端怎麼和合約互動 JSON 檔。
ABI 就像是以太坊生態系統中與合約進行互動的標準方式,無論是從區塊鏈外部還是從合約到合約的互動。我們需要一個固定模式並根據其類型進行編碼。其中包含可供外部使用或聆聽的 Function 和 Event。
在我們和合約互動時,需要傳遞一個 transaction 到智能合約的地址,此時以太坊的節點會在觀察完 ABI 之後會選擇要執行哪個函數或事件。
如果我們編譯以下程式碼:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
contract test {string public a;
function modify_a(string memory val) public {
a = val;
}
}
產生出來的 bytecode:
{
"functionDebugData": {},
"generatedSources": [],
"linkReferences": {},
"object":
"opcodes":"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ... ",
"sourceMap": "26:117:0:-:0;;;;;;;;;;;;;;;;;;;"}
而 ABI 會以 array 包含數個 JSON 的形式表示:
[
{
"inputs": [],
"name": "a",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "val",
"type": "string"
}
],
"name": "modify_a",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
ABI 涵蓋了各種細節,如:
-
包含資料類型的大小和類別,包含是否為 Const,唯讀與否、stateMutability(view/pure)
-
函式內容:
-
函式名稱
-
參數
-
回傳值(internalType、name、type)
-
記憶體配置
-
是否宣告為
payable
-
-
同時
internal
函數不會存在 ABI 之中 -
FunctionHashes:在合約被編譯之後,函數名稱會被哈希,例如:
"492bfa18": "myString()"
-
我們可以透過這個哈希之後的函數名稱以較底層的方式與區塊鏈上的合約互動
-
EVM
以太坊虛擬機(Ethereum Virtual Machine,EVM)是在以太坊中,用來處理智能合約的佈署以及執行的一個虛擬機。是使得以太坊和比特幣區分的最大關鍵,也就是說 EVM 使得以太坊區塊鏈從「分散式帳本」昇華成「分散式狀態機」。
我們在過往提到過非常多次「狀態」,包含各式各樣的記憶體更改,或者是狀態變數的宣告與使用等。在EVM這樣大型的狀態機的資料結構下,儲存了所有原先區塊鏈會儲存的「帳戶、交易」之外,還有「機器狀態」(Machine State)
虛擬機就是透過軟體模擬的計算機系統,進入這個虛擬系統之後,我們可以在裡面進行各種獨立地日常操作,包含獨立安裝軟體和保存數據等等。這樣一來,我們就可以在虛擬機中做任何想做的事情,不用擔心各種病毒和攻擊都不怕。即便遇到了也可以藉由重裝虛擬機來解決,並不會對真正的系統產生任何影響。
當以太坊鏈上發生轉帳交易的時候,以太坊虛擬機(EVM)會進行以下工作:
-
調取轉帳數值
-
分析合約指令
-
計算Gas的消耗(手續費)
-
確保發出轉賬的地址有足夠的Gas費
-
執行合約
-
實現轉帳到對應的地址
EVM 的功能:
-
安全性:EVM 是一個獨立的運作空間,使合約可以在內部獨立地運行。此外,設定 Gas 的總量限制使所有的程式碼都有一個終點,如果有惡意或意外的不當程式被執行時,因為有 Gas 的限制,可以讓不當程式停下來,進而保全以太坊不會停機。
-
穩定性:由於以太坊虛擬機部署在以太坊網絡的每個節點,整個以太坊網絡的數據由眾多節點共同維護和更新,這保持了整個區塊鏈數據的一致性,這同樣也使以太坊有極高的故障容錯性,保證零停機。
-
方便性:以太坊虛擬機可以做為獨立的程式碼運行環境。測試時不會占用到主鏈資源,也不受其他區塊鏈或網路的影響。
EVM 使用堆疊結構的記憶體架構,將所有記憶體內(in-memory)的值存儲在堆疊區裡。
由於橢圓加密曲線的相關操作,具有多個以 256 位元為單位,可「以址查詢」的資料結構:
所有 EVM 內部的 Operations 都已經被定義在黃皮書中。像是:XOR
, AND
, ADD
, SUB
。同時EVM 也包含各種區塊鏈相關的堆疊區operations,例如:ADDRESS
, BALANCE
, BLOCKHASH
等。