第一章:以太坊的學習


第一章:以太坊的學習


學習內容:第一個只能合約與區塊鏈知識

第一個智能合約:

首先我們知道以太坊的編譯智能合約的工具是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 狀態變量生成一個訪問函數。

function minter() returns (address) {return minter;}

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

mapping (address => uint) public balances; 

這行代碼創建了另 一個 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.sendermsg.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 (基於保證金的權益證明 算法)。
    重點關注:拜占庭與君士坦丁堡。

借鑒書籍:精通以太坊智能合約開發


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM