2021-01-25 10:28:39 +00:00
package sealing
import (
"bytes"
"context"
2022-04-12 22:31:31 +00:00
"fmt"
2022-06-16 11:15:49 +00:00
2022-06-14 15:00:51 +00:00
"github.com/ipfs/go-cid"
"golang.org/x/xerrors"
2021-09-29 20:16:19 +00:00
2021-01-25 10:28:39 +00:00
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
2022-09-26 16:47:48 +00:00
markettypes "github.com/filecoin-project/go-state-types/builtin/v9/market"
2021-01-25 10:28:39 +00:00
"github.com/filecoin-project/go-state-types/exitcode"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-state-types/network"
2023-11-14 00:06:11 +00:00
2021-01-25 10:28:39 +00:00
"github.com/filecoin-project/lotus/api"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
2021-01-25 10:28:39 +00:00
"github.com/filecoin-project/lotus/chain/types"
)
type CurrentDealInfoAPI interface {
ChainGetMessage ( context . Context , cid . Cid ) ( * types . Message , error )
2022-06-16 09:12:33 +00:00
StateLookupID ( context . Context , address . Address , types . TipSetKey ) ( address . Address , error )
StateMarketStorageDeal ( context . Context , abi . DealID , types . TipSetKey ) ( * api . MarketDeal , error )
2022-06-16 10:47:19 +00:00
StateSearchMsg ( ctx context . Context , from types . TipSetKey , msg cid . Cid , limit abi . ChainEpoch , allowReplaced bool ) ( * api . MsgLookup , error )
2022-06-17 09:20:33 +00:00
StateNetworkVersion ( ctx context . Context , tsk types . TipSetKey ) ( network . Version , error )
2021-01-25 10:28:39 +00:00
}
type CurrentDealInfo struct {
DealID abi . DealID
MarketDeal * api . MarketDeal
2022-06-16 09:12:33 +00:00
PublishMsgTipSet types . TipSetKey
2021-01-25 10:28:39 +00:00
}
type CurrentDealInfoManager struct {
CDAPI CurrentDealInfoAPI
}
// GetCurrentDealInfo gets the current deal state and deal ID.
// Note that the deal ID is assigned when the deal is published, so it may
// have changed if there was a reorg after the deal was published.
2022-06-17 09:20:33 +00:00
func ( mgr * CurrentDealInfoManager ) GetCurrentDealInfo ( ctx context . Context , tsk types . TipSetKey , proposal * market . DealProposal , publishCid cid . Cid ) ( CurrentDealInfo , error ) {
2021-01-25 10:28:39 +00:00
// Lookup the deal ID by comparing the deal proposal to the proposals in
// the publish deals message, and indexing into the message return value
2022-06-17 09:20:33 +00:00
dealID , pubMsgTok , err := mgr . dealIDFromPublishDealsMsg ( ctx , tsk , proposal , publishCid )
2021-01-25 10:28:39 +00:00
if err != nil {
return CurrentDealInfo { } , err
}
// Lookup the deal state by deal ID
2022-06-17 09:20:33 +00:00
marketDeal , err := mgr . CDAPI . StateMarketStorageDeal ( ctx , dealID , tsk )
2021-01-25 10:28:39 +00:00
if err == nil && proposal != nil {
// Make sure the retrieved deal proposal matches the target proposal
2022-06-17 09:20:33 +00:00
equal , err := mgr . CheckDealEquality ( ctx , tsk , * proposal , marketDeal . Proposal )
2021-01-25 10:28:39 +00:00
if err != nil {
return CurrentDealInfo { } , err
}
if ! equal {
return CurrentDealInfo { } , xerrors . Errorf ( "Deal proposals for publish message %s did not match" , publishCid )
}
}
return CurrentDealInfo { DealID : dealID , MarketDeal : marketDeal , PublishMsgTipSet : pubMsgTok } , err
}
// dealIDFromPublishDealsMsg looks up the publish deals message by cid, and finds the deal ID
// by looking at the message return value
2022-06-17 09:20:33 +00:00
func ( mgr * CurrentDealInfoManager ) dealIDFromPublishDealsMsg ( ctx context . Context , tsk types . TipSetKey , proposal * market . DealProposal , publishCid cid . Cid ) ( abi . DealID , types . TipSetKey , error ) {
2021-01-25 10:28:39 +00:00
dealID := abi . DealID ( 0 )
// Get the return value of the publish deals message
2022-06-17 09:20:33 +00:00
lookup , err := mgr . CDAPI . StateSearchMsg ( ctx , tsk , publishCid , api . LookbackNoLimit , true )
2021-01-25 10:28:39 +00:00
if err != nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "looking for publish deal message %s: search msg failed: %w" , publishCid , err )
2021-01-25 10:28:39 +00:00
}
2021-07-19 00:09:57 +00:00
if lookup == nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "looking for publish deal message %s: not found" , publishCid )
2021-07-19 00:09:57 +00:00
}
2021-01-25 10:28:39 +00:00
if lookup . Receipt . ExitCode != exitcode . Ok {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "looking for publish deal message %s: non-ok exit code: %s" , publishCid , lookup . Receipt . ExitCode )
2021-01-25 10:28:39 +00:00
}
2022-06-16 10:47:19 +00:00
nv , err := mgr . CDAPI . StateNetworkVersion ( ctx , lookup . TipSet )
2021-09-29 20:16:19 +00:00
if err != nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "getting network version: %w" , err )
2021-09-29 20:16:19 +00:00
}
retval , err := market . DecodePublishStorageDealsReturn ( lookup . Receipt . Return , nv )
if err != nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "looking for publish deal message %s: decoding message return: %w" , publishCid , err )
2021-09-29 20:16:19 +00:00
}
dealIDs , err := retval . DealIDs ( )
if err != nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "looking for publish deal message %s: getting dealIDs: %w" , publishCid , err )
2021-01-25 10:28:39 +00:00
}
2021-09-29 20:16:19 +00:00
// TODO: Can we delete this? We're well past the point when we first introduced the proposals into sealing deal info
2021-01-25 10:28:39 +00:00
// Previously, publish deals messages contained a single deal, and the
// deal proposal was not included in the sealing deal info.
// So check if the proposal is nil and check the number of deals published
// in the message.
if proposal == nil {
2021-09-29 20:16:19 +00:00
if len ( dealIDs ) > 1 {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf (
2021-01-25 10:28:39 +00:00
"getting deal ID from publish deal message %s: " +
"no deal proposal supplied but message return value has more than one deal (%d deals)" ,
2021-09-29 20:16:19 +00:00
publishCid , len ( dealIDs ) )
2021-01-25 10:28:39 +00:00
}
// There is a single deal in this publish message and no deal proposal
// was supplied, so we have nothing to compare against. Just assume
2021-09-29 20:16:19 +00:00
// the deal ID is correct and that it was valid
2022-06-16 10:47:19 +00:00
return dealIDs [ 0 ] , lookup . TipSet , nil
2021-01-25 10:28:39 +00:00
}
// Get the parameters to the publish deals message
pubmsg , err := mgr . CDAPI . ChainGetMessage ( ctx , publishCid )
if err != nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "getting publish deal message %s: %w" , publishCid , err )
2021-01-25 10:28:39 +00:00
}
2022-09-26 16:47:48 +00:00
var pubDealsParams markettypes . PublishStorageDealsParams
2021-01-25 10:28:39 +00:00
if err := pubDealsParams . UnmarshalCBOR ( bytes . NewReader ( pubmsg . Params ) ) ; err != nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "unmarshalling publish deal message params for message %s: %w" , publishCid , err )
2021-01-25 10:28:39 +00:00
}
// Scan through the deal proposals in the message parameters to find the
// index of the target deal proposal
dealIdx := - 1
for i , paramDeal := range pubDealsParams . Deals {
2022-06-17 09:20:33 +00:00
eq , err := mgr . CheckDealEquality ( ctx , tsk , * proposal , paramDeal . Proposal )
2021-01-25 10:28:39 +00:00
if err != nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "comparing publish deal message %s proposal to deal proposal: %w" , publishCid , err )
2021-01-25 10:28:39 +00:00
}
if eq {
dealIdx = i
break
}
}
2022-04-14 02:06:38 +00:00
fmt . Printf ( "found dealIdx %d\n" , dealIdx )
2021-01-25 10:28:39 +00:00
if dealIdx == - 1 {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "could not find deal in publish deals message %s" , publishCid )
2021-01-25 10:28:39 +00:00
}
2022-04-12 18:46:01 +00:00
if dealIdx >= len ( pubDealsParams . Deals ) {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf (
2022-04-12 18:46:01 +00:00
"deal index %d out of bounds of deal proposals (len %d) in publish deals message %s" ,
2021-09-29 20:16:19 +00:00
dealIdx , len ( dealIDs ) , publishCid )
2021-01-25 10:28:39 +00:00
}
2022-04-12 19:34:45 +00:00
valid , outIdx , err := retval . IsDealValid ( uint64 ( dealIdx ) )
2021-09-29 20:16:19 +00:00
if err != nil {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "determining deal validity: %w" , err )
2021-09-29 20:16:19 +00:00
}
if ! valid {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . New ( "deal was invalid at publication" )
2021-09-29 20:16:19 +00:00
}
2022-04-12 22:31:31 +00:00
// final check against for invalid return value output
// should not be reachable from onchain output, only pathological test cases
if outIdx >= len ( dealIDs ) {
2022-06-16 09:12:33 +00:00
return dealID , types . EmptyTSK , xerrors . Errorf ( "invalid publish storage deals ret marking %d as valid while only returning %d valid deals in publish deal message %s" , outIdx , len ( dealIDs ) , publishCid )
2022-04-12 22:31:31 +00:00
}
2022-06-16 10:47:19 +00:00
return dealIDs [ outIdx ] , lookup . TipSet , nil
2021-01-25 10:28:39 +00:00
}
2022-06-17 09:20:33 +00:00
func ( mgr * CurrentDealInfoManager ) CheckDealEquality ( ctx context . Context , tsk types . TipSetKey , p1 , p2 market . DealProposal ) ( bool , error ) {
p1ClientID , err := mgr . CDAPI . StateLookupID ( ctx , p1 . Client , tsk )
2021-01-25 10:28:39 +00:00
if err != nil {
return false , err
}
2022-06-17 09:20:33 +00:00
p2ClientID , err := mgr . CDAPI . StateLookupID ( ctx , p2 . Client , tsk )
2021-01-25 10:28:39 +00:00
if err != nil {
return false , err
}
return p1 . PieceCID . Equals ( p2 . PieceCID ) &&
p1 . PieceSize == p2 . PieceSize &&
p1 . VerifiedDeal == p2 . VerifiedDeal &&
2022-03-12 18:07:35 +00:00
p1 . Label . Equals ( p2 . Label ) &&
2021-01-25 10:28:39 +00:00
p1 . StartEpoch == p2 . StartEpoch &&
p1 . EndEpoch == p2 . EndEpoch &&
p1 . StoragePricePerEpoch . Equals ( p2 . StoragePricePerEpoch ) &&
p1 . ProviderCollateral . Equals ( p2 . ProviderCollateral ) &&
p1 . ClientCollateral . Equals ( p2 . ClientCollateral ) &&
p1 . Provider == p2 . Provider &&
p1ClientID == p2ClientID , nil
}