2021-01-25 10:28:39 +00:00
package sealing
import (
"bytes"
"errors"
"math/rand"
"sort"
"testing"
"time"
"golang.org/x/net/context"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
evtmock "github.com/filecoin-project/lotus/chain/events/state/mock"
"github.com/filecoin-project/lotus/chain/types"
market2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/market"
tutils "github.com/filecoin-project/specs-actors/v2/support/testing"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require"
)
2021-07-20 19:09:50 +00:00
var errNotFound = errors . New ( "could not find" )
2021-01-25 10:28:39 +00:00
func TestGetCurrentDealInfo ( t * testing . T ) {
ctx := context . Background ( )
dummyCid , _ := cid . Parse ( "bafkqaaa" )
dummyCid2 , _ := cid . Parse ( "bafkqaab" )
zeroDealID := abi . DealID ( 0 )
earlierDealID := abi . DealID ( 9 )
successDealID := abi . DealID ( 10 )
proposal := market . DealProposal {
PieceCID : dummyCid ,
PieceSize : abi . PaddedPieceSize ( 100 ) ,
Client : tutils . NewActorAddr ( t , "client" ) ,
Provider : tutils . NewActorAddr ( t , "provider" ) ,
StoragePricePerEpoch : abi . NewTokenAmount ( 1 ) ,
ProviderCollateral : abi . NewTokenAmount ( 1 ) ,
ClientCollateral : abi . NewTokenAmount ( 1 ) ,
Label : "success" ,
}
otherProposal := market . DealProposal {
PieceCID : dummyCid2 ,
PieceSize : abi . PaddedPieceSize ( 100 ) ,
Client : tutils . NewActorAddr ( t , "client" ) ,
Provider : tutils . NewActorAddr ( t , "provider" ) ,
StoragePricePerEpoch : abi . NewTokenAmount ( 1 ) ,
ProviderCollateral : abi . NewTokenAmount ( 1 ) ,
ClientCollateral : abi . NewTokenAmount ( 1 ) ,
Label : "other" ,
}
successDeal := & api . MarketDeal {
Proposal : proposal ,
State : market . DealState {
SectorStartEpoch : 1 ,
LastUpdatedEpoch : 2 ,
} ,
}
earlierDeal := & api . MarketDeal {
Proposal : otherProposal ,
State : market . DealState {
SectorStartEpoch : 1 ,
LastUpdatedEpoch : 2 ,
} ,
}
type testCaseData struct {
searchMessageLookup * MsgLookup
searchMessageErr error
marketDeals map [ abi . DealID ] * api . MarketDeal
publishCid cid . Cid
targetProposal * market . DealProposal
expectedDealID abi . DealID
expectedMarketDeal * api . MarketDeal
expectedError error
}
testCases := map [ string ] testCaseData {
"deal lookup succeeds" : {
publishCid : dummyCid ,
searchMessageLookup : & MsgLookup {
Receipt : MessageReceipt {
ExitCode : exitcode . Ok ,
Return : makePublishDealsReturnBytes ( t , [ ] abi . DealID { successDealID } ) ,
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
successDealID : successDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : successDealID ,
expectedMarketDeal : successDeal ,
} ,
"deal lookup succeeds two return values" : {
publishCid : dummyCid ,
searchMessageLookup : & MsgLookup {
Receipt : MessageReceipt {
ExitCode : exitcode . Ok ,
Return : makePublishDealsReturnBytes ( t , [ ] abi . DealID { earlierDealID , successDealID } ) ,
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
earlierDealID : earlierDeal ,
successDealID : successDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : successDealID ,
expectedMarketDeal : successDeal ,
} ,
"deal lookup fails proposal mis-match" : {
publishCid : dummyCid ,
searchMessageLookup : & MsgLookup {
Receipt : MessageReceipt {
ExitCode : exitcode . Ok ,
Return : makePublishDealsReturnBytes ( t , [ ] abi . DealID { earlierDealID } ) ,
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
earlierDealID : earlierDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
expectedError : xerrors . Errorf ( "could not find deal in publish deals message %s" , dummyCid ) ,
} ,
"deal lookup fails mismatch count of deals and return values" : {
publishCid : dummyCid ,
searchMessageLookup : & MsgLookup {
Receipt : MessageReceipt {
ExitCode : exitcode . Ok ,
Return : makePublishDealsReturnBytes ( t , [ ] abi . DealID { earlierDealID } ) ,
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
earlierDealID : earlierDeal ,
successDealID : successDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
expectedError : xerrors . Errorf ( "deal index 1 out of bounds of deals (len 1) in publish deals message %s" , dummyCid ) ,
} ,
"deal lookup succeeds, target proposal nil, single deal in message" : {
publishCid : dummyCid ,
searchMessageLookup : & MsgLookup {
Receipt : MessageReceipt {
ExitCode : exitcode . Ok ,
Return : makePublishDealsReturnBytes ( t , [ ] abi . DealID { successDealID } ) ,
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
successDealID : successDeal ,
} ,
targetProposal : nil ,
expectedDealID : successDealID ,
expectedMarketDeal : successDeal ,
} ,
"deal lookup fails, multiple deals in return value but target proposal nil" : {
publishCid : dummyCid ,
searchMessageLookup : & MsgLookup {
Receipt : MessageReceipt {
ExitCode : exitcode . Ok ,
Return : makePublishDealsReturnBytes ( t , [ ] abi . DealID { earlierDealID , successDealID } ) ,
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
earlierDealID : earlierDeal ,
successDealID : successDeal ,
} ,
targetProposal : nil ,
expectedDealID : zeroDealID ,
expectedError : xerrors . Errorf ( "getting deal ID from publish deal message %s: no deal proposal supplied but message return value has more than one deal (2 deals)" , dummyCid ) ,
} ,
"search message fails" : {
publishCid : dummyCid ,
searchMessageErr : errors . New ( "something went wrong" ) ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
expectedError : xerrors . Errorf ( "looking for publish deal message %s: search msg failed: something went wrong" , dummyCid ) ,
} ,
2021-07-20 19:09:50 +00:00
"search message not found" : {
publishCid : dummyCid ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
expectedError : xerrors . Errorf ( "looking for publish deal message %s: not found" , dummyCid ) ,
} ,
2021-01-25 10:28:39 +00:00
"return code not ok" : {
publishCid : dummyCid ,
searchMessageLookup : & MsgLookup {
Receipt : MessageReceipt {
ExitCode : exitcode . ErrIllegalState ,
} ,
} ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
expectedError : xerrors . Errorf ( "looking for publish deal message %s: non-ok exit code: %s" , dummyCid , exitcode . ErrIllegalState ) ,
} ,
"unable to unmarshal params" : {
publishCid : dummyCid ,
searchMessageLookup : & MsgLookup {
Receipt : MessageReceipt {
ExitCode : exitcode . Ok ,
Return : [ ] byte ( "applesauce" ) ,
} ,
} ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
expectedError : xerrors . Errorf ( "looking for publish deal message %s: unmarshalling message return: cbor input should be of type array" , dummyCid ) ,
} ,
}
runTestCase := func ( testCase string , data testCaseData ) {
t . Run ( testCase , func ( t * testing . T ) {
ctx , cancel := context . WithTimeout ( ctx , 5 * time . Second )
defer cancel ( )
ts , err := evtmock . MockTipset ( address . TestAddress , rand . Uint64 ( ) )
require . NoError ( t , err )
marketDeals := make ( map [ marketDealKey ] * api . MarketDeal )
for dealID , deal := range data . marketDeals {
marketDeals [ marketDealKey { dealID , ts . Key ( ) } ] = deal
}
mockApi := & CurrentDealInfoMockAPI {
SearchMessageLookup : data . searchMessageLookup ,
SearchMessageErr : data . searchMessageErr ,
MarketDeals : marketDeals ,
}
dealInfoMgr := CurrentDealInfoManager { mockApi }
res , err := dealInfoMgr . GetCurrentDealInfo ( ctx , ts . Key ( ) . Bytes ( ) , data . targetProposal , data . publishCid )
require . Equal ( t , data . expectedDealID , res . DealID )
require . Equal ( t , data . expectedMarketDeal , res . MarketDeal )
if data . expectedError == nil {
require . NoError ( t , err )
} else {
require . EqualError ( t , err , data . expectedError . Error ( ) )
}
} )
}
for testCase , data := range testCases {
runTestCase ( testCase , data )
}
}
type marketDealKey struct {
abi . DealID
types . TipSetKey
}
type CurrentDealInfoMockAPI struct {
SearchMessageLookup * MsgLookup
SearchMessageErr error
MarketDeals map [ marketDealKey ] * api . MarketDeal
}
func ( mapi * CurrentDealInfoMockAPI ) ChainGetMessage ( ctx context . Context , c cid . Cid ) ( * types . Message , error ) {
var dealIDs [ ] abi . DealID
var deals [ ] market2 . ClientDealProposal
for k , dl := range mapi . MarketDeals {
dealIDs = append ( dealIDs , k . DealID )
deals = append ( deals , market2 . ClientDealProposal {
Proposal : market2 . DealProposal ( dl . Proposal ) ,
ClientSignature : crypto . Signature {
Data : [ ] byte ( "foo bar cat dog" ) ,
Type : crypto . SigTypeBLS ,
} ,
} )
}
sort . SliceStable ( deals , func ( i , j int ) bool {
return dealIDs [ i ] < dealIDs [ j ]
} )
buf := new ( bytes . Buffer )
params := market2 . PublishStorageDealsParams { Deals : deals }
err := params . MarshalCBOR ( buf )
if err != nil {
panic ( err )
}
return & types . Message {
Params : buf . Bytes ( ) ,
} , nil
}
func ( mapi * CurrentDealInfoMockAPI ) StateLookupID ( ctx context . Context , addr address . Address , token TipSetToken ) ( address . Address , error ) {
return addr , nil
}
func ( mapi * CurrentDealInfoMockAPI ) StateMarketStorageDeal ( ctx context . Context , dealID abi . DealID , tok TipSetToken ) ( * api . MarketDeal , error ) {
tsk , err := types . TipSetKeyFromBytes ( tok )
if err != nil {
return nil , err
}
deal , ok := mapi . MarketDeals [ marketDealKey { dealID , tsk } ]
if ! ok {
return nil , errNotFound
}
return deal , nil
}
func ( mapi * CurrentDealInfoMockAPI ) StateSearchMsg ( ctx context . Context , c cid . Cid ) ( * MsgLookup , error ) {
if mapi . SearchMessageLookup == nil {
return mapi . SearchMessageLookup , mapi . SearchMessageErr
}
return mapi . SearchMessageLookup , mapi . SearchMessageErr
}
func makePublishDealsReturnBytes ( t * testing . T , dealIDs [ ] abi . DealID ) [ ] byte {
buf := new ( bytes . Buffer )
dealsReturn := market . PublishStorageDealsReturn {
IDs : dealIDs ,
}
err := dealsReturn . MarshalCBOR ( buf )
require . NoError ( t , err )
return buf . Bytes ( )
}