因為這些邏輯的具體實現是在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(§or_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個葉子。