Filecoin解读|Window PoST是什么?

Lotus提供的PoSt算法有两种方案:Winning PoSt和Window PoSt。

Lotus提供的PoSt算法有两种方案:Winning PoSt和Window PoSt。在Winning PoSt中,我们证明在生成块时已提交的部门数据。定期验证提交的部门数据,以确保正确保存数据。

本文将详细介绍WindowPoSt逻辑。

要了解Window PoSt,我们需要从管理智能合约中的部门开始。

以下是本文中使用的智能合约(specs-actors)最后提交的信息:

commit 382017dd33e9e818a51503893433628fab643dd3
Author: Alex North<445306+anorth@users.noreply.github.com>
Date:   Wed May 6 10:08:31 2020 +1000Set ConsensusMinerMinPower to 10TiB (#344)



矿工州


矿工的财产信息,抵押信息和部门状态保存在矿工状态中。我们选择与WindowPoSt相关的代码片段:

type State struct {
   ...
    ProvingPeriodStart abi.ChainEpoch
    NewSectors *abi.BitField
    Deadlines cid.Cid
    ...
}

ProvingPeriodStart-每个验证周期的开始时间。

NewSectors —新区块的信息

截止时间-每个挑战窗口都分为多个窗口。每个窗口都有一个截止日期。这就是WindowPoSt取名的地方。

1.1 验证期

WindowPoSt的配置规范在specs-actors / actors / builtin / miner / policy.go中定义。

const EpochDurationSeconds = 25
const SecondsInYear = 31556925
const SecondsInDay = 86400

const WPoStProvingPeriod = abi.ChainEpoch(SecondsInDay / EpochDurationSeconds)const WPoStChallengeWindow = abi.ChainEpoch(1800 / EpochDurationSeconds) // Half an hour (=48 per day)

const WPoStPeriodDeadlines = uint64(WPoStProvingPeriod / WPoStChallengeWindow)

const WPoStChallengeLookback = abi.ChainEpoch(20)

WPoStProvingPeriod —验证周期。每天需要证明一次。

WPoStChallengeWindow — ChallengeWindow为半小时。每个验证期分为47个ChallegeWindow。

WPoStPeriodDeadlines — ChallengeWindow的结尾。

WPoStChallengeLookback —从链中获取随机数据量并在每个ChallegeWindow中回顾先前块的时间。

总而言之,WindowPoSt的期限为一天,每个WindowPoSt分为48个窗口。扇区信息在窗口的不同时间段中有所不同。每个窗口都需要WindowPoSt提供的证明,并且窗口中的扇区数量决定了需要多少个证明。详细的逻辑将稍后介绍。

1.2截止日期

截止日期在specs-actor / actors / builtin / miner / deadlines.go中定义:

type DeadlineInfo struct {
    CurrentEpoch abi.ChainEpoch // Epoch at which this info was calculated.
    PeriodStart  abi.ChainEpoch // First epoch of the proving period (<= CurrentEpoch).
    Index        uint64         // Current deadline index, in [0..WPoStProvingPeriodDeadlines), or WPoStProvingPeriodDeadlines if period elapsed.
    Open         abi.ChainEpoch // First epoch from which a proof may be submitted, inclusive (>= CurrentEpoch).
    Close        abi.ChainEpoch // First epoch from which a proof may no longer be submitted, exclusive (>= Open).
    Challenge    abi.ChainEpoch // Epoch at which to sample the chain for challenge (< Open).
    FaultCutoff  abi.ChainEpoch // First epoch at which a fault declaration is rejected (< Open).
}

CurrentEpoch-当前截止时间。

PeriodStart —每个证明的开始冻结时间。

索引—截止日期(又名窗口)的索引。范围从0到47。

开放-在此窗口中允许提交证据的最早时间。

关闭-在此窗口中允许的最新时间或提交证据。

质询-此窗口中随机数的范围。

FaultCutoff-错误块只能在此截止块时间之前确定。

函数ComputeProvingPeriodDeadline在给定证明开始时间和当前块时间的情况下计算Deadline:

func ComputeProvingPeriodDeadline(periodStart, currEpoch abi.ChainEpoch) *DeadlineInfo {
    periodProgress := currEpoch - periodStart
  ...

    deadlineIdx := uint64(periodProgress / WPoStChallengeWindow)
    if periodProgress < 0 { // Period not yet started.
        deadlineIdx = 0
    }
    deadlineOpen := periodStart + (abi.ChainEpoch(deadlineIdx) * WPoStChallengeWindow)

    return &DeadlineInfo{
        CurrentEpoch: currEpoch,
        PeriodStart:  periodStart,
        Index:        deadlineIdx,
        Open:         deadlineOpen,
        Close:        deadlineOpen + WPoStChallengeWindow,
        Challenge:    deadlineOpen - WPoStChallengeLookback,
        FaultCutoff:  deadlineOpen - FaultDeclarationCutoff,
    }
}

我们可以从当前块时间和开始时间获取deadlienIdx。使用该索引,将更容易获得其他变量。




状态变更逻辑


WindowPoSt状态逻辑中有两个部分:调整每个windowPoSt证明的开始时间,以及更新需要证明的Sector信息。

2.1调整证明开始时间

创建矿工的智能合约时,将初始化证明的开始时间。

offset, err := assignProvingPeriodOffset(rt.Message().Receiver(), currEpoch, rt.Syscalls().HashBlake2b)
periodStart := nextProvingPeriodStart(currEpoch, 

AssignProvingPeriodOffset,通过函数HashBlake2b在[0,WPoStProvingPeriod]中随机生成偏移量。

函数nextProvingPeriodStart如下所示:


func nextProvingPeriodStart(currEpoch abi.ChainEpoch, offset abi.ChainEpoch) abi.ChainEpoch {
    currModulus := currEpoch % WPoStProvingPeriod
    var periodProgress abi.ChainEpoch // How far ahead is currEpoch from previous offset boundary.
    if currModulus >= offset {
        periodProgress = currModulus - offset
    } else {
        periodProgress = WPoStProvingPeriod - (offset - currModulus)
    }

    periodStart := currEpoch - periodProgress + WPoStProvingPeriod
    Assert(periodStart > currEpoch)
    return periodStart
}


简单地说,该函数查找具有给定偏移量的下一个证明开始时间。

在我们知道证明开始时间之后,未成年人的智能合约会在每个证明期结束时检查证明状态,并更新证明开始时间。

func handleProvingPeriod(rt Runtime) {
...
if deadline.PeriodStarted() {
      st.ProvingPeriodStart = st.ProvingPeriodStart + WPoStProvingPeriod
 }
 ...
 err = st.ClearPoStSubmissions()
 ...
}

2.2更新行业信息

在矿工的智能合约中,NewSectors通过三个界面进行更新:

verifyCommitSector:在提交PoREPommi的同时向NewSectors添加新的部门信息。

TerminateSector:从NewSectors中删除Sector信息。

handleProvingPeriod:定期将NewSectors信息“分配”到截止日期。换句话说,此功能维护需要在矿工的智能合约中证明的所有部门的列表。它在每个windowPoSt的开始将扇区分配给不同的Windows。




部门分配


使用功能AssignNewSectors,我们将需要验证的扇区分配到不同的Windows(截止日期):

func AssignNewSectors(deadlines *Deadlines, partitionSize uint64, newSectors []uint64, seed abi.Randomness) error {


请注意,partitionSize是一个分区中的扇区数:

func (p RegisteredProof) WindowPoStPartitionSectors() (uint64, error) {
  ...
    case RegisteredProof_StackedDRG32GiBSeal:
        return 2349, nil
    case RegisteredProof_StackedDRG2KiBSeal:
        return 2, nil
    case RegisteredProof_StackedDRG8MiBSeal:
        return 2, nil
    case RegisteredProof_StackedDRG512MiBSeal:
        return 2, nil
    default:
        return 0, errors.Errorf("unsupported proof type: %v", p)
  ...
}

对于32 GB的扇区,分区大小为2349。

AssignNewSectors中有2个步骤

1、填充每个窗口,直到扇区数达到partitionSize。

2、如果步骤1/之后还有剩余的扇区,则根据partitionSize为每个窗口相应地分配它们。

总结扇区分配,将partitionSize作为一个单位,并将其分配到每个Window中。这意味着在一个窗口中可以有多个扇形分区单位。要特别注意输入变量种子。尽管该功能试图随机化扇区分配,但是尚未实现。




WindowPoSt证明分派



Lotus项目中WindowPoSt的校对逻辑。以下是本文中使用的Lotus源代码的最后一次提交中的信息:


commit 10fe6084f1374891a6666fad61b9c3aa448b4554
Merge: 1d887b55 1831613f
Author: Łukasz Magiera
Date:   Wed May 6 01:45:33 2020 +0200Merge pull request #1670 from filecoin-project/feat/update-markets-0.2.0
   
 Update go-fil-markets


完整的WindowPoSt调度逻辑流程图如下:




WindowPoStScheduler:侦听链上的状态。它尝试在新的块高度处执行doPoSt。doPoSt分为两部分:在wunPoSt运行时生成的WindowPoSt证明(来自rust-fil-proofs的输出),以及将证明推向链条的SubmitPoSt。一个证明期内的所有证明都将记录在链中。handleProvingPeriod:在每个周期结束时,浏览所有Windows中的所有证明状态。


作为本课程到目前为止所涵盖内容的摘要:

1、一个证明期需要一天。一个时期分为48个窗口。每个窗口需要30分钟。

2、每个窗口都有其截止日期(为了正确提交证明而被截断)。

3、现有分区将基于partitionSize(2349扇区)进行分配,以填充每个窗口。如果分配后还有剩余的扇区,请继续使用partitionSize单位填充Windows。

如果扇区大小为32GB,则为2349个扇区,即73T。如果每个窗口包含一个扇区分区,则总存储空间将为:73T * 48 = 2.25P。也就是说,对于4.5P的存储空间,每个窗口将包含2个扇区分区。

存储数据越多,部门将越多,Windows占用的空间也越多。因此,这导致这种计算更多的时间复杂度。




生成WindowPoSt证明


在基本了解过程逻辑之后,我们最终来看看如何在WindowPoSt中生成证明。这些逻辑以rust-fil-proof或rust形式实现。从防锈到防锈,我们会遇到很多接口:

在这里,我们跳过中间接口,直接使用rust-fil-proofs中的接口功能。

pub fn generate_window_post<Tree: 'static + MerkleTreeTrait>(
   post_config: &PoStConfig,
   randomness: &ChallengeSeed,
   replicas: &BTreeMap<SectorId, PrivateReplicaInfo>,
   prover_id: ProverId,
) -> Result{

这里的post_config表示PoStConfig(configuration)。

随机性是随机信息,用于生成部门上质询的叶子数据。副本是WindowPoSt证明中生成的所有扇区的对应副本数据。




PoStConfig


PoStConfig在rust-filecoin-proofs-api/src/ registry.rs中定义:

pub fn as_v1_config(self) -> PoStConfig {
        match self {
      ...
            StackedDrgWindow2KiBV1
            | StackedDrgWindow8MiBV1
            | StackedDrgWindow512MiBV1
            | StackedDrgWindow32GiBV1 => PoStConfig {
                typ: self.typ(),
                sector_size: self.sector_size(),
                sector_count: self.sector_count(),
                challenge_count: constants::WINDOW_POST_CHALLENGE_COUNT,
                priority: true,
            }, // _ => panic!("Can only be called on V1 configs"),
        }
    }

请注意,在rust-fil-proofs/filecoin-proofs / src / constants.rs中定义了ector_count和challenge_count:

pub const WINDOW_POST_CHALLENGE_COUNT: usize = 10;pub static ref WINDOW_POST_SECTOR_COUNT: RwLock<HashMap> = RwLock::new(
   [
            (SECTOR_SIZE_2_KIB, 2),
            (SECTOR_SIZE_4_KIB, 2),
            (SECTOR_SIZE_16_KIB, 2),
            (SECTOR_SIZE_32_KIB, 2),
            (SECTOR_SIZE_8_MIB, 2),
            (SECTOR_SIZE_16_MIB, 2),
            (SECTOR_SIZE_512_MIB, 2),
            (SECTOR_SIZE_1_GIB, 2),
            (SECTOR_SIZE_32_GIB, 2349), // this gives 133,977,564 constraints, fitting just in a single partition
            (SECTOR_SIZE_64_GIB, 2300), // this gives 131,182,800 constraints, fitting just in a single partition
   ]
        .iter()
        .copied()
        .collect()
    );

例如,当挑战数为10时,分区的2349个扇区组成,每个扇区的大小为32 GB。

5.2产生挑战

每个部门包含10个挑战叶子。

这是它的计算方式(storage-proofs/post/src/fallback/vanilla.rs中的函数prove_all_partitions):

let inclusion_proofs = (0..pub_params.challenge_count)                  
   .into_par_iter()                                                    
   .map(|n| {                                                          
       let challenge_index = ((j * num_sectors_per_chunk + i)          
           * pub_params.challenge_count                                
           + n) as u64;                                                
       let challenged_leaf_start = generate_leaf_challenge(            
           pub_params,                                                
           pub_inputs.randomness,                                      
           sector_id.into(),                                          
           challenge_index,                                            
       )?;tree.gen_cached_proof(challenged_leaf_start as usize, levels)  
   })                                                                  
   .collect::<Result<Vec<_>>>()?;

Challenge_index是所有部门需要证明的挑战的索引。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);


然后,我们对随机数据,扇区ID和质询叶索引使用哈希函数。对结果和叶子总数取mod。对于32 GB的Sector,我们获得16 GB的叶子。

5.3零知识证明电路

除了不同的变量外,此处提到的电路逻辑与WinningPoSt电路相同。

要特别注意WindowPoSt中分区大小的计数不是一。计算方法如下:

let partitions = get_partitions_for_window_post(replicas.len(), &post_config);fn get_partitions_for_window_post(
   total_sector_count: usize,
   post_config: &PoStConfig,
) -> Option{
   let partitions = (total_sector_count as f32 / post_config.sector_count as f32).ceil() as usize;if partitions > 1 {
       Some(partitions)
   } else {
       None
   }
}


简而言之,分区数是副本数除以每个分区中的扇区数(2349)。

总结一下,证明的数量(分区)与需要证明的扇区数有关,并且等于副本数除以2349。在每个分区的证明中,将从每个扇区中提取10个叶节点。




摘  要


WindowPoSt定期证明所提交的扇区数据中是否存在。每个验证期需要一天。一个时期包括48个窗口。每个窗口需要30分钟。

所有Windows都需要WindowPoSt证明计算。证明数量等于窗口中扇区的数量除以分区中扇区的数量。为了证明分区,我们从分区的每个扇区中绘制了10个叶节点。

——End——



声明:此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。本网站所提供的信息,只供参考之用。

点击阅读全文