編者注:本譯文的首個版本見于此處,本次再版已經過校對。在此對原譯者kim表示感謝。
原文的寫作時間不確定,但可將其視為一個原點,反思以太坊的設計理念以及以太坊在這幾年間的演化。既是反思其創新,也是反思其有欠考慮的地方。
盡管以太坊的許多理念在早先的密碼學貨幣上已經運用并測試了5年之久,但從某些協議功能的處理方法上來說,以太坊與常見方式仍有許多不同。而且,以太坊可用于開發全新的經濟工具,因為它具有其他系統不具備的許多功能。本文會詳細描述以太坊所有的潛在優點,以及在構建以太坊協議過程中某些有爭議的地方。另外,也會指出我們的方案及替代方案的潛在風險。
原則
以太坊協議的設計遵循以下幾點原則:
三明治復雜模型:我們認為以太坊的底層協議應盡可能的簡單,接口設計應易于理解。那些不可避免的復雜部分應放入中間層。中間層不作文核心共識的一部分,且對最終用戶不可見,它包含:高級語言編譯器、參數序列化和反序列化腳本、存儲數據結構模型、leveldb存儲接口以及聯網協議等。當然,區分的界線不是絕對明確的,有時候需要酌情調整。
自由:不應限制用戶使用以太坊協議,也不應試圖優先支持或不支持某些以太坊合約或交易。這一點與“網絡中立”概念背后的指導原則相似。比特幣交易協議就?沒有?遵循這一原則:比特幣交易協議并不鼓勵區塊鏈的“非常規用途”;而且,有時候還有人用準-協議層的變更來攻擊以“未經授權”的方式使用區塊鏈的應用。因此,在以太坊,我們堅定支持僅使用交易手續費來達成大體激勵相容的辦法——用戶消耗整個網絡越多資源,需要付出的代價就越高,也即使其自己承擔成本。
泛化:以太坊協議的特性和操作碼應最大限度地體現低層次的概念,以便它們可以隨意組合,包括組合出今天看來沒什么用、但未來可能有用的東西。而且,通過剝離那些不需要的功能,低層次的概念可以更加高效。遵循這一原則的例子是,我們選擇LOG操作碼作為向dapp提供信息的方式,而不是像之前那樣記錄下所有交易和消息。在早先,“消息”的概念完完全全是多種概念的集合,它包含“函數調用”和“外在觀察者感興趣的事件信息”,而兩者是完全可以分離開來的。
沒有特點就是最大的特點:為了遵循泛化原則,我們拒絕將那些高級用例內嵌為協議的一部分,哪怕是經常使用的用例,也絕不這么做。如果人們真的想實現這些用例,可以在合約內創建子協議。比如,在以太坊中就缺少類似比特幣中的“時間鎖”功能。但是,通過以下協議可以模擬出這個功能:用戶發送簽名數據包到特定的合約中處理,如果數據包在特定合約中有效,則執行相應的函數。
不厭惡風險:如果風險的增加帶來了可觀的好處,我們愿意承擔更高的風險。
這些原則指導著以太坊的開發,但它們并不是絕對的;某些情況下,為了減少開發時間或者不希望一次作出過多改變,也會使我們推遲作出某些修改,把它留到將來的版本中去修改。
區塊鏈層協議
本節對以太坊中區塊鏈層協議的改變進行了描述,包括區塊和交易是如何工作的、數據如何序列化及存儲、賬戶背后的機制。
賬戶,而非UTXO?1
比特幣及其許多變種,都將用戶的余額信息存儲在UTXO結構中,系統的整個狀態由一系列的“未花費的輸出”組成。每個UTXO都有擁有者和自身的價值屬性。一筆交易在消費若干個UTXO同時也會生成若干個新的UTXO;而交易受到下列有效性要求的約束:
1.每個被引用的輸入必須有效,且未被使用過;2.交易的簽名必須與每筆輸入的所有者簽名匹配;3.輸入的總值必須等于或大于輸出的總值。
因此,比特幣系統中,用戶的“余額”是該用戶的私鑰能夠有效簽名的所有UTXO的總和。下圖展示了比特幣系統中交易輸入輸出過程:
-比特幣所用的三式記賬法-
但是,以太坊拋棄了UTXO的方案,轉而使用更簡單的方法:采用狀態的概念存儲一系列賬戶,每個賬戶都有自己的余額,以及以太坊特有的數據。如果交易發起方的賬戶余額足夠支付交易費用,則交易有效,那么發起方賬戶會扣除相應金額,而接收賬戶則計入該金額。某些情況下,接收賬戶內有需要執行的代碼,則交易會觸發該代碼的執行,那么賬戶的內部存儲器可能會發生變化,甚至可能會創建額外的消息發送給其他賬戶,從而導致新的交易發生。
盡管以太坊沒有采用UTXO的概念,但UTXO也不乏有一些優點:
較高程度的隱私保護:如果用戶每次交易都使用一個新的地址,那么賬戶之間的相互關聯就很困難。這樣做適用于對安全性要求高的貨幣系統,但不是對任何dapp都合適。因為dapp通常需要跟蹤用戶復雜的綁定狀態,而dapp的狀態并不能像貨幣系統中的狀態那樣簡單地劃分。
V神:ETH 1中的缺陷現在必須花費數年時間來解決:金色財經報道,以太坊創始人V神發推文稱,他最大的遺憾不是在2015年7月而非2014年9月啟動了以太坊。而是有關eth 1中那些我們現在必須花費數年時間來解決的缺陷,包括十六進制樹、RLP、Gas成本等。V神稱,現在做得更好,的確可以在數十年內獲得回報。[2020/5/16]
潛在的可擴展性:理論上來說,UTXO與某些類型的可擴展性方案更契合,因為只需持幣者擁有能夠證明自己貨幣所有權的默克爾證明即可,即使所有的人都遺忘了這一數據,真正受損也這個人,其他人不受影響。在以太坊賬戶系統中,如果所有人都丟失了某個賬戶對應的默克爾樹部分,那么該賬戶將無法處理任何能夠影響它的消息,包括發送給它的消息,它也無法處理。不過,并非只有UTXO能夠可擴展,也存在不依賴UTXO就能擴展的方式。
賬戶的好處有以下幾點:
節省大量空間:如果一個賬戶有5個UTXO,則從UTXO模式轉成賬戶模式,所需空間會從300字節降到30字節。具體計算如下:300=(20328)*5;30=2082;但實際節約并沒有這么大,因為賬戶需要被存儲在帕特里夏樹中。另外以太坊中交易也比比特幣中的更小,因為每次交易只需要生成一次引用,一次簽名,以及一個輸出。
可互換性更強:UTXO結構并沒有區塊鏈層的概念,所以不管是在技術還是法律上,通過建立一個紅名單/黑名單,并依據的這些“有效輸出”的來源區分它們并不是很實際。
簡單:以太坊編碼更簡單、更易于理解,尤其是在涉及到復雜腳本時。盡管任何去中心化應用都可以用UTXO方式來實現,但這種方式實質上是賦予腳本限制給定的UTXO所能輸出的UTXO的種類及其使用條件的能力。因此,UTXO實現方式比以太坊使用賬戶的方式要復雜的多。
輕客戶端:輕客戶端可以隨時通過沿指定方向掃描狀態樹來訪問與賬戶相關的所有數據。在UTXO范式中,每筆交易需要用到的引用都不同,這對于長時間運行并使用了上文提到的UTXO根狀態傳播機制的dapp應用來說,無疑是繁重的。
我們認為,賬戶的好處大大超過了其他方式,尤其是對于我們想要支持的、可包含任意狀態和代碼的dapp應用而言。另外,本著“沒有特點就是最大的特點”的指導原則,我們認為如果用戶真的關心私密性,則可以通過合約中的簽名數據包協議來建立一個加密“混幣器”混淆支付路徑。
賬戶方式的一個弱點是:為了阻止重放攻擊,每筆交易必須有一個“nonce”。因此,每個賬戶都要有一個實時更新的nonce值,每一筆新交易都在賬戶nonce值上遞增1作為自己的nonce。這就意味著,即使不再使用的賬戶,也不能從賬戶狀態中移除。解決這個問題的一個簡單方法是讓交易包含一個區塊號,使它們在一段時間后就無法再被重放,并且每隔一段時間段重置nonce。
若要在狀態中刪除某個賬戶,就必須先“ping”出它們來,而完整掃描區塊鏈協議的開銷是非常大的。在1.0上我們沒有實現這個機制,1.1及以上版本可能會使用這個機制。
校對注:這就是以太坊日后面臨的“狀態爆炸”問題的技術原因:所有狀態數據必須完整保存,無法合理地刪除賬戶。作為一種區塊鏈協議,以太坊的節點不僅要對事務的順序達成共識,還要對全局狀態達成共識(表現形式就是區塊頭里需要包括狀態根。因此,若要刪除狀態,也需要全網的共識,否則會陷入分裂。
校對注:這種以nonce來標記賬戶交易順序的做法,也使得用戶的交易必須順序執行,如果一筆交易無法得到處理,使用后續nonce的交易也無法得到處理。關于“加速”已發出的交易的上鏈進度,見這篇文章。
默克爾帕特里夏樹
默克爾帕特里夏樹,由AlanReiner提出設想,并在瑞波協議中得到實現,是以太坊的主要數據結構,用于存儲所有賬戶狀態,以及每個區塊中的交易和收據數據。MPT是默克爾樹和帕特里夏樹的結合,結合這兩種樹創建的結構具有以下屬性:
任一組鍵-值對所對應的根哈希值都是唯一的,想要謊稱某個鍵值對存在于某棵樹上是一定會被識破的。
增、刪、改一個鍵值對的時間復雜度是對數級別。
MPT為我們提供了一個高效、易更新、且代表整個狀態樹的“指紋”。關于MPT更詳細描述:https://github.com/ethereum/wiki/wiki/Patricia-Tree。
MPT的具體設計決策如下:
有兩類節點:KV節點和離散節點。KV節點的存在提高了效率,因為如果在特定區域樹是稀疏的,KV節點可作為一個“捷徑”來壓縮樹的高度。
動態 | V神反對將Defi抵押倉位稱為借貸:傳統借貸要還,Defi是杠桿交易:近日美國媒體Mashable發布了一篇名為《我沒有簽文件、沒有見人就借到了一筆錢》的文章,文章內描述了作者如何使用Compound借出一筆資金的事情,在這個過程中,貸款者不需要向任何機構提出申請,也不需要簽署任何文件就可以輕松地借到DAI穩定幣。 但該文章所描述的內容引發了以太坊創始人Vitalik(V神)的異見,他認為“媒體認為 DeFi 可以讓人不簽任何文件就貸出錢的說法不妥。主要原因是傳統的借貸需要簽署一些文件來確定貸款者可以清償債務,而在 DeFi 里,數字資產一直都在用戶手里,只是加了杠桿的倉位罷了”。 V神表示:”傳統貸款和 Defi 的區別在于,房子是不可以再分的,再加上貸款買房的時候房子也有人住,這就讓 Defi 和貸款在量數上產生了不同。“為此,Vitalik 將這種通過 DeFi 貸出資金的行為稱之為”CDP 抵押倉位“以及”主動杠桿交易“,并表示自己一直以來都反對在 DeFi 上使用借貸一詞。[2020/1/30]
離散節點是十六進制,不是二進制:這樣讓查找更有效率,我們現在認識到這種選擇并不理想,因為十六進制樹的查找效率在二進制中可以通過批次存儲節點來模擬。但是,MPT樹結構的實現是非常容易出錯的,最終至少會造成狀態根不匹配,所以我們決定擱置變更,等到1.1版本再說。
空值與非成員之間沒有區別:這樣做是為了簡化邏輯,以太坊中未啟用的賬戶的值默認為0,空字符串也用0表示。然而,需要強調的是,這樣做犧牲了一些通用性,因而也不是最優的。
終節點和非終節點的區別:技術上,標識一個節點“是否是終節點”是沒必要的,因為以太坊中所有的樹都被用于存儲固定長度的數據,但為了增加通用性,我們還是會添加這個標識,以期望以太坊的MPT的實現方式能夠被其他密碼學貨幣原樣采納。
在“安全樹”中采用SHA3(k)作為鍵:使用SHA3(k),想要通過生成許多的賬戶并重復調用SLOAD和SSTORE操作碼來DoS攻擊的難度會大大提高。注意,這也讓枚舉樹變得更困難;如果要使你的客戶端具備枚舉的功能,最簡單的方法就是維護一個映射?sha3(k)->k?的數據庫。
校對注:這里的意思是,如果使用k作為默克爾樹存儲數據的鍵,其分布可能很稀疏,而攻擊者可以容易地規劃出需要很深的樹路徑來存儲的賬戶,并對這些賬戶重復調用狀態訪問操作,以此造成網絡中的節點超負荷運行,但是,哈希函數的結果是隨機分布的,以sha3(k)作為鍵可以使鍵的分布較為均勻,樹高也會較矮)。
這種特性也是有得有失,這一方面意味著DoS攻擊會變得更困難,另一方面,也使得一個區塊中的交易的狀態樹訪問路徑,很少有重合的,因此每次搜索都是復雜度最差的情形。
此外,這也使得MPT不宜實現“無狀態性”,因為狀態訪問的路徑不重合,證據的空間效率也是最差情形。當然,也可以說,默克爾樹證據的空間效率本身也不夠高
RLP
RLP:遞歸長度前綴。
RLP編碼是以太坊中主要的序列化格式,它的使用無處不在:區塊、交易、賬戶狀態以及網絡協議消息。詳見RLP正式描述:?https://github.com/ethereum/wiki/wiki/RLP
RLP旨在成為高度簡化的序列化格式,它唯一的目的是存儲嵌套的字節數組?3。不同于?protobuf、BSON?等現有的解決方案,RLP并不定義任何指定的數據類型,如Boolean、float、double或者integer。它僅僅是以嵌套數組的形式存儲結構體,由協議來確定數組的含義。RLP也沒有顯式支持map集合,半官方的建議是采用?,,...]?的嵌套數組來表示鍵值對集合,k1,k2...按照字符串的標準排序。
與RLP具有相同功能的方案是protobuf或BSON,它們是一直被使用的算法。然而,以太坊中,我們更偏向于使用RLP,因為:它易于實現;絕對保證字節的一致性。
許多語言的鍵值對集合沒有明確的排序,并且浮點格式有很多特殊情況,這可能造成相同數據卻產生不同編碼和不同哈希值。通過內部開發協議,我們能確保它是帶著這些目標設計的。BitTorrent使用的編碼方式bencode也許可以替代RLP。不過它采用的是十進制的編碼方式,與采用二進制的RLP相比,稍微遜色了點。
壓縮算法
網絡協議和數據庫都采用了一個自定義的壓縮算法來存儲數據。該算法可描述為:對0使用行程編碼?4?并同時保留其他值,舉例如下:
聲音 | V神:個人冒險的結果有時可能使整個集體受益匪淺:V神今日發推表示,自由主義的集體主義即:自由很重要,盡管與多數人背道而馳通常是個錯誤,而且會給個人帶來更糟糕的結果,但是這些冒險的結果有時也會提供有價值的信息,進而使整個集體受益匪淺。[2019/9/29]
壓縮算法存在之前,以太坊協議的許多地方都有一些特殊情況,例如,sha3經常被重定義使得?sha3('')='',這樣不需要在賬戶中存儲代碼,可以節省64字節。然而,最近所有這些使得以太坊數據結構變得臃腫的特殊情況都被刪除了,取而代之的是將數據保存函數添加到區塊鏈協議之外的層,也就是將其放入網絡協議以及將其插入用戶數據庫實現。這樣增加了模塊化能力,簡化了共識層,使得對壓縮算法的持續更新部署起來相對簡單。
樹的使用
提醒:理解這部分的知識需要讀者了解布隆過濾器?5?的原理。簡介可見:http://en.wikipedia.org/wiki/Bloom_filter
以太坊區塊鏈中每個區塊頭都包含指向三個樹的指針:狀態樹、交易樹、收據樹。
狀態樹代表處理完該區塊后的整個狀態;
交易樹代表區塊中所有交易,這些交易由index索引作為key;
收據樹代表每筆交易相應的收據。
交易的收據是一個RLP編碼的數據結構:
其中:
medstate:交易處理后,狀態樹的根;
gas_used:交易處理后,gas的使用量;
logs:是許多?,data]?元素的列表。這些元素由交易執行期間調用的操作碼?LOG0?...?LOG4?生成;address?是生成日志的合約的地址;topics是最多4個32字節的值;data是任意大小的字節數組;
logbloom:交易中所有logs的address和topics組成的布隆過濾器。
區塊頭中也存在一個布隆過濾器,它是區塊中交易的所有布隆過濾器的或運算結果。這樣的構造使得以太坊協議對輕客戶端友好得無以復加。
注釋:
UTXO:unspenttransactionoutputs,字面理解是:未花費的交易輸出,也即未被任何交易引用為輸入的交易輸出。它是比特幣協議中用于存儲價值信息的數據結構。——校對注
Nonce,Numberusedonce或Numberonce的縮寫,在密碼學中Nonce是一個只被使用一次的任意或非重復的隨機數值,在加密技術中的初始向量和加密哈希函數都發揮著重要作用,在各類驗證協議的通信應用中確保驗證信息不被重復使用以對抗重放攻擊。——譯者注
嵌套數組:創建一個數組,并使用其他數組填充該數組。如數組pets:
varcats:String=;vardogs:String=;varpets:String=;
——譯者注
行程編碼:一種統計編碼。主要技術是檢測重復的比特或字符序列,并用它們的出現次數取而代之。——譯者注
布隆過濾器:由HowardBloom在1970年提出的二進制向量數據結構,它具有很好的空間和時間效率,被用來檢測一個元素是不是集合中的一個成員。——譯者注
叔塊獎勵
GHOST協議是一項不起的創新,由YonatanSompolinsky和AvivZohar在2013年10月首次提出的。它是解決快速出塊伴生問題的第一個認真嘗試。
GHOST的用意是解決這樣一個難題:更短的出塊時間會導致有更多區塊“過時”因而安全性會下降——因為區塊在網絡中傳播需要一定時間,如果礦工A挖到一個區塊并向全網廣播,在廣播的路上,B也挖出了區塊,那么B的區塊是過時的,且B的本次挖礦對網絡的安全沒有貢獻。
此外,還有一個中心化問題:如果A是一個礦池,有30%的算力,B有10%的算力。A有70%的時間產生過時的區塊,而B有90%的時間產生過時區塊。如果區塊的產出時間間隔很短,那么過時率就會變高,則A憑借其更大的算力使挖礦效率也更高。所以,區塊生成過快,容易導致網絡算力大的礦池在事實上壟斷挖礦過程。
根據Sompolinsky和Zohar的描述,GHOST解決了在計算哪個鏈是最長的鏈的過程中,因產生過時區塊而造成的網絡安全性下降的問題。也就是說,不僅是父區塊和更早的區塊,同時過時的旁支區塊也被添加到計算哪個塊具有最大的總工作量證明中去。
聲音 | V神:中繼注冊燃燒機制是以太坊在隱私解決方面的關鍵一步:V神在推特中轉發了一篇介紹中繼注冊燃燒機制的文章。該機制目的在于抵制垃圾交易,其在以太坊第二層網絡上建立一套仲裁注冊機制,每一筆交易在發送廣播中需要支付一定比例的費用,最后利用min_percent將其銷毀。V神評論稱,在中繼研究取得較大進展之后,這是以太坊短期在隱私解決方案上的關鍵一步。[2019/7/18]
為了解決第二個問題:中心化問題,我們采用了另一種策略:對過時區塊也提供區塊獎勵:挖到過時區塊的獎勵是該區塊基礎獎勵的7/8;而包含過時區塊的侄子區塊將收到1/32的基礎獎勵作為賞金。但是,交易費不會獎勵給叔塊和侄塊。
在以太坊中,過時區塊只能被其兄弟區塊的7代以內的直系后代區塊包含為叔塊。之所以這樣限制是因為,首先,GHOST協議若不限制過時區塊的代際距離,將會花費大量開銷在計算過時區塊的有效性上;其次,無限制的過時區塊激勵政策會讓礦工失去在主鏈上挖礦的熱情;最后,計算表明,過時區塊獎勵政策限制在7層內提供了大部分所需的效果,而且不會帶來負面效應。
度量中心化風險的一個模擬器可見此處:https://github.com/ethereum/economic-modeling/blob/master/ghost.py
一個更高層次的討論可見此處:https://blog.ethereum.org/2014/07/11/toward-a-12-second-block-time/
校對注:此處的“包含”在技術上的形式是:侄塊在區塊頭中引用叔塊的區塊哈希值,然后把叔塊的區塊頭包含在區塊體內。
區塊時間算法的設計決策包括:
區塊時間12s:選擇12秒是因為這已經是長于網絡延遲的最短時間間隔了。在2013年的一份關于測量比特幣網絡延遲的論文中,確定了12.6秒是新產生的區塊傳播到95%節點的時間;然而,該論文還指出傳播時間與區塊大小成比例,因此在更快的貨幣中,我們可以期待傳播時間大大減少。傳播間隔時間是恒定的,約為2秒。然而,為了安全起見,在我們的分析中,我們假定區塊的傳播需要12秒
7代祖先以內的限制:這樣設計的目的是希望只保留少量區塊,而將更早之前的區塊清除。已經證明7代的可引用范圍就可以提供大部分所需的效果。
1代后裔的限制::這也是出于簡潔性的設計目標,而且上述的模擬器顯示這不會帶來很大的中心化風險。
叔塊必須是有效的?:叔塊必須是有效的header,而不是有效的區塊。這樣做也是為了簡化,將區塊鏈模型保持為線性數據結構。不過,要求叔塊是有效的區塊也是有效的方法。
獎金分配:7/8的挖礦基礎獎勵分配給叔塊,1/32分給侄塊,它們交易費用都是0%。如果費用占多數,從中心化的角度看,這會使叔塊激勵機制無效;然而,這也是為什么只要我們繼續使用PoW,以太坊就會不斷發行以太幣的原因。
難度更新算法
目前以太坊通過以下規則進行難度更新:
難度更新規則的設計目標如下:
快速更新:區塊間的時間應該隨著hash算力的增減而快速調整;
低波動性:如果挖礦算力恒定,那么難度不應劇烈波動;
簡單:算法的實現應相對簡單;
低內存:算法不應依賴于過多的歷史區塊,要盡可能少的使用“內存變量”。假設有最新的十個區塊,將存儲在這十個區塊頭部的內存變量相加,這些區塊都可用于算法的計算;
不可爆破:算法不應讓礦工有過多篡改時間戳或者礦池反復添加或刪除算力的激勵
我們當前的算法在低波動性和抗爆破性上并不理想。最近,我們計劃把時間戳參數改為與父區塊和祖父區塊比較,所以礦工只有在連續挖2個區塊時,才有動力去修改時間戳。另一個更強大的模擬公式:?https://github.com/ethereum/economic-modeling/blob/master/diffadjust/blkdiff.py?
Gas和費用
比特幣中所有交易大體相同,因此它們的網絡成本用單一一種單位來模擬。以太坊中的交易要更復雜,所以交易費用需要考慮到賬戶的許多方面,包括網絡帶寬費用、存儲費用和計算費用。尤其重要的是,以太坊編程語言是圖靈完備的,所以交易會使用任意數量的寬帶、存儲和計算成本;而最終會使用多少數量是無法可靠預測的。防止有人使用無限循環來實施拒絕服務式攻擊是我們的一個關鍵目標。
V神被自由科學中心評為“天才獎”:根據官方消息,以太坊的發明者Vitalik Buterin被自由科學中心授予2018年的天才獎,EY稱該中心為全球領先的技術和科學中心之一。5月11日,V神將參加頒獎典禮。其它三個獲獎者分別為Sara Seager、Laurie Santos、George Church。[2018/4/28]
以太坊交易費用的基本機制如下:
每筆交易必須指明自身愿意消耗的gas數量,以及愿意為每單元gas支付的費用,在交易執行開始時,startgas*gasprice價值的以太幣會從發送者賬戶中扣除;
交易執行期間的所有操作,包括讀寫數據庫、發送消息以及每一步的計算都會消耗一定數量的gas;
如果交易執行完畢,消耗的gas值小于指定的限制值,則交易執行正常,并將剩余的gas值賦予變量?gas_rem?;在交易完成后,發送者會收到返回的gas_rem*gasprice價值的以太幣,而給礦工的獎勵是*gasprice價值的以太幣;
如果交易執行中,gas消耗殆盡,則所有的執行恢復原樣,但交易仍然有效,只是交易的唯一結果是將startgas*gasprice價值的以太幣支付給礦工,其他不變;
當一個合約發送消息給另一個合約,可以對這個消息引起的子執行設置一個gas限制。如果子執行耗盡了gas,則子執行恢復原樣,但gas仍然消耗。,這一點還未改變,但它在未來有可能會改變。見《值得考慮刪除的EVM功能》)
上述提到的幾點都是必須滿足的,例如:
如果交易不需要指定gas限制,那么惡意用戶就會發送一個有數十億步循環的交易。沒有人能夠處理這樣的交易,因為處理這樣的交易花的時間可能很長很長;但是誰也無法預先告知網絡上的礦工,這就會導致拒絕服務的漏洞產生。
一種替代嚴格gas計數的方法是時間限制,但它不可能有用,因為它們太主觀了。
startgas*gasprice的整個值,在開始時就應該設置好,這樣不至于在交易執行中造成該賬戶“破產”、無力繼續支付gas費用。一邊執行一邊檢查余額也不行,因為賬戶可以把余額放到別的地方。
如果在gas不夠的情況下,交易執行不會完全復原,合約就必須采用強有力的安全措施來防止合約發生變化。
如果子限制不存在,則惡意賬戶可以對其他合約實施拒絕服務攻擊。攻擊者可以先與受害合約達成一致意見,然后在計算過程開始時插入一個無限循環,那么發送消息給受害合約或者受害合約的任何補救嘗試,都會使整個交易死鎖。
要求交易發送者而不是合約來支付gas,這樣大大增加了開發人員的可操作性。以太坊早期的版本是由合約來支付gas的,這導致了一個相當嚴重的問題:每個合約必須實現“門衛”代碼,確保每個傳入的消息為合約提供了足夠的以太幣供其消耗。
gas消耗計算有以下特點:
對于任何交易,都將收取21000gas的基本費用。這些費用可用于支付運行橢圓曲線算法所需的費用以及存儲交易所花費的硬盤和帶寬空間。
交易可以包括無限量的“數據”。虛擬機中的某些操作碼,可以讓收到這樣交易的合約訪問這些數據。數據的“固定消耗量”規則是:每個零字節4gas,非零字節68gas。這個公式的產生是因為用戶向合約發送的交易中,大部分的交易數據由一系列的32字節的參數組成,其中多數參數具有許多前導零字節。該結構看起來似乎效率不高,但由于壓縮算法的存在,實際上還是很有效率的。我們希望此結構能夠代替其他更復雜的機制:這些機制根據預期字節數嚴格包裝參數,從而導致編譯階段復雜性大增。這是三明治復雜模型的一個例外,但由于成本效益比,這也是合理的模型。
用于設置賬戶存儲項的操作碼SSTORE的消耗是:1)將零值改為非零值時,消耗20000gas;2)將零值變成零值,或非零值變非零值,消耗5000gas;3)將非零值變成零值,消耗5000gas;此外,交易執行成功后會退回15000gas。退款金額上限是交易消耗gas總額的50%。這給了人們小小激勵去清除存儲項。我們注意到,正因為缺乏這樣的激勵,許多合約的存儲空間沒有被有效使用,從而導致了存儲數據的快速膨脹。這一設計既能提供“為存儲項持續收取租金”模式的大部分好處,又不會失去合約一旦確立就可以永久存在的保證。延遲退款機制是必要的,因為可以阻止拒絕服務攻擊:攻擊者可以發送一筆含有少量gas的交易,循環清除大量的存儲項,直到用光gas,這樣消耗了大量的驗證算力,但實際并沒有真正清除存儲,也不需要付出很多gas。50%的上限的是為了確保打包交易的礦工依然能夠確定執行交易的計算時間的上限。
合約提供的消息數據是沒有成本的。因為在消息調用期間不需要實質復制任何數據,調用數據可以簡單地視為指向父合約memory的指針,該指針在子進程執行時不會改變。
Memory是一個可以無限擴展的數組,然而,每擴展32字節的memory就會消耗1gas的成本,不足32字節以32字節計。
某些操作碼的計算時間極度依賴參數,gas開銷計算是動態變化的。例如,EXP的的開銷是指數級別的。復制操作碼的開銷是1gas1gas/32字節。Memory擴展的開銷不包含在這里。如若包含,會變成一個平方攻擊向量。
如果值不是零,操作碼CALL會額外消耗9000gas。這是因為任何值傳輸都會引起歸檔節點的歷史存儲顯著增大。請注意,操作的?實際消耗?是6700;但是此基礎上,我們強制增加了一個自動給予接收者的gas值,這個值最小2300。這樣做是為了讓接受交易的錢包至少有足夠的gas來生成log。
Gas機制的另一個重要部分是gas價格本身體現出的經濟學原理。比特幣中,默認的方法是采取純粹自愿的收費方式,礦工扮演守門人的角色并且動態設置收費的最小值。以太坊中允許交易發送者設置任意數目的gas。這種方式在比特幣社區非常受歡迎,因為它是“市場經濟”的體現:允許礦工和交易者之間依據供需關系來決定價格。然而,這種方式的問題是,交易處理并不遵循市場原則。盡管可以將交易處理看作是礦工向發送者提供的服務,但實際上礦工所處理的每個交易都必須由網絡中的每個節點處理,所以交易處理的大部分成本都由第三方機構承擔,而不是決定是否處理它的礦工。因此,“公地悲劇”問題很有可能發生。
當前,因為缺乏礦工在實際中的行為的明確信息,所以我們將采取一個非常簡單公平的方法:投票系統,來設定單個區塊可消耗的gas總額。礦工有權將在最新區塊的gas上限基礎上變更0.0975%(1/1024),作為當前區塊的gas上限。所以最終的gas上限應該是礦工們設置的中間值。我們希望將來能夠采用軟分叉的方法來使用更加精確的算法。
虛擬機
以太坊虛擬機是執行交易代碼的引擎,也是以太坊與其他系統的核心區別。請注意,虛擬機應該同“合約與消息模型”分開考慮。例如,SIGNEXTEND操作碼是虛擬機的一個功能,但實際上“某個合約可以調用其他合約并指定子調用的gas限定值”是“合約與消息模型”的一部分。
EVM的設計目標如下:
簡單:操作碼盡可能的少并且低級;數據類型盡可能少;虛擬機的結構盡可能少;
結果明確:在VM規范中,沒有任何可能產生歧義的空間,結果應該是完全確定的。此外,計算步驟應該是精確的,以便可以測量gas的消耗量;
節約空間:EVM組件應盡可能緊湊;
為預期用途而特化:在VM上構建的應用應能處理20字節的地址,以及32位的自定義加密值,擁有用于自定義加密的模數運算、讀取區塊和交易數據與狀態交互等能力;
簡單安全:為了讓VM不被利用,應該能夠容易地讓建立一套gas消耗成本模型的操作;
優化友好:應該易于優化,以便即時編譯和VM的加速版本能夠構建出來。
同時EVM也有如下特殊設計:
臨時/永久存儲的區別:我們先來看看什么是臨時存儲和永久存儲。臨時存儲:存在于VM的每個實例中,并在VM執行結束后消失。永久存儲:存在于區塊鏈狀態層。假設執行下面的樹:
A調用B;
B設置?B.S=5,B.M=9?;
B調用C;
C調用B。
此時,如果B試圖讀取?B.S?,它將得到B前面存入的數據,也就是5;但如果B試圖讀取?B.M?,它將得到0,因為B.M是臨時存儲,讀取它的時候是虛擬機的一個新的實例。在一個內部調用中,如果設置?B.M=13?和?B.S=17?,然后內部調用和C的調用都終止、回到了B的外部調用,此時讀取M,將會看到?B.M=9?,?B.S=17?。如果B的外部調用結束,然后A再次調用B,將看到?B.M=0,B.S=17?。這個區別的目的是:1.每個執行實例都分配有內存空間,不會因為循環調用而減損,這讓安全編程更加容易。2.提供一個能夠快速操作的內存形式:因為需要修改樹,所以存儲更新必然很慢。
棧/memory模式:早期,計算狀態有三種:棧,內存,存儲項。在臨時存儲端,棧和內存的替代方案是memory-only范式,或者是寄存器和內存的混合體。在這種情況下,每個指令都有三個參數,例如:?ADDR1R2R3:M=MM?。選擇棧范式的原因很明顯,它使代碼縮小了4倍。
單詞大小32字節:在大多數結構中,如比特幣,單詞大小是4或8字節。4或8字節對存儲地址和加密計算來說局限性太大了。而不對大小作限制又很難建立相應安全的gas模型。32字節是一個理想大小,因為它足夠存儲下許多密碼算法所需要的大數值以及地址,又不會因為太大而導致效率低下。
我們有自己的虛擬機:我們的虛擬機使用java、Lisp和Lua等語言開發。我們認為開發一款專業的虛擬機是值得的,因為:1)我們的VM規范比其他許多虛擬機簡單的多,因為其他虛擬機為復雜性付出的代價更小,也就是說它們更容易變得復雜;然而,在我們的方案中每額外增加一點復雜性,都會給集約化發展帶來障礙,并帶來潛在的安全缺陷,比如共識錯誤,這就讓我們的復雜性成本很高;2)我們的VM更加專業化,如支持32字節;3)我們不會有復雜的外部依賴,復雜的外部依賴會導致我們安裝失敗;4)完善的審查機制,可以具體到特殊的安全需求;即使使用外部VM,也無法節省太多工作量。
使用了可變、可擴展的memory大小:固定memory的大小是不必要的限制,太小或太大都不合適。如果內存大小是固定的,每次訪問內存都需要檢查訪問是否超出邊界,顯然這樣的效率并不高。
1024調用深度限制:許多編程語言在內存還沒有溢出時,就因為調用深度太深而崩潰了。所以僅使用區塊gas上限一種限制是不夠的。
無類型:只是為了簡潔。不過,DIV、SDIV、MOD、SMOD會使用有符號或無符號的操作碼;轉換成定點運算在所有情況下都很簡單,例如,在32位長度下,a*b->(a*b)/2^32,a/b->a*2^32/b?,、-和*在整數下不變。
校對注:在原譯本中還有如下一段,但其對應段落在當前版本的原文中已經刪除了:?棧大小沒有限制:沒什么特別理由!許多情況下,該設計不是絕對必要的;因為,gas的開銷和區塊gas上限總是會充當每種資源消耗的上限。
這個VM中某些操作碼的功能和用意很容易理解,但也有一些不太好理解,以下是一些特殊的原因:
ADDMOD,MULMOD:大多數情況下,?mulmod(a,b,c)=a*b%c?,但在橢圓曲線算法中,使用的是32字節模數運算,直接執行?a*b%c?實際上是在執行?((a*b)%2^256)%c?,會得到完全不同的結果。在32字節的空間中執行32字節數值的?a*b%c?計算的共識非常困難且繁瑣。
SIGNEXTEND:SIGNEXTEND操作碼的作用是為了方便從大的有符號整數到小的有符號整數的類型轉換。小的有符號整數是很有用的,因為未來的即時編譯虛擬機也許有能力檢測主要處理32字節整數又長時間運行的代碼塊,小的有符號整數能加快處理。
SHA3:在以太坊代碼中,SHA3作為安全的、高強度的、不定長數據哈希映射方法,應用非常廣泛。通常,在使用存儲器時,需要使用Hash函數來防止惡意沖突,在驗證默克爾樹和類似的以太坊數據結構時也需要使用到Hash函數。重要的是,與SHA3的相似的哈希函數,如SHA256、ECRECVOR、RIPEM160,不是以操作碼的形式包含在里面,而是以偽合約的形式。這樣做的目的是將它們放在一個單獨的類別中,如果當我們以后提出適當的“原生插件”系統時,可以添加更多這樣的合約,而不需要擴展操作碼。
ORIGIN:ORIGIN操作碼由交易的發送者提供,主要的作用是允許合約退回支付的gas。
COINBASE:COINBASE的主要作用是:1)允許子貨幣對網絡安全作出貢獻;2)使礦工能夠作為一個去中心化的經濟體,來設置基于子共識的應用,如Schellingcoin。
PREVHASH:PREVHASH可用作一個半安全的隨機來源。此外,允許合約求值上一個區塊的默克爾樹狀態證明,而不需要高度復雜的“以太坊輕客戶端”遞歸結構。
EXTCODESIZE,EXTCODECOPY:主要的作用是讓合約依據模板檢查其他合約的代碼,甚至是在與其他合約交互前,模擬它們。見:https://lesswrong.com/lw/aq9/decision_theories_a_less_wrong_primer/
JUMPDEST:當跳轉目的地限制在幾個索引時,JIT虛擬機實現起來更簡單。于是,我們需要:1)對有效變量跳轉目的地做限制;2)激勵使用靜態而不是動態跳轉。為了達到這兩個目標,我們定下了以下規則:1)緊接著push后的跳轉可以跳到任何地方,而不僅是另一個jump;2)其他的jump只能跳轉到JUMPDEST。對跳轉的限制是必須的,這樣就可通過查看代碼中的前一個操作來確定當前是一個靜態跳轉還是動態跳轉。缺乏對靜態跳轉的需求是激勵使用它們的原因。禁止跳轉進入push數據也會加快JIT虛擬機的編譯和執行。
LOG:LOG是事件的日志。
CALLCODE:該操作碼允許合約使用自己的存儲項,在單獨的棧空間和memory中調用其他合約的“函數”。這樣可以在區塊鏈上靈活實現標準庫代碼。
SELFDESTRUCT:允許合約刪除它自己,前提是它已經不需要存在了。SELFDESTRUCT并非立即執行,而是在交易執行完之后執行。這是因為如果允許SELFDESTRUCT在執行之后回滾,將會極大地提高緩存的復雜度,不利于高效的VM實現。
PC:盡管理論上不需要PC操作碼,因為所有PC操作碼的實例都可以根據將push操作的索引加入實際程序計數器來代替實現,但使用PC可以創建獨立代碼的位置。
原文鏈接:
https://eth.wiki/en/fundamentals/design-rationale
作者:?Vitalik
翻譯&校對:?kim?&?阿劍
從今年3月份開始關注BZZ并參加測試網測試,再到主網上線前的跳票,這小半年時間礦工可以說被SWARM項目方按在地上摩擦玩弄,但最近在Youtube和一些圈內渠道發現.
1900/1/1 0:00:00尊敬的ZT用戶: ZT創新板即將上線ROGE,并開啟ROGE/USDT交易對。具體上線時間如下:充值:已開啟;交易:2021年7月9日16:30; ROGE CZ回復關于TUSD數據推文,TRU.
1900/1/1 0:00:00本文系鏈捕手原創文章,作者谷昱。今日凌晨,跨鏈橋項目Anyswap發布公告表示,新推出的V3跨鏈流動性池在昨日凌晨遭到黑客攻擊,總計損失為239萬USDC與550萬MIM,黑客地址為0x0ae1.
1900/1/1 0:00:00可信網絡基金會主席、假新聞鑒別軟件WordProof創始人兼首席執行官SebastiaanvanderLans發文《區塊鏈可以幫助出版商提高受眾信任度》.
1900/1/1 0:00:00尊敬的用戶: XBX重磅上線,WBF攜手XBX推出“XBX重磅上線空投福利享不停”特別活動,助力用戶參與XBX/USDT交易!活動期間:2021年7月13日12:00-2021年7月17日12:.
1900/1/1 0:00:00一、項目介紹 AxieInfinity是在以太坊區塊鏈上構建的,受神奇寶貝啟發的數字寵物世界,任何人都可以通過熟練的游戲玩法和對生態系統的貢獻來獲得代幣獎勵.
1900/1/1 0:00:00