---
# System prepended metadata

title: ETH

---

# ETH
## ETH概述
+ 區塊鏈(1.0) -> 比特幣 ， 區塊鏈(2.0) -> 以太坊
+ 以太坊針對BTC系統做出的改良
    + 出塊時間 : 約15秒
    + 全新的共識機制 : ghost 協議共識機制
    + mining puzzle 改為注重內存的大小稱為 memory hard mining puzzle
        + 限制了 ASIC 晶片的使用，稱為 ASIC resistance
    + 未來(目前是POW) : POW -> POS
        + POW (proof of work)工作量證明
            + 挖擴，就是證明你為了發布區塊前做了一堆工作
        + POS (proof of stake) 權益證明
            + 不挖礦，以幣的持有量來決定下一個區塊
    + 對智能合約(Smart contract)的支持
        + Bitcoin : decentralized currency
        + Ethereum : decentralized contract
            + 把合約以程式碼呈現，接著把這些程式碼放到區塊鏈上，依靠區塊鏈的不可竄改性，保證這個程式碼的正確運行。
            + 這樣做有甚麼好處?
                + 透過事先寫好的程式碼，保證所有人受限於程式碼上的運行，能夠每位使用者的公平性
                + 不需要依靠司法手段便能維持合約的運行
                + 誰也改不了這份程式碼，連作者都不行

## ETH 帳戶
+ UTXO
    + BTC使用的模式
    + 隱私保護比較好
    + 要一次轉出全部的錢，若否則剩餘的錢將會變成手續費，或者可以再生成一個地址，傳給另一個自己(有機會的話我在自己測試)
+ ETH : account-based ledger
    + 帳戶餘額由全節點儲存並維護(儲存於狀態樹 balance 域)
    + 比較好的防禦 double spending attack
        + 你要花兩次就扣兩次R
    + 但是會遭遇 **replay attack**(重放攻擊)
        + A -> B(10ETH)
        + 然後(惡意節點) B 又重新廣播一次該筆交易
    + 以太坊解決 replay attack 的方法
        + 紀錄交易次數(nonce)
        + 轉帳時交易次數要包含在交易內容裡
        + ![](https://i.imgur.com/FJv9Rua.png)
        + 全節點會維護這個 nonce 的數量，每次收到從這個地址發布的交易，nonce值都會+1
        + 如果交易被重放，就驗證當前的 nonce 值和廣播的交易的 nonce 值，例如原本 nonce = 20，由A發布一筆交易後 nonce 為 21 ，replay 之後會 A 的 nonce 為21，大於等於目前的 nonce 值，表示該筆交易已經執行過了。
+ ETH有兩類帳戶
    + externally owned account
        + balance
        + nonce(計數器)
    + smart contract account
        + 不能主動發起交易
        + nonce(計數器)
            + 因為合約也能呼叫外部合約
        + code
        + storage
            + 創建合約後會返回地址，調用合約後 storage 就會有改變
        + balance
    + 智能合約常被用來做為金融衍伸品

## ETH 狀態樹
+ account-based ledger
    + address(帳戶地址) 映射 state(帳戶狀態)
    + address : 160 bits(20個字節)(40個16進位的數)
    + 為甚麼不用 Merkle tree
        + 以太坊紀錄的是全部帳戶的狀態，包括餘額和交易次數，若每次出塊都要維護一棵新的 Merkle tree 代價太大。
    + 為甚麼不用 sorted Merkle tree
        + 插入新的帳戶的代價也太大，每有一個新的帳戶大半棵樹就要重構，太蠢了
+ 以太坊使用的數據結構 -> **MPT**
    + **trie** (取自 re**trie**val)
        + 字典樹、前綴樹
        + ![](https://i.imgur.com/sYwN3Sv.png)
        + 分支數目取決於 Key 值裡每個元素的取值範圍
        + 單詞結束後需要標記結束標誌符
        + 查找效率取決於 Key 的長度
        + **以太坊的 trie**
            + 每個節點最多可能有 17 個分支(branching factor) (地址為 16 進位數字 0~f + 結束標誌符)
            + Key 值都一樣長，都是 40 位
            + 比特幣和以太坊的地址是不共用的，長度不一樣，
                + 以太坊地址
                    + 公鑰取 Hash 前面不要，只要後面的 40 位
            + 使用 trie 的話只要地址不一樣就不會產生碰撞
            + 不論單詞的插入順序為何，得到的樹狀結構唯一
            + 更新的局部性很好，要更新某個 Key 只需要遍歷那個 Key 不用遍歷整棵樹
            + 但儲存方式很浪費，一個節點只有一個子節點於是產生了 Patricia tree
        + 以太坊的 **Patricia tree** 或稱 **Patricia trie**
            + 路徑壓縮的前綴樹
            + ![](https://i.imgur.com/aKfga1V.png)
            + 樹的高度明顯縮短，訪問效率提升
            + 但若新增單詞，原路徑可能要重構
            + 如果單詞少，數量長，並且彼此間幾乎沒有相似，使用 Patricia tree 效率相較於 trie 高出許多
                + 例如由
                + misunderstanding
                + decentralization
                + disintermediation
                + 組成之前綴樹
                + ![](https://i.imgur.com/f0Iqo46.png)
            + 以太坊的帳戶大小為 2^160 ，若不使用壓縮前綴將會極度稀疏。
        + 以太坊的 **MPT (Merkle Patricia tree)**
            + 把一般的 pointer 換成 hash pointer
            + 所有的帳戶依靠壓縮組成 Patricia tree 然後把 pointer 換成 hash pointer，可以計算出 root hash，一樣寫在 block header 裡面
            + 以太坊中有 3 棵樹，所以有 3 個 root hash
                + 狀態樹
                + 交易樹
                + 收據樹
            + **root hash**
                + 防止竄改，保證每個帳戶狀態不被竄改
                + Merkle proof，證明帳戶有多少餘額，把自己的帳戶資料作為 hash 向上一路至 root hash
                + 能夠證明某個節點不存在，只要將該分支當成 Merkle proof 向上搜尋 root hash 即可
        + 以太坊使用的其實是 **Modified MPT**
            + ![](https://i.imgur.com/gn40621.png)
                + 右上角 4 個帳戶，為簡單起見帳戶地址較短，並且帳戶狀態只顯示餘額
                + **Extension node**
                    + 該部位出現壓縮的節點
                    + nibble : 16 進位數字的意思，1 個 nibble 就表示一個 16 進制數字
                + **Branch node**
                    + 分支節點
                + **Leaf node**
                    + 葉節點
                    + 最後(一/幾)位
                + 點跟點連接即表示，該節點內儲存著下一個點的 Hash 值
        + 每次發布區塊後，這些狀態樹節點的值會發生變化，這些改變並不是儲存在原區塊內的，而是在下一個區塊
            + ![](https://i.imgur.com/6mXIdzC.png)
                + 雖然每個區塊都有狀態樹，但其實是共享的
                    + 例如右邊主要都是指向左邊的節點
                    + 只有發生改變的節點要新建分支
                    + 以上圖的例子來看，帳戶的合約改變了，餘額改變了，但 code 沒變
                    + 每一個新的區塊都要建立一棵新的 MPT，但只會創建被更改的內容，而不是全部的 MPT
                + 為甚麼只記錄改變的部分
                    + 因為以太坊的出塊時間約為 15 秒，一旦出現分岔需要回穩(roll back)，歷史紀錄就可以用來回穩(roll back)
                    + 因為以太坊有智能合約，如果不保存之前的狀態，當交易執行完後想要推算之前的狀態，基本上是不可能的
        + **block header**
            + ParentHash
                + 前一個 block header 的 hash 值
            + UncleHash
            + CoinBase
                + 礦工的地址
            + Root
                + 狀態樹的 root hash
            + TxHash
                + 交易樹的 root hash
            + ReceiptHash
                + 收據樹的 root hash
            + Bloom
                + 跟收據樹有關，提供一種高效的符合條件的查詢結果
            + Diffculty
                + 挖礦難度
            + Number
            + GasLimit
                + 和汽油費相關，因為操作智能合約需要耗費手續費
            + GasUsed
            + Time
                + 大致產生區塊的時間
            + Extra
            + MixDigest
                + 跟挖礦過程有關，從 Nonce 經過計算得出的 hash 值
            + Nonce
                + 挖礦猜的隨機數
        + **block struct**
            + header 
                + 指向 block header 的 hash pointer
            + uncles
                + 指向 uncle block 的 hash pointer
            + transcations
                + 區塊內的交易列表
        + **extblock struct(external block)**
            + Header
            + Txs
            + Uncles

## ETH 交易樹和收據樹
+ **交易樹**
    + MPT
    + Merkle tree 類似比特幣
    + 只收集當前發布區塊內的交易
    + 每個區塊獨立，不共享節點
    + 能提供 Merkle proof
+ **收據樹**
    + MPT
    + 每筆交易都會產生一個收據
    + 因為智能合約執行十分複雜，依靠收據樹有利於查詢結果
    + 只收集當前發布區塊內的交易
    + 每個區塊獨立，不共享節點
    + 能提供 Merkle proof
+ **Bloom filter**
    + 將大資料計算成一個小的向量
    + 把資料取 Hash 後存入向量內
    + 所有元素處理完後，得到的向量便是這個集合的摘要(digest)
    + ![](https://i.imgur.com/6RBllrM.png)
    + 例如 : 我們想要知道元素 d 是否存在上圖的集合之中，但我們無法保存整個集合
        + 對 d 取 Hash ，若映射至 0 的位子，則說明集合內不包含 d
        + 但若和 a 映射至同樣區塊 -> (1) 哈希碰撞 (2) d == a
    + 使用 Bloom filter 可能出現 False positive(不在裡面可能會說在裡面，在裡面也會說在裡面，但絕對不可能在裡面說不在裡面)
    + 如果要刪除元素 ?
        + 不支持刪除操作
+ 每筆交易會產生一個收據，收據內就儲存一個 Bloom filter，紀錄交易類型、地址等訊息
    + **查找交易?**
        + 先查找哪個 block header裡的 Bloom filter 裡面有我要的交易類型
        + 如果有需要的 Bloom filter 的話在往下細找
        + 也可能都沒有 -> False positive
        + 可以快速過濾掉無關的區塊
+ 為何狀態樹不保存最近交易過的帳戶就好
    + 查找帳戶餘額不方便
    + 如果查找新帳戶還得一路查至創世塊，發現創世塊也沒有才能確定是新帳戶
+ 交易樹和收據樹的創造過程
    + ![](https://i.imgur.com/zmkyhxa.png)
    + 在 NewBlock 裡面創建交易樹和收據樹，並且取得他們的根哈希值
    + 若交易列表為空，則根哈希值為空根哈希值
    + 否則使用 DeriveSha() 取得交易列表的哈希值
    + 然後創建交易列表
    + 判斷收據列表是否為空，為空則根哈希值為空根哈希值
    + 若不為空則取根哈希值
    + 創建 Bloom filter(交易列表和收據列表長度是一樣的)
    + 先判斷 Uncle 區塊是否為空，是的話設定成空根哈希值
    + 否則使用 UncleHash 取得 Hash 通過循環創建 Uncle 數組
    + DeriveSha()
        + ![](https://i.imgur.com/fE1xhRA.png)
        + 創建一棵 trie
    + trie
        + ![](https://i.imgur.com/FS3wHal.png)
        + 一棵 MPT
    + 收據的結構
        + ![](https://i.imgur.com/uj4gK01.png)
        + Bloom 就是收據的 Bloom filter
    + Block Header的結構
        + ![](https://i.imgur.com/2feYXyF.png)
        + Bloom 就是整個區塊的 Bloom filter，每個收據的 Bloom filter 合併獲得
    + **CreateBloom()**
        + ![](https://i.imgur.com/jOX6MYN.png)
        + 對區塊的所有收據使用 LogsBloom 來生成 Bloom filter
        + 合併起來，得到整個區塊的 Bloom fliter
    + **LogsBloom()**
        + 生成每個收據的 Bloom filter
        + 首先對 Log 地址取 hash 後加到 Bloom filter 裡面
    + **bloom9()**
        + Bloom filter 中使用的 hash 函數
        + 把輸入映射到 digest 的 3 個位子
    + **BloomLookup()**
        + ![](https://i.imgur.com/NWRnWHS.png)
        + 把 Topic 轉換成 Bytes
        + 然後跟 Bloom filter 取 and 


## ETH GHOST
+ **共識機制 GHOST 協議**
    + 以太坊將出塊時間為十幾秒，由於底層使用的是 P2P 網路，經常會有礦工同時發布區塊，因此分岔是常態
    + **一個礦池若持有全網的 x% 算力，那收益也該是整個系統的 x%，但若共識機制設定不週全則有可能獲得收益超過比例**
        +  例如 : 若出塊時間短、且只承認最長合法鏈，則大礦池所在處最有可能成為最長合法鏈，其他分散的礦池並沒有辦法匹敵。(mining centralization)
    + **GHOST 協議**
        + **並不是以太坊發明的，以太坊只是稍微修改**
            + 作廢的區塊有安慰獎
            + **uncle block (分岔的區塊)**
                + 最長合法鏈外的區塊
                + 發布的區塊能夠把 uncle block 納入，uncle block 會獲得部分出塊獎勵 
                + 包含 uncle block 的區塊也會獲得部分獎勵
            + 鼓勵區塊進行合併
        + ![](https://i.imgur.com/XuBB3ct.png)
            + **A 區塊包含一個 uncle block 可以獲得 (1/32 * 當前出塊獎勵)ETH**
                + 最長合法鏈一次最多包含兩個 uncle block
            + **紅色的 uncle block 被 A 容納並廣播後會獲得 (7/8 * 當前出塊獎勵)ETH 稱作(uncle reward)**   
            + **藍色的 uncle block 被 A 容納並廣播後會獲得 (6/8 * 當前出塊獎勵)ETH**
                + 如果礦工惡意不包含 uncle block，則下一個區塊也能夠包含進去
                + 如果同時出現三個分岔的區塊，也能被包含進入區塊鏈
            + ![](https://i.imgur.com/bUbFvvB.png)
                + 隔 7 代的 uncle block 最少能獲得 2/8 的出塊獎勵，在前一代就沒辦法包含了
                + **為甚麼只能隔 7 代**
                    + 如果隔太遠，驗證過程會變的冗長
                    + 鼓勵出現分岔立刻合併，這時候取得的獎勵是最多的
            + **uncle block 內的交易不執行，也不檢查合法性**
                + 有可能 uncle block 內的交易和主鏈上的交易相互衝突
                + 不檢查內部交易的合法性，但會檢查是否為正常發布的區塊
            + ![](https://i.imgur.com/Q7mhxox.png)
                + **如果 uncle block 是一整條鏈要給錢嗎?**
                    + 後面的區塊通通不給錢
                        + 給錢的話 forking attack 的風險會變得非常低


## ETH 挖礦算法

+ 對於一個 POW(proof of work) 的系統來說，挖礦就是保證安全的途徑
    + **Block chain is secured by mining.**
+ BTC 後的虛擬貨幣，目標大抵都是 ASIC resistance
    + **最常見的做法就是增加對內存的需求**
        + memory hard mining puzzle
        + **LiteCoin**
            + **使用 scrypt 作為挖礦算法**
                + 對內存需求極高
                + 確實為 memory，但是對輕節點也是 memory hard，違反了原則 difficult to solve but easy to verify.
                    + 導致 LiteCoin 不敢將內存需求開很高(128k)
                        + ASIC resistance 失敗
        + **以太坊 的 memory hard mining puzzle**
            + 兩個數據集一大一小
                + **16MB cache**
                    + 輕節點可以用這個 16MB cache 完成驗證
                    + ![](https://i.imgur.com/Z4YPIhb.png)
                    + 起頭對一個 Seed 作運算，取得的元素作 Hash 得到第二個元素繼續作 Hash，這樣一路往下就形成了一個 cache。
                    + 大小會定期增長
                + **1GB data set DAG**
                    + 由 16MB cache 生產出來的
                    + 礦工需要保存這個 1GB 的大數據集
                    + ![](https://i.imgur.com/RRKPtPg.png)
                        + 大的數據會從小數據以偽隨機的方式取得某個位子，把該位子的 Hash 值做更新/迭代，算出下一個要讀取的位子，下一個位子再做更新/迭代，算出下下一個位子，這樣來回讀 256 次，得到的數值就是大數據的其中一格資料。
                    + 大小會定期增長(2018 : 2.5GB)
                    + **求解 Puzzle (ethash)**
                        + 從大數據中以偽隨機的方式讀取 128 個數
                            + 根據 block header ， nonce 算出一個初始的 Hash 值，根據這筆 Hash 值映射到大數據的某個位子，讀取出該位子的數字和其右邊的數字，爾後該位子的數值在做些更新/迭代，取得下一個位子，也一樣讀取該位子和其右邊，共循環 64 次，最後算出一個 Hash 值，然後比對是否符合難度。
                + **Sample code**
                    + ![](https://i.imgur.com/YgDe5j4.png)
                    + **獲得 cache**
                        + 第一個元素是 seed 的 hash 而後的都是前一個元素的 Hash
                        + 每隔 30000 個區塊會改變一次 seed 值，並且每隔 30000 個區塊 cache 的容量將會增大原本的1/128。
                    + ![](https://i.imgur.com/JdklpaL.png)
                    + **獲得 data set DAG 元素**
                        + 通過 cache 生成大數據中的第 i 個元素，依照偽隨機的方式讀取 cache 中的 256 位數
                        + **get_int_from_item**
                            + ETH source code 中沒有
                            + 用當前算出的 hash 讀出下個要算的位子
                        + **make_tiem**
                            + ETH source code 中沒有
                            + 用 cache 中某位子的數和當前的 hash 值計算出下一個 hash 值，迭代 256 次，得到一個 64 字節的 hash 值
                    + **獲得整個 data set DAG**
                        + ![](https://i.imgur.com/Ty5telB.png)
                    + **挖礦用的函數**
                        + ![](https://i.imgur.com/EhLcKUB.png)
                        + **data set DAG**
                            + 每隔 30000 個區塊會增加 1/128 倍大
                        + 根據 header nonce 算出初始 hash
                        + 讀取 DAG 中兩相鄰數
                        + 再根據當前訊息生出下一個 hash 值
                        + 循環 64 次返回一個 hash 值和 difficult 比較
                    + **驗證用的函數**
                        + ![](https://i.imgur.com/y42d0GZ.png)
                        + **full size 是 DAG 的 size**
                        + 因為輕節點沒有保存 DAG，每次驗證都要從 cache 重新生成
                        + 其他和挖礦函數是一樣的
                    + **礦工挖礦循環**
                        + ![](https://i.imgur.com/wNBzz9t.png)
                        + nonce : 0 ~ 2^64
                    + 由於礦工要驗證非常多 nonce 因此把整個 DAG 都儲存下來最快，而驗證只需要驗證一個 nonce 因此不需要儲存整個結構。
                    + **截至目前，以太坊挖礦還是以 GPU 為主**
                        + **ASIC resistance 較成功**
                            + DAG cache 較大，並且會定期增長
                            + **規劃要更新到 PoS(proof of stake)**
                                + 依照所持有的硬幣進行投票，不挖礦
                + **Pre-mining**
                    + 發行貨幣時預留一部份給開發者
                + **Pre-sale**
                    + 把 Pre-mining 的加密貨幣賣出，用於開發工作
                + [**關於以太坊**](https://ethernodes.org/)
                + [**關於以太坊**](https://etherscan.io/)
                + **其實有 ASIC 更安全?**
                    + 要發動攻擊的成本極高，因為必須買入 ASIC 礦機
                    + 如果一般電腦都能挖礦，發動攻擊成本就會降低
                       
## ETH 難度調整

+ 每個區塊都有可能調整難度，並且相較 BTC 複雜許多
+ **區塊難度 D(H)**
    + ![](https://i.imgur.com/eloUPfw.png)
    + D(H)是本區塊的難度
    + **P(H)Hd為父區塊的難度，每個區塊的難度都是以父區塊的難度基礎進行調整**
        + 父區塊 : 當前區塊鏈的最後一個區塊
    + **𝑥 × 𝜍2 用於適應調節區塊難度，維持穩定的出塊速度**
        + ![](https://i.imgur.com/lIMHhSv.png)
            + x 為調整的單位
        + ![](https://i.imgur.com/0x0klLi.png)
            + 𝜍2(sigma)為調整的系數
            + **y 和父區塊的 uncle 數有關，如果包含 uncle 則 y = 2 否則 y = 1**
                + 父區塊包含 uncle 時難度會大一個單位，因為包含 uncle 時發行的貨幣量太大，需要條高難度以維持穩定
                + -99 表示難度降低，最多一次降低 99 難度，主要是為了應對駭客攻擊，或其他黑天鵝事件
            **+ Hs 為本區塊時間戳，P(H)Hs為父區塊時間戳，相減去即為出塊時間**
                + 以這部分作為基礎來調整難度
                + **不帶 uncle 的父區塊(y = 1)為例**
                    + 出塊時間[1 , 8]表示難度為 1 ，太快了，應該要上調
                    + 出塊時間[9 , 17]表示可以接受
                    + 出塊時間[18, 26]表示出塊時間太長，降低難度
        + ![](https://i.imgur.com/lONX8xW.png)為難度炸彈
    + ![](https://i.imgur.com/SKSXlWB.png)
        + 每10萬個塊難度擴大一倍，後期難度增長非常快
        + 由於 ETH 未來將轉移至 PoS 會使用硬分岔的方式，如果礦工不願意更改的話，可能會出現以太坊分成兩個主鏈的問題，因此設置炸彈，讓後期的難度非常高
    + ![](https://i.imgur.com/Y9vDZxR.png)
        + Hi'稱為 (fake block number)，由 block number(Hi) 減少 300 萬得到，因為低估了 PoS 協議的開發難度，因此這麼做大約延後了1年半的時間(EIP100)
    + 基礎部分有下界，最小值為131072
+ **以太坊發展四階段**
    + Froniter
    + Homestead
    + Metropolis
        + **Byzantium**
            + 難度炸彈回調就是在這個階段執行的，EIP(Ethereum improvement proposal)同時下降 ETH 出塊獎勵
            + ![](https://i.imgur.com/idqXYCF.png)
                + 難度調整算法
            + ![](https://i.imgur.com/F6YxqmW.png)
                + 基礎數值計算
            + ![](https://i.imgur.com/mpp0O5E.png)
                + 難度炸彈計算
        + Constantinople
    + Serenity

## ETH 權益證明
+ **權益證明(proof of stake)**
    + 收益來源 -> 挖礦 -> 買設備 -> 算力 -> 越多錢收益越高，權益證明就是直接拚錢
    + **virtual mining**
        + 依靠持有貨幣的多寡進行投票，降低能耗
        + 如果要發動 51% 攻擊，必須獲得總系統 51% 貨幣才有辦法達成，類似股份制公司被收購
    + 早期 PoS 遇到的問題 Nothing at stake
        + 一般來說，挖礦只會往最長合法鏈挖
            + ![](https://i.imgur.com/xePw6oB.png)
            + 但是權益證明卻可以兩條鏈都下注
+ **工作量證明(proof of work)**
    + 浪費電
        + 比特幣挖礦的能耗一直隨著時間增長，總耗電量相當於智利整個國家的能耗(2018)，一筆交易相當於 34 個美國家庭用電量(2018)
        + 以太坊的能耗，相當於冰島這個國家的能耗(2018)，每筆交易相當於 2.25 個美國家庭用電量(2018)
        + 兩者間耗電量差別的原因是出塊時間的差距
        + 若是依靠設備進行挖礦，只要外界錢夠多就會影響到他的安全，小幣種遇到這種攻擊會直接完蛋(**AltCoin infanticide**)

+ **權益證明 + 工作量證明(proof of deposit)**
    + 持有幣的人把幣用在降低難度後，進行挖礦，但是使用於降低難度的貨幣可能會被鎖定，幾個區塊之內無法再使用這些貨幣降低難度。

+ **以太坊使用的權益證明**[Casper FFG](https://medium.com/taipei-ethereum-meetup/intro-to-casper-ffg-and-eth-2-0-95705e9304d6)
    + 過度階段曾打算和 PoW 混合使用
    + 引入驗證者(Validator)
        + 投入一定數量的以太幣才能成為Validator，並且這些以太幣會被系統鎖定
        + 兩輪投票制(Two-phase commit)
            + Prepare-Message
                + 要得到 2/3 以上的驗證者同意才能通過
            + Commit-Message
                + 要得到 2/3 以上的驗證者同意才能通過
            + 每 50 個區塊會投一次票稱為(epoch)，對於上一個 epoch 來說，當前的稱作 Commit-Message ，對下一個 epoch 來說，稱為 Prepare-Message
            + 連續兩個 epoch 得到 2/3 以上多數同意才算有效
            + ![](https://i.imgur.com/cW5Oj4Q.png)
            + 如果 Validator 做好自己的職責的話，可以獲得相對應的獎勵，不投票，亂投票也會被處罰

+ PoS目前並不成熟，但是 PoW 卻幾乎找不到 Bug，因此 PoS 未必比較好


## ETH 智能合約
+ 智能合約就是運行在區塊鏈上的一段 code ， code 的邏輯定義了合約的內容
    + 目前 Solidity 是最常見的撰寫智能合約的語言，接近 JavaScript
+ **智能合約會記錄帳戶狀態**
    + balance 餘額
    + nonce 交易次數
    + code 交易代碼
    + storage 存儲，因為以太坊的結構是一棵 MPT+
+ **撰寫 Solidity**
    + [語法整理](https://hackmd.io/@SpaceNinja/solidity)
+ **GAS**
    + GAS USED
        + 這個交易花了多少 GAS
    + GAS PRICE
        + 單位 GAS 的價格
    + GAS LIMIT
        + 這個交易最多願意支付多少的 GAS
+ **除了帳戶能調用合約外，合約也能調用合約，但是只有外部帳戶可以發起交易**
    + **直接調用**
        + ![](https://i.imgur.com/dnCr1WU.png)
    + **address.call()**
        + ![](https://i.imgur.com/qtAlzI4.png)
        + 第一個變數 funcsig 為調用的函數的簽名
        + 這個例子相當於A(addr).foo(“call foo by func call”)
        + 和直接調用的差別在錯誤處理方式不同
            + 直接調用
                + 如果發起交易時發生錯誤，會連同發起交易的帳戶一同回穩
            + address.call()
                + 錯誤處理要自己寫一個 return false;
    + **代理調用(delegatecall())**
        + 與 call() 相似，只是不能使用 value()
+ **fallback()**
    + function() public payable{}
    + 沒有參數，沒有返回值
    + 兩種情況下會被調用
        + 直接向合約地址轉帳，而不加任何 data
        + 被調用函數不存在
    + 如果轉帳金額不是 0 需要聲明 payable ，否則將拋出異常
+ **智能合約的創建**
    + 程式碼寫完後編譯成 Bytecode
    + 外部帳戶發起一筆轉帳交易到 0x0 的地址
        + 轉帳金額為 0 但必須支付 GAS fee
        + 合約的程式碼寫在 input data 裡
+ **汽油費(gas fee)**
    + 以太坊比起 BTC 平台，給予了一個圖靈完備的模型，可以完成非常多在 BTC 上幾乎做不到的事
        + 出現無窮迴圈怎麼辦?
            + 以太坊把這個問題交給了使用者，
            + ![](https://i.imgur.com/TZx3IG6.png)
                + AccountNonce : 交易序號，防止 replay attack
                + Price: 單位汽油的價格
                + GasLimit : 這個交易願意支付的最大 Gas fee
                + Recipient : 收款人的地址 
                + Amount : 轉帳金額
                + Payload : data 域
            + 發起智能合約時，先根據 Price，GasLimit 算出這筆交易可能消耗的汽油費，從發起者的帳戶扣掉，之後再根據實際執行情況，算出實際花費，多退，少的回穩交易
    + 不同的指令消耗的 Gas fee 是不一樣的
        + 例如 
            + + - 消耗的很少
            + 取 Hash 消耗的很多
            + 存儲變數消耗的 Gas fee 很多
            + 只是讀取公共數據則免費
+ **錯誤處理**
    + 在執行合約的過程中，若發生錯誤，會導致整個交易回穩，退回開始執行的狀態
        + 例如
            + 用完了 Gas 合約會直接回穩
        + 發生錯誤時消耗的 Gas 是不會退還的
            + 為了預防有人惡意的攻擊
        + assert
            + assert(bool condition)
            + 若條件不滿足就拋出，用於判斷內部錯誤
        + require
            + require(bool condition)
            + 若條件不滿足就拋掉，用於判斷輸入或外部錯誤
        + revert
    + 嵌套調用
        + 如果調用一個合約，那個合約發生錯誤，那兩個合約都會回穩嗎?
            + 直接調用 : 會
            + call : 不會，返回一個 false
+ ![](https://i.imgur.com/c1O2dZr.png)
    + Gas limit
        + 這個的區塊內能夠用於交易的 Gas 上限
        + 能夠微調 : 以上一個區塊的 Gas 作為基礎，可以調整 +-1/1024
+ Receipt 數據結構
    + status 域會告知礦工交易執行的結果
    + 收到合約的調用後，從本地的數據結構內扣除帳戶的餘額。
    + 收到合約後必須全節點必須在本地端執行一次，驗證交易，區塊內的所有交易都要無償的執行一遍
    + 可不可以不執行，直接繼續挖礦?
        + 不可能，因為本地端的 MPT 沒有更新，其他的礦工會驗證異常
    + 如果合約執行中，發生錯誤的交易需不需要發佈到區塊鏈上?
        + 要，因為必須扣除掉 Gas fee
+ 智能合約能夠依靠外部得到的訊息
    + ![](https://i.imgur.com/GAg9bGR.png)
    + 這堂課是2018的課，但 Solidity 的變化非常快，所以語法僅供參考就好
+ 智能合約可以調用的訊息
    + ![](https://i.imgur.com/HfqnJb9.png)
+ 地址類型
    + ![](https://i.imgur.com/5Z68X9M.png)
    + **addr.transfer()**
        + 當前合約給 addr 轉入多少錢
    + **addr.call()**
        + 當前的帳戶，調用 addr 的合約
+ 三種發送 ETH 的方法
    + ![](https://i.imgur.com/mDKN7dh.png)
        + address.transfer(uint256 amount)
            + 若失敗會導致連鎖式的交易回穩
            + 給的汽油不多，和 send 一樣 (2300)
        + address.send(uint256 amount)  returns(bool)
            + 若失敗只會回傳一個 false
            + 給的汽油不多，和 transfer 一樣 (2300)
        + address.call.value(uint256 amount)()
            + 如果用 call 的方式轉帳，會把剩下的 Gas 一併傳送過去
+ 簡單例子
    + ![](https://i.imgur.com/67xaJwT.png)
    + ![](https://i.imgur.com/kkqmpTa.png)
    + 有甚麼問題 ?
    + ![](https://i.imgur.com/IE3Tp7P.png)
        + 參數是拍賣合約的地址
        + ![](https://i.imgur.com/UrqFz0N.png)
        + 黑客用合約領錢，合約轉錢進入黑客的合約帳戶後，因為黑客的合約帳戶沒有 fallback() 函數，而會導致連鎖式的回穩，如此一來會導致合約內的所有使用者都收不到退款
        + 如此一來，**完全沒辦法**取出合約內的以太幣
+ 簡單例子2
    + 一樣的拍賣程式碼，把auctionEnd()改成另外兩個函數取代
    + ![](https://i.imgur.com/lUeV3C5.png)
    + ![](https://i.imgur.com/F8DCJOk.png)

    + 這樣可以嗎?
        + 由於轉帳後會立刻調用 fallback() 函數
        + ![](https://i.imgur.com/ZwhH8Fm.png)
        + bids[msg.sender] = 0 只有在上面的 if 結束後才會執行，這時 if 和黑客的 withdraw 陷入了遞迴調用，所以不會清零
        + 當三種情況發生時，遞迴調用會結束
            + 合約內的錢不夠了
            + 汽油費不夠了
            + stackoverflow
    + 匯款前應該先清空/扣款
    + 修改方式
        + 先清零在轉帳
        + 轉帳時用的是 sender() 而不是 call.value()，因為汽油費太低，不足以重複發起調用

## ETH TheDAO
+ **DAO(Decentralized Autonomous Organization)**
    + 建立在程式碼基礎上的組織，規章是寫在程式碼裡面的，通過區塊鏈的共識協議來維持這個組織的穩定
    + **The DAO**
        + 2016 年 5 月出現的
        + 類似眾籌的投資基金，資金來源是大家在區塊鏈上的眾籌取得，用以太幣換取投票權，有的投票權越多權重越大，可以決定要投資哪個項目，之後的收益也是按照投資的比例來分配
        + 極度的民主
        + **splitDAO**
            + 可以自己從 DAO 裡面拆分出來，成立 childDAO，收回所有投票權代幣換回以太幣，讓使用者自己成立基金投資，這是唯一一個把錢取出來的方法，拆分前有七天的辯論期，拆分後有二十八天的鎖定期
            + ![](https://i.imgur.com/1P4vtpO.png)
            + 沒有先清零就匯款，被黑客使用重入攻擊，盜領了整個組織三分之一的美元(5000萬)
            + **需要回穩交易的補救措施**
                + 依靠鎖定期的 28 天回穩交易，不讓黑客領走以太幣
                + Vitalik 支持回穩，因為 the DAO 裡面有太多資金，影響太劇烈
            + **不需要回穩交易的補救措施**
                + 並無違法，因為 code is law
                + 尤其是不該回穩，因為區塊鏈的不可竄改性
            + **該怎麼補救?**
                + **從發生攻擊前的區塊開始回穩 ?**
                    + 不只黑客的交易，會讓所有的交易都回穩了
                + **軟體升級，凡是跟 TheDAO 相關的帳戶，一概不允許交易(軟分岔)**
                    + **出現 bug**
                        + 要是跟 TheDAO 相關的帳戶交易不被認可，還需要收取 Gas fee 嗎 ?
                        + **以太坊沒有收取 Gas fee**
                            + 導致出現大量的惡意攻擊，因此一堆礦工又回穩了軟體升級
                + 強制讓 TheDAO 裡面的資金轉到另一個智能合約裡面，在讓使用者退款(硬分岔)
                    + **因為以太坊要在第 192 萬個區塊，執行軟體升級，不需要 TheDAO 使用者的簽名就能確認交易(其餘礦工會認為是非法交易)**
                        + **引起了極度激烈的辯論**
                            + 太扯了，憑什麼轉走別人的錢
                            + 最後用投票的方式，大部分人支持分岔，硬分岔成功了
                            + 新鏈上的以太幣稱為 ETH
                + **反對投票的人不認帳**
                    + 投票人數太少
                    + 大多數人意見也不一定是對的
                    + 不一定公平
                    + 所以，舊的鏈還是有礦工在上面挖，慢慢的，交易所上市了舊鏈的以太幣(ETC : Ethereum classic)
                + **硬分岔帶來的困擾 : 重入攻擊**
                    + 新鏈上的交易，放到舊鏈上同樣合法，反過來也行，後來給兩條鏈加上了 chain ID
                + **為甚麼不針對黑客的帳戶就好 ?**
                    + 因為 bug 改不了，凍結黑客的帳戶也修不了 bug，因此要從根部處理，否則任何人都能模仿黑客的手段攻擊

## ETH 反思
+ **Is smart contract really contract.**
    + 不智能阿，又沒有人工智慧
    + Smart contract is anything but smart.
+ **Irrevocability is a double edged sword.**
    + 不可竄改性導致 bug 無法修改
    + 帳戶凍結 : 軟分岔
    + 一旦發生問題也不能修改
    + 如果帳戶外洩?
        + 應該立刻把錢轉到另一個帳戶
    + 一旦上鏈的合約，無法阻止其被調用
    + The DAO剩下的錢怎麼處理
        + 用黑客的技術把錢轉走
+ **Nothing is Irrevocable**
    + 就連區塊鏈都是可以更改的，人是活的，程式碼是死的，不要迷信於區塊鏈的不可竄改性
+ **Is solidity the right programming language?**
    + **Solidity轉帳就是隱性的調用別人的 fallback 函數
    + 並非圖靈完備，但是能夠達成我們要的效果的語言最好
        + 太難找了
    + 目前大多是透過模板在撰寫合約
+ **開源的 Code 保證安全?**
    + 智能合約的代碼公開，但還是有出現漏洞的問題
    + **Many eyeball fallacy**
        + 雖然大家都能看到程式碼，但真的會去看的人很少
+ **What does decentralization mean?**
    + 分岔就正是去中心化的表現，礦工可以集結決定方向
+ **decentralized =/= distributed
    + 分布式不等於去中心化
    + 智能合約不該被用於計算，而是完成邏輯操作

## ETH 美鏈
+ **美鏈(Beauty Chain)一個部署在以太坊上的智能合約，有自己的代幣 BEC**
    + batchtransfer函數
        + 向多個接收者發送代幣，然後把代幣從調用者身上扣除
        + ![](https://i.imgur.com/qXMiSz0.png)
        + **uint256 amount = uint256(cnt) * _value;**
            + 溢位問題
            + ![](https://i.imgur.com/hNl0xK3.png)
+ **預防攻擊**
    + 使用已經撰寫好的函數庫
    + SafeMath
        + ![](https://i.imgur.com/tZfkTm0.png)
        + 這樣就可以預防乘法出錯


+ **ICO(Initial coin offering)**
    + 沒有自己的區塊鏈，代幣的發行、轉帳都是通過調用智能合約中的函數完成
    + 可以自己定義發行規則，帳戶餘額也是保存在智能合約裡面
    + ERC(Etherenum Request for Comments) 20 是以太坊上發行代幣的一個標準，規範所有發行代幣的合約應該實現的功能和遵循的街口
    + 對應著狀態樹中的一點














