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(ctxcontext.Context,addraddress.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 函数。 01 抽查个数设置 Sector 个数以及总的抽查的叶子个数的定义在 rust-fil-proofs/filecoin-proofs/hide/constants.rs 中: pubconstWINNING_POST_CHALLENGE_COUNT:usize=66; pubconstWINNING_POST_SECTOR_COUNT:usize=1;
也就是说,要从有效 Sector 中抽取一个 Sector,并在这个 Sector 上抽查 66 个叶子节点。 02Sector 的抽查逻辑 generate_winning_post_sector_challenge 函数实现了 Sector 的抽查逻辑。核心逻辑显然是如何抽查 Sector?具体的逻辑在 fallback::generate_sector_challenges 的函数中: letmuthasher=Sha256::new(); hasher.input(AsRef::<[u8]>::as_ref(&prover;_id)); hasher.input(AsRef::<[u8]>::as_ref(&randomness;)); hasher.input(&n.to;_le_bytes()[..]);
lethash=hasher.result();
letsector_challenge=LittleEndian::read_u64(&hash.as;_ref()[..8]); letsector_index=sector_challenge%sector_set_len;
简单的说,就是把 prover_id, 随机信息,抽查 Sector 的编号做 sha256 的 hash 计算,计算结果和当前有限的 Sector 个数取模。也就是 sector_index 就是最终抽查的 Sector 的 id。 03Challenge 的叶子抽查逻辑 generate_winning_post 在抽查的 Sector 形成的 merkle 树 (replica_r_last) 上,抽查叶子节点。抽查叶子节点的计算逻辑在 fallback::generate_leaf_challenge 的函数中: letmuthasher=Sha256::new(); hasher.input(AsRef::<[u8]>::as_ref(&randomness;)); hasher.input(§or;_id.to_le_bytes()[..]); hasher.input(&leaf;_challenge_index.to_le_bytes()[..]); lethash=hasher.result();
letleaf_challenge=LittleEndian::read_u64(&hash.as;_ref()[..8]);
letchallenged_range_index=leaf_challenge%(pub_params.sector_size/NODE_SIZEasu64);
把随机信息,sector id 和挑战叶子的编号进行 hash 计算。计算的结果和叶子的总个数取模。32G 的 Sector,叶子个数为 1G 个。 04 零知识证明电路 零知识证明的计算部分可以查看 rust-fil-proofs/post/fallback 目录。大体的逻辑模块和结构可以查看之前的文章介绍: Filecoin - PoREP 电路介绍 讲讲 rust-fil-proofs/post/fallback/circuit.rs 中的 Sector 结构吧。这个结构就代表一个抽查。从 synthesize 函数可以看出:
//1.Verifycomm_r letcomm_r_last_num=num::AllocatedNum::alloc(cs.namespace(||"comm_r_last"),||{ comm_r_last .map(Into::into) .ok_or_else(||SynthesisError::AssignmentMissing) })?;
letcomm_c_num=num::AllocatedNum::alloc(cs.namespace(||"comm_c"),||{ comm_c .map(Into::into) .ok_or_else(||SynthesisError::AssignmentMissing) })?;
letcomm_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.VerifyH(Comm_C||comm_r_last)==comm_r { lethash_num=::Function::hash2_circuit( cs.namespace(||"H_comm_c_comm_r_last"), &comm;_c_num, &comm;_r_last_num, )?;
//Checkactualequality 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.VerifyInclusionPaths for(i,(leaf,path))inleafs.iter().zip(paths.iter()).enumerate(){ PoRCircuit::::synthesize( cs.namespace(||format!("challenge_inclusion_{}",i)), Root::Val(*leaf), path.clone(), Root::from_allocated::(comm_r_last_num.clone()), true, )?; }
验证从叶子节点是否可以正确计算出 Merkle 树根。 总结: Lotus 的 PoSt 包括两部分:winningPoSt 和 windowPoSt。winningPoSt 是在获取出块权时,需要提供的 PoSt 证明。从所有有效的 Sector 中,抽取一个 Sector,并抽查该 Sector 上的 66 个叶子。 |