2019-12-10 04:19:59 +00:00
package retrievaladapter
import (
"context"
2020-01-24 20:19:52 +00:00
"io"
2019-12-10 04:19:59 +00:00
2021-04-05 17:56:53 +00:00
"github.com/filecoin-project/lotus/api/v1api"
2021-05-18 07:32:30 +00:00
"golang.org/x/xerrors"
2021-04-05 17:56:53 +00:00
2021-03-22 09:23:58 +00:00
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
2020-09-28 21:25:58 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
2020-08-16 10:09:58 +00:00
"github.com/filecoin-project/lotus/chain/types"
2020-08-17 13:39:33 +00:00
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
2020-08-17 13:26:18 +00:00
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
2020-08-16 10:09:58 +00:00
"github.com/filecoin-project/lotus/storage"
2019-12-10 04:19:59 +00:00
"github.com/filecoin-project/go-address"
2020-01-10 17:13:12 +00:00
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
2020-03-18 17:51:25 +00:00
"github.com/filecoin-project/go-fil-markets/shared"
2020-09-07 03:49:10 +00:00
"github.com/filecoin-project/go-state-types/abi"
2020-11-05 06:44:46 +00:00
specstorage "github.com/filecoin-project/specs-storage/storage"
2019-12-10 04:19:59 +00:00
)
2021-03-22 09:23:58 +00:00
var log = logging . Logger ( "retrievaladapter" )
2019-12-10 04:19:59 +00:00
type retrievalProviderNode struct {
2021-05-18 07:32:30 +00:00
miner * storage . Miner
pp sectorstorage . PieceProvider
full v1api . FullNode
2019-12-10 04:19:59 +00:00
}
// NewRetrievalProviderNode returns a new node adapter for a retrieval provider that talks to the
// Lotus Node
2021-05-18 07:32:30 +00:00
func NewRetrievalProviderNode ( miner * storage . Miner , pp sectorstorage . PieceProvider , full v1api . FullNode ) retrievalmarket . RetrievalProviderNode {
return & retrievalProviderNode { miner , pp , full }
2019-12-10 04:19:59 +00:00
}
2020-03-18 17:51:25 +00:00
func ( rpn * retrievalProviderNode ) GetMinerWorkerAddress ( ctx context . Context , miner address . Address , tok shared . TipSetToken ) ( address . Address , error ) {
tsk , err := types . TipSetKeyFromBytes ( tok )
if err != nil {
return address . Undef , err
}
2020-04-16 17:36:36 +00:00
mi , err := rpn . full . StateMinerInfo ( ctx , miner , tsk )
return mi . Worker , err
2020-02-29 03:23:55 +00:00
}
2020-07-30 12:31:31 +00:00
func ( rpn * retrievalProviderNode ) UnsealSector ( ctx context . Context , sectorID abi . SectorNumber , offset abi . UnpaddedPieceSize , length abi . UnpaddedPieceSize ) ( io . ReadCloser , error ) {
2021-04-20 09:19:00 +00:00
log . Debugf ( "get sector %d, offset %d, length %d" , sectorID , offset , length )
2020-07-30 12:31:31 +00:00
si , err := rpn . miner . GetSectorInfo ( sectorID )
2019-12-17 03:17:46 +00:00
if err != nil {
2020-01-24 20:19:52 +00:00
return nil , err
2019-12-17 03:17:46 +00:00
}
2020-03-17 20:19:52 +00:00
mid , err := address . IDFromAddress ( rpn . miner . Address ( ) )
if err != nil {
2020-03-22 21:39:06 +00:00
return nil , err
2020-03-17 20:19:52 +00:00
}
2020-11-05 06:44:46 +00:00
ref := specstorage . SectorRef {
ID : abi . SectorID {
Miner : abi . ActorID ( mid ) ,
Number : sectorID ,
} ,
ProofType : si . SectorType ,
2020-03-17 20:19:52 +00:00
}
2020-05-26 08:20:32 +00:00
2021-05-18 07:32:30 +00:00
var commD cid . Cid
if si . CommD != nil {
commD = * si . CommD
}
// Get a reader for the piece, unsealing the piece if necessary
log . Debugf ( "read piece in sector %d, offset %d, length %d from miner %d" , sectorID , offset , length , mid )
r , unsealed , err := rpn . pp . ReadPiece ( ctx , ref , storiface . UnpaddedByteIndex ( offset ) , length , si . TicketValue , commD )
if err != nil {
return nil , xerrors . Errorf ( "failed to unseal piece from sector %d: %w" , sectorID , err )
}
_ = unsealed // todo: use
2020-05-26 08:20:32 +00:00
return r , nil
2019-12-17 03:17:46 +00:00
}
2020-03-18 17:51:25 +00:00
func ( rpn * retrievalProviderNode ) SavePaymentVoucher ( ctx context . Context , paymentChannel address . Address , voucher * paych . SignedVoucher , proof [ ] byte , expectedAmount abi . TokenAmount , tok shared . TipSetToken ) ( abi . TokenAmount , error ) {
// TODO: respect the provided TipSetToken (a serialized TipSetKey) when
// querying the chain
2020-02-12 22:32:26 +00:00
added , err := rpn . full . PaychVoucherAdd ( ctx , paymentChannel , voucher , proof , expectedAmount )
return added , err
2019-12-10 04:19:59 +00:00
}
2020-03-18 17:51:25 +00:00
func ( rpn * retrievalProviderNode ) GetChainHead ( ctx context . Context ) ( shared . TipSetToken , abi . ChainEpoch , error ) {
head , err := rpn . full . ChainHead ( ctx )
if err != nil {
return nil , 0 , err
}
return head . Key ( ) . Bytes ( ) , head . Height ( ) , nil
}
2021-05-22 17:10:21 +00:00
func ( rpn * retrievalProviderNode ) IsUnsealed ( ctx context . Context , sectorID abi . SectorNumber , offset abi . UnpaddedPieceSize , length abi . UnpaddedPieceSize ) ( bool , error ) {
si , err := rpn . miner . GetSectorInfo ( sectorID )
if err != nil {
return false , xerrors . Errorf ( "failed to get sectorinfo, err=%s" , err )
}
mid , err := address . IDFromAddress ( rpn . miner . Address ( ) )
if err != nil {
return false , err
}
ref := specstorage . SectorRef {
ID : abi . SectorID {
Miner : abi . ActorID ( mid ) ,
Number : sectorID ,
} ,
ProofType : si . SectorType ,
}
log . Debugf ( "will call IsUnsealed now sector=%+v, offset=%d, size=%d" , sectorID , offset , length )
return rpn . pp . IsUnsealed ( ctx , ref , storiface . UnpaddedByteIndex ( offset ) , length )
}
// `storageDeals` param here is the list of storage deals made for the `payloadCID` the retrieval client is looking for.
//
// `pieceCID` is the CID of the specific Piece we want to retrieve the payload from. The client can either mandate that
// we retrieve the payload from a specific piece or we choose a Piece to retrieve the payload from, prioritizing
// a Piece for which an unsealed sector file already exists if possible.
//
// 1. For the `VerifiedDeal` flag in the response `PricingInput`, we are looking to answer the question "does there exist any verified storage deal for this `payloadCID`" ?
//
// 2. We also want to ensure that we return the `PieceSize` for the actual piece we want to retrieve the deal from.
func ( rpn * retrievalProviderNode ) GetRetrievalPricingInput ( ctx context . Context , pieceCID cid . Cid , storageDeals [ ] abi . DealID ) ( retrievalmarket . PricingInput , error ) {
resp := retrievalmarket . PricingInput { }
head , err := rpn . full . ChainHead ( ctx )
if err != nil {
return resp , xerrors . Errorf ( "failed to get chain head: %w" , err )
}
tsk := head . Key ( )
for _ , dealID := range storageDeals {
ds , err := rpn . full . StateMarketStorageDeal ( ctx , dealID , tsk )
if err != nil {
return resp , xerrors . Errorf ( "failed to look up deal %d on chain: err=%w" , dealID , err )
}
if ds . Proposal . VerifiedDeal {
resp . VerifiedDeal = true
}
if ds . Proposal . PieceCID . Equals ( pieceCID ) {
resp . PieceSize = ds . Proposal . PieceSize . Unpadded ( )
}
if resp . VerifiedDeal && resp . PieceSize != 0 {
break
}
}
if resp . PieceSize == 0 {
return resp , xerrors . New ( "failed to find matching piece, PieceSize is zero" )
}
return resp , nil
}