github.com/ethersphere/bee/v2@v2.2.0/pkg/postage/stampissuer.go (about) 1 // Copyright 2020 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 postage 6 7 import ( 8 "encoding/binary" 9 "errors" 10 "fmt" 11 "math/big" 12 "path" 13 "sync" 14 "time" 15 16 storage "github.com/ethersphere/bee/v2/pkg/storage" 17 "github.com/ethersphere/bee/v2/pkg/swarm" 18 "github.com/vmihailenco/msgpack/v5" 19 ) 20 21 var ( 22 // errStampItemMarshalBatchIDInvalid is returned when trying to 23 // marshal a stampItem with invalid batchID. 24 errStampItemMarshalBatchIDInvalid = errors.New("marshal postage.stampItem: batchID is invalid") 25 // errStampItemMarshalChunkAddressInvalid is returned when trying 26 // to marshal a stampItem with invalid chunkAddress. 27 errStampItemMarshalChunkAddressInvalid = errors.New("marshal postage.stampItem: chunkAddress 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 postage.stampItem: invalid size") 32 ) 33 34 const stampItemSize = swarm.HashSize + swarm.HashSize + swarm.StampIndexSize + swarm.StampTimestampSize 35 36 type StampItem struct { 37 // Keys. 38 BatchID []byte 39 chunkAddress swarm.Address 40 41 // Values. 42 BatchIndex []byte 43 BatchTimestamp []byte 44 } 45 46 // ID implements the storage.Item interface. 47 func (si StampItem) ID() string { 48 return fmt.Sprintf("%s/%s", string(si.BatchID), si.chunkAddress.String()) 49 } 50 51 // Namespace implements the storage.Item interface. 52 func (si StampItem) Namespace() string { 53 return "stampItem" 54 } 55 56 // Marshal implements the storage.Item interface. 57 func (si StampItem) Marshal() ([]byte, error) { 58 switch { 59 case len(si.BatchID) != swarm.HashSize: 60 return nil, errStampItemMarshalBatchIDInvalid 61 case len(si.chunkAddress.Bytes()) != swarm.HashSize: 62 return nil, errStampItemMarshalChunkAddressInvalid 63 } 64 65 buf := make([]byte, stampItemSize+1) 66 67 l := 0 68 copy(buf[l:l+swarm.HashSize], si.BatchID) 69 l += swarm.HashSize 70 copy(buf[l:l+swarm.HashSize], si.chunkAddress.Bytes()) 71 l += swarm.HashSize 72 copy(buf[l:l+swarm.StampIndexSize], si.BatchIndex) 73 l += swarm.StampIndexSize 74 copy(buf[l:l+swarm.StampTimestampSize], si.BatchTimestamp) 75 76 return buf, nil 77 } 78 79 // Unmarshal implements the storage.Item interface. 80 func (si *StampItem) Unmarshal(bytes []byte) error { 81 if len(bytes) != stampItemSize+1 { 82 return errStampItemUnmarshalInvalidSize 83 } 84 85 ni := new(StampItem) 86 87 l := 0 88 ni.BatchID = append(make([]byte, 0, swarm.HashSize), bytes[l:l+swarm.HashSize]...) 89 l += swarm.HashSize 90 ni.chunkAddress = swarm.NewAddress(bytes[l : l+swarm.HashSize]) 91 l += swarm.HashSize 92 ni.BatchIndex = append(make([]byte, 0, swarm.StampIndexSize), bytes[l:l+swarm.StampIndexSize]...) 93 l += swarm.StampIndexSize 94 ni.BatchTimestamp = append(make([]byte, 0, swarm.StampTimestampSize), bytes[l:l+swarm.StampTimestampSize]...) 95 96 *si = *ni 97 return nil 98 } 99 100 // Clone implements the storage.Item interface. 101 func (si *StampItem) Clone() storage.Item { 102 if si == nil { 103 return nil 104 } 105 return &StampItem{ 106 BatchID: append([]byte(nil), si.BatchID...), 107 chunkAddress: si.chunkAddress.Clone(), 108 BatchIndex: append([]byte(nil), si.BatchIndex...), 109 BatchTimestamp: append([]byte(nil), si.BatchTimestamp...), 110 } 111 } 112 113 // String implements the fmt.Stringer interface. 114 func (si StampItem) String() string { 115 return path.Join(si.Namespace(), si.ID()) 116 } 117 118 // stampIssuerData groups related StampIssuer data. 119 // The data are factored out in order to make 120 // serialization/deserialization easier and at the same 121 // time not to export the fields outside of the package. 122 type stampIssuerData struct { 123 Label string `msgpack:"label"` // Label to identify the batch period/importance. 124 KeyID string `msgpack:"keyID"` // Owner identity. 125 BatchID []byte `msgpack:"batchID"` // The batch stamps are issued from. 126 BatchAmount *big.Int `msgpack:"batchAmount"` // Amount paid for the batch. 127 BatchDepth uint8 `msgpack:"batchDepth"` // Batch depth: batch size = 2^{depth}. 128 BucketDepth uint8 `msgpack:"bucketDepth"` // Bucket depth: the depth of collision Buckets uniformity. 129 Buckets []uint32 `msgpack:"buckets"` // Collision Buckets: counts per neighbourhoods (limited to 2^{batchdepth-bucketdepth}). 130 MaxBucketCount uint32 `msgpack:"maxBucketCount"` // the count of the fullest bucket 131 BlockNumber uint64 `msgpack:"blockNumber"` // BlockNumber when this batch was created 132 ImmutableFlag bool `msgpack:"immutableFlag"` // Specifies immutability of the created batch. 133 } 134 135 // Clone returns a deep copy of the stampIssuerData. 136 func (s stampIssuerData) Clone() stampIssuerData { 137 return stampIssuerData{ 138 Label: s.Label, 139 KeyID: s.KeyID, 140 BatchID: append([]byte(nil), s.BatchID...), 141 BatchAmount: new(big.Int).Set(s.BatchAmount), 142 BatchDepth: s.BatchDepth, 143 BucketDepth: s.BucketDepth, 144 Buckets: append([]uint32(nil), s.Buckets...), 145 BlockNumber: s.BlockNumber, 146 ImmutableFlag: s.ImmutableFlag, 147 } 148 } 149 150 // StampIssuer is a local extension of a batch issuing stamps for uploads. 151 // A StampIssuer instance extends a batch with bucket collision tracking 152 // embedded in multiple Stampers, can be used concurrently. 153 type StampIssuer struct { 154 data stampIssuerData 155 mtx sync.Mutex 156 } 157 158 // NewStampIssuer constructs a StampIssuer as an extension of a batch for local 159 // upload. 160 // 161 // BucketDepth must always be smaller than batchDepth otherwise increment() panics. 162 func NewStampIssuer(label, keyID string, batchID []byte, batchAmount *big.Int, batchDepth, bucketDepth uint8, blockNumber uint64, immutableFlag bool) *StampIssuer { 163 return &StampIssuer{ 164 data: stampIssuerData{ 165 Label: label, 166 KeyID: keyID, 167 BatchID: batchID, 168 BatchAmount: batchAmount, 169 BatchDepth: batchDepth, 170 BucketDepth: bucketDepth, 171 Buckets: make([]uint32, 1<<bucketDepth), 172 BlockNumber: blockNumber, 173 ImmutableFlag: immutableFlag, 174 }, 175 } 176 } 177 178 // increment increments the count in the correct collision 179 // bucket for a newly stamped chunk with given addr address. 180 // Must be mutex locked before usage. 181 func (si *StampIssuer) increment(addr swarm.Address) (batchIndex []byte, batchTimestamp []byte, err error) { 182 bIdx := toBucket(si.BucketDepth(), addr) 183 bCnt := si.data.Buckets[bIdx] 184 185 if bCnt == si.BucketUpperBound() { 186 if si.ImmutableFlag() { 187 return nil, nil, ErrBucketFull 188 } 189 190 bCnt = 0 191 si.data.Buckets[bIdx] = 0 192 } 193 194 si.data.Buckets[bIdx]++ 195 if si.data.Buckets[bIdx] > si.data.MaxBucketCount { 196 si.data.MaxBucketCount = si.data.Buckets[bIdx] 197 } 198 199 return indexToBytes(bIdx, bCnt), unixTime(), nil 200 } 201 202 // Label returns the label of the issuer. 203 func (si *StampIssuer) Label() string { 204 return si.data.Label 205 } 206 207 // MarshalBinary implements the encoding.BinaryMarshaler interface. 208 func (si *StampIssuer) MarshalBinary() ([]byte, error) { 209 return msgpack.Marshal(si.data) 210 } 211 212 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. 213 func (si *StampIssuer) UnmarshalBinary(data []byte) error { 214 return msgpack.Unmarshal(data, &si.data) 215 } 216 217 // Utilization returns the batch utilization in the form of 218 // an integer between 0 and 4294967295. Batch fullness can be 219 // calculated with: max_bucket_value / 2 ^ (batch_depth - bucket_depth) 220 func (si *StampIssuer) Utilization() uint32 { 221 return si.data.MaxBucketCount 222 } 223 224 // ID returns the BatchID for this batch. 225 func (si *StampIssuer) ID() []byte { 226 id := make([]byte, len(si.data.BatchID)) 227 copy(id, si.data.BatchID) 228 return id 229 } 230 231 // Depth represent issued batch depth. 232 func (si *StampIssuer) Depth() uint8 { 233 return si.data.BatchDepth 234 } 235 236 // Amount represent issued batch amount paid. 237 func (si *StampIssuer) Amount() *big.Int { 238 return si.data.BatchAmount 239 } 240 241 // BucketDepth the depth of collision Buckets uniformity. 242 func (si *StampIssuer) BucketDepth() uint8 { 243 return si.data.BucketDepth 244 } 245 246 // BucketUpperBound returns the maximum number of collisions 247 // possible in a bucket given the batch's depth and bucket 248 // depth. 249 func (si *StampIssuer) BucketUpperBound() uint32 { 250 return 1 << (si.Depth() - si.BucketDepth()) 251 } 252 253 // BlockNumber when this batch was created. 254 func (si *StampIssuer) BlockNumber() uint64 { 255 return si.data.BlockNumber 256 } 257 258 // ImmutableFlag immutability of the created batch. 259 func (si *StampIssuer) ImmutableFlag() bool { 260 return si.data.ImmutableFlag 261 } 262 263 func (si *StampIssuer) Buckets() []uint32 { 264 si.mtx.Lock() 265 defer si.mtx.Unlock() 266 b := make([]uint32, len(si.data.Buckets)) 267 copy(b, si.data.Buckets) 268 return b 269 } 270 271 // StampIssuerItem is a storage.Item implementation for StampIssuer. 272 type StampIssuerItem struct { 273 Issuer *StampIssuer 274 } 275 276 // NewStampIssuerItem creates a new StampIssuerItem. 277 func NewStampIssuerItem(ID []byte) *StampIssuerItem { 278 return &StampIssuerItem{ 279 Issuer: &StampIssuer{ 280 data: stampIssuerData{ 281 BatchID: ID, 282 }, 283 }, 284 } 285 } 286 287 // ID is the batch ID. 288 func (s *StampIssuerItem) ID() string { 289 return string(s.Issuer.ID()) 290 } 291 292 // Namespace returns the storage namespace for a stampIssuer. 293 func (s *StampIssuerItem) Namespace() string { 294 return "StampIssuerItem" 295 } 296 297 // Marshal marshals the StampIssuerItem into a byte slice. 298 func (s *StampIssuerItem) Marshal() ([]byte, error) { 299 return s.Issuer.MarshalBinary() 300 } 301 302 // Unmarshal unmarshals a byte slice into a StampIssuerItem. 303 func (s *StampIssuerItem) Unmarshal(bytes []byte) error { 304 issuer := new(StampIssuer) 305 err := issuer.UnmarshalBinary(bytes) 306 if err != nil { 307 return err 308 } 309 s.Issuer = issuer 310 return nil 311 } 312 313 // Clone returns a clone of StampIssuerItem. 314 func (s *StampIssuerItem) Clone() storage.Item { 315 if s == nil { 316 return nil 317 } 318 return &StampIssuerItem{ 319 Issuer: &StampIssuer{ 320 data: s.Issuer.data.Clone(), 321 }, 322 } 323 } 324 325 // String returns the string representation of a StampIssuerItem. 326 func (s StampIssuerItem) String() string { 327 return path.Join(s.Namespace(), s.ID()) 328 } 329 330 var _ storage.Item = (*StampIssuerItem)(nil) 331 332 // toBucket calculates the index of the collision bucket for a swarm address 333 // bucket index := collision bucket depth number of bits as bigendian uint32 334 func toBucket(depth uint8, addr swarm.Address) uint32 { 335 return binary.BigEndian.Uint32(addr.Bytes()[:4]) >> (32 - depth) 336 } 337 338 // indexToBytes creates an uint64 index from 339 // - bucket index (neighbourhood index, uint32 <2^depth, bytes 2-4) 340 // - and the within-bucket index (uint32 <2^(batchdepth-bucketdepth), bytes 5-8) 341 func indexToBytes(bucket, index uint32) []byte { 342 buf := make([]byte, IndexSize) 343 binary.BigEndian.PutUint32(buf, bucket) 344 binary.BigEndian.PutUint32(buf[4:], index) 345 return buf 346 } 347 348 // BucketIndexFromBytes returns bucket index and within-bucket index from supplied bytes. 349 func BucketIndexFromBytes(buf []byte) (bucket, index uint32) { 350 index64 := IndexFromBytes(buf) 351 return uint32(index64 >> 32), uint32(index64) 352 } 353 354 // IndexFromBytes returns uint64 value from supplied bytes 355 func IndexFromBytes(buf []byte) uint64 { 356 return binary.BigEndian.Uint64(buf) 357 } 358 359 func unixTime() []byte { 360 buf := make([]byte, 8) 361 binary.BigEndian.PutUint64(buf, uint64(time.Now().UnixNano())) 362 return buf 363 } 364 365 // TimestampFromBytes returns uint64 value from supplied bytes 366 func TimestampFromBytes(buf []byte) uint64 { 367 return binary.BigEndian.Uint64(buf) 368 }