2021-01-25 10:28:39 +00:00
package sealing
import (
"bytes"
"errors"
"math/rand"
"sort"
"testing"
"time"
2022-06-14 15:00:51 +00:00
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require"
2021-01-25 10:28:39 +00:00
"golang.org/x/net/context"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-bitfield"
2021-01-25 10:28:39 +00:00
"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/crypto"
"github.com/filecoin-project/go-state-types/exitcode"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-state-types/network"
2021-01-25 10:28:39 +00:00
"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"
2023-11-13 23:59:34 +00:00
market0 "github.com/filecoin-project/specs-actors/actors/builtin/market"
tutils "github.com/filecoin-project/specs-actors/v2/support/testing"
2021-01-25 10:28:39 +00:00
)
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 ) {
2022-04-20 21:34:28 +00:00
success , err := markettypes . NewLabelFromString ( "success" )
2022-03-12 18:07:35 +00:00
require . NoError ( t , err )
2022-04-20 21:34:28 +00:00
other , err := markettypes . NewLabelFromString ( "other" )
2022-03-12 18:07:35 +00:00
require . NoError ( t , err )
2022-06-02 00:33:12 +00:00
another , err := markettypes . NewLabelFromString ( "another" )
require . NoError ( t , err )
2021-01-25 10:28:39 +00:00
ctx := context . Background ( )
dummyCid , _ := cid . Parse ( "bafkqaaa" )
dummyCid2 , _ := cid . Parse ( "bafkqaab" )
zeroDealID := abi . DealID ( 0 )
2022-06-02 00:33:12 +00:00
anotherDealID := abi . DealID ( 8 )
2021-01-25 10:28:39 +00:00
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 ) ,
2022-03-12 18:07:35 +00:00
Label : success ,
2021-01-25 10:28:39 +00:00
}
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 ) ,
2022-03-12 18:07:35 +00:00
Label : other ,
2021-01-25 10:28:39 +00:00
}
2022-06-02 00:33:12 +00:00
anotherProposal := 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 : another ,
}
2021-01-25 10:28:39 +00:00
successDeal := & api . MarketDeal {
Proposal : proposal ,
State : market . DealState {
SectorStartEpoch : 1 ,
LastUpdatedEpoch : 2 ,
} ,
}
earlierDeal := & api . MarketDeal {
Proposal : otherProposal ,
State : market . DealState {
SectorStartEpoch : 1 ,
LastUpdatedEpoch : 2 ,
} ,
}
2022-06-02 00:33:12 +00:00
anotherDeal := & api . MarketDeal {
Proposal : anotherProposal ,
State : market . DealState {
SectorStartEpoch : 1 ,
LastUpdatedEpoch : 2 ,
} ,
}
2021-01-25 10:28:39 +00:00
type testCaseData struct {
2022-06-16 10:47:19 +00:00
searchMessageLookup * api . MsgLookup
2021-01-25 10:28:39 +00:00
searchMessageErr error
marketDeals map [ abi . DealID ] * api . MarketDeal
publishCid cid . Cid
targetProposal * market . DealProposal
expectedDealID abi . DealID
expectedMarketDeal * api . MarketDeal
expectedError error
2022-06-02 00:33:12 +00:00
networkVersion network . Version
2021-01-25 10:28:39 +00:00
}
testCases := map [ string ] testCaseData {
"deal lookup succeeds" : {
publishCid : dummyCid ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2021-01-25 10:28:39 +00:00
ExitCode : exitcode . Ok ,
2022-06-02 00:33:12 +00:00
Return : makePublishDealsReturnBytesOldVersion ( t , [ ] abi . DealID { successDealID } ) ,
2021-01-25 10:28:39 +00:00
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
successDealID : successDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : successDealID ,
expectedMarketDeal : successDeal ,
} ,
"deal lookup succeeds two return values" : {
publishCid : dummyCid ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2021-01-25 10:28:39 +00:00
ExitCode : exitcode . Ok ,
2022-06-02 00:33:12 +00:00
Return : makePublishDealsReturnBytesOldVersion ( t , [ ] abi . DealID { earlierDealID , successDealID } ) ,
2021-01-25 10:28:39 +00:00
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
earlierDealID : earlierDeal ,
successDealID : successDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : successDealID ,
expectedMarketDeal : successDeal ,
} ,
"deal lookup fails proposal mis-match" : {
publishCid : dummyCid ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2021-01-25 10:28:39 +00:00
ExitCode : exitcode . Ok ,
2022-06-02 00:33:12 +00:00
Return : makePublishDealsReturnBytesOldVersion ( t , [ ] abi . DealID { earlierDealID } ) ,
2021-01-25 10:28:39 +00:00
} ,
} ,
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 ) ,
} ,
2022-06-02 00:33:12 +00:00
"deal lookup handles invalid actor output with mismatched count of deals and return values" : {
publishCid : dummyCid ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2022-06-02 00:33:12 +00:00
ExitCode : exitcode . Ok ,
Return : makePublishDealsReturnBytesOldVersion ( t , [ ] abi . DealID { earlierDealID } ) ,
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
earlierDealID : earlierDeal ,
successDealID : successDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
expectedError : xerrors . Errorf ( "invalid publish storage deals ret marking 1 as valid while only returning 1 valid deals in publish deal message %s" , dummyCid ) ,
} ,
"deal lookup fails when deal was not valid and index exceeds output array" : {
2021-01-25 10:28:39 +00:00
publishCid : dummyCid ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2021-01-25 10:28:39 +00:00
ExitCode : exitcode . Ok ,
2022-06-02 00:33:12 +00:00
Return : makePublishDealsReturn ( t , [ ] abi . DealID { earlierDealID } , [ ] uint64 { 0 } ) ,
2021-01-25 10:28:39 +00:00
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
earlierDealID : earlierDeal ,
successDealID : successDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
2022-06-02 00:33:12 +00:00
expectedError : xerrors . Errorf ( "deal was invalid at publication" ) ,
networkVersion : network . Version14 ,
} ,
"deal lookup succeeds when theres a separate deal failure" : {
publishCid : dummyCid ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2022-06-02 00:33:12 +00:00
ExitCode : exitcode . Ok ,
Return : makePublishDealsReturn ( t , [ ] abi . DealID { anotherDealID , successDealID } , [ ] uint64 { 0 , 2 } ) ,
} ,
} ,
marketDeals : map [ abi . DealID ] * api . MarketDeal {
anotherDealID : anotherDeal ,
earlierDealID : earlierDeal ,
successDealID : successDeal ,
} ,
targetProposal : & proposal ,
expectedDealID : successDealID ,
expectedMarketDeal : successDeal ,
networkVersion : network . Version14 ,
2021-01-25 10:28:39 +00:00
} ,
2022-06-02 00:33:12 +00:00
2021-01-25 10:28:39 +00:00
"deal lookup succeeds, target proposal nil, single deal in message" : {
publishCid : dummyCid ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2021-01-25 10:28:39 +00:00
ExitCode : exitcode . Ok ,
2022-06-02 00:33:12 +00:00
Return : makePublishDealsReturnBytesOldVersion ( t , [ ] abi . DealID { successDealID } ) ,
2021-01-25 10:28:39 +00:00
} ,
} ,
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 ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2021-01-25 10:28:39 +00:00
ExitCode : exitcode . Ok ,
2022-06-02 00:33:12 +00:00
Return : makePublishDealsReturnBytesOldVersion ( t , [ ] abi . DealID { earlierDealID , successDealID } ) ,
2021-01-25 10:28:39 +00:00
} ,
} ,
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 ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2021-01-25 10:28:39 +00:00
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 ,
2022-06-16 10:47:19 +00:00
searchMessageLookup : & api . MsgLookup {
Receipt : types . MessageReceipt {
2021-01-25 10:28:39 +00:00
ExitCode : exitcode . Ok ,
Return : [ ] byte ( "applesauce" ) ,
} ,
} ,
targetProposal : & proposal ,
expectedDealID : zeroDealID ,
2021-09-29 20:16:19 +00:00
expectedError : xerrors . Errorf ( "looking for publish deal message %s: decoding message return: failed to unmarshal PublishStorageDealsReturn: cbor input should be of type array" , dummyCid ) ,
2021-01-25 10:28:39 +00:00
} ,
}
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 ,
2022-06-02 00:33:12 +00:00
Version : data . networkVersion ,
2021-01-25 10:28:39 +00:00
}
dealInfoMgr := CurrentDealInfoManager { mockApi }
2022-06-16 09:12:33 +00:00
res , err := dealInfoMgr . GetCurrentDealInfo ( ctx , ts . Key ( ) , data . targetProposal , data . publishCid )
2021-01-25 10:28:39 +00:00
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 {
2022-06-16 10:47:19 +00:00
SearchMessageLookup * api . MsgLookup
2021-01-25 10:28:39 +00:00
SearchMessageErr error
MarketDeals map [ marketDealKey ] * api . MarketDeal
2022-06-02 00:33:12 +00:00
Version network . Version
2021-01-25 10:28:39 +00:00
}
func ( mapi * CurrentDealInfoMockAPI ) ChainGetMessage ( ctx context . Context , c cid . Cid ) ( * types . Message , error ) {
2022-06-02 00:33:12 +00:00
var keys [ ] marketDealKey
for k := range mapi . MarketDeals {
keys = append ( keys , k )
}
sort . SliceStable ( keys , func ( i , j int ) bool {
return keys [ i ] . DealID < keys [ j ] . DealID
} )
2022-04-20 21:34:28 +00:00
var deals [ ] markettypes . ClientDealProposal
2022-06-02 00:33:12 +00:00
for _ , k := range keys {
dl := mapi . MarketDeals [ k ]
2022-04-20 21:34:28 +00:00
deals = append ( deals , markettypes . ClientDealProposal {
Proposal : dl . Proposal ,
2021-01-25 10:28:39 +00:00
ClientSignature : crypto . Signature {
Data : [ ] byte ( "foo bar cat dog" ) ,
Type : crypto . SigTypeBLS ,
} ,
} )
}
2022-06-02 00:33:12 +00:00
2021-01-25 10:28:39 +00:00
buf := new ( bytes . Buffer )
2022-04-20 21:34:28 +00:00
params := markettypes . PublishStorageDealsParams { Deals : deals }
2021-01-25 10:28:39 +00:00
err := params . MarshalCBOR ( buf )
if err != nil {
panic ( err )
}
2022-06-02 00:33:12 +00:00
2021-01-25 10:28:39 +00:00
return & types . Message {
Params : buf . Bytes ( ) ,
} , nil
}
2022-06-17 09:20:33 +00:00
func ( mapi * CurrentDealInfoMockAPI ) StateLookupID ( ctx context . Context , addr address . Address , tsk types . TipSetKey ) ( address . Address , error ) {
2021-01-25 10:28:39 +00:00
return addr , nil
}
2022-06-16 09:12:33 +00:00
func ( mapi * CurrentDealInfoMockAPI ) StateMarketStorageDeal ( ctx context . Context , dealID abi . DealID , tsk types . TipSetKey ) ( * api . MarketDeal , error ) {
2021-01-25 10:28:39 +00:00
deal , ok := mapi . MarketDeals [ marketDealKey { dealID , tsk } ]
if ! ok {
return nil , errNotFound
}
return deal , nil
}
2022-06-16 10:47:19 +00:00
func ( mapi * CurrentDealInfoMockAPI ) StateSearchMsg ( ctx context . Context , from types . TipSetKey , msg cid . Cid , limit abi . ChainEpoch , allowReplaced bool ) ( * api . MsgLookup , error ) {
2021-01-25 10:28:39 +00:00
if mapi . SearchMessageLookup == nil {
return mapi . SearchMessageLookup , mapi . SearchMessageErr
}
return mapi . SearchMessageLookup , mapi . SearchMessageErr
}
2022-06-17 09:20:33 +00:00
func ( mapi * CurrentDealInfoMockAPI ) StateNetworkVersion ( ctx context . Context , tsk types . TipSetKey ) ( network . Version , error ) {
2022-06-02 00:33:12 +00:00
return mapi . Version , nil
2021-09-29 20:16:19 +00:00
}
2022-06-02 00:33:12 +00:00
func makePublishDealsReturnBytesOldVersion ( t * testing . T , dealIDs [ ] abi . DealID ) [ ] byte {
2021-01-25 10:28:39 +00:00
buf := new ( bytes . Buffer )
2021-09-29 20:16:19 +00:00
dealsReturn := market0 . PublishStorageDealsReturn {
2021-01-25 10:28:39 +00:00
IDs : dealIDs ,
}
err := dealsReturn . MarshalCBOR ( buf )
require . NoError ( t , err )
2022-06-02 00:33:12 +00:00
return buf . Bytes ( )
}
func makePublishDealsReturn ( t * testing . T , dealIDs [ ] abi . DealID , validIdxs [ ] uint64 ) [ ] byte {
buf := new ( bytes . Buffer )
dealsReturn := markettypes . PublishStorageDealsReturn {
IDs : dealIDs ,
ValidDeals : bitfield . NewFromSet ( validIdxs ) ,
}
err := dealsReturn . MarshalCBOR ( buf )
require . NoError ( t , err )
2021-01-25 10:28:39 +00:00
return buf . Bytes ( )
}