github.com/ethersphere/bee/v2@v2.2.0/pkg/cac/cac.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 cac 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "fmt" 11 12 "github.com/ethersphere/bee/v2/pkg/bmtpool" 13 "github.com/ethersphere/bee/v2/pkg/swarm" 14 ) 15 16 var ( 17 ErrChunkSpanShort = fmt.Errorf("chunk span must have exactly length of %d", swarm.SpanSize) 18 ErrChunkDataLarge = fmt.Errorf("chunk data exceeds maximum allowed length") 19 ) 20 21 // New creates a new content address chunk by initializing a span and appending the data to it. 22 func New(data []byte) (swarm.Chunk, error) { 23 dataLength := len(data) 24 25 if err := validateDataLength(dataLength); err != nil { 26 return nil, err 27 } 28 29 span := make([]byte, swarm.SpanSize) 30 binary.LittleEndian.PutUint64(span, uint64(dataLength)) 31 32 return newWithSpan(data, span) 33 } 34 35 // NewWithDataSpan creates a new chunk assuming that the span precedes the actual data. 36 func NewWithDataSpan(data []byte) (swarm.Chunk, error) { 37 dataLength := len(data) 38 39 if err := validateDataLength(dataLength - swarm.SpanSize); err != nil { 40 return nil, err 41 } 42 43 return newWithSpan(data[swarm.SpanSize:], data[:swarm.SpanSize]) 44 } 45 46 // validateDataLength validates if data length (without span) is correct. 47 func validateDataLength(dataLength int) error { 48 if dataLength < 0 { // dataLength could be negative when span size is subtracted 49 spanLength := swarm.SpanSize + dataLength 50 return fmt.Errorf("invalid CAC span length %d: %w", spanLength, ErrChunkSpanShort) 51 } 52 if dataLength > swarm.ChunkSize { 53 return fmt.Errorf("invalid CAC data length %d: %w", dataLength, ErrChunkDataLarge) 54 } 55 return nil 56 } 57 58 // newWithSpan creates a new chunk prepending the given span to the data. 59 func newWithSpan(data, span []byte) (swarm.Chunk, error) { 60 hash, err := DoHash(data, span) 61 if err != nil { 62 return nil, err 63 } 64 65 cacData := make([]byte, len(data)+len(span)) 66 copy(cacData, span) 67 copy(cacData[swarm.SpanSize:], data) 68 69 return swarm.NewChunk(swarm.NewAddress(hash), cacData), nil 70 } 71 72 // Valid checks whether the given chunk is a valid content-addressed chunk. 73 func Valid(c swarm.Chunk) bool { 74 data := c.Data() 75 76 if validateDataLength(len(data)-swarm.SpanSize) != nil { 77 return false 78 } 79 80 hash, _ := DoHash(data[swarm.SpanSize:], data[:swarm.SpanSize]) 81 82 return bytes.Equal(hash, c.Address().Bytes()) 83 } 84 85 func DoHash(data, span []byte) ([]byte, error) { 86 hasher := bmtpool.Get() 87 defer bmtpool.Put(hasher) 88 89 hasher.SetHeader(span) 90 if _, err := hasher.Write(data); err != nil { 91 return nil, err 92 } 93 94 return hasher.Hash(nil) 95 }