精通Filecoin:Lotus真实数据处理之Client处理存储
接上文:《精通 Filecoin:Lotus 真实数据处理之 Client 初始化》
客户端发起交易之后,最终调用 Lotus(Client
)API 的 ClientStartDeal
方法对交易进行处理。这个方法最终调用 Client
对象的 ProposeStorageDeal
方法处理交易:
result, err := a.SMDealClient.ProposeStorageDeal( ctx, params.Walle &providerInfo, params.Data, // 包含了客户端的数据 dealStart, calcDealExpiration(params.MinBlocksDuration, md, dealStart), params.EpochPrice, big.Zero(), rt, params.FastRetrieval, params.VerifiedDeal, )
Client
对象的 ProposeStorageDeal
方法处理如下:
计算客户数据的 commP。
commP, pieceSize, err := clientutils.CommP(ctx, c.pio, rt, data)
检查数据的大小。如果不符合,则直接返回。
创建交易提案对象。
dealProposal := market.DealProposal{ PieceCID: commP, PieceSize: pieceSize.Padded(), Client: addr, Provider: info.Address, StartEpoch: startEpoch, EndEpoch: endEpoch, StoragePricePerEpoch: price, ProviderCollateral: abi.NewTokenAmount(int64(pieceSize)), // TODO: real calc ClientCollateral: big.Zero(), VerifiedDeal: verifiedDeal, }
对交易提案进行签名。
clientDealProposal, err := c.node.SignProposal(ctx, addr, dealProposal)
把交易提案转化为 ipld 节点对象。
proposalNd, err := cborutil.AsIpld(clientDealProposal)
创建客户端交易对象。
deal := &storagemarket.ClientDeal{ ProposalCid: proposalNd.Cid(), ClientDealProposal: *clientDealProposal, State: storagemarket.StorageDealUnknown, Miner: info.PeerID, MinerWorker: info.Worker, DataRef: data, FastRetrieval: fastRetrieval, }
调用 fsm 状态组的 的方法,生成一个状态机,并开始跟踪客户端交易对象。
err = c.statemachines.Begin(proposalNd.Cid(), deal)
向 fsm 状态组发送 事件。
err = c.statemachines.Send(deal.ProposalCid, storagemarket.ClientEventOpen)
当状态机收到这个事件后,因为初始状态为默认值 0,即 ,事件处理器对象经过内部处理把状态从 修改为 ,从而调用其处理函数 确定是否接收交易。
返回结果。
return &storagemarket.ProposeStorageDealResult{ ProposalCid: deal.ProposalCid, }, c.discovery.AddPeer(data.Root, retrievalmarket.RetrievalPeer{ Address: dealProposal.Provider, ID: deal.Miner, PieceCID: &commP, })
1、`EnsureClientFunds` 函数
这个函数用来确认客户端有足够的资金来开始交易提案。
这个函数的执行如下:
获取客户适配器对象。
node := environment.Node()
获取区块链最顶端 tipset 对应的 tipset key 和 tipset 的高度。
tok, _, err := node.GetChainHead(ctx.Context())
确认客户端有足够多的资金来进行交易。
mcid, err := node.EnsureFunds(ctx.Context(), deal.Proposal.Client, deal.Proposal.Client, deal.Proposal.ClientBalanceRequirement(), tok)
EnsureFunds
方法在确认客户资金过程中,会调用 market Actor 对象的 AddBalance
方法进行处理。
如果客户有足够多的资金,则调用 fsm 上下文对象的 Trigger
方法,通过事件处理器生成一个事件对象,然后发送事件对象到状态机。此处生成的事件对象名称为 ClientEventFundsEnsured
。
if mcid == cid.Undef { return ctx.Trigger(storagemarket.ClientEventFundsEnsured) }
当状态机收到这个事件后,经过事件处理器把状态从 StorageDealEnsureClientFunds
修改为 StorageDealFundsEnsured
,从而调用其处理函数 ProposeDeal
处理提议的交易。
2、`ProposeDeal` 函数
这个函数处理提议的交易,把交易发送给 provider 对象。
生成交易提议对象。
proposal := network.Proposal{ DealProposal: &deal.ClientDealProposal, Piece: deal.DataRef, FastRetrieval: deal.FastRetrieval, }
创建一个到矿工的交易流。
s, err := environment.NewDealStream(ctx.Context(), deal.Miner)
客户环境对象的 方法直接底层网络对象()的同名方法进行处理。后者以指定的协议创建到指定矿工的流,并返回包装之后的流对象。包装之后的流对象是 对象(storagemarket/network/deal_stream.go)。
当存储矿工收到这个请求后,使用自身的 方法处理处理这个流。在这个方法中对流进行简单包装,然后调用矿工对象的 方法处理客户交易。
通过流把交易提案提交到矿工。
err := s.WriteDealProposal(proposal);
这个过程会把交易提案对象转化为 cbor 编码的对象。
读取流返回的响应对象。
resp, err := s.ReadDealResponse()
等待矿工确定是否接收交易。结果是矿工签名的消息。内容如下:
network.Response{ State: storagemarket.StorageDealWaitingForData, Proposal: deal.ProposalCid, }
关闭流。
err = s.Close()
获取当前链顶部相关信息。
tok, _, err := environment.Node().GetChainHead(ctx.Context())
验证矿工返回的响应。
err := clientutils.VerifyResponse(ctx.Context(), resp, deal.MinerWorker, tok, environment.Node().VerifySignature);
如果响应状态不是等待数据,则发送 事件。
if resp.Response.State != storagemarket.StorageDealWaitingForData { return ctx.Trigger(storagemarket.ClientEventUnexpectedDealState, resp.Response.State, resp.Response.Message) }
发送初始化数据传输事件。
return ctx.Trigger(storagemarket.ClientEventInitiateDataTransfer)
当状态机收到这个事件后,经过事件处理器把状态从 修改为 ,从而调用其处理函数 处理提议的交易。
3、`InitiateDataTransfer` 函数
初始化数据传输。
微信扫描关注公众号,及时掌握新动向
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场