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 }