本文作者:星际联盟 原创作品,转载请注明出处
扇区任务生成命令:
lotus-storage-miner pledge-sector
命令作用:
进程创建远程RPC API和lotus-storage-miner run进程即miner通信,上代码:
var pledgeSectorCmd = &cli.Command{
Name: "pledge-sector",
Usage: "store random data in a sector",
Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
return nodeApi.PledgeSector(ctx)
},
}
func GetStorageMinerAPI(ctx *cli.Context) (api.StorageMiner, jsonrpc.ClientCloser, error) {
addr, headers, err := GetRawAPI(ctx, repo.StorageMiner)
if err != nil {
return nil, nil, err
}
return client.NewStorageMinerRPC(addr, headers)
}
和lotus-storage-miner run进程通信通过变量代码中nodeApi接口进行。生成扇区即通过代码中的nodeApi.PledgeSector(ctx)调用miner接口函数进行。
lotus-storage-miner run进程被调用PledgeSector方法
PledgeSector函数如下:
func (m *Sealing) PledgeSector() error {
go func() {
ctx := context.TODO() // we can't use the context from command which invokes
// this, as we run everything here async, and it's cancelled when the
// command exits
size := sectorbuilder.UserBytesForSectorSize(m.sb.SectorSize())
sid, err := m.sb.AcquireSectorId()
if err != nil {
log.Errorf("%+v", err)
return
}
pieces, err := m.pledgeSector(ctx, sid, []uint64{}, size)
if err != nil {
log.Errorf("%+v", err)
return
}
if err := m.newSector(context.TODO(), sid, pieces[0].DealID, pieces[0].ppi()); err != nil {
log.Errorf("%+v", err)
return
}
}()
return nil
}
第一步操作sectorbuilder.UserBytesForSectorSize:获取扇区大小,当前只支持32GB。
第二步操作m.sb.AcquireSectorId:获取扇区ID,扇区ID生成规则:先上代码,再解释。
var lastUsedID uint64
b, err := ds.Get(lastSectorIdKey)
switch err {
case nil:
i, err := strconv.ParseInt(string(b), 10, 64)
if err != nil {
return nil, err
}
lastUsedID = uint64(i)
case datastore.ErrNotFound:
lastUsedID = cfg.FallbackLastID
default:
return nil, err
}
func (sb *SectorBuilder) AcquireSectorId() (uint64, error) {
sb.idLk.Lock()
defer sb.idLk.Unlock()
sb.lastID++
id := sb.lastID
err := sb.ds.Put(lastSectorIdKey, []byte(fmt.Sprint(id)))
if err != nil {
return 0, err
}
return id, nil
}
-->初次启动初始化为1
-->此后收到扇区生成任务,逐次加1
-->并将ID本地存储
-->重启后读取本地存储作为初始化。
第三步操作pledgeSector:生成扇区pieces。这一步操作比较涉及任务比较多。容小官一步步说来。
commP, err := m.fastPledgeCommitment(size, uint64(1))
if err != nil {
return nil, err
}
首先通过32GB扇区大小的随机数据生成commP结果。
sdp := actors.StorageDealProposal{
PieceRef: commP[:],
PieceSize: size,
Client: m.worker,
Provider: m.maddr,
ProposalExpiration: math.MaxUint64,
Duration: math.MaxUint64 / 2, // /2 because overflows
StoragePricePerEpoch: types.NewInt(0),
StorageCollateral: types.NewInt(0),
ProposerSignature: nil, // nil because self dealing
}
deals[i] = sdp
由commP生成StorageDealProposal结构。
params, aerr := actors.SerializeParams(&actors.PublishStorageDealsParams{
Deals: deals,
})
将StorageDealProposal结构通过CBOR序列化生成参数结果。
smsg, err := m.api.MpoolPushMessage(ctx, &types.Message{
To: actors.StorageMarketAddress,
From: m.worker,
Value: types.NewInt(0),
GasPrice: types.NewInt(0),
GasLimit: types.NewInt(1000000),
Method: actors.SMAMethods.PublishStorageDeals,
Params: params,
})
将结果作为Message的参数,推送到链上进行校验。
r, err := m.api.StateWaitMsg(ctx, smsg.Cid()) // TODO: more finality
if err != nil {
return nil, err
}
if r.Receipt.ExitCode != 0 {
log.Error(xerrors.Errorf("publishing deal failed: exit %d", r.Receipt.ExitCode))
}
var resp actors.PublishStorageDealResponse
if err := resp.UnmarshalCBOR(bytes.NewReader(r.Receipt.Return)); err != nil {
return nil, err
}
当等到链上校验通过时,将返回结果CBOR序列化生成resp结构。
ppi, err := m.sb.AddPiece(ctx, size, sectorID, m.pledgeReader(size, uint64(1)), existingPieceSizes)
if err != nil {
return nil, xerrors.Errorf("add piece: %w", err)
}
existingPieceSizes = append(existingPieceSizes, size)
out[i] = Piece{
DealID: resp.DealIDs[i],
Size: ppi.Size,
CommP: ppi.CommP[:],
}
写入32G文件至~/.lotusstorage/staging/目录,并生成commP,由此commP以及DealID生成扇区pieces信息。
第四步操作newSector:产生新扇区生成任务。
func (m *Sealing) newSector(ctx context.Context, sid uint64, dealID uint64, ppi sectorbuilder.PublicPieceInfo) error {
log.Infof("Start sealing %d", sid)
return m.sectors.Send(sid, SectorStart{
id: sid,
pieces: []Piece{
{
DealID: dealID,
Size: ppi.Size,
CommP: ppi.CommP[:],
},
},
})
}
根据sector ID以及上一步的扇区pieces信息,产生新扇区生成任务。发送事件至StateMachine扇区状态机。
func (fsm *StateMachine) send(evt Event) error {
fsm.eventsIn <- evt // TODO: ctx, at least
return nil
}
转发事件消息至状态机的eventsIn channel处。
// NOTE: This requires at least one event to be sent to trigger a stage
// This means that after restarting the state machine users of this
// code must send a 'restart' event
select {
case evt := <-fsm.eventsIn:
pendingEvents = append(pendingEvents, evt)
case <-fsm.stageDone:
if len(pendingEvents) == 0 {
continue
}
case <-fsm.closing:
return
}
当其收到事件,将进行扇区密封任务操作。
由于篇幅问题,扇区密封操作过程以及扇区状态变化,请继续关注野牛哥的下期分解。