Filecoin - winningPoSt邏輯介紹

買賣虛擬貨幣
Lotus的PoSt的部分從electionPoSt變成兩種新的PoSt,一種是winningPoSt,一種是windowPoSt。先講講winningPoSt吧。winningPoSt,顧名思義,在winning的時候進行的PoSt。所謂的winning,也就是獲取出塊權。簡單的說,winningPoSt,隨機抽查的一個sector,該sector中的66條隨機抽查的merkle path都正確。程式碼邏輯從Lotus的go的程式碼說起。一切從出塊開始 - lotus/miner/miner.go的Miner結構的mineOne函式。func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningBase) (*types.BlockMsg, error) {   mbi, err := m.api.MinerGetBaseInfo(ctx, addr, round, base.TipSet.Key())     rand, err := m.api.ChainGetRandomness(ctx, base.TipSet.Key(), crypto.DomainSeparationTag_WinningPoStChallengeSeed, base.TipSet.Height()+base.NullRounds, nil)     prand := abi.PoStRandomness(rand)
     postProof, err := m.epp.ComputeProof(ctx, mbi.Sectors, prand)其中,MinerGetBaseInfo函式是獲取一些基本資訊,其中包括需要抽取的sector資訊。ComputeProof函式就是計算winningPoSt證明。

因為這些邏輯的具體實現是在rust-fil-proofs,也就是rust語言實現的。從go到rust-fil-proofs,跨越了不少介面:

中間的介面就不介紹了,直接看rust-fil-proofs提供的兩個API函式。

1. 抽查個數設定

Sector個數以及總的抽查的葉子個數的定義在rust-fil-proofs/filecoin-proofs/src/constants.rs中:

 pub const WINNING_POST_CHALLENGE_COUNT: usize = 66;
 pub const WINNING_POST_SECTOR_COUNT: usize = 1;

也就是說,要從有效Sector中抽取一個Sector,並在這個Sector上抽查66個葉子節點。

2. Sector的抽查邏輯

generate_winning_post_sector_challenge函式實現了Sector的抽查邏輯。核心邏輯顯然是如何抽查Sector?具體的邏輯在fallback::generate_sector_challenges的函式中:

let mut hasher = Sha256::new();
hasher.input(AsRef::<[u8]>::as_ref(&prover_id));
hasher.input(AsRef::<[u8]>::as_ref(&randomness));
hasher.input(&n.to_le_bytes()[..]);

let hash = hasher.result();

let sector_challenge = LittleEndian::read_u64(&hash.as_ref()[..8]);
let sector_index = sector_challenge % sector_set_len;

簡單的說,就是把prover_id, 隨機資訊,抽查Sector的編號做sha256的hash計算,計算結果和當前有限的Sector個數取模。也就是sector_index就是最終抽查的Sector的id。

3. Challenge的葉子抽查邏輯

generate_winning_post在抽查的Sector形成的merkle樹(replica_r_last)上,抽查葉子節點。抽查葉子節點的計算邏輯在fallback::generate_leaf_challenge的函式中:

let mut hasher = Sha256::new();
hasher.input(AsRef::<[u8]>::as_ref(&randomness));
hasher.input(&sector_id.to_le_bytes()[..]);
hasher.input(&leaf_challenge_index.to_le_bytes()[..]);
let hash = hasher.result();

let leaf_challenge = LittleEndian::read_u64(&hash.as_ref()[..8]);

let challenged_range_index = leaf_challenge % (pub_params.sector_size / NODE_SIZE as u64);

把隨機資訊,sector id和挑戰葉子的編號進行hash計算。計算的結果和葉子的總個數取模。32G的Sector,葉子個數為1G個。

4. 零知識證明電路

零知識證明的計算部分可以檢視rust-fil-proofs/post/fallback目錄。大體的邏輯模組和結構可以檢視之前的文章介紹:

Filecoin - PoREP電路介紹

講講rust-fil-proofs/post/fallback/circuit.rs中的Sector結構吧。這個結構就代表一個抽查。從synthesize函式可以看出:

 // 1. Verify comm_r
        let comm_r_last_num = num::AllocatedNum::alloc(cs.namespace(|| "comm_r_last"), || {
            comm_r_last
                .map(Into::into)
                .ok_or_else(|| SynthesisError::AssignmentMissing)
        })?;

        let comm_c_num = num::AllocatedNum::alloc(cs.namespace(|| "comm_c"), || {
            comm_c
                .map(Into::into)
                .ok_or_else(|| SynthesisError::AssignmentMissing)
        })?;

        let comm_r_num = num::AllocatedNum::alloc(cs.namespace(|| "comm_r"), || {
            comm_r
                .map(Into::into)
                .ok_or_else(|| SynthesisError::AssignmentMissing)
        })?;

        comm_r_num.inputize(cs.namespace(|| "comm_r_input"))?;

comm_r作為public輸入,其他comm_r_last和comm_c作為private輸入。

// 1. Verify H(Comm_C || comm_r_last) == comm_r
        {
            let hash_num = <Tree::Hasher as Hasher>::Function::hash2_circuit(
                cs.namespace(|| "H_comm_c_comm_r_last"),
                &comm_c_num,
                &comm_r_last_num,
            )?;

            // Check actual equality
            constraint::equal(
                cs,
                || "enforce_comm_c_comm_r_last_hash_comm_r",
                &comm_r_num,
                &hash_num,
            );
        }

驗證comm_r是否由comm_c和comm_r_last計算得到。

// 2. Verify Inclusion Paths
for (i, (leaf, path)) in leafs.iter().zip(paths.iter()).enumerate() {
    PoRCircuit::<Tree>::synthesize(
        cs.namespace(|| format!("challenge_inclusion_{}", i)),
        Root::Val(*leaf),
        path.clone(),
        Root::from_allocated::<CS>(comm_r_last_num.clone()),
        true,
    )?;
}

驗證從葉子節點是否可以正確計算出Merkle樹根。

總結:

Lotus的PoSt包括兩部分:winningPoSt和windowPoSt。winningPoSt是在獲取出塊權時,需要提供的PoSt證明。從所有有效的Sector中,抽取一個Sector,並抽查該Sector上的66個葉子。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;