如何看合約在以太坊網路上的互動?合約帳戶裡有哪些欄位?合約中資料的儲存和資料結構大概長什麼樣子?
一般講帳戶 account 有兩種。一種是合約帳戶 contract account。一種是 EOA 外部帳戶。合約帳戶 contract account 內包含 balance 餘額、nonce 計數器、bytecode、storage 儲存空間(storage hash root)。有這四個資料欄位,可以稍微記一下。其中 storage hash root 是 storage tree 的 tree root。Storage tree 是把 key/value 資料 hash 起來所形成的 merkle tree。
EOA 外部帳戶 externally owned account (EOA)。EOA 的 A 就是 account。EOA 帳戶裡面只有 balance 餘額、nonce 計數器。這兩個資料欄位也可記。尤其 nonce 對 EOA 有意義。反觀 bytecode 和 storage 對 EOA 沒有意義。
實際上 bytecode 和 storage 也有在 EOA 裡面,但裡面沒有資料所以也就沒有意義。想來想去其實有點複雜。EOA 的這個 nonce 欄位的設計,主要是避免 replay attack 重放攻擊,或稱回放攻擊。其重點在於一個 transaction 綁定一個 nonce,讓這個 transaction 再重用一次的時候,就會失效。
JavaScript 裡面有方便記載資料的資料結構,常見的 JavaScript 資料結構,像是 object 或是 array。在以太坊智慧合約 Solidity 程式碼相對來看的話,JavaScript 和 Solidity 兩者確實有相似之處。例如,Solidity 裡面有 mapping 映射和 array 可以使用。
Solidity 裡的 mapping 映射和 JavaScript 的 object 比較像。
mapping(address account => uint256) private _balances;
例如,Solidity 中的 mapping 映射可以有 key 對應到 value。比如說查詢餘額 _balances 變數,就會使用到 mapping 映射這個資料結構,把一個地址對到一個數字。像上面這段程式碼,就是 mapping 這個 account 到 uint256 的餘額數字。
JavaScipt 的 object,也是 key 對應 value。兩者以程式語法的用法類似。
const obj = {
foo: 1,
};
JavaScript 例子來看,變數是 obj,有 foo 這個 key 對應到 1 這個 value。
關鍵的不同之處在於,JavaScript 使用這些資料結構的時候,資料都是儲存在電腦的 memory 記憶體中。反觀在 Solidity 中,可選擇暫時把資料存在電腦的 memory,或者可選擇永久的存在以太坊 p2p 網路的 storage 上。永久的存在以太坊 p2p 網路 storage 上的意思是,每個以太坊 p2p 網路的節點會花他的硬碟空間儲存這些資料。在以太坊中,storage 裡的這些 key/value 會被一個 merkle tree commit 起來。也就是說,你的合約帳戶會有 storage tree。
以太坊為什麼會需要有一個 Merkle Patricia Trees 的設計?以太坊的節點們,長得好好的,為何還需要另外設計一個 Merkle Patricia Trees。節點本來可以單純地只記餘額有多少,就這麼一件簡單的事,現在要變得很複雜,storage 還需要多一個 merkle tree,而這個 merkle tree 包括 storage hash root 和 storage tree。
這個設計,當初是為了 light client 輕客戶端,像是手機或是平板或是輕錢包(light wallet)。輕客戶端(light client)之所以叫 light 輕,是因為 light client 並不儲存所有的帳戶和所有的 storage。
V 加拿大先輩這篇 2015 年的文章「Merkling in Ethereum」,介紹以太坊中的雜湊樹設計,有幾段詳細介紹了以太坊為什麼要有 state tree 的設計:
https://blog.ethereum.org/2015/11/15/merkling-in-ethereum