github.com/celestiaorg/celestia-node@v0.15.0-beta.1/share/availability/full/availability.go (about)

     1  package full
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/filecoin-project/dagstore"
     9  	logging "github.com/ipfs/go-log/v2"
    10  
    11  	"github.com/celestiaorg/celestia-node/header"
    12  	"github.com/celestiaorg/celestia-node/share"
    13  	"github.com/celestiaorg/celestia-node/share/eds"
    14  	"github.com/celestiaorg/celestia-node/share/eds/byzantine"
    15  	"github.com/celestiaorg/celestia-node/share/ipld"
    16  	"github.com/celestiaorg/celestia-node/share/p2p/discovery"
    17  )
    18  
    19  var log = logging.Logger("share/full")
    20  
    21  // ShareAvailability implements share.Availability using the full data square
    22  // recovery technique. It is considered "full" because it is required
    23  // to download enough shares to fully reconstruct the data square.
    24  type ShareAvailability struct {
    25  	store  *eds.Store
    26  	getter share.Getter
    27  	disc   *discovery.Discovery
    28  
    29  	cancel context.CancelFunc
    30  }
    31  
    32  // NewShareAvailability creates a new full ShareAvailability.
    33  func NewShareAvailability(
    34  	store *eds.Store,
    35  	getter share.Getter,
    36  	disc *discovery.Discovery,
    37  ) *ShareAvailability {
    38  	return &ShareAvailability{
    39  		store:  store,
    40  		getter: getter,
    41  		disc:   disc,
    42  	}
    43  }
    44  
    45  func (fa *ShareAvailability) Start(context.Context) error {
    46  	ctx, cancel := context.WithCancel(context.Background())
    47  	fa.cancel = cancel
    48  
    49  	go fa.disc.Advertise(ctx)
    50  	return nil
    51  }
    52  
    53  func (fa *ShareAvailability) Stop(context.Context) error {
    54  	fa.cancel()
    55  	return nil
    56  }
    57  
    58  // SharesAvailable reconstructs the data committed to the given Root by requesting
    59  // enough Shares from the network.
    60  func (fa *ShareAvailability) SharesAvailable(ctx context.Context, header *header.ExtendedHeader) error {
    61  	dah := header.DAH
    62  	// short-circuit if the given root is minimum DAH of an empty data square, to avoid datastore hit
    63  	if share.DataHash(dah.Hash()).IsEmptyRoot() {
    64  		return nil
    65  	}
    66  
    67  	// we assume the caller of this method has already performed basic validation on the
    68  	// given dah/root. If for some reason this has not happened, the node should panic.
    69  	if err := dah.ValidateBasic(); err != nil {
    70  		log.Errorw("Availability validation cannot be performed on a malformed DataAvailabilityHeader",
    71  			"err", err)
    72  		panic(err)
    73  	}
    74  
    75  	// a hack to avoid loading the whole EDS in mem if we store it already.
    76  	if ok, _ := fa.store.Has(ctx, dah.Hash()); ok {
    77  		return nil
    78  	}
    79  
    80  	adder := ipld.NewProofsAdder(len(dah.RowRoots))
    81  	ctx = ipld.CtxWithProofsAdder(ctx, adder)
    82  	defer adder.Purge()
    83  
    84  	eds, err := fa.getter.GetEDS(ctx, header)
    85  	if err != nil {
    86  		if errors.Is(err, context.Canceled) {
    87  			return err
    88  		}
    89  		log.Errorw("availability validation failed", "root", dah.String(), "err", err.Error())
    90  		var byzantineErr *byzantine.ErrByzantine
    91  		if errors.Is(err, share.ErrNotFound) || errors.Is(err, context.DeadlineExceeded) && !errors.As(err, &byzantineErr) {
    92  			return share.ErrNotAvailable
    93  		}
    94  		return err
    95  	}
    96  
    97  	err = fa.store.Put(ctx, dah.Hash(), eds)
    98  	if err != nil && !errors.Is(err, dagstore.ErrShardExists) {
    99  		return fmt.Errorf("full availability: failed to store eds: %w", err)
   100  	}
   101  	return nil
   102  }