damocles-mamanger启动后会调用run方法,方法里面有一个for循环。不停的监听ChainNotify信息,当有区块产出或者别的headchain的事件发生后,就会调用fetchMinerProvingDeadlineInfos()方法,主动去从链节点获取deadline信息,然后调用
每个sector封装完毕以后,会放到一个partition里面进行接受挑战,2349个sector一般情况下是一个矿工是在24小时后内无法封装完成的,但是协议要求24小时以后几要开始做winningpost了。做winngpost时,会锁定partition,封装完成的sector将不能写进锁定的partition。将会写到另外一个partition。也就是说,2349个sector虽然理论上面只需要一个deadline进行挑战,但是因为上面的原因,2349个sector会被分配到两个以上的partition里面,所以2349个sector的windowpost的挑战,大概率会占有两个以上的deadline。
哪个sector在哪个deadline被挑战(要求做dindowpost),都记载在一个bitfield的结构体并存储在链上。而sector被挑战的具体点,则是根据epoch的不一样,去drand获取一个随机数,根据这个随机数来选择sector被挑战的具体点。
damocles-mamanger
启动后会调用run方法,方法里面有一个for循环。不停的监听ChainNotify信息,当有区块产出或者别的headchain的事件发生后,就会调用fetchMinerProvingDeadlineInfos()方法,主动去从链节点获取deadline信息,然后调用handleHeadChange()方法,处理headchange事件。在处理headechange事件的过程中,会根据当下的epoch信息,选择调用runner.start()进行winpost的proof生成或者runner.submit()方法把生的winpost proof提交上链。
deadline信息其实没有什么,主要的信息就是每个actorid的起始时间不一样,从而导致其24小时的,48个proving period的开始epooch不一样而已。deadline里面主要就是当下的deadline的startepoch信息。
启动for循环,监听chainnotify信息。
func (p *PoSter) Run(ctx context.Context) {
....
.....
ch, err := p.deps.chain.ChainNotify(ctx)
.......
.......
// 从链上获取deadline信息
dinfos := p.fetchMinerProvingDeadlineInfos(ctx, mids, highest)
if len(dinfos) == 0 {
continue CHAIN_HEAD_LOOP
}
//处理headchange消息。一般是有了新的区块出现。
p.handleHeadChange(ctx, lowest, highest, dinfos)
}
func (p *PoSter) handleHeadChange(ctx context.Context, revert *types.TipSet, advance *types.TipSet, dinfos map[abi.ActorID]map[abi.ChainEpoch]*dline.Info) {
// cleanup
for mid := range p.schedulers {
....
....
for open := range scheds {
sched := scheds[open]
// 某个actorid已经开始runner任务以获取deadline的任务,所以删除掉,不再次开启runner任务
if dls, dlsOk := dinfos[mid]; dlsOk {
// 尝试开启的 deadline 已经存在
if _, dlOk := dls[open]; dlOk {
delete(dls, open)
}
}
.......
.......
// post config 由于某种原因缺失时,无法触发 start 或 submit
if pcfg != nil && sched.isActive(currHeight) {
if sched.shouldStart(pcfg, currHeight) {
sched.runner.start(pcfg, advance)
}
if sched.couldSubmit(pcfg, currHeight) {
sched.runner.submit(pcfg, advance)
}
}
....
....
}
.....
....
for mid := range dinfos {
.....
.....
dls := dinfos[mid]
for open := range dls {
dl := dls[open]
sched := newScheduler(
dl,
p.runnerConstructor(
ctx,
p.deps,
mid,
maddr,
minfo.WindowPoStProofType,
dl,
),
)
if _, ok := p.schedulers[mid]; !ok {
p.schedulers[mid] = map[abi.ChainEpoch]*scheduler{}
}
mdLog.Debugw("init deadline runner", "open", dl.Open)
p.schedulers[mid][dl.Open] = sched
if sched.isActive(currHeight) && sched.shouldStart(pcfg, currHeight) {
sched.runner.start(pcfg, advance) // 开启runner任务,计算windowpost proof
}
.....
.....
}
}
func (pr *postRunner) start(pcfg *modules.MinerPoStConfig, ts *types.TipSet) {
// 因为是sync.once 对象,所以只会运行一次。即:计算一次windowpost proof
pr.startOnce.Do(func() {
pr.startCtx.pcfg = pcfg
pr.startCtx.ts = ts
baseLog := pr.log.With("tsk", ts.Key(), "tsh", ts.Height())
go pr.handleFaults(baseLog)
go pr.generatePoSt(baseLog)
})
}
// 这里计算windowpost proof
func (pr *postRunner) generatePoStForPartitionBatch(glog *logging.ZapLogger, rand core.WindowPoStRandomness, batchIdx int, batch []chain.Partition, batchPartitionStartIdx int) {
....
....
....
....
//有些类型的失败,会重新计算windowpost proof
for attempt := 0; ; attempt++ {
alog := pblog.With("attempt", attempt)
/
needRetry, err := proveAttempt(alog)
if err != nil {
alog.Errorf("attempt to generate window post proof: %v", err)
}
if !needRetry {
alog.Info("partition batch done")
break
}
select {
case <-pr.ctx.Done():
return
case <-time.After(5 * time.Second):
}
alog.Debug("retry partition batch")
}
}
当runner.start()方法真正的生成了windowpost proof,pr.proofs.proofs
就不会是nil。就可以提交到链上了。
func (pr *postRunner) submit(pcfg *modules.MinerPoStConfig, ts *types.TipSet) {
// check for proofs
pr.proofs.Lock()
proofs := pr.proofs.proofs
pr.proofs.proofs = nil
pr.proofs.Unlock()
if proofs == nil {
return
}
go pr.submitPoSts(pcfg, ts, proofs)
}
func (pr *postRunner) submitSinglePost(slog *logging.ZapLogger, pcfg *modules.MinerPoStConfig, proof *miner.SubmitWindowedPoStParams) {
uid, resCh, err := pr.publishMessage(stbuiltin.MethodsMiner.SubmitWindowedPoSt, proof, false)
if err != nil {
slog.Errorf("publish post message: %v", err)
return
}
....
...
.....
}
windowpost proof不会直接提交到链节点,而是先提交给messager,messager把收到的windowpost proof信息,放到自己的数据库后,方法就会返回成功。 但是这个时候,信息还没有上链,所以会调用waitmessage()方法,等待消息上链。
func (pr *postRunner) publishMessage(method abi.MethodNum, params cbor.Marshaler, useExtraMsgID bool) (string, <-chan msgResult, error) {
.....
.....
uid, err := pr.deps.msg.PushMessageWithId(pr.ctx, mid, &msg, &spec)
if err != nil {
return "", nil, fmt.Errorf("push msg with id %s: %w", mid, err)
}
.....
m, err := pr.waitMessage(uid, pr.startCtx.pcfg.Confidence)
.....
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!