Skip to content

Instantly share code, notes, and snippets.

@Terryhung
Last active April 1, 2025 17:52
Show Gist options
  • Save Terryhung/d6143cf263b4c4577e35a53bc7ca4bc6 to your computer and use it in GitHub Desktop.
Save Terryhung/d6143cf263b4c4577e35a53bc7ca4bc6 to your computer and use it in GitHub Desktop.
Test Review

func updateSafrole (Safrole)

處理與 Safrole 協議相關的狀態更新。Safrole 負責驗證者的選擇、輪換和 Ticket 管理

目的是根據最新的 slot、entropy、offenders 以及 extrinsics 中提交的 tickets 來更新 Safrole 狀態。最終返回更新後的 SafrolePostState,並根據不同條件可能附帶返回 epochMark 以及 ticketsMark

架構

  1. 初始化與驗證: 讀取配置、計算當前與新 epoch/phase,並驗證 slot 以及 extrinsics 的合法性。

  2. VRF: 建立 RingContext、Commitment 與 Verifier,並根據是否發生 epoch 變更更新驗證者集合與 ticketsVerifier。

  3. update entropy: 根據新的 entropy 更新熵池,新熵池的更新邏輯根據是否發生 epoch 變更而不同。

  4. 決定更新形式(ticketsOrKeys): 根據新舊 epoch 與 phase 的狀態,決定更新後的 ticketsOrKeys 型態。

  5. 生成 Epoch Mark 與 Tickets Mark:

  6. 處理 extrinsics 中的新 ticket 並更新 accumulator: 確保新票據已排序且唯一,然後合併進票據 accumulator,並在必要時修剪超出的部分。

  7. 組合新的 SafrolePostState: 把所有更新後的部分組合成新的 state 並返回,附帶必要的 epochMark 與 ticketsMark。

state change:

    public mutating func mergeWith(postState: SafrolePostState) {
        safroleState.nextValidators = postState.nextValidators
        safroleState.ticketsVerifier = postState.ticketsVerifier
        safroleState.ticketsOrKeys = postState.ticketsOrKeys
        safroleState.ticketsAccumulator = postState.ticketsAccumulator
        entropyPool = postState.entropyPool
        validatorQueue = postState.validatorQueue
        currentValidators = postState.currentValidators
        previousValidators = postState.previousValidators
        timeslot = postState.timeslot
    }

func updateDisputes

架構

  1. 初始化與 Epoch 計算 從 config 中取得 epochLength,根據當前 timeslot 計算 currentEpoch 與 lastEpoch(currentEpoch 為 0 時,lastEpoch 為 nil)。

  2. 處理每個 verdict

  3. 更新 punishSet 與收集 offenders 建立 validSigners 為 currentValidators 與 previousValidators 的 ed25519 聯集,排除已在 judgements.punishSet 中的。 將 validatorKey 插入 newJudgements.punishSet,同時加入 offenders 陣列。

  4. 檢查 report hash 的唯一性 建立 allReports 為 disputes.verdicts 的 reportHash 與 judgements.goodSet、banSet、wonkySet 的聯集。 檢查 allReports 的數量是否與預期(各集合的 count 之和)一致,否則拋出 duplicatedReport。

  5. 統計 votes 與根據票數分類報告

  6. 核查 culprits 與 banSet 的一致性 遍歷 disputes.culprits,確保每個 culprit.reportHash 都存在於 newJudgements.banSet 中,否則拋出 invalidCulprit。

  7. 移除報告 遍歷 newReports,對於每筆包含 workReport 的報告,如果其 hash 存在於 tobeRemoved 中,則將該報告設為 nil。

  8. 返回更新後的 state 與 offenders 使用更新後的 newJudgements 與 newReports 建構 DisputesPostState,並返回該 state 以及 offenders 陣列。

state change:

    public mutating func mergeWith(postState: DisputesPostState) {
        judgements = postState.judgements
        reports = postState.reports
    }

func updateReports

分開處理 availability 與 reports 的 extrinsics

availability (Assurance):

  1. 清除過期 Reports 遍歷 reports(複製為 newReports),若 report 的 timeslot 加上 preimageReplacementPeriod 小於或等於當前 timeslot,就設為 nil。

  2. Assurances 驗證 對 extrinsic.assurances 進行驗證(parentHash 與 signature 檢查)。

  3. 累計每個 Core 的票數 根據 assurances 的布林值統計每個 core 的票數,存入 availabilityCount 陣列。

  4. 產生 Available Reports 對每個 core,若票數達到 TwoThirdValidatorsPlusOne 閾值,從 reports 中取出對應的 workReport,並將 newReports 中該位置設為 nil。

State 變更: availableReports 陣列被建立;newReports 中滿足條件的 report 被移除(設為 nil)。

  1. 返回更新結果 返回更新後的 newReports(保留未達標的 report)和 availableReports(已達標的 workReport)。

state change:

availableReports

reports (Guarateeing):

  1. Core Assignment 與 Key 準備

  2. 逐筆處理 extrinsic.guarantees

  3. Duplicate WorkPackage 檢查

  4. refinementContext 驗證

  5. 更新 Reports 與輸出

update accumulate

  1. 取得 Accumulatable Reports 根據 availableReports 與當前 index(timeslot mod epochLength)呼叫 getAllAccumulatableReports,取得 accumulatableReports 與 newQueueItems。

  2. 執行 Accumulation 建立 initialAccState(包含 accounts、validatorQueue、authorizationQueue、privilegedServices)。 呼叫 execution(...) 執行 accumulation,獲得 accumulateOutput(包含更新後的 validatorQueue、authorizationQueue、privilegedServices、transfers、commitments 及 gasUsed)。

  3. 處理 Transfers 分組 accumulateOutput.transfers 依 ServiceIndex,對每組呼叫 onTransfer(...) 計算 gasUsed,組成 transfersStats。

  4. 更新 Accounts State 將 accountsMutRef.value 賦值回 self,更新內部 service accounts state(同時影響 validatorQueue、authorizationQueue、privilegedServices)。

  5. 更新 Accumulation History 從 accumulatableReports 中取出 accumulated reports(根據 accumulateOutput.numAccumulated),構成 newHistoryItem。 將 accumulationHistory 按 epochLength 做更新:新 history item 放到最後一個位置,其餘 history 向前移位。

  6. 更新 Accumulation Queue

  7. 計算 Accumulation Root 與 Statistics 將 accumulateOutput.commitments 排序後,依據編碼結果進行 binary Merklization,產生 accumulation root。 根據 accumulateOutput.gasUsed 與 accumulated reports 統計各 service 的 gas 使用量與報告數,形成 accumulateStats。

update authorization

         let authorizationResult = try newState.update(
                    config: config,
                    timeslot: block.header.timeslot,
                    auths: block.extrinsic.reports.guarantees.map { ($0.workReport.coreIndex, $0.workReport.authorizerHash) }
                )

這個 function 主要是用來更新 authorization 的狀態(AuthorizationPostState)

state change:

public mutating func mergeWith(postState: AuthorizationPostState) {
        coreAuthorizationPool = postState.coreAuthorizationPool
    }
}

func updatePreimages

這個 function 主要的功能是處理 ExtrinsicPreimages,並根據檢查結果建立一組 PreimageUpdate,最終返回一個 PreimagesPostState。

  1. 檢查排序與唯一性 從輸入的 ExtrinsicPreimages 中取出 preimages,並驗證它們是否已排序且唯一;若不符合,則拋出 preimagesNotSorted 錯誤。

  2. 逐筆處理每個 preimage

  • 對每個 preimage:

    • 計算 hash(使用 blake2b256hash())。

    • 使用 get(...) 檢查先前狀態中是否已存在相同 preimage;若存在,拋出 duplicatedPreimage 錯誤。

    • 再次呼叫 get(...) 驗證該 preimage 是否已被請求(isRequested 為非 nil),否則拋出 preimageNotUsed 錯誤。

    • 如果檢查通過,則建立一個 PreimageUpdate(包含 serviceIndex、hash、data、length 及 timeslot)並加入更新列表。

state change:

    public mutating func mergeWith(postState: PreimagesPostState) {
        for update in postState.updates {
            self[serviceAccount: update.serviceIndex, preimageHash: update.hash] = update.data
            self[serviceAccount: update.serviceIndex, preimageHash: update.hash, length: update.length] =
                LimitedSizeArray([update.timeslot])
        }
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment