您好,登錄后才能下訂單哦!
這篇文章主要介紹“區塊鏈以太坊上的分片技術是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“區塊鏈以太坊上的分片技術是什么”文章能幫助大家解決問題。
譯者序
2017年8月,比特幣網絡進行了硬分叉,產生了比特幣現金(Bitcoin Cash),這個硬分叉的技術解釋就是對比特幣網絡進行“擴容”。比特幣現金網絡中的區塊大小為8M,是比特幣網絡區塊大小的8倍(比特幣網絡區塊大小為1M),從而提高了每個區塊的交易容量,反映為網絡整體吞吐量的提高。而其他使用了與比特幣網絡類似的數據存儲形式的加密貨幣網絡,也必將伴隨交易量的增加逐漸開始需要面對“擴容”的問題。“分片”(Sharding)就是以太坊網絡為了解決擴容問題而設計的一種技術方案。
“分片”的大致設計思路是:將區塊鏈網絡中的每個區塊變為一個子區塊鏈,子區塊鏈中可以容納若干(目前為100個)打包了交易數據的Collation(大概可以稱為“校驗塊”,為了在分片的情景中將其與區塊的概念區分開),這些Collation最終組成一個在主鏈上區塊;因為這些Collation是整體作為區塊存在的,所以其數據必定是全部由某個特定的礦工所打包生成,本質上和現有協議中的區塊沒有區別,所以不再需要增加額外的網絡確認。這樣,每個區塊的交易容量就大概擴大了100倍;而且這種設計還有利于未來的繼續擴展,整個擴展計劃目前也被大致分為4個階段;本文所介紹的僅僅是第一階段的相關實現細節。
本文的目的是為那些希望理解分片建議詳情,乃至去實現它的朋友提供一份相對完整的細節說明和介紹。本文僅作為二次分片(quadratic sharding)的第一階段的描述;第二、三、四階段目前不在討論范圍,同樣,超級二次分片(super-quadratic sharding)(“Ethereum 3.0”) 也不在討論范圍。
假設用變量 c 來表示一個節點的有效計算能力,那么在一個普通的區塊鏈里,交易容量就被限定為 O(c),因為每個節點都必須處理所有的交易。二次分片的目的,就是通過一種雙層的設計來增加交易容量。在第一層中是不需要硬分叉的,主鏈就保持原樣。不過,一種被稱為校驗器管理合約(validator manager contract,VMC)的合約需要被發布到主鏈上,它用來維持分片系統。這個合約中會存在 O(c) 個 分片 (目前為100),每個分片都像是個獨立的“銀河”:它具有自己的賬戶空間,交易需要指定它們自己應該被發布到哪個分片中,并且分片間的通信是受限的(事實上,在第一階段,不存在這種通信能力)。
分片運行在一個普通的符合最長鏈規則的權益證明系統中,權益數據將保存在主鏈上(具體來說,是在VMC中)。所有分片共享一個通用驗證器池,這也意味著:任何通過VMC注冊的驗證器,理論上都可以在任意時間被授權來在任意分片上創建區塊。每個分片會有一個 O(c) 的區塊大小/gas上限(block size/gas limit),這樣,系統的整體容量就變成了 O(c^2) 。
分片系統中的大多數用戶都會運行兩部分程序。(i) 一個在主鏈上的全節點(需要 O(c) 資源)或輕量節點(需要 O(log(c)) 資源)。 (ii) 一個通過RPC與主鏈交互的“分片客戶端”(由于這個客戶端同樣運行在當前用戶的計算機中,所以它被認為是可信的);它也可以作為任意分片的輕客戶端、作為特定分片的全客戶端(用戶需要指定他們正在“監視”某個特定的分片),或者作為一個驗證器節點。在這些情況下,一個分片客戶端的存儲和計算需求也將不會超過 O(c) (除非用戶指定他們正在監視 每個 分片;區塊瀏覽器和大型的交易所可能會這么做)。
在本文中, Collation (校對塊)被用來與 Block (區塊)相區別,因為: (i) 它們是不同的RLP(Recursive Length Prefix)對象:交易是第0層的對象,collation是用來打包交易的第一層的對象,而block則是用來打包collation(header)的第二層的對象; (ii) 在分片的情景中這更加清晰。通常, Collation 必須由 CollationHeader (校對塊頭)和 TransactionList (交易列表)組成; Collation 的詳細格式和 Witness (見證人)會在 無狀態客戶端 那節定義。 Collator (校對器)是由主鏈上 驗證器管理合約 的 getEligibleProposer 函數所生成的示例。算法會在隨后的章節中介紹。
常量
LOOKAHEAD_PERIODS: 4
PERIOD_LENGTH: 5
COLLATION_GASLIMIT: 10,000,000 gas
SHARD_COUNT: 100
SIG_GASLIMIT: 40000 gas
COLLATOR_REWARD: 0.001 ETH
驗證器管理合約(Validator Manager Contract,VMC)
我們假定VMC存在于地址 VALIDATOR_MANAGER_ADDRESS 上(在已有的“主分片”上),它支持下列函數:
deposit(address validationCodeAddr, address returnAddr) returns uint256 :添加一個驗證器到驗證器集合中,驗證器的大小就是函數調用時的 msg.value (比如存入的以太幣數量)。這個函數會返回驗證器的索引號。 validationCodeAddr 用來存儲驗證代碼的地址,這里的“驗證代碼”指一個單純的函數,這個函數需要一個32字節的哈希值和一個簽名作為輸入,如果簽名與哈希值匹配則返回1,否則返回0。如果validationCodeAddr這個地址存儲的代碼不能通過 [單純性檢查合約] 的單純性驗證,deposit函數就會失敗。這里的“單純性驗證”包含了對“驗證代碼”的實際的靜態檢查以確保它的輸出僅僅依賴于它的輸入,而不會受任何狀態的影響;它的代碼沒有使用任何會影響狀態的操作碼(opcode);并且它的運行也不會導致狀態的變動(比如,來防止攻擊者創建這樣的惡意“驗證代碼”:當用它校驗投票表決時返回true,但當驗證器行為不端、甚至已經有行為不端者的證據被提供給這個函數時,它又返回false)。
withdraw(uint256 validatorIndex, bytes sig) returns bool :校驗簽名的正確性(例如,一個以0值和 sha3("withdraw") + sig 作為數據,附帶了200000個gas的,對 validationCodeAddr 的調用返回1),如果正確,它會將驗證器從驗證器集合中移除,并退還存入的以太幣。
getEligibleProposer(uint256 shardId, uint256 period) returns address :使用一個區塊哈希(block hash)作為種子基于預設的算法從驗證器集合中選擇一個簽名者(signer)。驗證器被選中幾率應該與其存款數量成正比。這個函數應該可以返回一個當前周期內的值或者以 LOOKAHEAD_PERIODS 為上限的任意未來周期內的值。
addHeader(bytes header) returns bool :嘗試處理一個collation header,成功時返回true,失敗時返回false。
getShardHead(uint256 shardId) returns bytes32 :返回驗證器管理合約內由參數所指定的分片的header哈希。
這里也有一個日志類型:
CollationAdded(indexed uint256 shard_id, bytes collationHeader, bool isNewHead, uint256 score)
校對塊頭(Collation Header)
我們首先以一個有下列內容的RLP列表來定義一個“collation header”:
[ shard_id: uint256, expected_period_number: uint256, period_start_prevhash: bytes32, parent_collation_hash: bytes32, tx_list_root: bytes32, coinbase: address, post_state_root: bytes32, receipts_root: bytes32, sig: bytes ]
這里:
shard_id 分片的ID;
expected_period_number 是collation希望被包含進的周期序號,這是由 period_number = floor(block.number / PERIOD_LENGTH) 計算出來的;
period_start_prevhash 前一區塊,即區塊 PERIOD_LENGTH * expected_period_number - 1 的區塊哈希(這其實就是希望被包含進的周期起始區塊之前的最后一個區塊的哈希)。分片中使用區塊數據的操作碼(例如NUMBER和DIFFICULTY)會使用這個區塊的數據,除了COINBASE操作碼,它會使用分片的coinbase;
parent_collation_hash 是父collation的哈希;
tx_list_root 是包含在當前collation中的交易數據的查找樹(trie)根哈希;
post_state_root 是分片中當前collation之后的新狀態根;
receipts_root 是收據查找樹(receipt trie)根哈希;
sig 是一個簽名。
當 addHeader(header) 的調用返回true時,** collation header **有效。驗證器管理合約會在滿足下列條件時這么做:
shard_id 是0到 SHARD_COUNT 之間的數值;
expected_period_number 與當前周期號相等(比如 floor(block.number / PERIOD_LENGTH) )
一個具有相同的分片 parent_collation_hash 的collation已經被接受;并且
sig 是一個有效的簽名。就是說,如果我們計算 validation_code_addr = getEligibleProposer(shard_id, current_period) ,然后使用 sha3(shortened_header) ++ sig (這里的 shortened_header 是“collation header” 去掉 sig之后的RLP編碼格式)來調用 validation_code_addr 的話,調用結果應該為1。
當滿足以下條件時,** collation **有效:
(i) 它的“collation header”有效;
(ii) 在 parent_collation_hash 的 post_state_root 上執行collation的結果為給定的 post_state_root 和 receipts_root ;
并且 (iii) 總共使用的氣(gas)小于等于 COLLATION_GASLIMIT 。
Collation狀態轉換函數
執行一個collation時的狀態轉換處理如下:
按順序執行由 tx_list_root 所指定的樹上的每個交易;并且
將 COLLATOR_REWARD 的獎勵分配給coinbase。
getEligibleProposer 的細節
這里是用Viper寫的一個簡單實現:
當驗證器被要求在一個給定的分片上創建區塊時,一個驗證器僅會被給予數分鐘的通知(準確地說,就是持續 LOOKAHEAD_PERIODS * PERIOD_LENGTH 個區塊的通知)。在Ethereum 1.0中,創建一個區塊需要為驗證交易而訪問全部的狀態。這里,我們的目標是避免需要驗證器保留整個系統的狀態(因為這樣就將使運算資源需求變為 O(c^2) 了)。取而代之,我們允許驗證器在僅知曉根狀態(state root)的情況下創建collation,而將其他責任交給交易發送者,由他們提供“見證數據”(witness data),例如Merkle分支,以此來驗證交易對賬戶產生影響的“前狀態”(pre-state),并提供足夠的信息來計算交易執行后的“后狀態根”(post-state root)。
(應該注意到,使用非無狀態范式(non-stateless paradigm)來實現分片,理論上是可能的;然而,這需要: (i) 租用存儲空間來保持存儲的有界性;并且 (ii) 驗證器需要使用 O(c) 的時間在一個分片中創建區塊。上述方案避免了對這些犧牲的需求。)
數據格式
我們修改了交易的格式,以使交易必須指定一個 訪問列表 來列舉出它可以訪問的狀態(后邊我們會更精確的描述這點,這里不妨把它想象為是一個地址列表)。任何在VM執行過程中試圖讀寫交易所指定的訪問列表以外的狀態,都會返回一個錯誤。這可以防止這樣的攻擊:某人發送了一個消耗5百萬gas的隨機執行,然后試圖訪問一個交易發送者和collator都沒有見證人的隨機賬戶;可以防止collator包含進像這樣浪費collator時間的交易。
交易發送者必須指定“見證人”(witness),這在被簽名的交易體 之外 ,但也被打包進交易。這里的見證人是一個Merkle樹節點的RLP編碼的列表(RLP-encoded list),它是由交易在其訪問列表中所指定的狀態的組成部分。這使collator僅使用狀態根就可以處理交易。在發布collation的時候,collator也會發送整個collation的見證人。
交易打包格式
Collation格式
也請參考 ethresearch 上的帖子 無狀態客戶端的概念 。
無狀態客戶端狀態轉換函數
通常,我們可以將傳統的“有狀態”客戶端執行狀態轉換的函數描述為: stf(state, tx) -> state' (或 stf(state, block) -> state' )。在無狀態客戶端模型中,節點不保存狀態,所以 apply_transaction 和 apply_block 可以寫為:
這里, state_obj 是一個數據元組,包含了狀態根和其他 O(1) 大小的狀態數據(使用的gas、receipts、bloom filter等等); witness 就是見證人; block 就是區塊的余下部分。其返回的輸出是:
一個新的 state_obj 包含了新的狀態根和其他變量;
從見證人那里讀取的對象集合(用于區塊創建);和
為了組成新的狀態查找樹而被創建的一組新的狀態對象。
這使得函數是“單純性的”(pure),僅處理小尺寸對象(small-sized objects)(相反的例子就是現行的以太坊狀態數據,現在已經 數百G字節 ),從而使他們可以方便地在分片中使用。
客戶端邏輯
一個客戶端應該有一個如下格式的配置:
如果指定了validator地址,那么客戶端會在主鏈上檢查這個地址是否是有效的validator。如果是,那么在每次在主鏈上開始一個新周期時(例如,當 floor(block.number / PERIOD_LENGTH) 變化的時候),客戶端將為所有分片的周期 floor(block.number / PERIOD_LENGTH) + LOOKAHEAD_PERIODS 調用 getEligibleProposer 。如果這個調用返回了某個分片 i 的驗證器地址,客戶端會運行算法 CREATE_COLLATION(i) (參考下文)。
對于 watching 列表中的每個分片 i ,每當一個新collation header出現在主鏈上,它就會從分片網絡中下載完整的collation,并對其進行校驗。它將內部保持追蹤所有有效的header(這里的有效性是回溯的,例如,一個header如果是有效的,那么他的父header也應該是有效的),并且將head具有最高得分的分片鏈接受為主分片鏈,同時從創世(genesis)collation到head的所有collation都是有效的和可用的。注意,這表示主鏈的重組 和 分片鏈的重組都將影響分片head。
逆向匹配候選head
為了實現監視分片的算法和創建collation,我們要做的第一件事就是使用下面的算法來按由高到低的順序匹配候選head。首先,假設存在一個(非單純的、有狀態的)方法 getNextLog() ,它可以取得某個還沒有被匹配的給定分片的最新的 CollationAdded 日志。這可以通過逆向匹配最新的區塊的所有日志來達成,即從head開始,反方向掃描receipt中的每個區塊。我們定義一個有狀態的方法 fetch_candidate_head :
用普通的語言重新表述,這里就是反向掃描 CollationAdded 日志(對正確的分片),直到獲得一個 isNewHead = True 的日志。首先返回那個日志,然后用從老到新的順序返回所有與那個日志分值相同的、 isNewHead = False 的所有最新日志。隨后到前一個 isNewHead = True 的日志(即確保分值會比前一個NewHead低,但比其他人高),再到這個日志之后的所有具有該分值的最新collation,而后到第四個。
這就是說這個算法確保了首先按照分值的由高到低、然后按照從老到新的順序檢查潛在的候選head。
例如,假定 CollationAdded 日志具有以下哈希和分值:
... 10 11 12 11 13 14 15 11 12 13 14 12 13 14 15 16 17 18 19 16
然后, isNewHead 將被按如下賦值:
... T T T F T T T F F F F F F F F T T T T F
如果我們將collation命名為 A1..A5、 B1..B5、 C1..C5 和 D1..D5 ,那么精確的返回順序將是:
D4 D3 D2 D1 D5 B2 C5 B1 C1 C4 A5 B5 C3 A3 B4 C2 A2 A4 B3 A1
監視一個分片
如果一個客戶端在監視一個分片,它應該去嘗試下載和校驗那個分片中的所有collation(檢查任何給定的collation,僅當其父collation已經被校驗過)。要取得head,需要持續調用 fetch_candidate_head() ,直到它返回一個被校驗過的collation,也就是head。通常情況下它會立即返回一個有效的collation,或者最多因為網絡延遲或小規模的攻擊導致生成過幾個無效或者不可用的collation,而需要稍微嘗試幾次。只有在遭遇一個真正長時間運行的51%攻擊時,這個算法會惡化到 O(N) 的時間。
CREATE_COLLATION
這個處理由三部分組成,第一部分可以被叫做 GUESS_HEAD(shard_id) ,其示意代碼如下:
fetch_and_verify_collation(c) 包含了從分片網絡取得 c 的所有數據(包括見證人信息)并校驗它們的處理。上述算法等價于“選取最長有效鏈,盡可能的檢查有效性,如果其數據無效,則轉而處理已知的次長鏈”。這個算法應該僅當校驗器執行超時時才會停止,這就是該創建collation的時候了。每個 fetch_and_verify_collation 的執行都應該返回一個“寫集合”(參考上文的“無狀態客戶端”那節)。保存所有這些“寫集合”,把它們組合在一起,就構成了recent_trie_nodes_db 。
我們現在可以來定義 UPDATE_WITNESS(tx, recent_trie_nodes_db) 了。在運行 GUESS_HEAD的過程中,某節點會接收到一些交易。當它要把交易(嘗試)包含進collation的時候,這個算法需要先運行交易。假定交易有一個訪問列表 [A1 ... An] 和一個見證人 W ,對于每個 Ai 使用當前狀態樹的根取得 Ai 的Merkle分支,使用 recent_trie_nodes_db 和 W 一起作為數據庫。如果原始的 W 正確,并且交易不是在客戶端做這些檢查之前就已經發出的話,那么這個取得Merkle分支的操作總是會成功的。在將交易包含進collation之后,狀態變動的“寫集合”也應該被添加到 recent_trie_nodes_db 中。
下面我們就要來 CREATE_COLLATION 了。作為例證,這里是這個方法中可能的、收集交易信息處理的完整示意代碼。
最后,有一個額外的步驟,最終確定collation(給collator發放獎勵,也就是 COLLATOR_REWARD的ETH)。這需要詢問網絡以獲得collator賬戶的Merkle分支。當得到網絡對此的回應之后,發放獎勵之后的“后狀態根”(post-state root)就可以被計算出來了。Collator就可以用 (header, txs, witness) 的形式打包這個collation了。這里,見證人(witness)就是所有交易的見證人和collator賬戶的Merkle分支。
交易的格式
交易的格式現在將變為(注意這里包含了 賬戶抽象 和 讀/寫列表 ):
完成交易的處理過程也將變為:
校驗 chain_id 和 shard_id 是正確的;
從 target 賬戶中減去 start_gas * gasprice wei;
檢查目標 account 是否有代碼,如果沒有,校驗 sha3(code)[12:] == target ;
如果目標賬戶為空,使用 code 作為初始代碼,在 target 中執行一個合約的創建;否則,跳過這個步驟;
執行一個消息,使用:剩余的gas作為startgas, target 作為地址,0xff...ff 作為發送者,0作為value,以及當前交易的 data 作為data;
如果上述任何一個執行失敗,并且消耗了 <= 200000 的氣(例如: start_gas - remaining_gas <= 200000 ),那么這個交易是無效的;
否則, remaining_gas * gasprice 將被退還,已支付的交易費將被添加到一個交易費計數(注意:交易費不會被直接加入coinbase余額,而是在區塊最終確認時立即添加)。
雙層查找樹重新設計
現存的賬戶模型將被替換為:在一個單層查找樹中收錄進所有賬戶的余額、代碼和存儲。具體來講,這個映射為:
賬戶X的余額: sha3(X) ++ 0x00
賬戶X的代碼: sha3(X) ++ 0x01
賬戶X的存儲鍵值K: sha3(X) ++ 0x02 ++ K
訪問列表
一個賬號的訪問列表看起來大概像這樣:
[[address, prefix1, prefix2...], [address, prefix1, prefix2...], ...]
從根本上說,這意味著:“這個交易可以訪問這里給定的所有賬戶的余額和代碼,并且賬戶列表中給出的每個賬戶的前綴中至少有一個是該賬戶存儲的一個鍵的前綴”。我們可以將其轉換為“前綴列表格式”,基本上就是一個內部存儲查找樹的前綴列表(參考前面的章節):
我們可以通過取得交易的訪問列表,將其變換為前綴列表格式,然后對前綴列表中的每個前綴執行 get_witness_for_prefix ,并將這些調用結果組成一個集合;以此來計算某個交易見證人。
get_witness_for_prefix 會返回查找樹節點中可以訪問以指定前綴開始的所有鍵值的一個最小集合。
在EVM中,任何嘗試對訪問列表以外的賬戶的訪問(直接調用、SLOAD或者通過類似 BALANCE或 EXTCODECOPY 的opcode的操作)都會導致運行這種代碼的EVM實例拋出異常。
請參考ethresearch上的帖子 賬戶讀/寫列表 。
gas的消耗
待定。
通過分離區塊proposer和collator,我們實現了二次擴展,這是一種快速、不徹底的中等安全權益證明分片,以此在不對協議或軟件架構做太多更改的情況下增加了大約100倍的吞吐量。這也被用來作為一個完整的二次分片多階段計劃的第一階段,后續階段大致如下:
第二階段(two-way pegging,即雙向限定) :參考 USED_RECEIPT_STORE 章節,仍在撰寫;
第三階段,選項a :將collation header作為uncle加入,而不是交易;
第三階段,選項b:將collation header加入一個數組,數組中的元素 i 必須為分片 i 的collation header或者空字符串,并且額外的數據必須為這個數組的哈希(軟分叉);
第四階段(tight coupling,即緊耦合) :如果區塊指向無效或不可用的collation,那么區塊也將變為無效;增加數據可用性證明。
關于“區塊鏈以太坊上的分片技術是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。