Lotus 扇区续期详解


2022-04-23 0 sector-extend

转载声明:

当前 Filecoin 网络⾸批 540 天扇区⼤量到期,扇区续期成为大家的一个强烈需求。有不少网友微信给我留言说能否写一篇有关 lotus 扇区续期的文章, 奈何这段时间一直忙其他事情去了,没空写。昨天偶然在 Medium 网站上看到一篇相关的文章,写得很不错,于是转载过来略作修改之后分享给大家。

原文地址:Filecoin:扇区到期续期/删除流程详情 (opens new window), 感谢作者 BitRainforest (opens new window) 的分享。

# 1. 背景

Lotus 扇区默认的生命周期是 540 天,扇区过期之后网络将消减 Miner 有效算力并退回质押币。在扇区过期之前,你有 2 种选择:

  1. 等待扇区过期,退回质押币,然后持币退场。
  2. 续期扇区生命周期,继续质押,保持算力。

本文主要探讨的就是上述 2 种选择的具体执行方案。

# 2. 相关源码解读

这里首先给大家做一些必要的源码解读,因为有些概念可能代码的解释能力能强一些。如果你对代码没有兴趣,可以直接跳读到 解读结论 部分。

源码版本

lotus: v1.15.1 (opens new window),git 提交 ID 为 731da455d46cb88ee5de9a70920a2d29dec9365c

specs-actors: v7.0.0 (opens new window), git 提交 ID 为 a2b807566418e72d3586ee285be620dc85ea41a6。这里顺便提一下, specs-actors 是 Filecoin 的共识实现项目,所有跟共识相关的内容都在该项目中定义的。

在此之前,我们先学习 2 个概念:

V1 扇区和 V1_1 扇区:

简单来说,在 2020-11-25 之前上链的扇区都是 V1 扇区,在这之后都是 V1_1 扇区。

网络版本:

Filecoin 共识每更改一次,网络都要强制升级一次,每强制升级一次,网络版本就会增加一次,当前网络版本为 14,下一次网络强制升级的版本为 15。

# 2.1 扇区生命周期定义

  1. specs-actors actor/builtin/miner/policy.go 中关于扇区最大生命周期的定义:

    // The maximum number of epochs past the current epoch that sector lifetime may be extended.
    // A sector may be extended multiple times, however, the total maximum lifetime is also bounded by
    // the associated seal proof's maximum lifetime.
    // 每次最多续期 540 天
    const MaxSectorExpirationExtension = 540 * builtin.EpochsInDay // PARAM_SPEC
    
  2. specs-actors actors/builtin/sector.go 中对于扇区的最⼤生命周期定义:

    // For V1 Stacked DRG sectors, the max is 540 days since Network Version 11
    // 	according to https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0014.md
    // 540 扇区生命周期扇区
    const EpochsIn540Days = stabi.ChainEpoch(540 * EpochsInDay)
    // 5 年生命周期扇区
    const EpochsInFiveYears = stabi.ChainEpoch(5 * EpochsInYear)
    // 网络 11 对于各种扇区的最大生命周期的定义(之后网络没有再更改过)
    var SealProofPoliciesV11 = map[stabi.RegisteredSealProof]*SealProofPolicy{
       // V1 扇区的最大生命周期为 540 天
       stabi.RegisteredSealProof_StackedDrg2KiBV1: {
          SectorMaxLifetime: EpochsIn540Days,
       },
       stabi.RegisteredSealProof_StackedDrg8MiBV1: {
          SectorMaxLifetime: EpochsIn540Days,
       },
       stabi.RegisteredSealProof_StackedDrg512MiBV1: {
          SectorMaxLifetime: EpochsIn540Days,
       },
       stabi.RegisteredSealProof_StackedDrg32GiBV1: {
          SectorMaxLifetime: EpochsIn540Days,
       },
       stabi.RegisteredSealProof_StackedDrg64GiBV1: {
          SectorMaxLifetime: EpochsIn540Days,
       },
       // V1_1 扇区的最大生命周期是 5 年
       stabi.RegisteredSealProof_StackedDrg2KiBV1_1: {
          SectorMaxLifetime: EpochsInFiveYears,
       },
       stabi.RegisteredSealProof_StackedDrg8MiBV1_1: {
          SectorMaxLifetime: EpochsInFiveYears,
       },
       stabi.RegisteredSealProof_StackedDrg512MiBV1_1: {
          SectorMaxLifetime: EpochsInFiveYears,
       },
       stabi.RegisteredSealProof_StackedDrg32GiBV1_1: {
          SectorMaxLifetime: EpochsInFiveYears,
       },
       stabi.RegisteredSealProof_StackedDrg64GiBV1_1: {
          SectorMaxLifetime: EpochsInFiveYears,
       },
    }
    
  3. specs-actors actors/builtin/sector.go 中对于订单的⽣命周期定义:

    // Minimum deal duration.
    var DealMinDuration = abi.ChainEpoch(180 * builtin.EpochsInDay) // PARAM_SPEC
    // Maximum deal duration
    var DealMaxDuration = abi.ChainEpoch(540 * builtin.EpochsInDay) // PARAM_SPEC
    

# 2.2 SealProof 对应表

const (
	RegisteredSealProof_StackedDrg2KiBV1   = RegisteredSealProof(0)
	RegisteredSealProof_StackedDrg8MiBV1   = RegisteredSealProof(1)
	RegisteredSealProof_StackedDrg512MiBV1 = RegisteredSealProof(2)
	RegisteredSealProof_StackedDrg32GiBV1  = RegisteredSealProof(3)
	RegisteredSealProof_StackedDrg64GiBV1  = RegisteredSealProof(4)
	RegisteredSealProof_StackedDrg2KiBV1_1   = RegisteredSealProof(5)
	RegisteredSealProof_StackedDrg8MiBV1_1   = RegisteredSealProof(6)
	RegisteredSealProof_StackedDrg512MiBV1_1 = RegisteredSealProof(7)
	RegisteredSealProof_StackedDrg32GiBV1_1  = RegisteredSealProof(8)
	RegisteredSealProof_StackedDrg64GiBV1_1  = RegisteredSealProof(9)
)

这里补充一下,其实区分 V1V1_1 扇区,除了上面我们提到的过看提交时间的方法,通过查看扇区的 SealProof 属性也可以判断出来的:

# usage 
lotus state sector <miner-id> <sector-num>
# e.g
lotus state sector t010001 0

从上述命令返回结果中可以得到 SealProof, 对照上边的 SealProof 对应表就可以得出结果:

  • SealProof:3 : V1 版本的 32GiB 扇区
  • SealProof:8 : V1_1 版本的 32GiB 扇区
  • SealProof:9 : V1_1 版本的 64GiB 扇区
  • ...

# 2.3 获取链上扇区的 API 定义

type Partition interface {
	// AllSectors returns all sector numbers in this partition, including faulty, unproven, and terminated sectors
	// List all sector BitFields of a partition(简单理解就是扇区编号)
	AllSectors() (bitfield.BitField, error)
// Subset of sectors detected/declared faulty and not yet recovered (excl. from PoSt).
	// Faults ∩ Terminated = ∅
	// 列出 partition faulty 的扇区,扇区在没有正常做 wdpost 或者扇区因为某些原因没有正常提交证明,此状态中会增加扇区
	FaultySectors() (bitfield.BitField, error)
// Subset of faulty sectors expected to recover on next PoSt
	// Recoveries ∩ Terminated = ∅
	// 列出 partition Recovering 的扇区,扇区在 faulty 后,下次证明前1⼩时会触发恢复检查逻辑,发送 Recovering 消息。(恢复前必须发送 Recovering 消息,不然是⽆法做证明的)注意还有恢复截⽌时间 FaultCutoff
	RecoveringSectors() (bitfield.BitField, error)
// Live sectors are those that are not terminated (but may be faulty).
	// 列出 partition Live 的扇区,此类是正常需要提交证明扇区的集合。到期的扇区会在⼀定时间后从这⾥移除。
	LiveSectors() (bitfield.BitField, error)
// Active sectors are those that are neither terminated nor faulty nor unproven, i.e. actively contributing power.
	// 列出 partition Active 的扇区,此类是证明成功过的扇区的集合。
	ActiveSectors() (bitfield.BitField, error)
// Unproven sectors in this partition. This bitfield will be cleared on
	// a successful window post (or at the end of the partition's next
	// deadline). At that time, any still unproven sectors will be added to
	// the faulty sector bitfield.
	// 列出 partition Unproven 的扇区,此类是封存后扇区第⼀次上链时会存在这个地⽅,证明成功过后,会从当中移除。
	UnprovenSectors() (bitfield.BitField, error)
}

# 2.4 解读结论

从上面的源码解读我们可以获取到以下信息:

  1. 扇区每次续期的时长不能超过 540 天,在扇区没有超过最大生命周期之前,你可以随时再次续期。
  2. V1 扇区的最大生命周期为 540 天,V1_1 扇区的最大生命周期为 5 年。
  3. 订单的最小生命周期为 180 天,最大生命周期为 540 天。

# 3. 演示环境

本演示环境是采用官⽅原版代码(无改动)搭建的本地 2K 网络:

lotusversion1.15.1+2k+git.731da455d
lotus-minerversion1.15.1+2k+git.731da455d

# 4. 扇区续期

扇区续期需要根据扇区类型选择可以续期的策略。扇区可以续期多次,但是扇区有最⼤的⽣命时长。举例: 你租房东的房⼦,每次签订⼏个⽉到⼏年不等的租期,但房东的房⼦有使⽤年限,会有不能续租的⼀天。续期时扇区可以续⾄ 180–540 天, 假设你有⼀个扇区现在还剩下 30 天且是 V1_1 的扇区,那么你可以为此扇区增加 150–510 天; 假设你的扇区还剩下 90 天且是 V1_1 扇区,那么你可以为此扇区增加 90–450 天。以此类推,注意不是增加180–540!!!

V1 的扇区总的寿命只有 540 天,所以如果你在最开始改动代码设置封存 180 天的扇区,那么你可以续期 360 天。

可能有朋友会有疑问,我改了配置⽂件封存 180 天的扇区,为什么查询却是 210 天左右?

答:确实是的,它会⾃⼰增加 30 天左右,原因是 ProveCommit 消息可以在 PreCommit 消息之后 30 天左右提交,这样在 gas 费偏高的时候你可以暂时不提交,节省 gas 费用。

订单(Deal)的⽣命周期也是180–540天,但是它没有最⼤的寿命⼀说。扇区的⽣命周期会以其中的 Deal 最久的设定扇区的周期。

# 4.1 续期命令介绍

续期主要有以下两种命令:

# 1. 第一种方式,
lotus-miner sectors extend --new-expiration=<epoch> <sectorNums...>
# 例如:续期 480 号扇区至 1595712 区块高度:
lotus-miner sectors extend --new-expiration=1595712 480
# 操作成功会返回一条上链消息:
bafy2bzaceczyf5efox72ohgamjzky34jsckli6c3hrg2wiy5z5zopfwzqeaes

# 2. 第二种方式
lotus-miner sectors renew --really-do-it --from=<epoch> --to=<epoch> --extension=<epoch>
# 例如:续期在 607922 和 607924 高度之间过期的扇区
lotus-miner sectors renew --really-do-it --from=607922 --to=607924 --extension=1555200

如果你想续期 V1 版本扇区可以直接使用 sectors extend 命令(操作更便捷),如果你想续期 V1_1 以及之后版本的扇区,推荐使⽤ sectors renew 命令(参数更多,功能更丰富)。

lotus-miner sectors extend 命令参数详解:

  • new-expiration : 准备续期到的⾼度
  • v1-sectors : 动判断 V1 版本扇区,并全部续期到最长的期限
  • tolerance : 续期容差,默认值:20160(7 天)。如果扇区过期到的⾼度跟续期⾼度的高度差⼩于 7 天,那就不续期
  • expiration-ignore : 默认值:120,当扩展 V1 扇区时,跳过过期时间⼩于 120 的扇区
  • expiration-cutoff : 当扩展 V1 扇区时,跳过过期时间大于 xxx 的扇区,默认值:0,表示不限制

lotus-miner sectors renew 命令参数详解:

  • from : 筛选过期扇区高度开始区间,默认值:120,从当前高度往后推迟 120 个高度开始
  • from : 筛选过期扇区高度开始区间,默认值:92160,从当前高度往后推迟 92160 高度结束
  • sector-file : 以⽂件列表,每⾏⼀个扇区 ID 作为输⼊,进⾏定制化续期,这个参数加上后会忽略上边两个参数
  • extension : 续期时长,默认将扇区续期 540 天
  • tolerance : 续期容差,默认值:20160(7 天)。如果扇区过期到的⾼度跟续期⾼度的高度差⼩于 7 天,那就不续期
  • max-fee : ⼀条消息最多可以使⽤多少 FIL

# 4.2 续期最佳实践

V1 版本的扇区续期直接使⽤默认的参数就可以达到最优的效果

lotus-miner sectors extend — v1-sectors

此命令是将V1版本的扇区全部续期⾄最长的期限,如有特殊需求可使⽤上边介绍的参数。

对于 V1 版本之后的扇区续期,Actor v7 版本每条消息最多能包含 25000 个扇区,所以⼀次性续期超过 25000 会⾃动拆分消息。

第⼀种续期⽅式:使⽤ from + to 或者 lotus-miner sectors check-expire 筛选出想续期的扇区, 使⽤ --extension 指定要将这部分扇区过期时间增加多久,⽐如想将原本还有30天的扇区,续期成还有 180 天,这个地⽅就写 150 天对应的⾼度个数。 如果要增加的⾼度⼤于它允许的最⼤过期⾼度,那就会续期⾄最⼤。

  1. 使⽤ from + to 筛选过期时间满⾜我们设定条件的扇区,对它们进⾏调整到 540 天的周期,它会去⾃动设定这些扇区的分别的到期时间:

    lotus-miner sectors renew --from 607922 --to 607924 --extension 1555200
    # 输出
    Renewing 3 sectors:
    {
    "Extensions": [
       {
          "Deadline": 1,
          "Partition": 0,
          "Sectors": "4",
          "NewExpiration": 1561026
       },
       {
          "Deadline": 2,
          "Partition": 0,
          "Sectors": "2,5",
          "NewExpiration": 1561026
       }
    ]
    }
    3 sectors renewed
    
  2. 如果只续期 1 个⾼度,需要指定 tolerance 为 0,因为默认忽略续期 7 天以内的扇区

    lotus-miner sectors renew --from 607922 --to 607924 --extension 1 --tolerance 0
    
  3. 使⽤ --new-expiration 参数指定要将这部分扇区过期时间扩展至哪个⾼度,⽐如想将原本还有 30天的扇区,续期成还有 180 天,那你就要计算出它当前的过期时间 + 150 天的⾼度。此命令会将所有筛选出来的扇区续期⾄同一⾼度,它们会基本在同⼀时间段到期。

    # 1. 把你想要续期的扇区输入到一个文件 sectors.list, 每一行一个扇区 ID
    # 2. 使用 --sector-file 参数传入文件列表
    lotus-miner sectors renew --sector-file sectors.list --new-expiration 608000
    

# 5. 删除过期扇区

# 5.1 查看即将过期扇区

lotus-miner sectors check-expire --cutoff=<epoch>

命令参数说明:

  • cutoff : 查询当前过期时间⼩于多少天的扇区,默认是 60 天,注意单位是⾼度。

如果你想查询所有的扇区信息,直接设定⼀个很⼤的值:

lotus-miner sectors check-expire --cutoff 5184000

# 输出结果
ID  SealProof  InitialPledge  Activation                    Expiration                   MaxExpiration                 MaxExtendNow
2   5          59.605 nFIL    122 (4 hours 51 minutes ago)  607922 (in 3 weeks 6 days)   5256122 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)
4   5          59.605 nFIL    119 (4 hours 52 minutes ago)  607923 (in 3 weeks 6 days)   5256119 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)
5   5          59.605 nFIL    121 (4 hours 51 minutes ago)  607924 (in 3 weeks 6 days)   5256121 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)
6   5          59.605 nFIL    266 (4 hours 42 minutes ago)  608067 (in 3 weeks 6 days)   5256266 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)
7   5          59.605 nFIL    264 (4 hours 42 minutes ago)  608067 (in 3 weeks 6 days)   5256264 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)
8   5          59.605 nFIL    263 (4 hours 42 minutes ago)  608067 (in 3 weeks 6 days)   5256263 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)
9   5          59.605 nFIL    265 (4 hours 42 minutes ago)  608067 (in 3 weeks 6 days)   5256265 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)
10  5          59.605 nFIL    262 (4 hours 42 minutes ago)  608067 (in 3 weeks 6 days)   5256262 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)
11  5          59.605 nFIL    270 (4 hours 41 minutes ago)  608067 (in 3 weeks 6 days)   5256270 (in 34 weeks 5 days)  1559699 (in 10 weeks 2 days)

输出结果字段说明:

  • ID : 扇区编号
  • SealProof : 扇区的一个参数,具有辨别网络版本和扇区大小的属性
  • InitialPledge : 初始质押,注意这里不包含交易的部分
  • Activation : 扇区激活的高度,这里是 ProveCommit 消息上链无误的高度
  • Expiration : 扇区过期的高度,这个地方是默认或者设定后在 PreCommit 消息上链无误后确认下来的
  • MaxExpiration : 最大的可以续期到多少高度,上边因为是 localnet 环境,出块时间不是 30s ,只看高度是精准的。
  • MaxExtendNow : 目前可以续期到多少高度

以上查出来的信息可以作为决策的依据还有后边续期方式的入参。

# 5.2 期扇区的筛选逻辑

  1. 查看本地的 SectorsList 然后跟链上 AllocatedSectors 取交集
  2. 过滤掉 LiveSectors、UnprovenSectors 扇区
  3. 过滤掉没有提交 ProveCommitSector 消息的扇区
  4. 过滤掉 Removed 状态的扇区

# 5.3 删除过期扇区

lotus-miner sectors expired --remove-expired

命令参数说明:

  • show-removed : 打印已删除的扇区,默认值:false
  • remove-expired : 删除已过期扇区,默认值:false,只会打印所有的过期扇区,不会删除
  • expired-epoch : 筛选多少⾼度以前过期且符合删除逻辑的扇区,默认值:900