封閉世界執行
把指令集封閉起來,只是內核重複了四次的同一個動作的一個實例。每一次,可能發生的事情都被提前枚舉完,落在集合之外的東西是被拒絕,而不是被處理。操作
不在枚舉集合裡的 payload——在驗證階段被拒,不是執行期
剛才被要求做的究竟是什麼
真相
把每區塊工作集當成權威。它是日誌,引擎狀態才是來源
此刻什麼是真的
輸入
任何在兩台機器上會不同的東西:牆鐘、熵、浮點、雜湊序
換一個節點還是不是這些位元組
因果
無主的狀態變更。不屬於任何使用者交易的效果走系統通道,不是豁免
這一筆為什麼會發生
封閉的是
拒絕的是
於是帳本能回答
四重封閉,同一個動作:把空間提前枚舉完,拒絕落在外面的一切。
指令集
參與者或驗證者能做的每一件事,都屬於一組固定的、帶型別的操作:
載荷不屬於這個集合的交易,在驗證階段就會被拒絕。這個集合只能透過協議升級改變,絕不會因為部署了什麼新東西而改變。
執行一個區塊
執行是一串固定的階段序列。這個次序不是實作細節:它決定了一次強平看不看得到觸發它的那個價格,也決定了一筆撤單能不能搶在到來的吃單之前生效。價格固定本區塊的標記價格
載入讀入本區塊的帳戶與訂單
資金費結算資金費收付
風險金庫減倉 → 強平 → ADL
撮合撮合前 → 撮合 → 撮合後
定案收集輸出,清除無用狀態
每個下游階段面對的是每個商品一個價格,不是不斷變動的價格
按載入時的部位結算,不按最後的部位
被迫產生的流量先了結,才放行新的自主流量
跑在風險之後,所以同一區塊裡的訂單無法對強平搶先交易
歸零的部位和已清空的帳戶不會留存
一個區塊,六個階段
這個位置決定了什麼
條件單就在這一序列裡掃描並推進,所以本區塊標記價格觸發的條件,在本區塊內就生效,不留到下一個區塊。
強平跑在撮合之前,所以同一個區塊裡的訂單無法對強平搶先交易。等到自主訂單開始撮合,強平產生的流量已經了結。
兩類狀態
內核嚴格區分什麼會持久存在、什麼只是臨時草稿。區塊執行期間
引擎狀態市場 · 帳戶 · 訂單簿 · 部位 · 清算所跨區塊存續
區塊工作集標記價格 · 被觸及帳戶 · 成交 · 暫存輸出只存續一個區塊
已提交鏈上狀態帶版本 · 可認證權威來源
髒資料追蹤很容易被誤當成權威。一個區塊內發生了哪些變化,是拿來建構確定性輸出的 — 不是狀態本身所在的地方。
引擎據以重建的權威來源
讀取
套用
物化
結果為什麼可複現
這裡的確定性是強制出來的,不是指望來的。每個誠實節點在同樣的前置狀態上執行同樣的區塊,都會產出逐位元組相同的結果,因為執行路徑上沒有任何東西讀得到節點之間會有差異的輸入:- 全程定點數運算。 結算數學跑在整數定點數上,捨入語義是明確的:保證金在負指數上無條件進位,手續費依對哪一方有利分別無條件捨去或無條件進位。結算路徑上沒有浮點數,因為兩台機器之間只要有一次捨入差異,就是一次分叉。
- 不用系統時鐘。 區塊內的先後使用共識提交的規範位置和區塊時間戳記,絕不使用本地時間。
- 運行期沒有隨機性。 任何需要隨機數的地方,都從鏈上狀態確定性地導出。
- 確定性走訪。 集合只要走訪順序會體現在輸出裡,就一定是有序的,不做雜湊隨機化。
風險公式是純的
保證金要求、強平價格、減倉對象選擇、手續費和未平倉合約量上限,都由公式決定,而這些公式實作成純的、無狀態的函式:接受輸入值、回傳結果,既不讀取也不修改帳本狀態。 狀態變更完全由清算所驅動:清算所呼叫這些函式,並把結果落到狀態上。持有狀態的模組——帳戶、部位、訂單簿——存放資料並暴露修改方法,但自身不驅動業務流程。 這是一條刻意劃出的邊界:一次保證金計算可以脫離環境,對著一張輸入輸出表單獨核對;任何人的餘額要變動,也有且只有一條程式碼路徑。輸出
執行產出狀態寫入和事件,每一項都綁定到引起它的那筆交易;此外還有一條通道,容納不屬於任何單筆使用者交易的系統級影響:資金費、保險基金變動、區塊計數器。這些構成交易輸出,由狀態層提交、由索引器對外提供。 內核裡發生的任何事,下游都看得見。只要動到了狀態,就會出現在某個人的輸出裡,或者出現在系統通道裡。後續閱讀
撮合
訂單簿、價格優先與時間優先,以及訂單有效期和自成交防護如何在其上生效。
清算所
餘額、部位與保證金發生變化的唯一路徑。
狀態模型
內核的輸出一旦提交,會變成什麼。
IntentionBFT
順序和價格是從哪裡來的。