2019-08-07 18:57:48 +00:00
package deals
import (
"context"
"github.com/ipfs/go-merkledag"
unixfile "github.com/ipfs/go-unixfs/file"
"golang.org/x/xerrors"
2019-08-26 08:02:26 +00:00
2019-09-06 22:39:47 +00:00
"github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/types"
2019-08-26 08:02:26 +00:00
"github.com/filecoin-project/go-lotus/lib/sectorbuilder"
2019-08-26 10:04:57 +00:00
"github.com/filecoin-project/go-lotus/storage/sectorblocks"
2019-08-07 18:57:48 +00:00
)
2019-08-12 21:48:18 +00:00
type handlerFunc func ( ctx context . Context , deal MinerDeal ) ( func ( * MinerDeal ) , error )
2019-08-07 18:57:48 +00:00
func ( h * Handler ) handle ( ctx context . Context , deal MinerDeal , cb handlerFunc , next DealState ) {
go func ( ) {
2019-08-12 21:48:18 +00:00
mut , err := cb ( ctx , deal )
2019-08-07 18:57:48 +00:00
select {
case h . updated <- dealUpdate {
newState : next ,
id : deal . ProposalCid ,
err : err ,
2019-08-12 21:48:18 +00:00
mut : mut ,
2019-08-07 18:57:48 +00:00
} :
case <- h . stop :
}
} ( )
}
// ACCEPTED
2019-08-12 21:48:18 +00:00
func ( h * Handler ) accept ( ctx context . Context , deal MinerDeal ) ( func ( * MinerDeal ) , error ) {
2019-08-07 18:57:48 +00:00
switch deal . Proposal . SerializationMode {
//case SerializationRaw:
//case SerializationIPLD:
case SerializationUnixFs :
default :
2019-08-12 21:48:18 +00:00
return nil , xerrors . Errorf ( "deal proposal with unsupported serialization: %s" , deal . Proposal . SerializationMode )
2019-08-07 18:57:48 +00:00
}
2019-09-06 22:39:47 +00:00
curHead , err := h . full . ChainHead ( ctx )
if err != nil {
return nil , err
}
for i , voucher := range deal . Proposal . Payment . Vouchers {
err := h . full . PaychVoucherCheckValid ( ctx , deal . Proposal . Payment . PayChActor , voucher )
if err != nil {
return nil , xerrors . Errorf ( "validating payment voucher %d: %w" , i , err )
}
if voucher . Extra != nil { // TODO: should we reject vouchers with no extra params?
if voucher . Extra . Actor != deal . Proposal . MinerAddress {
return nil , xerrors . Errorf ( "validating payment voucher %d: extra params actor didn't match miner address in proposal: '%s' != '%s'" , i , voucher . Extra . Actor , deal . Proposal . MinerAddress )
}
if voucher . Extra . Method != actors . MAMethods . PaymentVerifyInclusion {
return nil , xerrors . Errorf ( "validating payment voucher %d: expected extra method %d, got %d" , i , actors . MAMethods . PaymentVerifyInclusion , voucher . Extra . Method )
}
}
if voucher . MinCloseHeight > curHead . Height ( ) + deal . Proposal . Duration {
return nil , xerrors . Errorf ( "validating payment voucher %d: MinCloseHeight too high (%d), max expected: %d" , i , voucher . MinCloseHeight , curHead . Height ( ) + deal . Proposal . Duration )
}
if voucher . TimeLock > curHead . Height ( ) + deal . Proposal . Duration {
return nil , xerrors . Errorf ( "validating payment voucher %d: TimeLock too high (%d), max expected: %d" , i , voucher . TimeLock , curHead . Height ( ) + deal . Proposal . Duration )
}
if len ( voucher . Merges ) > 0 {
return nil , xerrors . Errorf ( "validating payment voucher %d: didn't expect any merges" , i )
}
// TODO: make sure that current laneStatus.Amount == 0
if types . BigCmp ( voucher . Amount , deal . Proposal . TotalPrice ) < 0 {
return nil , xerrors . Errorf ( "validating payment voucher %d: not enough funds in the voucher" , i )
}
minPrice := types . BigMul ( types . BigMul ( h . pricePerByteBlock , types . NewInt ( deal . Proposal . Size ) ) , types . NewInt ( deal . Proposal . Duration ) )
if types . BigCmp ( minPrice , deal . Proposal . TotalPrice ) > 0 {
return nil , xerrors . Errorf ( "validating payment voucher %d: minimum price: %s" , i , minPrice )
}
}
for i , voucher := range deal . Proposal . Payment . Vouchers {
if err := h . full . PaychVoucherAdd ( ctx , deal . Proposal . Payment . PayChActor , voucher ) ; err != nil {
return nil , xerrors . Errorf ( "consuming payment voucher %d: %w" , i , err )
}
}
2019-08-07 20:16:26 +00:00
2019-08-07 18:57:48 +00:00
log . Info ( "fetching data for a deal" )
2019-09-06 22:39:47 +00:00
err = h . sendSignedResponse ( StorageDealResponse {
2019-08-07 18:57:48 +00:00
State : Accepted ,
Message : "" ,
Proposal : deal . ProposalCid ,
} )
if err != nil {
2019-08-12 21:48:18 +00:00
return nil , err
2019-08-07 18:57:48 +00:00
}
2019-08-12 21:48:18 +00:00
return nil , merkledag . FetchGraph ( ctx , deal . Ref , h . dag )
2019-08-07 18:57:48 +00:00
}
// STAGED
2019-08-12 21:48:18 +00:00
func ( h * Handler ) staged ( ctx context . Context , deal MinerDeal ) ( func ( * MinerDeal ) , error ) {
2019-08-07 18:57:48 +00:00
err := h . sendSignedResponse ( StorageDealResponse {
State : Staged ,
Proposal : deal . ProposalCid ,
} )
if err != nil {
2019-08-07 19:48:53 +00:00
log . Warnf ( "Sending deal response failed: %s" , err )
2019-08-07 18:57:48 +00:00
}
root , err := h . dag . Get ( ctx , deal . Ref )
if err != nil {
2019-08-12 21:48:18 +00:00
return nil , xerrors . Errorf ( "failed to get file root for deal: %s" , err )
2019-08-07 18:57:48 +00:00
}
// TODO: abstract this away into ReadSizeCloser + implement different modes
n , err := unixfile . NewUnixfsFile ( ctx , h . dag , root )
if err != nil {
2019-08-12 21:48:18 +00:00
return nil , xerrors . Errorf ( "cannot open unixfs file: %s" , err )
2019-08-07 18:57:48 +00:00
}
2019-08-26 10:04:57 +00:00
uf , ok := n . ( sectorblocks . UnixfsReader )
2019-08-07 18:57:48 +00:00
if ! ok {
// we probably got directory, unsupported for now
2019-08-26 10:04:57 +00:00
return nil , xerrors . Errorf ( "unsupported unixfs file type" )
2019-08-07 18:57:48 +00:00
}
2019-08-26 08:02:26 +00:00
sectorID , err := h . secst . AddUnixfsPiece ( deal . Proposal . PieceRef , uf , deal . Proposal . Duration )
2019-08-07 18:57:48 +00:00
if err != nil {
2019-08-12 21:48:18 +00:00
return nil , xerrors . Errorf ( "AddPiece failed: %s" , err )
2019-08-07 18:57:48 +00:00
}
log . Warnf ( "New Sector: %d" , sectorID )
2019-08-12 21:48:18 +00:00
return func ( deal * MinerDeal ) {
deal . SectorID = sectorID
} , nil
2019-08-07 18:57:48 +00:00
}
// SEALING
2019-08-12 21:48:18 +00:00
func getInclusionProof ( ref string , status sectorbuilder . SectorSealingStatus ) ( PieceInclusionProof , error ) {
for i , p := range status . Pieces {
if p . Key == ref {
return PieceInclusionProof {
Position : uint64 ( i ) ,
ProofElements : p . InclusionProof ,
} , nil
}
}
return PieceInclusionProof { } , xerrors . Errorf ( "pieceInclusionProof for %s in sector %d not found" , ref , status . SectorID )
}
2019-08-14 20:27:10 +00:00
func ( h * Handler ) waitSealed ( deal MinerDeal ) ( sectorbuilder . SectorSealingStatus , error ) {
status , err := h . secst . WaitSeal ( context . TODO ( ) , deal . SectorID )
if err != nil {
return sectorbuilder . SectorSealingStatus { } , err
}
2019-08-12 21:48:18 +00:00
2019-08-14 20:27:10 +00:00
switch status . SealStatusCode {
case 0 : // sealed
case 2 : // failed
return sectorbuilder . SectorSealingStatus { } , xerrors . Errorf ( "sealing sector %d for deal %s (ref=%s) failed: %s" , deal . SectorID , deal . ProposalCid , deal . Ref , status . SealErrorMsg )
case 1 : // pending
return sectorbuilder . SectorSealingStatus { } , xerrors . Errorf ( "sector status was 'pending' after call to WaitSeal (for sector %d)" , deal . SectorID )
case 3 : // sealing
return sectorbuilder . SectorSealingStatus { } , xerrors . Errorf ( "sector status was 'wait' after call to WaitSeal (for sector %d)" , deal . SectorID )
default :
return sectorbuilder . SectorSealingStatus { } , xerrors . Errorf ( "unknown SealStatusCode: %d" , status . SectorID )
2019-08-12 21:48:18 +00:00
}
2019-08-14 20:27:10 +00:00
2019-08-12 21:48:18 +00:00
return status , nil
}
func ( h * Handler ) sealing ( ctx context . Context , deal MinerDeal ) ( func ( * MinerDeal ) , error ) {
2019-08-14 20:27:10 +00:00
status , err := h . waitSealed ( deal )
2019-08-12 21:48:18 +00:00
if err != nil {
return nil , err
}
2019-08-28 21:11:29 +00:00
// TODO: don't hardcode unixfs
2019-08-28 23:01:38 +00:00
ip , err := getInclusionProof ( string ( sectorblocks . SerializationUnixfs0 ) + deal . Ref . String ( ) , status )
2019-08-12 21:48:18 +00:00
if err != nil {
return nil , err
}
err = h . sendSignedResponse ( StorageDealResponse {
State : Sealing ,
Proposal : deal . ProposalCid ,
PieceInclusionProof : ip ,
} )
if err != nil {
log . Warnf ( "Sending deal response failed: %s" , err )
}
return nil , nil
}