以太坊下一階段硬分叉內容簡介

NIC Lin
Taipei Ethereum Meetup
12 min readSep 12, 2017

原先規劃的Metropolis硬分叉將分為兩次的硬分叉。目前預計在九月底(更新為2017/10/17,第4370000區塊)進行第一次的硬分叉Byzantium,第二次的硬分叉Constantipole時間未定。

source: pixabay

Byzantium包含了以下EIP:
EIP 100:調整難度算法,將uncle區塊納入計算考量
EIP 140:EVM新增 REVERT opcode,終止交易執行但不沒收gas
EIP 196:橢圓曲線純量加法乘法的precompiled contract
EIP 197:橢圓曲線配對驗證的precompiled contract
EIP 198:Bit integer做指數模數運算的precompiled contract
EIP 211:EVM新增兩個opcode,讓合約函式能回傳任意長度的資料
EIP 214:EVM新增 STATICCALL opcode,來確保某些呼叫不會改寫合約狀態
EIP 649:延後難度炸彈並降低區塊獎賞
EIP 658:將交易執行的結果寫在Receipt中,讓使用者可以藉此得知交易執行是否順利

Constantipole包含以下EIP:
EIP 86:新增NULL_SENDER機制和CREATE2 opcode,將帳戶和簽章機制抽象化
EIP 96:將block hash資料寫進precompiled contract裡

EIP 100:調整難度算法,將uncle區塊納入計算考量

目前難度算法的考量包含 (1)和上一個區塊間隔的時間 和 (2)上一個區塊的難度。硬分叉之後將新增一個考量因素 — 上一個區塊包含的uncle區塊數量。

因為uncle區塊也會有獎賞,所以把uncle區塊納入考量將可以使發行量更穩定。

EIP 140:EVM新增REVERT opcode,終止交易執行但不沒收gas

目前拋出例外狀況的唯一方式是 throwrevert ),其對應到一個沒有被記錄的opcode,所以會觸發invalid opcode的例外狀況。這個例外會將該呼叫所做的任何資料更動還原,並沒收剩下的gas,這讓除錯變得很困難。硬分叉之後將新增 REVERT opcode,在遇到例外狀況時,會復原該呼叫所做的資料更動,但不會將剩下的gas沒收,且可以回傳資訊給執行呼叫的函式。

注意如果在Solidity裡使用sendcallcallcodedelegatecall語法且在執行時觸發例外,將只會復原呼叫後所做的更動並回傳1或0,而不是終止執行並將所有做的更動還原。

invalid opcode例外在Solidity會由throwassert觸發;REVERT在Solidity對應到revert()require語法。

EIP 196:橢圓曲線純量加法乘法的precompiled contract
EIP 197:橢圓曲線配對驗證的precompiled contract

為了避免zkSNARK運算的gas消耗超過區塊gas限制,將zkSNARK會需要用到的橢圓曲線運算變成precompiled contract。

zkSNARK將可以提昇使用者的匿名性並增加交易的隱私性,也可以幫助整體的scalability(例如其產生的proof大小會比未來sharding技術用到的data availability proof的大小還小)。

注意,目前此EIP使用的橢圓曲線為alt_bn128,已經有其他EIP提議為其他橢圓曲線如scep256k1建立運算的precompiled contract。

EIP 198:Bit Integer做指數模數運算的precompiled contract

為了避免大數運算例如RSA驗證運算消耗的gas太大,將大數的指數模數運算變成precompiled contract。

未來將可以對目前廣泛應用的RSA簽章做驗證,銜接更多主流的應用。

EIP 211:EVM新增兩個opcode,讓合約函式能回傳任意長度的資料

目前合約呼叫的回傳值必須是固定長度的,即在呼叫前就必須知道回傳值的大小。因此呼叫沒辦法回傳動態長度的值像是字串和動態陣列。雖然EIP 5 和 EIP 8已經提出過回傳動態資料的提議,但因其回傳大小需要在呼叫時指定,因此gas的計算和呼叫(CALL , CALLCODE , DELEGATECALL)的gas是綁在一起的,這會讓呼叫的gas計算變得麻煩。

硬分叉之後新增 RETURNDATASIZERETURNDATACOPY opcode,讓呼叫可回傳動態長度的資料,並直接按資料長度扣除相對應的gas。

這些回傳值不會被寫入Memory,而是被保存在另外的Buffer裡。回傳值會被保留直到下一個呼叫被觸發為止。

EIP 214:EVM新增STATICCALL opcode,來確保某些呼叫不會改寫合約狀態

為避免合約呼叫(CALLCALLCODEDELEGATECALL)期間狀態修改到狀態導致無法預期的錯誤,新增了一個STATICCALL opcode,讓預期不會修改狀態的呼叫(constant)在修改到狀態時立刻終止執行並拋出例外。

STATICCALL 的效用會延續到子呼叫。假設A使用STATICCALL呼叫B,B呼叫C並在C裡修改了狀態,則依然會立刻終止執行並拋出例外。這提供了另一層的re-entrancy攻擊的保護。

注意,部署新的合約也會被視為修改狀態。被視為修改狀態的執行如下:
CREATECREATE2LOG[0-4]SSTORESELFDESTRUCT 、附帶ether的CALL

EIP 649 — 延後難度炸彈並降低區塊獎賞

目前預估的區塊時間
九月下半,30秒
十月底,39秒

難度炸彈預計延後約一年半,同時區塊獎賞從5 ether降低至3 ether,uncle區塊的獎賞也依比例調降。

難度炸彈確定被延後,在Byzantium硬分叉之後難度會突然被調回至以往的每15秒一個區塊左右。如果區塊獎賞沒有改變,會突然讓ether暴增(相比於硬分叉前),因此在難度調回正常的同時,降低區塊獎賞,讓平均產出的ether總量能維持在和硬分叉之前差不多。

有人發起了拒絕降低區塊獎賞的硬分叉宣傳。

EIP 658:將交易執行的結果寫在Receipt中,讓使用者可以藉此得知交易執行是否順利

EIP 98提議將Receipt裡的state root欄位移除,EIP 658結合EIP 98將state root欄位改成填入1 byte的資料。如果值為1,代表該筆交易執行成功;0則表示gas用完或執行時拋出例外。

硬分叉之前區塊中的每筆交易是有順序性的,交易按照編號執行,每執行完一個交易會將執行完當下的state結果存至state root欄位中(存merkle root),下一筆交易正常要接續這個state來繼續執行。藉由state root,我們可以很快製作出一個proof來證明某區塊中的某筆交易B是不合法的。我們只需要上一筆交易A執行完的state root,並以這個state來接續執行交易B,最後確認執行完的state是否和交易B所說的state一樣。這讓light client能更放便快速的驗證某筆交易的正確性。

拿掉state root之後,便無法產生針對某筆不合法交易的proof,而必須是針對一整個區塊的proof。light client將必須下載整個區塊的資料來驗證。雖然這會增加light client的負擔,但目前因為驗證bloom filter資料亦需要light client去下載整個區塊,所以造成的負擔提升不大。

這同時也是在為未來交易處理平行化鋪路 — 未來交易之間不會有順序性,可參考EIP 648

EIP 86:新增NULL_SENDER機制和CREATE2 opcode,將帳戶和簽章機制抽象化

硬分叉後,新增NULL_SENDER,讓使用者可以把交易的發起人(from欄位)設為0。在處理這種特殊交易時,會將交易的目標(to欄位)當作真正的交易發起人,並執行該帳戶的合約,並且由該帳戶來支付手續費。想像未來每個帳戶都是合約,使用者將執行的邏輯寫入這些合約,這些合約就代表使用者。你可以將合約設計成多簽章合約、使用抗量子計算的演算法或其他零知識證明會用到的外星科技,而不再被強制只能使用目前的橢圓曲線演算法。

另外這種特殊交易也不會有nonce機制,意即沒有原生的replay protection。使用者必須自己在合約裡設計nonce機制,如果沒有的話,其他人將可以一直重複送同樣一筆交易,最後把合約裡的錢都消耗完。

包含nonce,其他參數如to(真正的to,即使用者原本要互動的對象)、valuegaspricedata、簽名等都要當作參數塞進交易的data欄位中。
目前的交易長這樣:
(nonce=xxx, to=xxx, value=xxx, gas=xxx, gasprice=xxx, data=xxx)
使用NULL_SENDER的交易長這樣:
(nonce=0, to=0, value=0, gas=xxx, gasprice=0, data=[nonce=xxx, to=xxx, value=xxx, gasprice=xxx, data=xxx]),data裡可以是使用者自定的格式,不一定要有noncegasprice等等的。

新增 CREATE2 opcode,讓合約位置的產生變成是可預測的。新合約的地址由(1)交易發起人的地址、(2)合約程式碼的雜湊值和(3)任意指定的值來決定,因此使用者在合約程式碼完成後便可以知道合約會被部署到哪個地址。

也因為手續費不再由交易發起人支付,加上交易是否成功皆受合約的邏輯所影響。因此礦工必須要親自執行交易才知道他會不會收到手續費,這對礦工增加不少風險。一些解決方法如白名單、或事先掃過合約程式碼,確認合約餘額、優先選擇gas較少的交易等等,預計這種交易要成為主流還需要很長的時間。

EIP 96:將block hash資料寫進precompiled contract裡

將block hash資料寫進precompiled contract裡。硬分叉之後,讀取特定區塊的block hash不需要VM再透過其他方式取回,而是透過一個呼叫去procompiled contract裡讀取變數。

這讓block hash變成鏈的state的一部分。(1)這使得VM讀取block hash時不必抽離執行到其他地方取回雜湊值,而是維持執行,將雜湊值讀取視為一個單純的合約呼叫,降低協議設計的複雜度。

(2)這讓區塊和更久遠的區塊之間建立直接的連結。目前一個區塊與一個區塊間是用prev_block_hash建立連結,而區塊和十個區塊之前的區塊的連結是透過中間十個prev_block_hash建立的連結。在硬分叉之後,以前的block hash會存state裡,代表這會是區塊的一部分資料。如果我能驗證某區塊A的block hash值存在在區塊B的state裡,就能證明他們的直接連結關係。這會讓light client驗證速度大幅提升。

注意,並非全部的block hash值都存在precompiled contract裡,合約要讀取block hash仍然只能讀去過去256個區塊的block hash值。例如編號500的區塊裡的precompiled contract會存編號244到編號499的區塊的block hash,但編號501區塊的precompiled contract裡會用編號500區塊的block hash覆寫掉編號244區塊的block hash。

這裡有其他EIP和ISSUE的整理提供參考,目前還未整理完全。

Reference:

[1]https://github.com/ethereum/EIPs/pull/609/files
[2]https://github.com/ethereum/EIPs#accepted-eips-planned-for-adoption
[3]https://github.com/ethereum/EIPs/issues/100
[4]https://github.com/ethereum/EIPs/pull/206
[5]https://github.com/ethereum/EIPs/pull/213/files
[6]https://github.com/ethereum/EIPs/pull/198
[7]https://github.com/ethereum/EIPs/pull/211
[8]https://github.com/ethereum/EIPs/pull/214
[9]https://github.com/ethereum/EIPs/issues/649
[10]https://github.com/ethereum/pm/issues/21#issuecomment-324811376
[11]http://www.cahf.co
[12]https://github.com/ethereum/EIPs/issues/648
[13]https://github.com/ethereum/EIPs/pull/208/files
[14]https://github.com/ethereum/EIPs/pull/208#issuecomment-313872489
[15]https://github.com/ethereum/EIPs/pull/210

--

--