github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/internal/stampindex/stampindex.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 stampindex
     6  
     7  import (
     8  	"encoding/binary"
     9  	"errors"
    10  	"fmt"
    11  
    12  	"github.com/ethersphere/bee/v2/pkg/storage"
    13  	"github.com/ethersphere/bee/v2/pkg/storage/storageutil"
    14  	"github.com/ethersphere/bee/v2/pkg/storer/internal"
    15  	"github.com/ethersphere/bee/v2/pkg/swarm"
    16  )
    17  
    18  var (
    19  	// errStampItemMarshalScopeInvalid is returned when trying to
    20  	// marshal a Item with invalid scope.
    21  	errStampItemMarshalScopeInvalid = errors.New("marshal stampindex.Item: scope is invalid")
    22  	// errStampItemMarshalBatchIDInvalid is returned when trying to
    23  	// marshal a Item with invalid batchID.
    24  	errStampItemMarshalBatchIDInvalid = errors.New("marshal stampindex.Item: batchID is invalid")
    25  	// errStampItemMarshalBatchIndexInvalid is returned when trying
    26  	// to marshal a Item with invalid batchIndex.
    27  	errStampItemMarshalBatchIndexInvalid = errors.New("marshal stampindex.Item: batchIndex is invalid")
    28  	// errStampItemUnmarshalInvalidSize is returned when trying
    29  	// to unmarshal buffer with smaller size then is the size
    30  	// of the Item fields.
    31  	errStampItemUnmarshalInvalidSize = errors.New("unmarshal stampindex.Item: invalid size")
    32  )
    33  
    34  var _ storage.Item = (*Item)(nil)
    35  
    36  // Item is an store.Item that represents data relevant to stamp.
    37  type Item struct {
    38  	// Keys.
    39  	scope      []byte // The scope of other related item.
    40  	BatchID    []byte
    41  	StampIndex []byte
    42  	StampHash  []byte
    43  
    44  	// Values.
    45  	StampTimestamp []byte
    46  	ChunkAddress   swarm.Address
    47  }
    48  
    49  // ID implements the storage.Item interface.
    50  func (i Item) ID() string {
    51  	return fmt.Sprintf("%s/%s/%s", string(i.scope), string(i.BatchID), string(i.StampIndex))
    52  }
    53  
    54  // Namespace implements the storage.Item interface.
    55  func (i Item) Namespace() string {
    56  	return "stampIndex"
    57  }
    58  
    59  func (i Item) GetScope() []byte {
    60  	return i.scope
    61  }
    62  
    63  func (i *Item) SetScope(ns []byte) {
    64  	i.scope = ns
    65  }
    66  
    67  // Marshal implements the storage.Item interface.
    68  func (i Item) Marshal() ([]byte, error) {
    69  	switch {
    70  	case len(i.scope) == 0:
    71  		return nil, errStampItemMarshalScopeInvalid
    72  	case len(i.BatchID) != swarm.HashSize:
    73  		return nil, errStampItemMarshalBatchIDInvalid
    74  	case len(i.StampIndex) != swarm.StampIndexSize:
    75  		return nil, errStampItemMarshalBatchIndexInvalid
    76  	}
    77  
    78  	buf := make([]byte, 8+len(i.scope)+swarm.HashSize+swarm.StampIndexSize+swarm.StampTimestampSize+swarm.HashSize+swarm.HashSize)
    79  
    80  	l := 0
    81  	binary.LittleEndian.PutUint64(buf[l:l+8], uint64(len(i.scope)))
    82  	l += 8
    83  	copy(buf[l:l+len(i.scope)], i.scope)
    84  	l += len(i.scope)
    85  	copy(buf[l:l+swarm.HashSize], i.BatchID)
    86  	l += swarm.HashSize
    87  	copy(buf[l:l+swarm.StampIndexSize], i.StampIndex)
    88  	l += swarm.StampIndexSize
    89  	copy(buf[l:l+swarm.StampTimestampSize], i.StampTimestamp)
    90  	l += swarm.StampTimestampSize
    91  	copy(buf[l:l+swarm.HashSize], internal.AddressBytesOrZero(i.ChunkAddress))
    92  	l += swarm.HashSize
    93  	copy(buf[l:l+swarm.HashSize], i.StampHash)
    94  	return buf, nil
    95  }
    96  
    97  // Unmarshal implements the storage.Item interface.
    98  func (i *Item) Unmarshal(bytes []byte) error {
    99  	if len(bytes) < 8 {
   100  		return errStampItemUnmarshalInvalidSize
   101  	}
   102  	nsLen := int(binary.LittleEndian.Uint64(bytes))
   103  	if len(bytes) != 8+nsLen+swarm.HashSize+swarm.StampIndexSize+swarm.StampTimestampSize+swarm.HashSize+swarm.HashSize {
   104  		return errStampItemUnmarshalInvalidSize
   105  	}
   106  
   107  	ni := new(Item)
   108  	l := 8
   109  	ni.scope = append(make([]byte, 0, nsLen), bytes[l:l+nsLen]...)
   110  	l += nsLen
   111  	ni.BatchID = append(make([]byte, 0, swarm.HashSize), bytes[l:l+swarm.HashSize]...)
   112  	l += swarm.HashSize
   113  	ni.StampIndex = append(make([]byte, 0, swarm.StampIndexSize), bytes[l:l+swarm.StampIndexSize]...)
   114  	l += swarm.StampIndexSize
   115  	ni.StampTimestamp = append(make([]byte, 0, swarm.StampTimestampSize), bytes[l:l+swarm.StampTimestampSize]...)
   116  	l += swarm.StampTimestampSize
   117  	ni.ChunkAddress = internal.AddressOrZero(bytes[l : l+swarm.HashSize])
   118  	l += swarm.HashSize
   119  	ni.StampHash = append(make([]byte, 0, swarm.HashSize), bytes[l:l+swarm.HashSize]...)
   120  	*i = *ni
   121  	return nil
   122  }
   123  
   124  // Clone  implements the storage.Item interface.
   125  func (i *Item) Clone() storage.Item {
   126  	if i == nil {
   127  		return nil
   128  	}
   129  	return &Item{
   130  		scope:          append([]byte(nil), i.scope...),
   131  		BatchID:        append([]byte(nil), i.BatchID...),
   132  		StampIndex:     append([]byte(nil), i.StampIndex...),
   133  		StampHash:      append([]byte(nil), i.StampHash...),
   134  		StampTimestamp: append([]byte(nil), i.StampTimestamp...),
   135  		ChunkAddress:   i.ChunkAddress.Clone(),
   136  	}
   137  }
   138  
   139  // String implements the fmt.Stringer interface.
   140  func (i Item) String() string {
   141  	return storageutil.JoinFields(i.Namespace(), i.ID())
   142  }
   143  
   144  // LoadOrStore tries to first load a stamp index related record from the store.
   145  // If the record is not found, it will try to create and save a new record and
   146  // return it.
   147  func LoadOrStore(
   148  	s storage.IndexStore,
   149  	scope string,
   150  	chunk swarm.Chunk,
   151  ) (item *Item, loaded bool, err error) {
   152  	item, err = Load(s, scope, chunk.Stamp())
   153  	if err != nil {
   154  		if errors.Is(err, storage.ErrNotFound) {
   155  			stampHash, err := chunk.Stamp().Hash()
   156  			if err != nil {
   157  				return nil, false, err
   158  			}
   159  			return &Item{
   160  				scope:          []byte(scope),
   161  				BatchID:        chunk.Stamp().BatchID(),
   162  				StampIndex:     chunk.Stamp().Index(),
   163  				StampTimestamp: chunk.Stamp().Timestamp(),
   164  				ChunkAddress:   chunk.Address(),
   165  				StampHash:      stampHash,
   166  			}, false, Store(s, scope, chunk)
   167  		}
   168  		return nil, false, err
   169  	}
   170  	return item, true, nil
   171  }
   172  
   173  // Load returns stamp index record related to the given scope and stamp.
   174  func Load(s storage.Reader, scope string, stamp swarm.Stamp) (*Item, error) {
   175  	item := &Item{
   176  		scope:      []byte(scope),
   177  		BatchID:    stamp.BatchID(),
   178  		StampIndex: stamp.Index(),
   179  	}
   180  	err := s.Get(item)
   181  	if err != nil {
   182  		return nil, fmt.Errorf("failed to get stampindex.Item %s: %w", item, err)
   183  	}
   184  	return item, nil
   185  }
   186  
   187  // Store creates new or updated an existing stamp index
   188  // record related to the given scope and chunk.
   189  func Store(s storage.IndexStore, scope string, chunk swarm.Chunk) error {
   190  	stampHash, err := chunk.Stamp().Hash()
   191  	if err != nil {
   192  		return err
   193  	}
   194  	item := &Item{
   195  		scope:          []byte(scope),
   196  		BatchID:        chunk.Stamp().BatchID(),
   197  		StampIndex:     chunk.Stamp().Index(),
   198  		StampTimestamp: chunk.Stamp().Timestamp(),
   199  		ChunkAddress:   chunk.Address(),
   200  		StampHash:      stampHash,
   201  	}
   202  	if err := s.Put(item); err != nil {
   203  		return fmt.Errorf("failed to put stampindex.Item %s: %w", item, err)
   204  	}
   205  	return nil
   206  }
   207  
   208  // Delete removes the related stamp index record from the storage.
   209  func Delete(s storage.Writer, scope string, stamp swarm.Stamp) error {
   210  	item := &Item{
   211  		scope:      []byte(scope),
   212  		BatchID:    stamp.BatchID(),
   213  		StampIndex: stamp.Index(),
   214  	}
   215  	if err := s.Delete(item); err != nil {
   216  		return fmt.Errorf("failed to delete stampindex.Item %s: %w", item, err)
   217  	}
   218  	return nil
   219  }