github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/mock/mockreserve.go (about)

     1  // Copyright 2023 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 mockstorer
     6  
     7  import (
     8  	"context"
     9  	"math/big"
    10  	"sync"
    11  
    12  	"github.com/ethersphere/bee/v2/pkg/storage"
    13  	"github.com/ethersphere/bee/v2/pkg/storer"
    14  	"github.com/ethersphere/bee/v2/pkg/swarm"
    15  )
    16  
    17  type chunksResponse struct {
    18  	chunks []*storer.BinC
    19  	err    error
    20  }
    21  
    22  // WithSubscribeResp mocks a desired response when calling IntervalChunks method.
    23  // Different possible responses for subsequent responses in multi-call scenarios
    24  // are possible (i.e. first call yields a,b,c, second call yields d,e,f).
    25  // Mock maintains state of current call using chunksCalls counter.
    26  func WithSubscribeResp(chunks []*storer.BinC, err error) Option {
    27  	return optionFunc(func(p *ReserveStore) {
    28  		p.subResponses = append(p.subResponses, chunksResponse{chunks: chunks, err: err})
    29  	})
    30  }
    31  
    32  // WithChunks mocks the set of chunks that the store is aware of (used in Get and Has calls).
    33  func WithChunks(chs ...swarm.Chunk) Option {
    34  	return optionFunc(func(p *ReserveStore) {
    35  		for _, c := range chs {
    36  			c := c
    37  			if c.Stamp() != nil {
    38  				stampHash, _ := c.Stamp().Hash()
    39  				p.chunks[c.Address().String()+string(c.Stamp().BatchID())+string(stampHash)] = c
    40  			} else {
    41  				p.chunks[c.Address().String()] = c
    42  			}
    43  		}
    44  	})
    45  }
    46  
    47  // WithEvilChunk allows to inject a malicious chunk (request a certain address
    48  // of a chunk, but get another), in order to mock unsolicited chunk delivery.
    49  func WithEvilChunk(addr swarm.Address, ch swarm.Chunk) Option {
    50  	return optionFunc(func(p *ReserveStore) {
    51  		p.evilAddr = addr
    52  		p.evilChunk = ch
    53  	})
    54  }
    55  
    56  func WithCursors(c []uint64, e uint64) Option {
    57  	return optionFunc(func(p *ReserveStore) {
    58  		p.cursors = c
    59  		p.epoch = e
    60  	})
    61  }
    62  
    63  func WithCursorsErr(e error) Option {
    64  	return optionFunc(func(p *ReserveStore) {
    65  		p.cursorsErr = e
    66  	})
    67  }
    68  
    69  func WithRadius(r uint8) Option {
    70  	return optionFunc(func(p *ReserveStore) {
    71  		p.radius = r
    72  	})
    73  }
    74  
    75  func WithReserveSize(s int) Option {
    76  	return optionFunc(func(p *ReserveStore) {
    77  		p.reservesize = s
    78  	})
    79  }
    80  
    81  func WithPutHook(f func(swarm.Chunk) error) Option {
    82  	return optionFunc(func(p *ReserveStore) {
    83  		p.putHook = f
    84  	})
    85  }
    86  
    87  func WithSample(s storer.Sample) Option {
    88  	return optionFunc(func(p *ReserveStore) {
    89  		p.sample = s
    90  	})
    91  }
    92  
    93  var _ storer.ReserveStore = (*ReserveStore)(nil)
    94  
    95  type ReserveStore struct {
    96  	mtx         sync.Mutex
    97  	chunksCalls int
    98  	putCalls    int
    99  	setCalls    int
   100  
   101  	chunks    map[string]swarm.Chunk
   102  	evilAddr  swarm.Address
   103  	evilChunk swarm.Chunk
   104  
   105  	cursors    []uint64
   106  	cursorsErr error
   107  	epoch      uint64
   108  
   109  	radius      uint8
   110  	reservesize int
   111  
   112  	subResponses []chunksResponse
   113  	putHook      func(swarm.Chunk) error
   114  
   115  	sample storer.Sample
   116  }
   117  
   118  // NewReserve returns a new Reserve mock.
   119  func NewReserve(opts ...Option) *ReserveStore {
   120  	s := &ReserveStore{
   121  		chunks: make(map[string]swarm.Chunk),
   122  	}
   123  	for _, v := range opts {
   124  		v.apply(s)
   125  	}
   126  	return s
   127  }
   128  
   129  func (s *ReserveStore) EvictBatch(ctx context.Context, batchID []byte) error { return nil }
   130  func (s *ReserveStore) IsWithinStorageRadius(addr swarm.Address) bool        { return true }
   131  func (s *ReserveStore) IsFullySynced() bool                                  { return true }
   132  func (s *ReserveStore) StorageRadius() uint8 {
   133  	s.mtx.Lock()
   134  	defer s.mtx.Unlock()
   135  	return s.radius
   136  }
   137  func (s *ReserveStore) SetStorageRadius(r uint8) {
   138  	s.mtx.Lock()
   139  	s.radius = r
   140  	s.mtx.Unlock()
   141  }
   142  
   143  // IntervalChunks returns a set of chunk in a requested interval.
   144  func (s *ReserveStore) SubscribeBin(ctx context.Context, bin uint8, start uint64) (<-chan *storer.BinC, func(), <-chan error) {
   145  	s.mtx.Lock()
   146  	defer s.mtx.Unlock()
   147  
   148  	r := s.subResponses[s.chunksCalls]
   149  	s.chunksCalls++
   150  
   151  	out := make(chan *storer.BinC)
   152  	errC := make(chan error, 1)
   153  
   154  	go func() {
   155  		for _, c := range r.chunks {
   156  			select {
   157  			case out <- &storer.BinC{Address: c.Address, BatchID: c.BatchID, BinID: c.BinID, StampHash: c.StampHash}:
   158  			case <-ctx.Done():
   159  				select {
   160  				case errC <- ctx.Err():
   161  				default:
   162  				}
   163  			}
   164  		}
   165  	}()
   166  
   167  	return out, func() {}, errC
   168  }
   169  
   170  func (s *ReserveStore) ReserveSize() int {
   171  	return s.reservesize
   172  }
   173  
   174  func (s *ReserveStore) ReserveLastBinIDs() (curs []uint64, epoch uint64, err error) {
   175  	return s.cursors, s.epoch, s.cursorsErr
   176  }
   177  
   178  // PutCalls returns the amount of times Put was called.
   179  func (s *ReserveStore) PutCalls() int {
   180  	s.mtx.Lock()
   181  	defer s.mtx.Unlock()
   182  	return s.putCalls
   183  }
   184  
   185  // SetCalls returns the amount of times Set was called.
   186  func (s *ReserveStore) SetCalls() int {
   187  	s.mtx.Lock()
   188  	defer s.mtx.Unlock()
   189  	return s.setCalls
   190  }
   191  
   192  // Get chunks.
   193  func (s *ReserveStore) ReserveGet(ctx context.Context, addr swarm.Address, batchID []byte, stampHash []byte) (swarm.Chunk, error) {
   194  	if s.evilAddr.Equal(addr) {
   195  		//inject the malicious chunk instead
   196  		return s.evilChunk, nil
   197  	}
   198  
   199  	if v, ok := s.chunks[addr.String()+string(batchID)+string(stampHash)]; ok {
   200  		return v, nil
   201  	}
   202  
   203  	return nil, storage.ErrNotFound
   204  }
   205  
   206  // Put chunks.
   207  func (s *ReserveStore) ReservePutter() storage.Putter {
   208  	return storage.PutterFunc(
   209  		func(ctx context.Context, c swarm.Chunk) error {
   210  			s.mtx.Lock()
   211  			s.putCalls++
   212  			s.mtx.Unlock()
   213  			return s.put(ctx, c)
   214  		},
   215  	)
   216  }
   217  
   218  // Put chunks.
   219  func (s *ReserveStore) put(_ context.Context, chs ...swarm.Chunk) error {
   220  	s.mtx.Lock()
   221  	defer s.mtx.Unlock()
   222  	for _, c := range chs {
   223  		c := c
   224  		if s.putHook != nil {
   225  			if err := s.putHook(c); err != nil {
   226  				return err
   227  			}
   228  		}
   229  		stampHash, err := c.Stamp().Hash()
   230  		if err != nil {
   231  			return err
   232  		}
   233  		s.chunks[c.Address().String()+string(c.Stamp().BatchID())+string(stampHash)] = c
   234  	}
   235  	return nil
   236  }
   237  
   238  // Has chunks.
   239  func (s *ReserveStore) ReserveHas(addr swarm.Address, batchID []byte, stampHash []byte) (bool, error) {
   240  	if _, ok := s.chunks[addr.String()+string(batchID)+string(stampHash)]; !ok {
   241  		return false, nil
   242  	}
   243  	return true, nil
   244  }
   245  
   246  func (s *ReserveStore) ReserveSample(context.Context, []byte, uint8, uint64, *big.Int) (storer.Sample, error) {
   247  	return s.sample, nil
   248  }
   249  
   250  type Option interface {
   251  	apply(*ReserveStore)
   252  }
   253  type optionFunc func(*ReserveStore)
   254  
   255  func (f optionFunc) apply(r *ReserveStore) { f(r) }