天天看點

第一章:以太坊智能合約的學習第一個智能合約:區塊鍊基礎概念

第一章:以太坊的學習

  • 第一個智能合約:
  • 區塊鍊基礎概念
    • 交易/事務(Transaction):
    • 區塊:
    • 共識協定:工作量證明(PoW)
    • 權益證明(PoS)
    • 以太坊虛拟機(EVM)
      • 編譯合約
    • 賬戶
      • 鑰匙檔案
      • 賬戶狀态
    • 以太坊錢包
    • 交易
    • 消息調用
    • 費用( gas)
      • 思考費用的作用
    • 以太坊網絡
      • 主網(Mainnet)
      • 測試網絡(Testnet)
      • 私有網絡、開發者模式
      • 模拟環境網絡
    • 存儲、記憶體和能
    • 委托調用和庫
    • 日志
    • 自毀
    • 以太坊路線圖

學習内容:第一個隻能合約與區塊鍊知識

第一個智能合約:

首先我們知道以太坊的編譯智能合約的工具是solidity。

pragma solidity ^0.4.24;

contract Coin {
    // 關鍵字 public 讓狀态變量可以從外部讀取,它隻在建立合約時運作。 這個構造函數做了一件事, 就是儲存(外部)函數調用者的位址 msg.sender。
    address public minter;
    mapping (address => uint) public balances;
    
    // 定義了一個事件,用戶端可以根據事件變化做出反應
    event Sent(address from,address to,uint amount);
    
    // 構造函數,隻有在建立合約是運作一次
    constructor() public {
        minter = msg.sender;
    }
    
    // 挖礦方法,用來産生新的貨币,mint 是可以真正被使用者或其他合約調用的方法。 不過如果 mint 被非合約創 建者調用, 則什麼事也不會發生。
    function mint(address receiver, uint amount) public {
        if (msg.sender != minter) return;
        balances[receiver] += amount;
    }
    
    // 發送貨币,send 同樣是可以真正被使用者或其他合約調用的方法。send 可以被任何人(前 提是擁有這些币〉調用,用來向他人發送币 。
    function send(address receiver, uint amount) public {
        if (balances[msg.sender] < amount) return;
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
}
           

第一行代碼是告訴編譯器如何編譯這段代碼,也就是導入solidity版本包,不同的版本包可以一些代碼變量無法使用貨相容。一般在第三位的變動較小。

pragma solidity ^0.4.24;

address public minter;

關鍵宇 public 用來表示狀态變量的可見性,這一點和其他語言如 Java 和 C++是類似的,不同的是這行代碼聲明了一個可以被公開通路的位址類型(address)的狀态變量,Solid町的編譯器會自動為 public 狀态變量生成一個通路函數。

注意,我們沒有必要自己添加這個函數,如果我們添加了一個同名的函數, 編譯器生成的将不再生效。

這行代碼建立了另 一個 public 狀态變量 ,它的資料類型是更加複雜的 mapping (在本書第 4 章中會進一步講解〉,該類型儲存一個個鍵值對。 mapping 可以被看作一個哈希表,它會執行虛拟初始化,使所有可能存在的鍵都對應一 個全零的值。 不過和其他語言中的 mapping 不一樣的是, Solidity 中的 mapping 不能周遊通路(無法獲得所有鍵或值的清單),對于同樣的 public 狀态變量,編 譯器會為它生成一個通路函數,函數代碼如下:

function balances(address _account) public view returns (uint) {
	return balances[_account];
}
           

我們可以通過這個生成的通路函數來查詢某個賬戶的餘額。

event Sent(address from,address to,uint amount);

聲明了事件,

emit Sent(msg.sender, receiver, amount);

觸發事件

編譯solidity的網址:https://remix.ethereum.org

區塊鍊基礎概念

交易/事務(Transaction):

學習過資料庫的讀者,應該能夠了解”事務“的含義,如果對事務一詞 不是很了解,可以搜尋“資料庫事務”來學習。

舉個例子,想象一張表, 裡面列出了某個電子貨币所有賬戶的餘額。當從一個賬戶到另一個賬戶的轉賬請求發生時,這個資料庫的事務特性確定從一個 賬戶中減掉的金額會被加到另一個賬戶上。如果因為某種原因 , 往目标賬戶上增加金額的操作無法進行,那麼原賬戶中的金額也不會發生任何變化。 此外,當一個事務特性被應用到這個資料庫的時候,其他事務不能修改該 資料庫。 發送者(建立者)對一個交易進行密碼學簽名,可以非常直覺地了解 為該資料庫增加了通路保護。 在上面的電子貨币的例子中, mint()函數中的一 個簡單檢查就可以確定隻有持有賬戶密鑰的人才能從該賬戶向外轉賬。

區塊:

一個區塊就是若幹交易的集合,它會被标記上時間戳和前一個區塊的 Hash 辨別。區塊在經過哈希運算後生成一份工作量證明 , 進而驗證區塊中的交易。 有效的區塊經過全網絡的共識之後會被追加到區塊鍊中。 為什麼要這樣設計呢?因為在比特币及以太坊這樣的完全去中心化的區塊 鍊中,需要解決被稱為雙花攻擊 Cdouble-spend attack)的難題,即如果網絡中 存在兩筆互相沖突的交易,它們都想花光同一個賬戶中的錢時會發生什麼情況 呢?

實際上網絡會自動選擇一個交易序列,并打包到區塊中,然後在所有參與 節點中執行和分發(廣播)。 如果兩筆交易互相沖突,那麼最終被确認為後發生 的交易将被拒絕,不會被包含到區塊中。

這些塊按時間形成了一個線性序列,這正是區塊鍊這個詞的來源。區塊以 一定的時間間隔被添加到鍊上。對于以太坊,這個間隔大約是 17 秒。

作為“順序選擇機制”(也就是挖礦)的一部分,可能有時會發生區塊 被復原的情況,但通常僅在鍊的“末端”。 在末端增加的塊越多,其發生復原的 機率越小。是以交易被復原甚至從區塊鍊中抹除也是可能的,但交易發生後等 待的時間越長, 這種情況發生的機率就越小。

創世區塊( Genesis Block )是區塊鍊中的第一個區塊,其區塊序号是 0。 它 是區塊鍊中唯一一個不指向前一個區塊的區塊,因為沒有前一個區塊。 隻有 當網絡中的兩個節點有相同的創世區塊時,它們才會彼此配對。

共識協定:工作量證明(PoW)

目前以太坊使用工作量證明共識協定防止區塊鍊被篡改。 工作量證明系統 需要解決一個複雜的數學難題以建立新的區塊。 解決難題需要大量算力,這就 使建立區塊很困難。 每一個礦工都各自解決數學難題,其中 第一個解決難題的礦工是勝利者, 其得到的回報是 5 個以太币和該區塊中全部 交易的交易費。 區塊有一個 區塊頭(Header)和一系列交易。每一個區塊都存儲前一個區塊的哈希值,由 此建立一個相連的鍊。

那麼礦工是如何解決難題的?

礦工首先從所收到的廣播中收集新的未 挖出的交易,然後過濾掉不合法的交易。 合法的交易必須滿足正确地使用私鑰 簽名、賬戶有足夠的餘額進行交易等條件。

區塊頭包含前一個區塊的哈希值、 區塊序号、随機數(Nonce)、 目标值( Target)、 時間戳(Timestamp)、 難度值 (Difficulty)、礦工位址 (Address)等内容。 時間戳表示區塊初始時間 。 以太坊使用 Ethash 雜湊演算法, 目标值越低,發現随機數需要的時間越 多; 目标值越高,發現随機數需要的時間越少。

網絡中的任何節點都可以檢查區塊鍊是否合法,首先檢查交易在區塊鍊中 是否合法以及時間戳的驗證情況, 然後檢查區塊的目标值和随機數是否合法、 礦工是否得到合法的回報等。 如果網絡中的節點接收到兩個不同的合法區塊鍊, 那麼所有區塊的整體難度值較高的那個區塊鍊将被視為合法的區塊鍊。

權益證明(PoS)

以太坊最終會切換到使用權益證明,以解決工作量證明過于消耗資源的問 題。 權益證明的主要思路是: 作為驗證節點 , 首先必須擁有一定數量的 以太币, 根據以太币的數量和時間會産生用于下注驗證區塊的權益。隻有擁有權益的節 點才能有效驗證區塊,當所驗證的區塊被打包進鍊後,将獲得和權益成正比的 區塊獎勵。如果是驗證惡意或錯誤的區塊,那麼所下注的權益将被扣除。

以太坊虛拟機(EVM)

以太坊虛拟機 (EVM, Ethereum Virtual Machine)是以太坊中智能合約的運作環境。

編譯合約

在以太坊虛拟機上運作的是合約的宇節碼形式,需要在部署之前先對合約 進行編譯,可以選擇使用 Remix 或 sole 編譯器,之後章節會繼續講解。

賬戶

這裡的賬戶可 以簡單了解為銀行給我們開設的賬戶 ,**不過在以太坊中賬戶 不用申請,而是使用者根據需要在錢包中生成的。**轉賬行為是由一個賬戶發起, 把财産轉移到另一個賬戶 的過程 (實際上這個财産也是一個數字〉。 以太坊也是 類似的,同時在以太坊上賦予了賬戶更多的功能,實際上賬戶在以太坊中有很 重要的作用 。 以太坊中有兩類賬戶 。

  • 外部擁有賬戶 CEOA):該類賬戶和銀行賬戶很相似,不過它由公鑰/ 私鑰對控制,即由人控制,沒有關聯任何代碼
  • 合約賬戶 : 該類賬戶由存儲在賬戶中的代碼控制。

外部擁有賬戶(本書中有時會簡稱“外部賬戶”)和合約賬戶由同樣的位址 空間來表示。 外部賬戶的位址是由公鑰決定的,合約賬戶的位址是在建立該合 約時确定的(這個位址由合約建立者的位址和該位址發出過的交易數量 Nonce 計算得到)。

兩者的差別是?

一個外部賬戶 可以通過建立和用自己的私鑰對交易進行簽名,以發送消息給另一個外部賬戶 或合約賬戶 。 在兩個外部賬戶之間傳送的消息隻是簡單的價值轉移(類似于銀 行賬戶間轉賬)。

從外部賬戶到合約賬戶的消息會激活合約賬戶的代碼,允許它執行 各種動作。 比如轉移代币、寫入内部存儲、挖出一個新代币、執行一些運算、 建立一個新的合約等操作。

不像外部賬戶,合約賬戶不可以自己發起一筆交易 。 但合約賬戶可以在響 應交易時觸發另一筆交易 。 是以,在以太坊上任何動作都是由外部賬戶觸發的 交易所發起的(即動作的發起者必須是外部賬戶)。

鑰匙檔案

每個外部賬戶都是由一對鑰匙定義的,即一個私鑰和一個公鑰。 賬戶 以地 址為索引 (位址就像銀行賬号的那一串數字),取公鑰的最後 20 個宇節。 每對私鑰/ 位址都被編碼在一個鑰匙檔案裡。 鑰匙檔案是 JSON 文本檔案,可以用任何文本編輯器打開和浏覽。 鑰匙檔案的關鍵部分賬戶私鑰,通常用建立賬戶時設定的密碼進行加密。以太坊節點資料目錄的 keystore 子目錄下找到鑰匙檔案。

如果忘記密碼或者鑰匙檔案丢失。 就會丢失所有的以太币。沒有密碼不可能進入賬戶,也沒有“忘記密碼” 選項。 是以一定不要忘記密碼。

賬戶狀态

賬戶狀态有四個組成部分, 不論賬戶類型是什麼,都存在這四個組成部分。

  • Nonce:如果賬戶是一個外部擁有賬戶,則 Nonce 代表從此賬戶位址發送的交易序号: 如果賬戶是一個合約賬戶, 則 Nonce 代表此賬戶建立 的合約序号。
注意:以太坊有兩種 Nonce。 一種是賬戶 Nonce ,表示賬戶的交易數量;另一種是工作量證明 Nonce,用于計算滿足工作量證明的随機數。
  • Balance:此位址擁有的以太币餘額數量
  • storageRoot: Merkle Patricia 樹的根節點 Hash 值。 Merkle Patricia 樹會将此賬戶存儲内容的 Hash值進行編碼,預設是空值。
  • codeHash:此賬戶EVM代碼的Hash 值。 對于合約賬戶,就是被 Hash 的代碼并作為codeHash 儲存;對于外部擁有賬戶,codeHash I或是一個 空宇符串的 Hash 值。
以太坊最新的區塊總是儲存全局共享狀态,但每個區塊僅僅修改部分狀态。

以太坊錢包

以太坊錢包是以太坊賬戶的管理工具,如使用錢包可以生成賬戶、賬戶轉 賬等,,例如這筆交易可以是轉賬、 挖礦、智能合約的部署和合 約函數調用等。

Geth 是以太坊官方提供的用戶端(錢包〉,是開發智能合約常用的工具之一,它基于 Go 語言開發。 後面在開展學習。

交易

交易可以包含二進制數 據負載(Payload)和以太币(Ether)。

如果目标賬戶包含代碼,該代碼會執行, Payload 就是輸入資料。

如果目标賬戶是零賬戶(賬戶位址是 0),交易将建立一個新合約,新合約位址将由建立者的位址和該位址發出過的交易數量 (Nonce)計算得到。 這筆交易(建立合約交易)的 Payload 被當作 EVM 宇節碼執行,輸出作為合約代碼被永久存儲。這意味着為了建立一個合約,不需要向合約發送真正的合約代碼, 而是發送能夠傳回真正代碼的代碼。

一個合約也可以通過一個特殊的指令來建立其他合約(不是簡單地向零地 址發起調用 )。建立合約的調用跟普通的消息調用的差別在于, Payload 資料執 行的結果被存儲為合約代碼,調用者(建立者)在枝上可以得到新合約的位址。

消息調用

合約可以通過消息調用的方式來調用其他合約 , 或者發送以太币到非合約賬戶。消息調用和交易非常類似,它們都有一個源、 一個目标、資料負載、以太币、 gas (費用)和傳回資料。 事實上每筆交易都可以被認為是一個頂層的消 息調用,這個消息調用會依次産生更多的消息調用。

一個合約可以決定剩餘 gas 的配置設定。 比如在内部消息調用時使用多少 gas, 或者期望保留多少 gas。 如果在内部消息調用時發生了費用不足(out-of-gas) 異常(或者其他異常),合約将會得到通知(異常會“冒泡”到合約的調用核)。

當合約調用時,被調用的合約會擁有嶄新的記憶體, 以及能夠訪 問調用的 Payload (由被稱為“calldata" 的獨立區域所提供的資料)。 當調用執行結束後, 傳回資料将被存儲在調用者預先配置設定好的一塊記憶體中 。

調用層數被限制為 1024, 是以對于更加複雜的操作,我們應該使用循環而 不是遞歸。

費用( gas)

gas 的目的是限制執行交易所需的工作量,同時為執行支付費用 。 當 EVM 執行交易時, gas 将按照特定規則(這個規則在以太坊黃皮書中有詳細說明〉被逐漸消耗。

思考費用的作用

施加費用可以防止使用者超負荷使用網絡。 以太坊是一個圖靈完備的系統, 它允許有循環,并使以太坊受到停機問題的影響,這個問題讓你無法确定程式 是否無限制地運作。如果沒有費用的話, 惡意的執行者通過執行一個包含無限 循環的交易就可以很容易地讓網絡癱瘓。 是以,費用保護網絡不受蓄意攻擊。

gas price (gas 價格,以太币計)是由交易建立者設定的, 發送者賬戶需要 預付的交易費用(可以了解為給礦工的預算) =gas price × gas limit。 如果交易完成還有剩餘 gas,這些 gas 将被返還給發送者賬戶 。

無論執行到什麼位置,一旦 gas 被耗盡(比如降為負值),就會觸發一個費用不足異常,目前調用幀所做的所有狀态修改都将被還原。 前面介紹過交易(事務〉是一個原子操作,要麼所有操作全部成功,要麼操作失敗所有的執行都将被還原,不能出現中間狀态。

另外, gas 不僅僅是用來支付計算的費用,也是用來支付存儲的費用 。

以太坊網絡

主網(Mainnet)

以太坊網絡實時資料如區塊、散表難度、 gas 價格和 gas 花費等,可以在 https://ethstats.net 上查詢到。部署在主網上的智能合約,任何應用都可以 調用,相關交易資訊可以在 https://etherscan.io 上查詢到。

測試網絡(Testnet)

在主網上任何合約的執行都會消耗真實的以太币,不适合進行開發、調試和測試。是以以太坊專門提供了測試網絡,在測試網絡中可以很容易獲得免費的以太币 。測試網絡同樣是一個全球網絡。 目前以太坊公開的測試網絡有:

Ropsten、Rinkeby、Kovan

目前開發人員最常用的測試網絡是 Ropsten 和Rinkeby, 使用測試網絡依然有一個缺點,即需要花較長時間初始化節點。 在實際使用中,測試網絡更适合擔當如灰階釋出之類的角色。

私有網絡、開發者模式

我們可以自己建立私有鍊進行開發,通過上面提到的 Geth 就可以很容易建立一個屬于自己的測試網絡,在自己的測試網絡中,想挖多少以太币就挖多少,也省去了同步網絡的耗時。 或者直接使用 Geth 提供的開發者模式。相比私有鍊,在開發者網絡(模式)下,會自動配置設定一個有大量餘額的開發者賬戶給我們使用 。

模拟環境網絡

還有一種建立測試網絡的方法是使用 Ganache。 Ganache 在本地使用記憶體模 拟的一個以太坊環境,對于開發調試來說更友善、快捷。而且 Ganache 可以在 啟動時幫我們建立 10 個存有資金的測試賬戶 。 在進行合約開發時,可 以在 Ganache 中測試通過後 , 再部署到 Geth 節點中。

存儲、記憶體和能

每個賬戶都有一塊持久化存儲區域,被稱為存儲(storage)。key 和 value 的長度均為 256 位。在合約中不能周遊賬戶的存儲。存儲的讀寫操作開銷較大,修改操作開銷更大。一個合約隻能對它自己的存儲 進行讀寫。

開銷指的是消耗gas 的量。

第一個存儲區被稱為記憶體( memory)。可以以宇節為粒度尋址。第二個存儲區被稱為棧(stack) ,

委托調用和庫

存在一種特殊類型的消息調用,被稱為委托調用( delegatecall)。 它跟上面講到的消息調用幾乎完全一樣,不同的是它将目标位址的代碼加載到發起調用的合約上下文中來執行,并且 msg.sender 和 msg.value 不變。 例如: A 調用 B, B 委托調用 C,此時 msg.sender 為 A。 而如果是普通調用,則 msg.sender 為 B。

這意味着一個合約在運作時可以從另外一個位址動态加載代碼。 存儲目前 位址和餘額都指向發起調用的合約,隻有代碼是從被調用位址擷取的。這使得 Solidity 可以實作庫能力。

日志

以太坊允許日志跟蹤各種交易和資訊,日志是用一種特殊的可索引的資料結構來存儲的。 Solidity 用日志特性來實作事件。建立合約之後就無法通路日志資料了,但是可以從區塊鍊外高效地通路這些日志資料。

由于部分日志資料被存儲在布隆過濾器中,是以可以高效并安全地搜尋日志。是以那些沒有下載下傳整個區塊鍊的網絡節點(輕用戶端)也可以找到這些日志。

布隆過濾器( Bloom Filter )是由布隆( Burton Howard Bloom )在 1970 年提出的。 它實際上是由一個很長的二進制向量和一系列随機映射函數組成的, 可以用于檢索一個元素是否在一個集合中。 布隆過濾器的優點是空間效率和 查詢時間都遠遠超過一般的算法。

自毀

隻有當某個位址上的合約執行自毀(selfdestruct) 操作時,才會從區塊鍊上移除合約代碼。 合約位址上剩餘的以太币會被發送給指定的目标, 然後其(目前和未來的區塊上)存儲和代碼被移除。

其實合約的删除也依賴以太坊的各種用戶端程式實作(可以選擇是否删除舊合約)。 另外,歸檔節點可選擇無限期保留合約存儲和代碼。 目前,外部賬戶不能從狀态中移除。

以太坊路線圖

以太坊的發展分為四個階段。

前沿 (Frontier)

家園 (Homestead)

第二階段,以太坊的第一個正式版本

大都會(Metropolis)

第三階段 ,引入四大特性: zk-Snarks (基于“零知識證明”)、 PoS (Proof of Stake, 權益證明)早期實施、智能合約更靈活和穩定、使用抽象賬戶。大都會拆分為兩個階段實施(兩個硬分叉):拜占庭(Byzantium )和君士坦丁堡 (Constantinople)。

  • 拜占庭。拜占庭硬分叉在第 437 萬個區塊高度發生,後來引入了 zk-Snarks 及抽象賬戶等。
  • 君士坦丁堡。主要特性就是平滑處理掉所有由于 “拜占庭”所引發的問題,并引入 PoW 和 PoS 的混合鍊模式。

    甯靜(Serenity)

    第四階段,有兩大主要特性:深度抽象和 Casper (基于保證金的權益證明 算法)。

    重點關注:拜占庭與君士坦丁堡。

借鑒書籍:精通以太坊智能合約開發
第二章:以太坊智能合約内容、資料類型的認識https://blog.csdn.net/qq_44423523/article/details/108296586

繼續閱讀