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 }