github.com/ethersphere/bee/v2@v2.2.0/pkg/steward/steward.go (about)

     1  // Copyright 2021 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package stewardess provides convenience methods
     6  // for reseeding content on Swarm.
     7  package steward
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  
    14  	"github.com/ethersphere/bee/v2/pkg/postage"
    15  	"github.com/ethersphere/bee/v2/pkg/retrieval"
    16  	"github.com/ethersphere/bee/v2/pkg/storage"
    17  	storer "github.com/ethersphere/bee/v2/pkg/storer"
    18  	"github.com/ethersphere/bee/v2/pkg/swarm"
    19  	"github.com/ethersphere/bee/v2/pkg/topology"
    20  	"github.com/ethersphere/bee/v2/pkg/traversal"
    21  )
    22  
    23  type Interface interface {
    24  	// Reupload root hash and all of its underlying
    25  	// associated chunks to the network.
    26  	Reupload(context.Context, swarm.Address, postage.Stamper) error
    27  
    28  	// IsRetrievable checks whether the content
    29  	// on the given address is retrievable.
    30  	IsRetrievable(context.Context, swarm.Address) (bool, error)
    31  }
    32  
    33  type steward struct {
    34  	netStore     storer.NetStore
    35  	traverser    traversal.Traverser
    36  	netTraverser traversal.Traverser
    37  	netGetter    retrieval.Interface
    38  }
    39  
    40  func New(ns storer.NetStore, r retrieval.Interface, joinerPutter storage.Putter) Interface {
    41  	return &steward{
    42  		netStore:     ns,
    43  		traverser:    traversal.New(ns.Download(true), joinerPutter),
    44  		netTraverser: traversal.New(&netGetter{r}, joinerPutter),
    45  		netGetter:    r,
    46  	}
    47  }
    48  
    49  // Reupload content with the given root hash to the network.
    50  // The service will automatically dereference and traverse all
    51  // addresses and push every chunk individually to the network.
    52  // It assumes all chunks are available locally. It is therefore
    53  // advisable to pin the content locally before trying to reupload it.
    54  func (s *steward) Reupload(ctx context.Context, root swarm.Address, stamper postage.Stamper) error {
    55  	uploaderSession := s.netStore.DirectUpload()
    56  	getter := s.netStore.Download(false)
    57  
    58  	fn := func(addr swarm.Address) error {
    59  		c, err := getter.Get(ctx, addr)
    60  		if err != nil {
    61  			return err
    62  		}
    63  
    64  		stamp, err := stamper.Stamp(c.Address())
    65  		if err != nil {
    66  			return fmt.Errorf("stamping chunk %s: %w", c.Address(), err)
    67  		}
    68  
    69  		return uploaderSession.Put(ctx, c.WithStamp(stamp))
    70  	}
    71  
    72  	if err := s.traverser.Traverse(ctx, root, fn); err != nil {
    73  		return errors.Join(
    74  			fmt.Errorf("traversal of %s failed: %w", root.String(), err),
    75  			uploaderSession.Cleanup(),
    76  		)
    77  	}
    78  
    79  	return uploaderSession.Done(root)
    80  }
    81  
    82  // IsRetrievable implements Interface.IsRetrievable method.
    83  func (s *steward) IsRetrievable(ctx context.Context, root swarm.Address) (bool, error) {
    84  	fn := func(a swarm.Address) error {
    85  		_, err := s.netGetter.RetrieveChunk(ctx, a, swarm.ZeroAddress)
    86  		return err
    87  	}
    88  	switch err := s.netTraverser.Traverse(ctx, root, fn); {
    89  	case errors.Is(err, storage.ErrNotFound):
    90  		return false, nil
    91  	case errors.Is(err, topology.ErrNotFound):
    92  		return false, nil
    93  	case err != nil:
    94  		return false, fmt.Errorf("traversal of %q failed: %w", root, err)
    95  	default:
    96  		return true, nil
    97  	}
    98  }
    99  
   100  // netGetter implements the storage Getter.Get method in a way
   101  // that it will try to retrieve the chunk only from the network.
   102  type netGetter struct {
   103  	retrieval retrieval.Interface
   104  }
   105  
   106  // Get implements the storage Getter.Get interface.
   107  func (ng *netGetter) Get(ctx context.Context, addr swarm.Address) (swarm.Chunk, error) {
   108  	return ng.retrieval.RetrieveChunk(ctx, addr, swarm.ZeroAddress)
   109  }